Skip to content

Commit

Permalink
Add brushless hardware support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Dahan authored and alexrudd2 committed Oct 16, 2023
1 parent b4a2949 commit 3e89b85
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 193 deletions.
11 changes: 5 additions & 6 deletions src/__tests__/planning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import {Plan, plan, Device, AxidrawFast, XYMotion, PenMotion, defaultPlanOptions
import {Vec2} from '../vec';

describe("plan", () => {
const device = Device.Axidraw
const device = Device()
const positions = {
up: AxidrawFast.penUpPos,
down: AxidrawFast.penDownPos,
zero: device.penPctToPos(0)
down: AxidrawFast.penDownPos
}
it.skip("handles an empty input", () => {
expect(plan([], AxidrawFast)).toEqual(new Plan([]))
Expand All @@ -31,7 +30,7 @@ describe("plan", () => {
expect(xyMotions(p)).toEqual([
{from: {x: 0, y: 0}, to: {x: 10, y: 10}, penPos: 0},
{from: {x: 10, y: 10}, to: {x: 10, y: 10}, penPos: positions.down},
{from: {x: 10, y: 10}, to: {x: 0, y: 0}, penPos: positions.zero},
{from: {x: 10, y: 10}, to: {x: 0, y: 0}, penPos: positions.up},
]);
});

Expand All @@ -41,7 +40,7 @@ describe("plan", () => {
expect(xyMotions(p)).toEqual([
{from: {x: 0, y: 0}, to: {x: 10, y: 10}, penPos: 0},
{from: {x: 10, y: 10}, to: {x: 20, y: 10}, penPos: positions.down},
{from: {x: 20, y: 10}, to: {x: 0, y: 0}, penPos: positions.zero},
{from: {x: 20, y: 10}, to: {x: 0, y: 0}, penPos: positions.up},
]);
});

Expand All @@ -56,7 +55,7 @@ describe("plan", () => {
{from: {x: 10, y: 10}, to: {x: 20, y: 10}, penPos: positions.down},
{from: {x: 20, y: 10}, to: {x: 10, y: 20}, penPos: positions.up},
{from: {x: 10, y: 20}, to: {x: 20, y: 20}, penPos: positions.down},
{from: {x: 20, y: 20}, to: {x: 0, y: 0}, penPos: positions.zero},
{from: {x: 20, y: 20}, to: {x: 0, y: 0}, penPos: positions.up},
]);
});

Expand Down
86 changes: 40 additions & 46 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Vec2 } from "./vec";
import { formatDuration } from "./util";
import { Device, PlanOptions, defaultPlanOptions } from "./planning";
import { PaperSize } from "./paper-size";
import { Hardware } from "./ebb";

function parseSvg(svg: string) {
const window = new Window
Expand All @@ -16,49 +17,19 @@ function parseSvg(svg: string) {
}

export function cli(argv: string[]): void {
yargs.strict()
.option("device", {
alias: "d",
describe: "device to connect to",
type: "string"
yargs
.strict()
.option('hardware', {
describe: 'select hardware type',
choices: ['v3', 'brushless'] as const,
default: 'v3',
coerce: value => value as Hardware
})
.option('device', {
alias: 'd',
describe: 'device to connect to',
type: 'string'
})
.command('$0', 'run the saxi web server',
yargs => yargs
.option("port", {
alias: "p",
default: Number(process.env.PORT || 9080),
describe: "TCP port on which to listen",
type: "number"
})
.option("enable-cors", {
describe: "enable cross-origin resource sharing (CORS)",
type: "boolean"
})
.option("max-payload-size", {
describe: "maximum payload size to accept",
default: "200 mb",
type: "string"
})
.option("firmware-version", {
describe: "print the device's firmware version and exit",
type: "boolean"
}),
args => {
if (args["firmware-version"]) {
connectEBB(args.device).then(async (ebb) => {
if (!ebb) {
console.error(`No EBB connected`);
return process.exit(1);
}
const fwv = await ebb.firmwareVersion();
console.log(fwv);
await ebb.close();
});
} else {
startServer(args.port, args.device, args["enable-cors"], args["max-payload-size"]);
}
}
)
.command('plot <file>', 'plot an svg, then exit',
yargs => yargs
.positional("file", {
Expand Down Expand Up @@ -205,6 +176,7 @@ export function cli(argv: string[]): void {
const planOptions: PlanOptions = {
paperSize,
marginMm: args.margin,
hardware: args.hardware,

selectedGroupLayers: new Set([]), // TODO
selectedStrokeLayers: new Set([]), // TODO
Expand Down Expand Up @@ -234,7 +206,7 @@ export function cli(argv: string[]): void {
const p = replan(linesToVecs(lines), planOptions)
console.log(`${p.motions.length} motions, estimated duration: ${formatDuration(p.duration())}`)
console.log("connecting to plotter...")
const ebb = await connectEBB(args.device)
const ebb = await connectEBB(args.hardware, args.device)
if (!ebb) {
console.error("Couldn't connect to device!")
process.exit(1)
Expand All @@ -251,17 +223,39 @@ export function cli(argv: string[]): void {
.check(args => args.percent >= 0 && args.percent <= 100),
async args => {
console.log('connecting to plotter...')
const ebb = await connectEBB(args.device)
const ebb = await connectEBB(args.hardware, args.device)
if (!ebb) {
console.error("Couldn't connect to device!")
process.exit(1)
}
await ebb.setPenHeight(Device.Axidraw.penPctToPos(args.percent), 1000)
const device = Device(ebb.hardware)
await ebb.setPenHeight(device.penPctToPos(args.percent), 1000)

console.log(`moving to ${args.percent}%...`)
await ebb.close()
})
.parse(argv);
.command('$0', 'run the saxi web server',
args => args
.option('port', {
alias: 'p',
describe: 'TCP port on which to listen',
default: 9080,
type: 'number',
})
.option('enable-cors', {
describe: 'enable cross-origin resource sharing (CORS)',
default: false,
type: 'boolean',
})
.option('max-payload-size', {
describe: 'maximum payload size to accept',
default: '200mb',
}),
args => {
startServer(args.port, args.hardware, args.device, args['enable-cors'], args['max-payload-size'])
}
)
.parse(argv)
}

function linesToVecs(lines: any[]): Vec2[][] {
Expand Down
12 changes: 9 additions & 3 deletions src/ebb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ function modf(d: number): [number, number] {
return [fracPart, intPart];
}

export type Hardware = 'v3' | 'brushless'

export class EBB {
public port: SerialPort;
private commandQueue: Iterator<any, any, Buffer>[];
private writer: WritableStreamDefaultWriter<Uint8Array>;
private readableClosed: Promise<void>
public hardware: Hardware

private microsteppingMode = 0;

Expand All @@ -22,7 +25,8 @@ export class EBB {

private cachedFirmwareVersion: [number, number, number] | undefined = undefined;

public constructor(port: SerialPort) {
public constructor (port: SerialPort, hardware: Hardware = 'v3') {
this.hardware = hardware
this.port = port;
this.writer = this.port.writable.getWriter();
this.commandQueue = [];
Expand Down Expand Up @@ -155,8 +159,10 @@ export class EBB {
await this.command(`SR,${(timeout * 1000) | 0}${power != null ? `,${power ? 1 : 0}` : ''}`)
}

public setPenHeight(height: number, rate: number, delay = 0): Promise<void> {
return this.command(`S2,${height},4,${rate},${delay}`);
// https://evil-mad.github.io/EggBot/ebb.html#S2 General RC Servo Output
public async setPenHeight (height: number, rate: number, delay = 0): Promise<void> {
const output_pin = this.hardware === 'v3' ? 4 : 5
return await this.command(`S2,${height},${output_pin},${rate},${delay}`)
}

public lowlevelMove(
Expand Down
28 changes: 14 additions & 14 deletions src/massager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as Optimization from "optimize-paths";
import * as Planning from "./planning";
import {Device, Plan, PlanOptions} from "./planning";
import {Device, Plan, PlanOptions, plan} from "./planning";
import {dedupPoints, scaleToPaper, cropToMargins} from "./util";
import {Vec2, vmul, vrot} from "./vec";

Expand All @@ -12,6 +11,7 @@ const mmPerSvgUnit = mmPerInch / svgUnitsPerInch

export function replan(inPaths: Vec2[][], planOptions: PlanOptions): Plan {
let paths = inPaths;
const device = Device(planOptions.hardware)

// Rotate drawing around center of paper to handle plotting portrait drawings
// along y-axis of plotter
Expand Down Expand Up @@ -71,27 +71,27 @@ export function replan(inPaths: Vec2[][], planOptions: PlanOptions): Plan {
}

// Convert the paths to units of "steps".
paths = paths.map((ps) => ps.map((p) => vmul(p, Device.Axidraw.stepsPerMm)));
paths = paths.map((ps) => ps.map((p) => vmul(p, device.stepsPerMm)))

// And finally, motion planning.
console.time("planning pen motions");
const plan = Planning.plan(paths, {
penUpPos: Device.Axidraw.penPctToPos(planOptions.penUpHeight),
penDownPos: Device.Axidraw.penPctToPos(planOptions.penDownHeight),
console.time('planning pen motions')
const theplan = plan(paths, {
penUpPos: device.penPctToPos(planOptions.penUpHeight),
penDownPos: device.penPctToPos(planOptions.penDownHeight),
penDownProfile: {
acceleration: planOptions.penDownAcceleration * Device.Axidraw.stepsPerMm,
maximumVelocity: planOptions.penDownMaxVelocity * Device.Axidraw.stepsPerMm,
corneringFactor: planOptions.penDownCorneringFactor * Device.Axidraw.stepsPerMm,
acceleration: planOptions.penDownAcceleration * device.stepsPerMm,
maximumVelocity: planOptions.penDownMaxVelocity * device.stepsPerMm,
corneringFactor: planOptions.penDownCorneringFactor * device.stepsPerMm
},
penUpProfile: {
acceleration: planOptions.penUpAcceleration * Device.Axidraw.stepsPerMm,
maximumVelocity: planOptions.penUpMaxVelocity * Device.Axidraw.stepsPerMm,
corneringFactor: 0,
acceleration: planOptions.penUpAcceleration * device.stepsPerMm,
maximumVelocity: planOptions.penUpMaxVelocity * device.stepsPerMm,
corneringFactor: 0
},
penDropDuration: planOptions.penDropDuration,
penLiftDuration: planOptions.penLiftDuration,
});
console.timeEnd("planning pen motions");

return plan;
return theplan
}
Loading

0 comments on commit 3e89b85

Please sign in to comment.