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 committed Oct 4, 2023
1 parent bdf03b2 commit c6e58f7
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 204 deletions.
87 changes: 40 additions & 47 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,28 @@ import { Vec2 } from "./vec";
import { formatDuration } from "./util";
import { PlanOptions, defaultPlanOptions } from "./planning";
import { PaperSize } from "./paper-size";
import { Hardware } from "./ebb";

function parseSvg(svg: string) {
const window = new Window
function parseSvg (svg: string) {
const window = new Window()
window.document.documentElement.innerHTML = svg
return window.document.documentElement
}

export function cli(argv: string[]): void {
yargs.strict()
.option("device", {
alias: "d",
describe: "device to connect to",
type: "string"
export function cli (argv: string[]): void {
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 @@ -246,7 +218,28 @@ export function cli(argv: string[]): void {
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.hardware, args.device, args.port, args['enable-cors'], args['max-payload-size'])
}
)
.parse(argv)
}

function linesToVecs(lines: any[]): Vec2[][] {
Expand Down
18 changes: 12 additions & 6 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,10 +25,11 @@ export class EBB {

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

public constructor(port: SerialPort) {
this.port = port;
this.writer = this.port.writable.getWriter();
this.commandQueue = [];
public constructor (port: SerialPort, hardware: Hardware = 'v3') {
this.hardware = hardware
this.port = port
this.writer = this.port.writable.getWriter()
this.commandQueue = []
this.readableClosed = port.readable
.pipeThrough(new RegexParser({ regex: /[\r\n]+/ }))
.pipeTo(new WritableStream({
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
32 changes: 16 additions & 16 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 @@ -10,8 +9,9 @@ const svgUnitsPerInch = 96
const mmPerInch = 25.4
const mmPerSvgUnit = mmPerInch / svgUnitsPerInch

export function replan(inPaths: Vec2[][], planOptions: PlanOptions): Plan {
let paths = inPaths;
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 c6e58f7

Please sign in to comment.