Skip to content

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_check checks nothing. The handler writes 'u' to the runit control fifo /var/run/svs/glowforge-updater/control and reports :completed, or :failed if 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_reset replaces the application with a script. The action posts a command to the hardware task, which tells runit not to restart the application and then execls /usr/bin/factory_reset.sh, passing reboot when the request's flag asks for one.
  • head_firmware_update flashes the laser head. It takes head_firmware_filename from 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 AArx 64500 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_critical fails 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, and CMrx 33000 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 (WSS type:"log"), and the fault:* / 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 total grew in steps of 262,144 bytes (256 KiB) per interval: the factory topping its ring up on the wire (see Cloud protocol).