From d3bab940b351a672d6b96e1c75bafbfa15cc9151 Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Mon, 17 Aug 2026 11:52:41 -0400 Subject: [PATCH] docs: user-facing guides to motion, laser drive and cooling Two pages aimed at someone who owns the machine rather than works on it, written for the documentation site. Nothing here is new behavior - it is the behavior the machine already has, explained where an owner can find it instead of spread across a kernel contract, a services contract and three driver headers. MOTION.md follows one thread: everything physical comes out of a single fixed-tick byte stream, so the page starts there - the byte layout, speed as step density rather than clock, the ring and the two ways to fill it, and the hardware's own stop, halt and resume-with-waypoint. The laser is presented as part of that stream rather than beside it, which is what makes the three contract rules (power before fire, no consecutive power bytes, end dark) and the persisting duty legible instead of arbitrary. Then geometry and limits, device ownership and the liveness check the operator sees, and the two modes in full: GRBL from connection through the arming sequence, the stop/pause/fault table and homing-or-not; cloud from the preloaded pulse file through the pause backtrack, the park that ignores the lid, and the ring's cap on job length. A comparison table and the motion-related settings close it. COOLING.md explains the engine as what it is - one owner of the thermal hardware answering a single question for whichever controller runs - and gives the reasons behind the numbers rather than just the numbers: why the flow check heats and measures the downstream rise, why 40 percent is the duty (below it, convection mimics flow), why the settle gate uses a split-half mean instead of peak-to-peak, and why one bad reading is a suspicion rather than a fault. Over-temperature, the two diagnostics tools and when to run them, the settings, and a situation-to-response table. The fire watch is described honestly: the lid IR channels are first of all a photometer for the lid lamp, the gate ships watch-only, and it is not a fire alarm. Both pages state what is not implemented - low-temperature gates, TEC control, a fire watch that acts, limit-switch homing - so nobody plans around them. Constants come from the sources that own them (the feeder contract, cool.h, the board header, the services contract), not from prose. README links both. Documentation only, no behavior change and no catalog consequence: docs/ is outside every layer and .md is excluded from the layer content hash. --- README.md | 2 + docs/COOLING.md | 366 ++++++++++++++++++++++++++++++++++++++++ docs/MOTION.md | 437 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 805 insertions(+) create mode 100644 docs/COOLING.md create mode 100644 docs/MOTION.md diff --git a/README.md b/README.md index 7707669..8465e24 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ panel, and a standard Grbl interface. * [Installation Instructions](https://github.com/ScottW514/forgefirm/blob/master/INSTALL.md) * [Build Instructions](https://github.com/ScottW514/forgefirm/blob/master/BUILD.md) * [Connecting LightBurn](https://github.com/ScottW514/forgefirm/blob/master/docs/LIGHTBURN.md) +* [How motion and the laser are driven](https://github.com/ScottW514/forgefirm/blob/master/docs/MOTION.md) +* [How cooling and airflow work](https://github.com/ScottW514/forgefirm/blob/master/docs/COOLING.md) * [How the laser safing works](https://github.com/ScottW514/forgefirm/blob/master/docs/SAFETY.md) * [How a release is accepted](https://github.com/ScottW514/forgefirm/blob/master/docs/ACCEPTANCE.md) * [Community Support](https://community.openglow.org) diff --git a/docs/COOLING.md b/docs/COOLING.md new file mode 100644 index 0000000..9cb781a --- /dev/null +++ b/docs/COOLING.md @@ -0,0 +1,366 @@ +# Cooling and airflow + +The tube is water-cooled and the enclosure is air-cleared, and both matter +while the laser fires: coolant that has stopped circulating will let a tube +overheat within a cut, and smoke that is not pulled out spoils the work and +fogs the optics. ForgeFIRM runs this as one service — the **cooling engine** — +that owns every piece of thermal hardware and answers one question at a time +for whichever controller is running: *is it safe to fire right now?* + +This page explains what the system is made of, how it decides, what you will +see when it intervenes, and what you can tune. + +- The beam itself is gated in hardware; see [Laser safety](SAFETY.md). +- For how the laser and motion are driven, see [Motion and laser drive](MOTION.md). + +--- + +## 1. What the hardware is + +**The coolant loop** is closed: a pump, a radiator with fans, the laser tube, +and two thermistors — one **upstream** of the tube and one **downstream** of a +small inline heater. Pro machines are specified with a thermoelectric cooler +(TEC) on the loop; the board cannot tell whether one is fitted (§9). The +heater exists for diagnostics, not for warming the machine up: it is +how the engine proves the coolant is actually moving (§4). + +**The airflow path** has four independently driven pieces: + +| Piece | What it does | +|---|---| +| Exhaust blower | pulls smoke out of the enclosure | +| Two intake fans | feed clean air in behind it | +| Air assist (in the head) | blows the cut line clear at the focal point | +| Purge air (in the head) | keeps the optics clean; on whenever the machine is on | + +Every fan reports a tachometer, so the engine can tell a commanded duty from an +actual airflow, and the panel shows real speeds rather than setpoints. + +**Coolant temperature is read, not guessed.** Both thermistors are converted +with the factory's own beta-equation curve, checked against a thermometer. A +sensor reading at either rail is treated as open or shorted — not as a +temperature. + +--- + +## 2. One owner, two clients + +The cooling engine lives in `forgectrl`, the machine-services daemon, and it is +the **only** thing that writes fans, pump, TEC and heater. Whichever controller +is running — GRBL or cloud — is a client of it, over two channels: + +- **The controller reports its job state** about once a second: idle, running + or cooling down, whether the laser is armed, and (in cloud mode) the fan + duties the job asks for. The reports are level-triggered, so a lost one + simply corrects itself on the next. +- **The engine publishes a verdict** the controller reads and enforces in its + own process: may the laser fire, should the job hold, may it resume. + +Two properties of that split are worth understanding, because they explain the +machine's behavior in odd situations: + +**A missing verdict is a bad verdict.** If the file is absent or more than two +seconds old, a controller treats it as *fire blocked, hold*. The engine going +away looks exactly like a fault, never like permission. + +**Arming requires being seen.** The engine only grants fire when it is +receiving fresh job reports. A controller about to fire is, by contract, one +that is reporting — an armed window the engine cannot see never gets a green +light. + +**If a controller goes silent** past five seconds, the engine blocks fire +immediately and stands the machine down through the normal cooldown, because a +smoke clear is the right physical response to a job that died mid-cut. If the +silence happens while the laser is armed, or while the pulse engine still says +a program is playing, the engine additionally stops motion and locks the laser +latch itself. It also refuses to let exhaust and intake drop below cooldown +duty while a program is still running. + +**If a diagnostic takes the hardware over** (§6), the engine suspends its own +writes and publishes fire-blocked until the diagnostic finishes. + +**If the engine itself is provably gone** while the laser is armed, the +controller writes the factory run duties to the fans once, holds the job, and +stands down. That is the single sanctioned exception to single-owner control, +and the duties are compiled in so that a lost configuration file cannot take +the fans with it. + +--- + +## 3. What the fans do, and when + +The engine runs in phases. Duties are the factory machine's own values. + +| Phase | Pump | Air assist | Exhaust | Intake | Heater | +|---|---|---|---|---|---| +| **Idle** | on | 204 | off | off | off | +| **Run** (or armed, whatever the reported mode) | on | 1023 | 65535 | 43278 | flow checks only | +| **Cooldown — smoke clear** (15 s) | on | run duty | run duty | run duty | off | +| **Cooldown — thermal** | on | idle | 32768 | 21639 | off | +| **Over-temp / fault hold** | on | run duty | forced | forced | off | + +Notes on the phases: + +- **The pump runs whenever the machine is on**, including at idle. Circulation + is cheap; a stagnant loop with a warm tube is not. +- **The heater is off at idle by design.** An always-on flow heater measurably + warms the loop within minutes, eating headroom below the start gate for no + benefit while nothing can fire. +- **Being armed counts as running.** If the laser is armed, the engine forces + the run profile and the flow checks regardless of what mode the controller + reported — fire never happens without cut airflow and active flow + verification. +- **Cooldown has two stages**: a smoke clear at full run duty, then reduced + airflow (the radiator cools the loop measurably) until the upstream coolant + temperature is back under the resume gate or the cooldown budget expires. +- **The TEC is left off.** Its output has no readback, so the machine cannot + tell whether one is fitted; driving it blind is not something ForgeFIRM does + (see §9). + +In **GRBL mode** the run profile follows your sender's `M8`/`M9` (LightBurn's +per-layer Air Assist), OR'd with the armed window. In **cloud mode** the job's +own header carries the duties and the client passes them through, so a print +gets the fan profile the service designed for it and a lens hunt stays quiet. + +--- + +## 4. Coolant flow verification + +### The problem + +A pump can stop, an impeller can slip, a line can airlock — and none of it +shows up in a temperature reading until the tube is already in trouble. +Absolute coolant temperature only tracks a loop that is *circulating*, and +"coolant should warm up while cutting" is not a usable signal either: a light +engrave may add no measurable heat at all. + +### The method + +The small heater sits between the two thermistors. Each check runs it at a +fixed duty for a fixed window and watches how far the **downstream** sensor +climbs: + +- **flowing coolant carries that heat away** — the downstream sensor rises a + little; +- **a stagnant loop cooks the sensor** — the downstream sensor rises a lot. + +The discriminator is the rise, not the difference between sensors, and the +operating point is measured rather than assumed: + +| Parameter | Value | Why | +|---|---|---| +| Heater duty | 40 % | Below about 40 %, natural convection sheds the heat well enough to *mimic* flow — dead-pump trials have looked healthier than a working pump. At 40 % heat input outruns convection, and it is the cheapest duty that does. | +| Window | 50 s | Long enough for the bands to separate cleanly. | +| Fault threshold | 14.4 °C rise | Midway between the observed flowing band and the observed stagnant band. | +| Re-check interval | 150 s | A pump that stops mid-job is invisible otherwise. | + +Each check costs the loop under a degree of heating, and with cut-profile fans +running the loop still nets cooler over a long job. + +### Checks start from a settled loop + +Measuring a rise from a baseline captured while the loop is still cooling from +earlier heat produces garbage — and it fails in the dangerous direction: it can +report flow with the pump stopped. So a check is *requested*, and starts only +once the two sensors agree within 1.5 °C **and** the downstream reading has +stopped drifting. + +Stationarity is judged by comparing the mean of the first half of a 15-second +window against the second half, not by peak-to-peak spread. On a settled loop, +peak-to-peak noise is about 0.5 °C while the split-half difference is about +0.1 °C — any peak-to-peak threshold tight enough to catch real drift would sit +below the noise floor and never open the gate. + +### One bad reading is a suspicion, not a fault + +Transients happen: cycling the pump by hand can burp an airlock that clears +itself within minutes. So the engine runs a two-step decision: + +1. **First over-limit check → `COOLANT FLOW SUSPECT`.** A warning, a hold + request, and an immediate re-check — no waiting for the normal cadence. +2. **The next completed check decides.** Over-limit again, with no clean check + in between → `COOLANT FLOW FAULT`. Clean → the suspicion clears and the job + continues. + +Two more rules close the loopholes: + +- **A suspicion that cannot resolve escalates.** If no verdict can be produced + within the confirmation budget (default 480 s), it becomes a fault: a loop + that will not settle after a fault-level reading has shown no evidence of + health. +- **Cleared suspicions still count.** Three of them in one job earn an + aggregated "check your coolant" warning; the counter resets when cooldown + reaches idle. + +A clean check from the fault state logs a recovery. + +### What the verdicts do + +| Verdict | Effect | +|---|---| +| `OK` | Fire permitted. | +| `SUSPECT` | Hold requested, cut airflow held; auto-resumes on a clean re-check. | +| `FAULT` | Fire gated and the hold stands — for the operator to resolve. | +| `OVERTEMP` | Hold with forced cooling airflow; auto-resumes below the resume gate (§5). | +| `FIRE` | Motion stopped, latch locked, hold until the next run session (§7). | + +Practical note: **expect a legitimate suspicion on the first checks after +manually stopping and starting the pump.** That is an airlock, the machinery +above absorbs it, and it clears on its own. + +--- + +## 5. Over-temperature + +The engine uses the factory's coolant windows: + +- **Run ceiling 33 °C** — above this, the verdict goes `OVERTEMP` with a hold + request and cooling airflow forced on. +- **Resume gate 31 °C** — below this, recovery is signaled and the controller + resumes automatically. + +The **upstream** sensor gates, because it reads the coolant actually entering +the tube. + +What you see depends on what the machine was doing. A running cycle takes a +feed hold and resumes by itself once the loop recovers — your sender shows the +hold state and a warning message. A jog is canceled instead (a jog cannot be +held). Fire stays gated for the whole excursion. + +--- + +## 6. Diagnostics: verifying and calibrating flow + +The web panel's **Diagnostics** tab runs the two cooling tools. Both take the +hardware over: the active controller is suspended for the duration, the engine +stands aside, and the controller is restored on every exit path — completion, +error, or your pressing Abort. The laser stays latched throughout. Progress, +both coolant temperatures and a scrolling log stream to the page while it runs. + +Both tools run at your *configured* duty, window and threshold, so the verdict +applies to the check the machine actually performs, and both use cut-profile +chassis fans — the condition the numbers were characterized under. Any +pump-off window aborts immediately if the downstream sensor passes 48 °C. + +**Flow verify** (about 3 minutes) — one check with the pump running and one +with it commanded off. + +- **PASS** = your threshold separates the two readings. +- Margins under 1.5 °C add a warning that you should re-calibrate. +- A failure here means the threshold no longer suits the loop, or the loop has + a real problem. + +**Flow calibrate** (15–25 minutes) — three trials of each case, alternating, +with settle gates between them. It reports both bands and recommends a +threshold midway between the highest flowing reading and the lowest stagnant +one, with an **Apply** button that writes it to your settings. + +- If the gap between the bands is under 3 °C it refuses to recommend anything + and tells you to raise the heater duty and rerun. + +**When to calibrate:** after replacing coolant, after changing or servicing the +pump, if flow verify warns about thin margins, or if you see suspicions that +you can trace to nothing real. The shipped default suits the factory loop; a +rebuilt one may differ. + +--- + +## 7. The fire watch + +Alongside the flow work, the engine watches for evidence of things going wrong +at one-second resolution: + +- **Emission evidence.** The kernel samples the *gated output* of the hardware + AND-gate — actual emission, not a commanded state. Emission seen with no + armed window in the recent past stops motion and locks the latch, and keeps + doing so while the evidence persists. +- **Laser power-good degradation** during an armed window is warned once per + session. +- **Stepper-driver faults** appearing during a run are warned, and HV current + is ranged for each job in the same log line. +- **Lid infrared channels** are polled every tick, and every job logs their + baseline and peaks. + +**About the lid IR fire watch specifically:** it ships in *watch-only* mode and +logs rather than acts. The reason is honest and worth stating — those sensors +are, first of all, a photometer for the lid lamp. A full-power cut raises them +only a few counts above the level the lamp sets, a candle burning on the bed +raises them about the same amount, and anything that changes the lamp (a camera +snapshot, for instance) moves them by tens of counts. A fixed threshold would +therefore stop jobs for lighting changes while still missing a small flame. A +lamp-aware design is planned; until then the channels are recorded, not acted +on, and **the fire watch is not a fire alarm**. Never leave a running laser +unattended. + +--- + +## 8. Settings + +All of these live in the panel's Machine tab, are validated on entry, and can +only be changed while the machine is idle. The engine re-reads them at the +start of every run, so a change takes effect on your next job. + +| Setting | Default | What it controls | +|---|---|---| +| `cool_flow_rise` | 14.4 °C | Downstream rise that counts as no-flow. Set this from **flow calibrate**. | +| `cool_flow_heater_pct` | 40 % | Heater duty during a check. Raising it separates the bands further at the cost of warming the loop more. | +| `cool_flow_check_s` | 50 s | Length of a check window. `0` disables flow verification entirely. | +| `cool_recheck_s` | 150 s | How often checks repeat during a job. | +| `cool_confirm_max_s` | 480 s | How long a suspicion may stay unresolved before it escalates to a fault. | +| `cool_temp_max` | 33 °C | Run ceiling — above it, hold. | +| `cool_temp_resume` | 31 °C | Resume gate — below it, continue. | +| `cool_cooldown_s` | 15 s | Smoke-clear phase at run duty after a job. | +| `cool_cooldown_max_s` | 300 s | Cap on the thermal cooldown phase. | + +Two settings are deliberately not on the panel: + +- `cool_fire_ir_delta` — the lid-IR fire gate (§7). It is `0`, watch-only, and + changing it by hand is not recommended until the watch is lamp-aware. +- `GFCOOL_*` environment overrides exist for bench work; they win for the + lifetime of the process and are not a normal operating path. + +--- + +## 9. Not implemented yet + +Stated plainly so nobody counts on them: + +- **Low-temperature gates and warm-up.** The factory holds a job and warms the + coolant when the loop is below roughly 16 °C, and refuses to fire at all near + freezing. ForgeFIRM does not yet; a cold-room machine will start cutting at a + temperature the factory would have waited out. Two settings — a hard floor + and a warm-up gate — are planned. +- **TEC control.** ForgeFIRM never drives the thermoelectric cooler. Presence + cannot be detected (the output has no readback), so this will become a user + setting plus a simple hysteresis around the factory's setpoints. +- **A fire watch that acts** (§7). + +--- + +## 10. Quick reference: what the machine does when + +| Situation | Machine response | +|---|---| +| Idle | Pump on, purge air on, fans at idle, heater off, TEC off. | +| Job starts (or the laser arms) | Cut airflow, flow check requested once the loop is settled. | +| Flow check over limit, first time | `SUSPECT`: warning, hold, immediate re-check. | +| Second consecutive over limit | `FAULT`: fire gated, hold stands until you resolve it. | +| Suspicion unresolved past the budget | Escalates to `FAULT`. | +| Three cleared suspicions in one job | Aggregated "check your coolant" warning. | +| Upstream coolant above 33 °C | `OVERTEMP`: hold + forced cooling; auto-resume under 31 °C. | +| Job ends | 15 s smoke clear at run duty, then reduced airflow until the loop is under the resume gate. | +| Controller stops reporting | Fire blocked at once, stand-down through cooldown. | +| Silence while armed, or a program still playing | Motion stopped and the latch locked by the engine itself. | +| Verdict file missing or stale | The controller treats it as fire-blocked and holds. | +| Diagnostic running | Engine suspends its writes and publishes fire-blocked. | +| Engine gone while armed | Controller writes factory run duties once, holds, stands down. | + +--- + +## See also + +- [Motion and laser drive](MOTION.md) — arming, job phases, both controller modes. +- [Laser safety](SAFETY.md) — the hardware chain the beam actually passes through. +- [LightBurn setup & operation](LIGHTBURN.md) — `M8`/`M9` and air assist in practice. +- `forgectrl/docs/SERVICES.md` — the machine-services contract, including the + report and verdict channels in full. diff --git a/docs/MOTION.md b/docs/MOTION.md new file mode 100644 index 0000000..b19640e --- /dev/null +++ b/docs/MOTION.md @@ -0,0 +1,437 @@ +# Motion and laser drive + +Everything the machine does physically — every step of the gantry, every lens +move, every laser pulse — comes out of **one stream of bytes** played by +hardware at a fixed rate. This page explains that stream, why the laser is part +of it rather than beside it, and how the two controller modes (GRBL and cloud) +feed it. + +You do not need any of this to run a job. It is here so that what the machine +does makes sense, and so the settings you can change mean something. + +- To cut from LightBurn, see [LightBurn setup & operation](LIGHTBURN.md). +- For the safety chain that gates the beam, see [Laser safety](SAFETY.md). +- For fans, pump and coolant, see [Cooling and airflow](COOLING.md). + +--- + +## 1. The pulse stream + +The control board does not decide, moment by moment, when to move a motor. +Instead a hardware timer (EPIT) fires at a fixed **machine tick**, and a DMA +engine (SDMA) hands the next byte of a prepared stream straight to the GPIO +register that drives the stepper and laser lines. No software runs between the +timer and the pins. + +That is what makes motion smooth: step timing cannot be disturbed by a busy +CPU, a camera stream, a network client, or a garbage collector. The worst a +loaded system can do is fail to supply bytes fast enough — and that case is +detected and treated as a fault rather than as silent damage. + +### One byte per tick + +Each byte covers exactly one tick. If the top bit is clear, the byte commands +steps and fire; if it is set, the byte sets laser power. + +| Bit | Meaning | +|---|---| +| 0 | X step | +| 1 | X direction (set = −X) | +| 2 | Y step | +| 3 | Y direction (set = +Y; the two Y motors are driven complementary) | +| 4 | **Laser fire during this tick** | +| 5 | Z step | +| 6 | Z direction (set = lens up, away from the bed = +Z) | +| 7 | 0 = step byte · 1 = power byte (low 7 bits are the power level) | + +**Speed is density, not clock.** The tick rate never changes inside a job. +Going faster means setting a step bit in more of the bytes; going slower means +spacing them out. A move is planned in the usual way — acceleration, cruise, +deceleration — and then resampled onto this fixed grid. + +Two consequences worth knowing: + +- **Resolution is bounded by the tick rate.** At the default GRBL machine tick + of 28160 Hz, one axis can take at most 28160 steps per second — about + 528 mm/s, comfortably above the machine's 200 mm/s top speed. +- **There is a hardware ceiling.** The playback script needs about 6 µs per + byte, so beyond roughly 165 kHz the timer outruns it. Ticks are chosen far + below that. + +### The ring, and two ways to fill it + +Pulse bytes go into a 16 MiB ring buffer in reserved memory. There are two ways +to use it, and the mode you run decides which: + +- **Live streaming (GRBL mode).** The controller keeps only a small window of + the job in the ring — a fraction of a second — and refills it continuously + while the job plays. A write that would overflow is refused, and the feeder + backs off; that is normal flow control, not an error. If the feeder ever + falls behind far enough to empty the ring, the machine enters an **underrun** + state: motion stops instantly, and position is no longer trusted. +- **Preloading (cloud mode).** The whole job is written into the ring before it + starts. Nothing can starve, but the ring size caps job length — roughly + 1 MiB per 100 seconds at the cloud's 10 kHz tick, so about 28 minutes. A job + larger than the ring is rejected cleanly before it runs. + +### Stopping and resuming at the hardware level + +The pulse engine itself offers three ways out of a running program, and both +modes are built on them: + +- **Controlled stop** — the tick rate ramps down at a set rate (125000 Hz/s by + default) until motion halts. No steps are lost, so position stays accurate. + This is what a feed hold, a jog cancel, a lid-open cancel and a soft reset + all use. +- **Halt** — an immediate stop with no ramp. Steps can be lost; used only for + emergencies. +- **Resume with a waypoint** — from a controlled stop the program can be + resumed a chosen number of steps backward (laser forced off) or forward. This + is how the factory's pause-and-resume works, and cloud mode uses it. It is + only available for a preloaded job: a live-streamed ring no longer holds the + bytes to back into, and the kernel refuses the request. + +Whenever a stream ends — normally or by starvation — the playback script drives +the fire and step lines low as a hardware backstop. + +--- + +## 2. Laser drive is part of the motion stream + +The laser is not a separate subsystem that gets told "on" and "off" while +motion happens elsewhere. **Power and fire ride the same bytes as the steps**, +on the same grid: + +- A **power byte** (top bit set) sets the PWM duty of the laser drive: 7 bits + written straight into the hardware PWM against a 127-count period, at a + carrier near 40 kHz. 127 is full power. +- The **fire bit** (bit 4) requests emission for that one tick, and only that + tick. + +Because both travel with the steps, power and position cannot drift apart. A +power change lands at exactly the point along the path where it was planned, +regardless of what the rest of the system is doing. + +Three rules follow from the hardware, and both controllers obey them: + +1. **Power before fire.** Starting a program resets the duty to about 100 %, so + a stream must set power before its first fire bit — otherwise the first + pulses would fire at full power. +2. **No two power bytes in a row.** The playback script applies the first of a + run of power bytes and discards the rest, so power changes are spaced by at + least one step byte. +3. **End dark.** Every stream ends with fire clear; the end-of-data backstop is + the safety net, not the mechanism. + +Also worth knowing: **the duty setting persists after a program ends.** The +laser-off guarantee rests entirely on the fire bit and the hardware chain, never +on power being zero. + +### What actually lets the beam out + +The fire bit is a *request*. Emission additionally requires the hardware safety +chain — lid switches, the remote interlock loop, HV good, supply rails, the +charge-pump watchdog the kernel feeds only while a program is playing, and the +physical button latch — to agree. On top of that, ForgeFIRM keeps the kernel's +**laser latch** locked except inside an operator-armed job window (§5.4), and +the kernel relocks it whenever the pulse device is closed. + +Fire only ever rides motion segments of laser blocks. Jogs, rapids and homing +are fire-free by construction, not by convention. See [SAFETY.md](SAFETY.md) +for the chain itself. + +--- + +## 3. Geometry, speeds and limits + +| Property | Value | +|---|---| +| X/Y resolution | 0.15 mm per full step, ×8 microstepping → 53.333 µsteps/mm | +| Z resolution | 0.3534 mm per half-step → 2.832 half-steps/mm | +| Work area | 495 × 279 mm | +| Z travel | about 10.6 mm (0.417"), hall-referenced at the top | +| Max X/Y rate | 12000 mm/min (200 mm/s) | +| Max Z rate | 300 mm/min | +| Acceleration | 700 mm/s² X, 590 mm/s² Y, 50 mm/s² Z | +| Laser PWM carrier | 39.98 kHz, 7-bit duty | + +Origin is the **back-left** corner, and the workspace is all-positive from +there. **+Y moves the gantry toward the front of the machine.** Z counts +positive upward, away from the bed. + +Z is never driven blind: the lens carriage is referenced against a hall sensor +at the top of travel, and moves are supervised against it. + +The machine has **no limit or home switches** as it ships. What that means in +practice — how each mode establishes an origin, and how the machine behaves +without one — is in §5.7 and §6.3. + +--- + +## 4. Who owns the motion hardware + +`forgectrl`, the machine-services daemon, owns the pulse device for as long as +it runs and hands the open connection to whichever controller is active. Only +one controller — GRBL or cloud — runs at a time, and switching between them is +a live operation from the web panel. + +Two behaviors follow from this that you will notice: + +- **The 40 V motor rail stays up while the machine is on.** Handing the device + from one controller to another never cycles it. The stepper drivers on this + board can latch into an unserviceable state on a rail glitch — the position + counters keep counting while the motors produce nothing — so the rail is left + alone. +- **The machine proves it can move before the first job of a session.** Before + the first controller start, forgectrl makes a short test move (always to the + right first — a cable lives at the left end of travel) and confirms it with + the accelerometer in the print head. If it sees no motion it powers the rail + down and retries with progressively longer off periods; if the drivers still + will not wake, it reports a **motion fault** instead of starting a + controller, and the panel offers a retry. Position counters advancing are + never accepted as proof that the machine moved. + +--- + +## 5. GRBL mode + +GRBL mode turns the machine into a standard Grbl-speaking laser cutter. It is +the default and the one to use for your own designs. + +### 5.1 Connecting + +The controller speaks **Grbl 1.1 over TCP port 23**. Point LightBurn, UGS, +cncjs or any Grbl sender at the machine's address on port 23. Setup details and +a first job are in [LIGHTBURN.md](LIGHTBURN.md). + +Only one sender at a time is meaningful. Opening a second connection displaces +the first — which is also why the web panel reads position from the machine's +own counters and never from the Grbl socket. + +### 5.2 From G-code to pulse bytes + +1. Your sender streams G-code over TCP. +2. grblHAL parses it and plans motion in the usual way: look-ahead, junction + deviation, acceleration ramps. +3. A producer thread runs the planner's step generator against a virtual clock + a thousand times finer than the machine tick and places each step event on + the byte grid. +4. A high-priority shipper thread writes due bytes to the pulse device roughly + every 10 ms, keeping a bounded queue ahead of real time. + +The queue depth is the trade: deeper means more immunity to system load, +shallower means a feed hold or a power override takes effect sooner. The +default is 200 ms, and the machine tick defaults to 28160 Hz — the same tick +the factory firmware uses for travel moves. + +### 5.3 Laser mapping + +- `$32` (laser mode) is **on by default**, so `M3`/`M4` and `S` behave the way + senders expect. `M4` gives dynamic power scaled with speed through + acceleration ramps; `M3` gives constant power. +- `$30` is 1000, and S values map linearly onto the 7-bit power byte — + `S1000` = full power, `S500` ≈ half. +- Power changes are emitted ahead of the tick they apply to, so a power change + and the motion it belongs to stay together. + +### 5.4 Arming: the button press is part of every job + +The first laser-on of a job does not fire. Instead the controller: + +1. **Checks the coolant verdict.** If a flow fault or an over-temperature + condition stands, arming is refused outright ([COOLING.md](COOLING.md)). +2. **Checks that a print head is present.** No head, no arming. +3. **Forces the cut airflow profile on**, so every fire window is covered by + running fans and active flow verification. +4. **Unlocks the kernel laser latch, lights the button white, and pauses the + job** — the sender keeps getting status reports, so it does not time out — + until you press the physical button. + +A press with the lid open does not arm; the hardware button latch would not +clear on it either. A soft reset, or a lid or interlock open, cancels the job +instead. If nobody presses within `laser_button_timeout_s` (default 300 s), the +job ends in an alarm with the latch relocked. The coolant verdict is re-checked +after the press, so a window can never open against a fault that appeared +during the wait. + +**The window is per job, not per fire.** It survives `S` changes and `M5`/`M3` +toggles, so nothing re-prompts mid-job, and it closes — relocking the latch — +when any of these happens: + +- program end (`M2`, `M30`, `%`) — the normal case, within the cycle; +- the sender's connection changes (the consent belonged to that session); +- `laser_disarm_s` (default 60 s) of spindle-off idle, counted down in Hold, + Door and Tool Change as well as Idle; +- immediately on alarm, homing, reset, or a stream fault. + +### 5.5 Pausing, stopping and faults + +| You do | What happens | +|---|---| +| Feed hold (`!`) | Controlled ramp to a stop, position kept, laser off. The disarm grace keeps counting. | +| Cycle start (`~`) | Resumes from the hold. A live-streamed job cannot back up, so the cut resumes where the deceleration ended. | +| Jog cancel (`0x85`) | Controlled stop, jog abandoned, position kept. | +| Soft reset (`^X`) | Controlled deceleration into Alarm, latch relocked, machine position retained; `$X` clears the alarm. | +| Press the button mid-job | Pause; press again to resume (§5.6). | +| Open the lid or the interlock loop | The job is **canceled**, not paused (§5.6). | +| Ring runs dry (underrun) | Motion stops instantly. While armed this is a hard fault: alarm, latch relocked, position invalidated — re-home before trusting coordinates. A motion-only job gets one sanctioned retry. | +| Coolant fault or over-temp | Feed hold with cut airflow forced on; fire is gated. Over-temp resumes automatically once the loop recovers. | +| Controller crash or hang | The daemon stops motion and relocks the latch, then restarts the controller. | + +### 5.6 Lid, interlock and button + +ForgeFIRM reproduces the factory machine's behavior: + +- **A lid or interlock open during a job cancels it.** Motion stops within + milliseconds of the switch edge, the job is not resumable, the latch relocks, + and the head returns to the position the job started from — **with the lid + still open**, exactly as the factory does. The return-home move always runs + to completion. +- **The button pauses and resumes.** In GRBL mode a press is a feed hold and + the next press is a cycle start. A pause is not a cancel: the armed window + stays open across it. +- **Idle lid cycles are ignored.** Opening the lid to load material, or + powering up with it open, does not leave the controller parked — senders + connect normally. +- Jogs, homing and hunts are not lid-gated (the beam is blocked in hardware + regardless). + +The next job re-arms with a fresh button press — the same press the hardware +button latch itself requires, which is why software and hardware cannot +disagree about whether the machine is armed. + +If you prefer stock Grbl door behavior, set `lid_policy = hold`: the job parks +in the Door state and a cycle start after the lid closes finishes the move with +its position intact. + +### 5.7 Homing, and running unhomed + +The homing method is a setting (`homing_mode`), chosen in the web panel: + +- **`gfcloud`** — camera homing through the Glowforge web service, the same + cycle the factory machine runs. `$H` suspends the stream engine, runs the + session, then hands the machine back. Takes roughly a minute and needs a + signed-in Glowforge account. +- **`switches`** — the future limit-switch cycle. Not enabled yet; brackets for + the switches are in the project's `3d-models/` directory. +- **`none`** — `$H` is rejected. + +**The machine cuts fine unhomed.** Without a reference, coordinates are +relative to wherever the head happened to be, so the panel shows position in +red to say so, and your sender should use a job-start mode that does not depend +on machine coordinates. After a successful home the position is anchored and +shown normally. + +Anything that invalidates position — an underrun, a stream fault — drops the +anchor deliberately, so a stale origin cannot be reused. + +--- + +## 6. Cloud mode + +Cloud mode runs the factory experience: the Glowforge app and web service, the +camera bed image, the lens hunt, "push the button to print". It is kept and +maintained on purpose. Behavior specific to the service — actions, events, +credentials — is in the cloud-mode documentation (`python3-gfhardware/forgefirm-app/docs/CLOUD.md`). + +### 6.1 What is different about the motion path + +In cloud mode the machine does not plan anything. The service sends a +**precomputed pulse file** — already resampled to the byte format described in +§1 — which the client downloads, writes into the ring, and plays: + +1. The service issues a print action with a URL for the motion data. +2. The client downloads it and validates the header before a byte reaches the + ring. A job larger than the ring is refused cleanly. +3. The header's own parameters are applied: the machine tick (10 kHz for prints + and hunts), the acceleration ramp, and the per-job fan duties, which are + passed to the cooling engine as the run profile. +4. The button wait arms the laser, exactly as in GRBL mode. +5. The ring plays to the end; the client supervises it and reports state. + +Because the whole job is preloaded, there is no feeder to starve — but there is +also no live re-planning, and job length is capped by the ring. + +### 6.2 Pause, cancel and park + +- **The button pauses and resumes a print**, and here it does so exactly as the + factory does: a press stops motion under control and then backs the stream up + 2000 ticks with the laser off; the next press runs forward and re-enables the + laser after a 1950-tick lead, so the resumed cut overlaps the material + already burned instead of starting cold. Both counts are settings + (`cloud_pause_backtrack_ticks`, `cloud_resume_lead_ticks`). Motions and + hunts do not pause. +- **A lid or interlock open, or a cancel from the app, ends the job.** Motion + stops, whatever remains in the ring is dropped so nothing can play later, and + the head parks back at the job's starting point — ignoring the lid, as the + factory does. The job is reported as canceled. +- **The service dead-reckons position**, so the park after every print, + finished or aborted, matters: cutting it short would offset everything until + the next camera home. That is why the park ignores the lid and the cancel + flag. + +### 6.3 Homing and hunts + +Cloud homing is camera-based: the service takes a lid image, moves the head, +takes another, and computes where it is. The lens hunt references Z against the +hall sensor. Hunts are not lid-gated. Connecting zeroes the machine's counters +at the head's current position, so GRBL-mode coordinates do not survive a +switch to cloud mode and back — re-home after switching. + +--- + +## 7. The two modes side by side + +| | GRBL mode | Cloud mode | +|---|---|---| +| Who plans motion | grblHAL on the machine | the Glowforge service | +| Input | G-code over TCP:23 | a downloaded pulse file | +| Ring use | live-streamed, small window | whole job preloaded | +| Machine tick | 28160 Hz default | 10 kHz (from the job header) | +| Job length limit | none | ~28 minutes (ring size) | +| Needs internet | no | yes | +| Laser arming | button press per job | button press per job | +| Button mid-job | feed hold / cycle start | pause with backtrack / resume with lead | +| Lid or interlock open | cancel + return to job start | cancel + park at job start | +| Homing | `$H` (camera or, later, switches) | automatic, camera-based | +| Fan control | `M8`/`M9` plus the armed window | per-job duties from the job header | +| Underrun possible | yes (handled as a fault) | no (nothing is streamed) | + +Only one mode runs at a time. Switch from the panel's Status tab; the switch is +allowed only when the machine is idle. + +--- + +## 8. Settings that affect motion + +Machine settings live in the web panel and are stored on the machine. They can +only be changed while the machine is idle. + +| Setting | Default | Effect | +|---|---|---| +| `controller_mode` | `grbl` | Which controller runs: `grbl` or `cloud`. | +| `homing_mode` | `gfcloud` | What `$H` does: `gfcloud`, `switches`, `none`. | +| `gfcloud_home_x/y/z` | 0 / 0 / Z max | Coordinates assigned after a successful camera home. | +| `gfcloud_home_timeout_s` | 300 | How long a homing session may take before it alarms. | +| `lid_policy` | `cancel` | `cancel` = factory behavior; `hold` = stock Grbl door parking. | +| `laser_button_timeout_s` | 300 | How long the machine waits at the button prompt. | +| `laser_disarm_s` | 60 | Spindle-off grace before the armed window closes. | +| `rail_settle_s` | 2.5 | Motor-rail off period when a controller takes the device standalone. | +| `cloud_pause_backtrack_ticks` | 2000 | Cloud pause: laser-off backtrack after the stop. | +| `cloud_resume_lead_ticks` | 1950 | Cloud resume: laser-off lead before firing again. | + +Grbl `$` settings (steps/mm, rates, accelerations, laser mode) are set through +your sender in the usual way; the defaults above are baked in from the factory +machine's own measured values. If you change a baked default and it does not +appear to take, remember that stored settings win — `$RST=$` restores the +defaults. + +--- + +## See also + +- [LightBurn setup & operation](LIGHTBURN.md) — practical sender setup. +- [Laser safety](SAFETY.md) — the hardware chain and what each interlock does. +- [Cooling and airflow](COOLING.md) — the fire gates referenced above. +- `kernel-module-glowforge/UAPI.md` — the pulse-stream contract in full detail. +- `forgectrl/docs/SERVICES.md` — device ownership, mode supervision, switch map.