Factory firmware¶
This page describes how the factory machine works: what the factory firmware needs from the Glowforge service, the session it runs, the pulse file it plays, the header every job carries, and the holds around a print. The wire protocol itself is on Cloud protocol; how ForgeFIRM's cloud mode reproduces the factory experience is on Cloud mode.
The factory machine cannot cut without the service¶
The factory firmware plans nothing on the machine. Every job arrives from the Glowforge web service as a precomputed pulse file: a byte stream already resampled to the machine tick, locked to the machine's serial number, and gzip-compressed. The service commands the job over a WebSocket whose TLS the factory firmware pins to the server's public key; the machine downloads the pulse file over HTTPS, writes it into a DMA ring in reserved memory, and clocks it out with the i.MX6 EPIT timer and SDMA engine into the GPIO register that drives the steppers and the laser (see The step engine). Without the service there is no job to play.
The factory validates its own homing by camera: the service takes a lid image, moves the head, takes another, and computes where the head is. The lens hunt references Z against the hall sensor in the head. See Homing internals.
The factory firmware version the ForgeFIRM cloud client is validated against,
and advertises to the service, is 2.6.0-2228.
The session¶
Once a machine is signed in and the WebSocket is open, the service drives it through a sequence of actions. The machine handles each and replies with the matching events:
| Action | What the machine does |
|---|---|
settings |
Sends the full machine settings report (the ~600 4-character setting codes). |
update_check |
Hands off to the updater (below). |
hunt |
Downloads the focus-homing pulse file and runs it; emits hunt:starting / hunt:completed. |
lid_image / head_image / lidar_image |
Captures a JPEG and uploads it to the presigned storage URL supplied in the action's endpoint field; emits :capture:* and :upload:* events. |
motion |
Downloads and runs the motion pulse file; emits motion:starting / motion:completed. |
print |
Downloads the print pulse file, waits for the button, then runs the warm-up, the cut, the return to home and the completion events. |
The action vocabulary, the envelopes and the events are on Cloud protocol.
What the three unprompted actions do in the factory¶
None of the three appears on the wire in any captured session; the descriptions come from the 2.6.0 binary. All three are hand-offs to programs ForgeFIRM does not have:
update_checkchecks nothing. The handler writes'u'to the runit control fifo/var/run/svs/glowforge-updater/controland reports:completed, or:failedif that write fails; on a service-sent failure or cancel it writes'd'to stop the service again. Everything an update means happens in that separate daemon. The application carries no update endpoint at all, and a cut refuses to start while the updater holds its lock.factory_resetreplaces the application with a script. The action posts a command to the hardware task, which tells runit not to restart the application and thenexecls/usr/bin/factory_reset.sh, passingrebootwhen the request's flag asks for one.head_firmware_updateflashes the laser head. It takeshead_firmware_filenamefrom the request, reads it out of/glowforge/fw/head/and runs/usr/bin/head-update.sh.
ForgeFIRM refuses two of these and answers the third without the hand-off; the
policy is on Cloud mode. The two refusals report
:failed rather than :cancelled on purpose. In this protocol a cancel is what the service says when it withdraws an action; failure is what a machine says when the thing did not happen, and it is the factory's own report when its reset script cannot be launched.
The factory updater itself, and the .fw package it applies, are on
Boot and storage.
The pulse (.puls) file¶
Motion, hunt, and print "pulse" files describe a job. Each begins with a small
header: a magic (GF1), a header length, and a series of 4-character key/value
tags (machine-setting overrides for the job), followed by a raw, per-tick
step/laser byte stream that clocks the X/Y/Z steppers and the laser. The byte
layout is on The step engine.
The body may be gzip-compressed; a compressed body's uncompressed length is in
the gzip ISIZE trailer. The header names the serial number the job is locked to
(MCsn) and the pulse-data format (PDfm).
The factory uses a 10 kHz machine tick for prints and hunts and 28160 Hz for
travel moves. A print header carries its own step frequency (STfr), the
acceleration ramp, and the fan duties for the job.
ForgeFIRM's helpers for reading and generating the format are in
gfutilities (Cloud mode).
The pulse header¶
Every pulse file opens with a header: a length, then a flat list of 4-character tags each carrying a 32-bit little-endian value. It is the job's operating envelope, and the factory firmware treats a well-formed one as a precondition for cutting at all. Of the tags it knows, 346 are accepted in a header and 29 are mandatory; a header missing any one of the 29 is refused outright, and a known tag that is not header-legal is refused too. An unrecognized tag is only logged and skipped, which is why a newer service can talk to an older machine.
The header is not a set of echoes. Roughly two thirds of the fields come back holding whatever the machine last reported, but the service substitutes real operating values for the ones that matter: fan duties, per-sensor temperature ceilings, lid IR flame thresholds, head accelerometer limits and a high-voltage current cap all arrive filled in per job.
Every tag the service fills in has a disposition in ForgeFIRM, and the job log
says which: each job names its lifecycle keys, counts the keys with no applier,
and splits that count into declared ignored (a decision with a reason, listed
below) and undecided (N of M header keys have no applier here (D declared
ignored, U undecided), the undecided ones named at debug level). The undecided
count is the one that should be zero.
| Tags | Disposition |
|---|---|
AArd, EFrd, IFrd |
Applied: the run-phase fan duties, handed to the forgectrl cooling engine as the per-job profile on the scale the service uses (air assist 0 to 1023, exhaust and intake 0 to 65535). While the laser is armed the engine raises any of them to its configured run duty (the airflow floors were measured there), so a print's fans never run slower than the cut profile, while a hunt's 0 duties stand and the hunt is measured, not judged. |
STfr |
Applied: step frequency. |
XSrc, YSrc / XShc, YShc |
Applied: stepper current while running / idle. |
XSdm, YSdm |
Applied: decay mode. |
XSmm, YSmm |
Applied, and refused on anything but 8: every header the service has sent carries 8, the factory's own analog config, and 8 is the only mode a cloud job runs at on this machine (the xy_microsteps setting is GRBL mode's). Any other value is refusing the job: in the log and :cancelled to the service, before a byte reaches the ring; such a report is what starts the work on finer modes in cloud mode. |
ZSmd |
Applied: Z microstep mode; 0 is full-step, and every capture carries 0, so the service's Z counts are full steps (15 for 0.5 in of material, 4 for 0.1 in, from the service's zero, 4 full steps below the hall edge, where its hunt parks the lens: The motion hardware). |
CMrx, CMrn |
Passed through as the job's coolant window (millidegrees, sent as degrees) on every POST /cool/state while the job is loaded. The engine applies each only where it is stricter than its configured value, never looser, never to a gate the operator turned off; the coolant ceiling is the consumer. |
EFrx, IFrx, AArx |
Passed through as the tach floors (the maximum periods, sent as the minimum speed each means in the kernel's units); the airflow gates are the consumers, and a header can only raise a floor for its job. A sentinel (0, 1023, the signed extremes, the unsigned rail) or an absurd value is dropped. |
AArn, EFrn, IFrn |
Read, inert: the tach minimum periods are maximum speeds, which nothing gates on. |
MCsn, PDfm |
Refused on: the serial the job is locked to and the pulse-data format, both checked before a byte reaches the ring; a mismatch is refusing the job: in the log and :cancelled to the service. |
CFrh, CCwp, CCrp, CCup |
Logged: the lifecycle keys, named in every job's log and driving nothing, which is what the factory does with them (below, "A print's warm-up and its rest"). |
AAw?, EFw?, IFw? |
Declared ignored: the warm-up phase fan profile. The run profile covers the warm-up hold, and every captured header sets the warm-up values equal to the run values. |
PTmn, PTmx |
Declared ignored: the supply temperature window. The service sends the whole ADC range (a window that cannot trip) and the factory binds the pair to nothing; the supply's raw reading is watched per job by the engine instead. |
BT??, HT??, LT??, IT??, FT?? |
Declared ignored: the board, head, lid, interconnect and fused temperature ceilings, sent in a unit that is not millidegrees and not established. The chassis (board) sensor is watched per job; the other four locations have no sensor on this platform. |
CTrn, CTrx |
Declared ignored: the coolant window in raw counts, older files only; CMrn/CMrx carry the same window. |
HA?? |
Declared ignored: the head accelerometer thresholds. They are LIS2HH12 interrupt-generator register values, and forgectrl's crash watch runs the same mechanism on its own local knobs (the header defaults are the seeded values), so the per-job copies add nothing. |
IR?? |
Declared ignored: the lid IR flame thresholds. The lid IR channels read the lid lamp, so these absolute numbers are the prior for the lamp-aware fire watch (see the cooling engine), not a gate. |
HIix, HIrx |
Declared ignored: the HV current caps. The sampled LASER_ON witness covers the idle case, and HV current is ranged in every job's log line. |
TRuc |
Declared ignored: thermal report upload conditions, a knob for the factory's telemetry, which is out of scope. |
WPon |
Declared ignored: the pump is held on as part of the engine's idle posture; per-job pump control would belong in its per-job profile. |
| everything else | Undecided: counted and named at debug level by every job. Of the 346 header-legal tags, the factory binds 283 to a source; 20 configure the client's own network backoff, 39 belong to the three fans of an air filter, the camera families are exposure and gain values the mainline driver's units do not take, and most of the rest are per-phase idle variants of the limits above. A hunt header from the live service leaves 49 undecided. |
Thermal policy is the cooling engine's, on purpose: it runs its own coolant ceiling and critical line, flow verification, airflow gates, emission witness and silence timeout, and a remote service can tighten those limits for a job and never loosen them. The factory's own policy, decoded from its firmware and from the captured headers, is below.
The factory's own envelope¶
What the service actually fills in, across 23 captured headers and this board's own factory logs:
- Fans. A cut job carries the real duties: air assist 1023, exhaust 65535, intake 43278, which are exactly ForgeFIRM's run profile. A hunt or a motion file carries air assist 204 with the extraction fans off.
- Tachometers. Every tach window is zero in every capture except
AArx64500 on cuts. The factory's intake and exhaust tach monitors treat zero as not configured, so a stalled extraction fan is not caught there at all: it is caught by the temperature it causes. When a fan alert does fire during a cut, the factory pauses the print on the same transition a user pause takes. - Temperature, in two tiers. A plain alert pauses; a
*_temp_criticalfails the machine. The units are per sensor: the coolant family is carried twice, once in raw counts with the thermistor's hot end as "min", and once in millidegrees. A stock machine's live coolant window is 10 to 30 C at idle and 5 to 35 C for warm-up and run, andCMrx33000 on a cut is exactly the 33 C ceiling the machine ships with. - Coolant flow: the factory does not verify it. The calorimetric flow controller in its firmware is never armed, and its heater is written only at phase changes. Nothing in the factory machine can tell a circulating loop from a stagnant one.
ForgeFIRM's answer to each of those is on the cooling engine: every gate is a plain setting with an off end, a header value can only ever tighten a local one, every fan is held to a measured floor with a fault rather than a pause for the session, there is a coolant critical line above the ceiling's pause, the board temperatures are watched per job, and coolant flow is verified by interrogation. The rest of the envelope is declared tag by tag in the table above.
How the factory sets power¶
The Glowforge UI's power setting never reaches the machine. Three cloud cuts of one 1 inch square, same location, same material, same speed, changing only the UI power (Precision Power 1, Precision Power 100, then Full Power), with the pulse file captured from each, settle it:
- The three headers are identical. No key differs. The power model lives entirely in the service, which bakes it into the FIRE bits.
- The motion is identical too: 5420 steps, 101.62 mm, 10.81 s at 9.44 mm/s in all three.
- Analog duty is not a power control. All three runs carry the power byte exactly three times, always 127 (full duty): once as the cut begins, then a refresh about every 27,000 ticks (about 2.7 s).
- Dose is FIRE-bit density on a fixed 7-tick period, 700 µs at the print tick of 10 kHz, about 1.43 kHz, with the on-count dithered between adjacent integers by an error accumulator:
| UI setting | On-runs seen | Mean of 7 | Density |
|---|---|---|---|
| Precision Power 1 | 1 (359 times), 2 (212 times) | 1.371 | 0.1953 |
| Precision Power 100 | 5 (236 times), 6 (334 times) | 5.576 | 0.7952 |
| Full Power | continuous | 7 | 0.9965 |
The period was exactly 7 in all 570 measured cycles of both dithered runs, and the mix of adjacent on-counts matches the fractional part exactly: Precision Power 1 wants 1.371 on-ticks, and 2-runs are 212 of 571, or 0.371. That is an accumulator carrying a remainder, not a repeating pattern.
On the UI scale, Precision Power 1 to 100 is linear in density (about 0.006 per unit, intercept about 0.189). Full Power sits off that line, where about 134 on the same scale would land, which fits a setting the UI presents as outside the normal range. So the factory's "1 percent" is the bottom of the band that does useful work, not 1 percent of the physical range, which is why no user of a factory machine ever meets the tube's dead band (The laser).
Velocity compensation is real but partial. Density falls as the head slows into a corner, by the same relative factor at every power setting (corner over cruise 0.38, 0.38, 0.41). Measured per step interval, though, fire ticks per step rise from 3.89 at 9.44 mm/s to 7.00 at 1.22 mm/s, so dose per unit length still climbs about 1.8 times at a corner, against the roughly 7.7 times it would climb with no compensation at all. Only about 24 of the 5420 step intervals are below cruise speed, so the direction and the rough magnitude are solid and the exact law is not. ForgeFIRM's own corner rolloff is a setting (the grblHAL driver).
The service pushes very little outside the header. Across every captured
session, counting every action type, the service has pushed seven keys through
per-action settings: IMct, NRic, HCil, HCae, HCex, HCag and
HCga. The opening settings action on its own carries one, NRic. Nothing
thermal, nothing about fans, nothing that bounds the machine arrives that way.
The operating envelope reaches the machine only in the pulse header, per job.
A print's warm-up and its rest¶
The factory holds twice around a print, and so does ForgeFIRM. Measured on a factory slot: 3.05 s between configuring the run and starting it, and about 10.35 s of rest after the park before the machine goes idle. A motion or a hunt gets neither.
Both are equipment protection rather than ceremony. The warm-up is what gets air and coolant moving before the first fire; the rest is what purges the enclosure and the tube after the last one. The service assumes both have happened, so a machine that skips them is running hardware nobody looked after.
In ForgeFIRM, MOTION.WARM_UP_DELAY and MOTION.COOL_DOWN_DELAY carry the
seconds and default to the factory's measurements. 0 skips either, deliberately,
and a skipped period says so in the log rather than passing in silence: a
config that carries explicit zeros keeps them until someone changes them. The
configuration file is described on Cloud mode.
The pulse header looks like the source of these periods and is not. Every
captured print header carries CCwp 5000 and CCrp 10000, with CFrh for
the park, and a motion or a hunt carries none of them; the correlation is real
and the causation is not. In the 2.6.0 application all four lifecycle keys
(CCrp, CCup, CCwp, CFrh) are parsed, stored, copied into the settings
batch and acted on by nothing: a tag reaches behavior either through a
peripheral that registers it against a source or through an inlined lookup by
index, and these four have neither. The factory's warm-up and rest come from
somewhere other than the job, so configured periods defaulted to what the
factory was measured doing are the right model rather than a placeholder. The
keys stay in the per-job log line as a record of what the service sends.
Pause, cancel and park in the factory¶
- The button pauses and resumes a print. 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. ForgeFIRM carries both counts as settings (see Cloud mode).
- A lid or interlock open during a job cancels it. The head returns to the position the job started from with the lid still open. The service dead-reckons machine position, so the park after every print, finished or aborted, matters: a park cut short would offset every subsequent motion until the next camera re-home.
- The factory runs a focus hunt with the lid open, and a hunt includes a head capture. ForgeFIRM's cameras capture only with the lid closed, so under ForgeFIRM a hunt needs the lid shut (see Cameras).
- The factory streams continuous telemetry: the binary sensor firehose
(
POST /api/sensor), in-band advisory logs (WSStype:"log"), and thefault:*/estop:*/interlock:*reporting namespace. ForgeFIRM's scope decision on these channels is on Cloud mode. - The factory live-appends to its ring. In a captured print the progress
frame's
totalgrew in steps of 262,144 bytes (256 KiB) per interval: the factory topping its ring up on the wire (see Cloud protocol).