Files
forgefirm/docs/MOTION.md
T
ScottW514 d612c0099a Prove the density-only model; record the rasters and the decision
The stream harness keeps the analog rendering as the host-test
conservatism reference (rule 13's mask, the duty ladders) and drops the
M101 switch sessions; rule 18 stays as the derived-floor proof, now
satisfied from boot by the precompute. The lifecycle harness's
state-files scenario asserts the derived floor is in $$ before any arm.
The catalog's laser.power-model-switch goes and laser.power-floor reads
the one floor key with no M-code needed. The mswitch drill goes; the
m4corner drill becomes a single density pass; the dpatch drill returns
to density only.

The CAMPAIGN-LOG records the first rasters (254 and 508 DPI grayscale
wedges: tonality held to ~14 pulse slots per pixel, no dither artifact,
one benign stale-verdict suppression under CPU starvation) and the
decision that ends the analog mode - the strike transient fires a spot
at every beam-on, and the finish comparison found no advantage. BRINGUP,
LIGHTBURN, MOTION and SAFETY describe the density-only present.
2026-08-30 19:41:50 -04:00

22 KiB
Raw Blame History

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.


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 32 MiB ring buffer in reserved memory, the same size the factory firmware uses. 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 56 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 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.

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).
  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 are not lid-gated. The core is blind to the door signal while it is idle, jogging or homing, so a jog both starts and runs with the lid open — the beam is blocked in hardware regardless.
  • Homing is lid-gated in practice, even though the core does not see the door during $H. With homing_mode = gfcloud — the only method that works today — the cycle is a cloud homing session (§5.7), and its move to the home corner is an ordinary motion action: refused with the lid open, and stopped if the lid opens partway through. The camera steps need the lid closed anyway. Only the lens/Z hunt inside that session ignores the lid (§6.3), which is where hunts happen in GRBL mode — there is no hunt outside a cloud homing session. Under homing_mode = switches a Z reference would just be part of the core homing cycle.

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 uses the machine's builtin credentials.
  • 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 ~56 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.
laser_floor_density 10 The S-range floor, percent of full: the lowest pulse density that still marks. Loaded into $35 at every spindle precompute; $35 is derived, never typed.
laser_pulse_ticks 20 Density base period in machine ticks (35.5 us each).
laser_pulse_min_ticks 3 Shortest density pulse in ticks; below it a period is skipped and its debt carried.
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 — practical sender setup.
  • Laser safety — the hardware chain and what each interlock does.
  • Cooling and airflow — 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.