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.