ok fix?
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Wall Plotter — zigzag</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #1c1c20;
|
||||
color: #ddd;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
h1 {
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
right: 0.75rem;
|
||||
z-index: 20;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
opacity: 0.85;
|
||||
pointer-events: none;
|
||||
text-align: right;
|
||||
}
|
||||
#sketch-host {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
#sketch-host canvas {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
||||
<script src="../../lib/WallPlotter.js?v=2.2.5"></script>
|
||||
<script src="sketch.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Wall Plotter — zigzag</h1>
|
||||
<main id="sketch-host"></main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,109 @@
|
||||
let wp;
|
||||
|
||||
let area = 320;
|
||||
|
||||
function setup() {
|
||||
const canvas = createCanvas(windowWidth, windowHeight);
|
||||
canvas.parent('sketch-host');
|
||||
|
||||
wp = new WallPlotter({
|
||||
penUpGcode: 'M280 P0 S120',
|
||||
penDownGcode: 'M280 P0 S30',
|
||||
penUpDelayMs: 250,
|
||||
penDownDelayMs: 150,
|
||||
});
|
||||
wp.setSpeed(10000, 10000);
|
||||
wp.setTimingMultiplier(1); // tune if machine is slower than speed estimate, the higher the slower
|
||||
wp.enableKeyInputs();
|
||||
wp.enablePlotButtons();
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(28, 28, 32);
|
||||
wp.showInfos();
|
||||
}
|
||||
|
||||
function windowResized() {
|
||||
resizeCanvas(windowWidth, windowHeight);
|
||||
}
|
||||
|
||||
async function plot() {
|
||||
await drawArt(false);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
await drawArt(true);
|
||||
}
|
||||
|
||||
async function drawArt(testOnly) {
|
||||
wp.startPath();
|
||||
|
||||
|
||||
wp.move(-area, -area);
|
||||
//wp.wait(1000 * 10);
|
||||
|
||||
let step = area / 40;
|
||||
|
||||
let points = [];
|
||||
let last_points = [];
|
||||
let row = -1;
|
||||
|
||||
for (let y = -area; y <= area; y+=step){
|
||||
|
||||
// clear points
|
||||
if(points.length>0){
|
||||
last_points = deepCopy(points);
|
||||
points = [];
|
||||
}
|
||||
|
||||
let px = -area;
|
||||
let py = y+random(10)
|
||||
if (row==1){
|
||||
py = last_points[0].y;
|
||||
}
|
||||
|
||||
|
||||
wp.move(px, py);
|
||||
points.push(createVector(px,py));
|
||||
|
||||
let index = 1;
|
||||
for (let x = -area+step; x <= area; x+=step){
|
||||
px = x;
|
||||
py = y+random(step);
|
||||
|
||||
if (row==1 && index%2==0){
|
||||
py = last_points[index].y
|
||||
}
|
||||
|
||||
if (row==1 && index%2!=0){
|
||||
py = last_points[index-1].y+random(step*3);
|
||||
}
|
||||
|
||||
if (last_points.length>0 && row==-1 && index%2!=0){
|
||||
py = last_points[index].y
|
||||
}
|
||||
|
||||
if (last_points.length>0 && row==-1 && index%2==0){
|
||||
py = last_points[index-1].y+random(step*1);
|
||||
}
|
||||
|
||||
wp.draw(px, py);
|
||||
points.push(createVector(px,py));
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
row = row*-1;
|
||||
|
||||
}
|
||||
await wp.runPath(testOnly);
|
||||
}
|
||||
|
||||
|
||||
function deepCopy(copy_array){
|
||||
let new_array = []
|
||||
for(let i=0; i<copy_array.length; i++){
|
||||
new_array.push(copy_array[i]);
|
||||
}
|
||||
return new_array;
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
||||
<script src="../../lib/WallPlotter.js?v=2.0.2"></script>
|
||||
<script src="../../lib/WallPlotter.js?v=2.2.5"></script>
|
||||
<script src="sketch.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -47,6 +47,7 @@ Keyboard controls. Call from `setup()`. No arguments.
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `C` | Connect / disconnect |
|
||||
| `T` | Run Test (preview) |
|
||||
| `H` | Set home here (`G92 X0 Y0`) — required before Plot |
|
||||
| `0` | Travel to `(0, 0)` |
|
||||
| `P` | Toggle pen |
|
||||
|
||||
+89
-36
@@ -18,7 +18,7 @@ class WallPlotter {
|
||||
// Static constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static VERSION = '2.2.3';
|
||||
static VERSION = '2.2.5';
|
||||
static MIN_FEED_MM_MIN = 1;
|
||||
static MAX_FEED_MM_MIN = 10000;
|
||||
/** Offline overlay size when not connected (mm) — 150×150 cm centred on origin */
|
||||
@@ -165,7 +165,7 @@ class WallPlotter {
|
||||
// Public API — setup / UI
|
||||
// ===========================================================================
|
||||
|
||||
/** Enable keyboard jog, home, pen toggle, connect (C), stop (S). Call from setup(). */
|
||||
/** Enable keyboard jog, home, pen toggle, connect (C), test (T), stop (S). Call from setup(). */
|
||||
enableKeyInputs() {
|
||||
if (this._keysEnabled) return;
|
||||
this._keysEnabled = true;
|
||||
@@ -588,13 +588,24 @@ class WallPlotter {
|
||||
|
||||
const waitOk = options.waitOk !== false;
|
||||
const okPromise = waitOk
|
||||
? this._waitForOk(options.timeoutMs ?? WallPlotter.OK_TIMEOUT_MS)
|
||||
? this._waitForOk(options.timeoutMs ?? WallPlotter.OK_TIMEOUT_MS, {
|
||||
command,
|
||||
waitLogAfterMs: options.waitLogAfterMs,
|
||||
})
|
||||
: null;
|
||||
await this.writer.write(new TextEncoder().encode(command + '\n'));
|
||||
this.onLog('→ ' + command);
|
||||
if (!okPromise) return;
|
||||
try {
|
||||
await okPromise;
|
||||
const written = this.writer.write(
|
||||
new TextEncoder().encode(command + '\n')
|
||||
);
|
||||
if (okPromise) {
|
||||
await Promise.all([
|
||||
written.then(() => this.onLog('→ ' + command)),
|
||||
okPromise,
|
||||
]);
|
||||
} else {
|
||||
await written;
|
||||
this.onLog('→ ' + command);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.name === 'AbortError') throw err;
|
||||
this._okIgnoreUntil = performance.now() + 400;
|
||||
@@ -818,6 +829,15 @@ class WallPlotter {
|
||||
} finally {
|
||||
this.feedRateTravel = savedFeedTravel;
|
||||
this.feedRateDraw = savedFeedDraw;
|
||||
if (!testOnly && this.connected) {
|
||||
try {
|
||||
await this.penUp();
|
||||
} catch (_) {
|
||||
/* port may be gone */
|
||||
}
|
||||
} else {
|
||||
this.isPenDown = false;
|
||||
}
|
||||
this.isPlotting = false;
|
||||
this.isPaused = false;
|
||||
this._pauseStartedAt = 0;
|
||||
@@ -898,7 +918,7 @@ class WallPlotter {
|
||||
}
|
||||
|
||||
async _animatePosition(fromX, fromY, cmd, duration) {
|
||||
this.isPenDown = cmd.type === 'draw';
|
||||
if (this._plotTestOnly) this.isPenDown = cmd.type === 'draw';
|
||||
const waitMs = Math.max(duration, 1);
|
||||
let elapsed = 0;
|
||||
let last = performance.now();
|
||||
@@ -976,11 +996,11 @@ class WallPlotter {
|
||||
|
||||
async _sendSegmentAndWait(cmd) {
|
||||
await this._sendSegment(cmd);
|
||||
const motionMs = Math.round(this._lastSegmentMs);
|
||||
await this.send('M400', {
|
||||
timeoutMs: Math.max(
|
||||
WallPlotter.OK_TIMEOUT_MS,
|
||||
Math.round(this._lastSegmentMs) + 20000
|
||||
),
|
||||
timeoutMs: Math.max(WallPlotter.OK_TIMEOUT_MS, motionMs + 20000),
|
||||
// M400 is supposed to last as long as the move. Don't call that "stuck".
|
||||
waitLogAfterMs: Math.max(WallPlotter.OK_WAIT_LOG_MS, motionMs + 1500),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -988,9 +1008,15 @@ class WallPlotter {
|
||||
// Internals — serial I/O
|
||||
// ===========================================================================
|
||||
|
||||
_waitForOk(timeoutMs) {
|
||||
_waitForOk(timeoutMs, options = {}) {
|
||||
const command = options.command || '';
|
||||
const waitLogAfterMs = options.waitLogAfterMs ?? WallPlotter.OK_WAIT_LOG_MS;
|
||||
const waitLabel = command ? ` (${command})` : '';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
let timer = null;
|
||||
let waitLogTimer = null;
|
||||
let waitLogInterval = null;
|
||||
|
||||
const finish = (fn, arg) => {
|
||||
@@ -1004,6 +1030,17 @@ class WallPlotter {
|
||||
fn(arg);
|
||||
};
|
||||
|
||||
const armTimer = () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
const err = new Error(
|
||||
`timeout waiting for Marlin ok${waitLabel} (${Math.round(timeoutMs / 1000)}s silent)`
|
||||
);
|
||||
err.name = 'TimeoutError';
|
||||
entry.reject(err);
|
||||
}, timeoutMs);
|
||||
};
|
||||
|
||||
const entry = {
|
||||
resolve: () => {
|
||||
if (this.statusText === 'Waiting for plotter…') {
|
||||
@@ -1015,20 +1052,17 @@ class WallPlotter {
|
||||
finish(resolve);
|
||||
},
|
||||
reject: (err) => finish(reject, err),
|
||||
bump: () => {
|
||||
if (!settled) armTimer();
|
||||
},
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
entry.reject(
|
||||
new Error(
|
||||
`timeout waiting for Marlin ok (${Math.round(timeoutMs / 1000)}s)`
|
||||
)
|
||||
);
|
||||
}, timeoutMs);
|
||||
armTimer();
|
||||
|
||||
const waitLogTimer = setTimeout(() => {
|
||||
waitLogTimer = setTimeout(() => {
|
||||
if (settled) return;
|
||||
const started = performance.now();
|
||||
this.onLog('… waiting for Marlin ok');
|
||||
this.onLog(`… waiting for Marlin ok${waitLabel}`);
|
||||
if (this.statusText !== 'Waiting for plotter…') {
|
||||
this._statusBeforeOkWait = this.statusText;
|
||||
}
|
||||
@@ -1036,9 +1070,9 @@ class WallPlotter {
|
||||
this._redrawSketch();
|
||||
waitLogInterval = setInterval(() => {
|
||||
const s = Math.round((performance.now() - started) / 1000);
|
||||
this.onLog(`… still waiting for Marlin ok (${s}s)`);
|
||||
this.onLog(`… still waiting for Marlin ok${waitLabel} (${s}s)`);
|
||||
}, WallPlotter.OK_WAIT_PING_MS);
|
||||
}, WallPlotter.OK_WAIT_LOG_MS);
|
||||
}, waitLogAfterMs);
|
||||
|
||||
this._okResolvers.push(entry);
|
||||
});
|
||||
@@ -1066,12 +1100,7 @@ class WallPlotter {
|
||||
const { value, done } = await plotter.reader.read();
|
||||
if (done) break;
|
||||
plotter._rxBuf += new TextDecoder().decode(value);
|
||||
const lines = plotter._rxBuf.split(/\r?\n/);
|
||||
plotter._rxBuf = lines.pop();
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed) plotter._handleResponse(trimmed);
|
||||
}
|
||||
plotter._drainRxBuffer();
|
||||
}
|
||||
} catch (err) {
|
||||
if (plotter.readLoopActive) {
|
||||
@@ -1090,9 +1119,28 @@ class WallPlotter {
|
||||
loop();
|
||||
}
|
||||
|
||||
_drainRxBuffer() {
|
||||
const lines = this._rxBuf.split(/\r?\n/);
|
||||
this._rxBuf = lines.pop();
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed) this._handleResponse(trimmed);
|
||||
}
|
||||
// USB-CDC sometimes delivers a complete "ok" without a trailing newline
|
||||
// until the next chunk. Don't leave that sitting in the buffer.
|
||||
const rest = this._rxBuf.trim();
|
||||
if (/^(echo:\s*)?ok\b/i.test(rest)) {
|
||||
this._handleResponse(rest);
|
||||
this._rxBuf = '';
|
||||
}
|
||||
}
|
||||
|
||||
_handleResponse(line) {
|
||||
if (/busy:\s*processing/i.test(line)) {
|
||||
this._machineBusy = true;
|
||||
for (const entry of this._okResolvers) {
|
||||
if (typeof entry.bump === 'function') entry.bump();
|
||||
}
|
||||
const now = performance.now();
|
||||
if (now - this._lastBusyLogAt > 4000) {
|
||||
this._lastBusyLogAt = now;
|
||||
@@ -1101,14 +1149,14 @@ class WallPlotter {
|
||||
return;
|
||||
}
|
||||
this.onLog('← ' + line);
|
||||
if (/^ok\b/i.test(line)) {
|
||||
if (/^(echo:\s*)?ok\b/i.test(line)) {
|
||||
this._machineBusy = false;
|
||||
if (performance.now() < this._okIgnoreUntil) {
|
||||
this.onLog('← ok ignored (stale)');
|
||||
return;
|
||||
}
|
||||
const next = this._okResolvers.shift();
|
||||
if (next) next.resolve();
|
||||
if (next) {
|
||||
next.resolve();
|
||||
} else if (performance.now() < this._okIgnoreUntil) {
|
||||
this.onLog('← ok ignored (stale, no waiter)');
|
||||
}
|
||||
}
|
||||
this._parseMachineBounds(line);
|
||||
if (line.startsWith('X:')) {
|
||||
@@ -1176,6 +1224,10 @@ class WallPlotter {
|
||||
await this.stopPlot();
|
||||
return false;
|
||||
}
|
||||
if (k === 't') {
|
||||
this._onTestButton();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this._keysEnabled || !this.connected) return;
|
||||
|
||||
@@ -1613,6 +1665,7 @@ class WallPlotter {
|
||||
'H set current pos as home (G92) — required first',
|
||||
'0 go to origin (0, 0)',
|
||||
'P toggle pen (test up/down angles)',
|
||||
'T run test (preview)',
|
||||
'S stop sending (in-flight stroke still finishes)',
|
||||
'C connect / disconnect',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user