Skip to content

Commit

Permalink
feat(axidraw): arbitrary unit support, measure draw time
Browse files Browse the repository at this point in the history
- add AxiDrawOpts.unitsPerInch to support any worldspace units
- remove paperSize opt
- add command constants for better mem use
- update AxiDraw.draw() to measure & return time taken
- add/update doc strings
  • Loading branch information
postspectacular committed Dec 6, 2022
1 parent cc43e84 commit 32e3212
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 24 deletions.
45 changes: 37 additions & 8 deletions packages/axidraw/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ export type MotorCommand = ["on" | "off"];
/** Pen config, min/down position, max/up position (in %) */
export type PenConfigCommand = ["pen", number?, number?];

/** Pen up/down, optional delay (in ms) */
/**
* Pen up/down, optional delay (in ms), if omitted values used from
* {@link AxiDrawOpts}.
*/
export type PenUpDownCommand = ["u" | "d", number?];

/** Move to abs pos (in mm), optional speed factor (1 = normal, 0.5 = half speed) */
/**
* Move to abs pos (in worldspace coords, default mm), optional speed factor
* (default: 1)
*/
export type MoveXYCommand = ["m", ReadonlyVec, number?];

/** Explicit delay (in ms) */
Expand All @@ -37,11 +43,12 @@ export type DrawCommand =

export interface AxiDrawOpts {
/**
* Bounding rect of document in mm
* Conversion factor from geometry worldspace units to inches.
* Default units are millimeters.
*
* @defaultValue [297, 210]
* @defaultValue 25.4
*/
pageSize: [number, number];
unitsPerInch: number;
/**
* Hardware resolution (steps / inch)
*
Expand All @@ -51,7 +58,7 @@ export interface AxiDrawOpts {
/**
* Steps per second
*
* @defaultValue 1500
* @defaultValue 4000
*/
speed: number;
/**
Expand All @@ -69,29 +76,51 @@ export interface AxiDrawOpts {
/**
* Delay after pen up
*
* @defaultValue 0
* @defaultValue 300
*/
delayUp: number;
/**
* Delay after pen down
*
* @defaultValue 0
* @defaultValue 300
*/
delayDown: number;
/**
* Time in ms to subtract from actual delay time until next command
*
* @defaultValue 0
*/
preDelay: number;
/**
* Sequence for `start` {@link DrawCommand}
*
* @defaultValue `[ON, PEN, UP]`
*/
start: DrawCommand[];
/**
* Sequence for `end` {@link DrawCommand}
*
* @defaultValue `[UP, HOME, OFF]`
*/
stop: DrawCommand[];
/**
* Logger instance
*/
logger: ILogger;
}

export const START: StartCommand = ["start"];

export const STOP: StopCommand = ["stop"];

export const HOME: HomeCommand = ["home"];

export const PEN: PenConfigCommand = ["pen"];

export const UP: PenUpDownCommand = ["u"];

export const DOWN: PenUpDownCommand = ["d"];

export const ON: MotorCommand = ["on"];

export const OFF: MotorCommand = ["off"];
59 changes: 43 additions & 16 deletions packages/axidraw/src/axidraw.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
import type { Fn0 } from "@thi.ng/api";
import { delayed } from "@thi.ng/compose";
import { illegalState } from "@thi.ng/errors";
import { assert, unsupported } from "@thi.ng/errors";
import { ConsoleLogger } from "@thi.ng/logger";
import { abs2, mulN2, ReadonlyVec, set2, sub2, Vec } from "@thi.ng/vectors";
import { SerialPort } from "serialport";
import type { AxiDrawOpts, DrawCommand } from "./api.js";
import {
AxiDrawOpts,
DOWN,
DrawCommand,
HOME,
OFF,
ON,
PEN,
UP,
} from "./api.js";

export const DEFAULT_OPTS: AxiDrawOpts = {
logger: new ConsoleLogger("axidraw"),
pageSize: [297, 210],
unitsPerInch: 25.4,
stepsPerInch: 2032,
speed: 4000,
up: 60,
down: 30,
delayUp: 300,
delayDown: 300,
preDelay: 0,
start: [["on"], ["pen"], ["u"]],
stop: [["u"], ["home"], ["off"]],
start: [ON, PEN, UP],
stop: [UP, HOME, OFF],
};

export class AxiDraw {
serial!: SerialPort;
opts: AxiDrawOpts;
isConnected = false;
pos: Vec = [0, 0];

constructor(opts: Partial<AxiDrawOpts> = {}) {
this.opts = { ...DEFAULT_OPTS, ...opts };
}

/**
* Async function. Attempts to connect to the drawing machine via given
* (partial) serial port path/name, returns true if successful.
*
* @param path
*/
async connect(path: RegExp = /^\/dev\/tty\.usbmodem/) {
for (let port of await SerialPort.list()) {
if (path.test(port.path)) {
Expand All @@ -37,17 +53,20 @@ export class AxiDraw {
path: port.path,
baudRate: 38400,
});
this.isConnected = true;
return true;
}
}
return false;
}

/**
* Converts sequence of {@link DrawCommand}s into actual EBB commands and
* sends them via configured serial port to the AxiDraw. The optional
* `cancel` predicate is checked prior to each individual command and
* processing is stopped if that function returns a truthy result.
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
* commands and sends them via configured serial port to the AxiDraw. The
* optional `cancel` predicate is checked prior to each individual command
* and processing is stopped if that function returns a truthy result.
*
* Returns number of milliseconds taken for drawing.
*
* @remarks
* This function is async and if using `await` will only return once all
Expand All @@ -70,11 +89,16 @@ export class AxiDraw {
* @param cancel
*/
async draw(commands: Iterable<DrawCommand>, cancel?: Fn0<boolean>) {
assert(
this.isConnected,
"AxiDraw not yet connected, need to call .connect() first"
);
let t0 = Date.now();
if (!cancel) cancel = () => false;
const { opts: config, pos } = this;
const { stepsPerInch, speed, preDelay } = config;
// scale factor: mm -> motor steps
const scale = stepsPerInch / 25.4;
const { stepsPerInch, unitsPerInch, speed, preDelay } = config;
// scale factor: worldspace units -> motor steps
const scale = stepsPerInch / unitsPerInch;
let targetPos: Vec = [0, 0];
let delta: Vec = [0, 0];
for (let $cmd of commands) {
Expand All @@ -98,6 +122,8 @@ export class AxiDraw {
case "pen":
{
let val = a !== undefined ? a : config.down;
// unit ref:
// https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
this.send(`SC,5,${(7500 + 175 * val) | 0}\r`);
val = b !== undefined ? b : config.up;
this.send(`SC,4,${(7500 + 175 * val) | 0}\r`);
Expand Down Expand Up @@ -129,14 +155,15 @@ export class AxiDraw {
}
break;
default:
illegalState(`unknown command: ${$cmd}`);
unsupported(`unknown command: ${$cmd}`);
}
if (wait > 0) {
wait -= preDelay;
wait = Math.max(0, wait - preDelay);
config.logger.debug(`waiting ${wait}ms...`);
await delayed(0, Math.max(0, wait));
await delayed(0, wait);
}
}
return Date.now() - t0;
}

/**
Expand All @@ -156,7 +183,7 @@ export class AxiDraw {
const commands = pts.map((p) => <DrawCommand>["m", p, speed]);
return onlyGeo
? commands
: [["u"], commands[0], ["d"], ...commands.slice(1), ["u"]];
: [UP, commands[0], DOWN, ...commands.slice(1), UP];
}

protected send(msg: string) {
Expand Down

0 comments on commit 32e3212

Please sign in to comment.