Sensors¶
This page lists the machine's sensors: what each one measures, its raw range, and how a raw reading converts to a physical value. One formula per sensor, stated once. The sysfs attribute reference (names, permissions, groups) is on the kernel module; this page keeps what the sensor is and how its reading converts.
The reference implementation of the conversions is forgectrl src/status.c;
gfhardware's cooling.py implements the coolant conversion for the cloud
client. The GRBL controller converts nothing: it enforces the cooling engine's
published verdict.
Consumers must not reintroduce private variants.
Most analog readings come through the board's PIC analog/digital I/O
controller (the pic/* group), whose 10-bit ADC returns 0 to 1023.
Coolant temperature (pic/water_temp_1, pic/water_temp_2)¶
Both water sensors are 10 kΩ B3380 NTC thermistors in a 10 kΩ divider behind a 1.3× gain stage, read by a 10-bit ADC. The factory firmware converts with the single-parameter B (beta) equation, and ForgeFIRM uses the same formula:
F = 1024 * 1.3 # ADC steps x gain = 1331.2
R = 10000 / (F / raw - 1) # divider resistor 10 kΩ
Rinf = 10000 * exp(-3380 / 298.15) # R0 = 10 kΩ at 25 C, beta = 3380
degC = 3380 / ln(R / Rinf) - 273.15
Higher raw = colder (NTC), so a rising reading means a falling temperature.
The constants are the factory's own, not a curve fit, which is why they are
exact. Treat raw <= 0 or raw >= F as an open or shorted sensor, not a
temperature.
Reference points: raw 640 → 27.0 C, 680 → 23.9 C, 740 → 19.2 C; inversely 31.04 C → raw 591, 50.01 C → raw 391. Checked against a thermometer with the loop at room-temperature equilibrium: within about 1 C of measured.
water_temp_1 is downstream of the flow-check heater, water_temp_2
upstream. The run and resume ceilings gate on the upstream sensor (see
the cooling engine).
Three things that move a coolant reading, and are not temperature¶
All three were found on the bench reference and all three are small, but each is the size of a gate's margin, so each is corrected or accounted for somewhere in the stack.
The processor's load at the moment of conversion, about 6 counts (0.35 C). The PIC converts its inputs in a free-running loop and a read returns the last conversion, so what a reader gets was converted before it asked. The ADC references the PIC's own supply while the sensor dividers hang on the board's reference, so the count follows whatever the supply is doing, and the SoC's load moves it: a value converted while the processor idled reads about 6 counts below one converted under load, with both regimes tight (an interquartile range of 2 over 200 reads). A reader that wakes and reads at once therefore gets one regime, and its next read a fraction of a millisecond later gets the other. The kernel module removes the split by keeping the processor busy before every transaction (the kernel module).
The air-assist fan's return current, about 20 counts (1.2 C). The fan's return shares a ground path with the two thermistors' reference, so both sensors read low by the same amount while it runs, in proportion to the fan's current: nothing below the fan's start duty of 256, and about 1.2 C at the run duty near 22 C. It is not crosstalk on the sensor cable and it is not the high-voltage supply; the step appears with the fan and reverses with it, and a session in which the fan never left idle showed none. The correction is a per-machine setting the coolant-offset diagnostic measures (the cooling engine).
A lit tube, 0.6 to 1.1 C, toggling. With the tube firing, both readings step between two levels mid-run, together, up to twenty-odd times in a run. It is not the fans (steady fans alone show none), not motion (a jog with the fan at run duty shows none), and not the armed state (an armed window with no emission shows none): every toggle sits inside a lit period. The remaining candidate is the high-voltage supply's input current on a return the thermistor reference shares, or its switching. The source stays uncharacterized by decision: the toggle sits inside the over-temperature ceiling's 2 C hysteresis, and the flow check reads means rather than single samples, so it has no consequence for any gate. Settling it wants a scope on the two sensor lines during a cut.
Rare excursions of 10 to 25 counts appear in every regime at a few samples per hundred; the diagnostics that care take interquartile means and drop them.
Chassis temperature (LM75)¶
Read through /sys/class/hwmon, resolved by name, never by index:
enumerate hwmon*/name and use the node whose name starts with lm75
(temp1_input, millidegrees C). hwmon numbering depends on probe order; on
this platform hwmon0 is imx_thermal_zone (the CPU die) and hwmon1 is
lm75b, so a hardcoded hwmon0 reads the CPU die.
SoC die temperature¶
The i.MX6 on-die monitor is thermal_zone0 (imx_thermal_zone, the same node
as hwmon0), governor step_wise. forgectrl reports the temperature as
soc_c and the throttle state as soc_throttle (0 at full speed).
The SoC guards itself. Its trip points are the consumer-grade ones the
driver derives from the part's own fuses: a hot point of 95 C, critical at hot
minus 5, passive at hot minus 10, so 85 C passive and 90 C critical on
this part. The passive trip is bound to cpufreq-cpu0 (996, 792 and 396 MHz
operating points; performance is the only governor built, so the core sits
at 996 MHz until the trip lowers it) and to both GPU cooling devices. The
critical trip is the kernel's orderly power-off. A throttle slows the step
producer, the camera and the protocol thread before it slows the step stream,
because the ring is already in hand.
The board carries no heatsink or fan on the SoC, only the mounting holes for one, and it needs none. Under a full core for five minutes on top of a live camera stream, in a 30 C chassis, the bare die climbs 2.9 C in the first 30 seconds and then plateaus at 70.8 C from three and a half minutes on: 14 C under the passive trip and 19 C under the power-off, with the core still at 996 MHz and no cooling device leaving state 0. The die-to-chassis delta at full load is about 41 C, so by arithmetic the passive trip is a hot-chassis case (above roughly 44 C ambient inside the machine), not a load case.
ForgeFIRM watches this rather than gating on it: the range over each job and any throttle go into one log line at the job's end (the cooling engine).
Board temperatures at idle¶
For scale, with the room at about 22 C and the machine on for hours:
| Sensor | Reading |
|---|---|
| Chassis (LM75) | 29.0 C |
Power supply (pic/pwr_temp) |
589 raw |
| SoC die | 42.8 C |
Power-supply and TEC temperatures (pic/pwr_temp, pic/tec_temp)¶
pwr_temp (0 to 1023): best guess degC = raw * 0.08715 - 21,
unverified. The slope is positive, so this is not the coolant NTC path:
the factory firmware instantiates the beta-equation conversion only for the two
water sensors. The conversion stays unverified by decision (the supply's
heatsink cannot be reached with a thermometer while the machine runs), so
ForgeFIRM reports this sensor as a raw count (supply_raw) and never as
degrees.
tec_temp (0 to 1023): the thermoelectric cooler temperature. The conversion
is unknown (it does not use the coolant beta conversion). Pro machines only: on
a Basic or Plus there is no TEC and no sensor, and the reading sits at the 1023
rail. Report raw values as raw, never as trusted degrees.
High-voltage supply (pic/hv_current, pic/hv_voltage)¶
hv_current (0 to 1023): HV current. The exact meaning of the value is not
established; ForgeFIRM ranges it in every job's log line.
hv_voltage (0 to 1023): HV voltage. Every power supply examined ties the
input of this A/D to ground.
Lid IR sensors (pic/lid_ir_1 to pic/lid_ir_4)¶
Four IR sensors in the lid, each 0 to 1023. Their purpose is flame detection within the enclosure, and the factory sends per-job IR thresholds in every pulse header (see Factory firmware).
They are first of all a photometer for the lid lamp. Measured against
lid_led (sysfs brightness 0 to 1023) on the bench reference, all four
channels follow the lamp as a straight line, about 0.16 counts per unit:
lid_led |
Channels 1 and 2 | Channels 3 and 4 |
|---|---|---|
| 0 | 2 | 1 to 2 |
| 128 | 32 to 33 | 33 to 35 |
| 256 | 54 to 56 | 57 to 61 |
| 512 | 95 to 98 | 103 to 105 |
| 768 | 131 to 133 | 139 to 143 |
| 1023 | 161 to 163 | 172 to 177 |
Channels 3 and 4 read about 7 percent above 1 and 2.
Against that lamp-set level, the things a fire watch would like to see barely move the reading. A full-power cut raises the channels only +4 to +6 counts, and a candle burning on the bed raises them +3 to +6, against about ±3 counts of ambient noise and about +22 counts of day-to-day drift. A candle and a cut are the same size, so no threshold separates them.
Two consequences shape ForgeFIRM's fire watch: a fixed absolute threshold has to sit above a fully lit lamp or the lamp itself trips it, and what such a threshold catches is a developed fire, not a small flame. The design is on the cooling engine.
Fan tachometers¶
All tachometer attributes report the period between pulses; 0 = stopped or stalled (not "infinite speed").
| Attribute | Period unit | Pulses per revolution | RPM |
|---|---|---|---|
thermal/tach_exhaust |
ns | 2 | 60e9 / (period * 2) |
thermal/tach_intake_1, _2 |
ns | 2 | 60e9 / (period * 2) |
head/air_assist_tach |
µs | 8 | 60e6 / (period * 8) |
The purge-air fan in the head has no tachometer. head/purge_air_current
(0 to 1023) is a raw current reading, qualitative only: about 1 with the fan
off, about 628 with it on.
Head sensors¶
- Lens hall sensor (
head/hall_sensor, 0 or 1): the lens position reference. 1 = home, read from an edge low in the lens's travel (13 half-steps above the bottom stop on the bench reference's head) up to the top stop; the edge's height varies from unit to unit. The factory's hunt steps 4 full steps down from the edge to its zero, and its prints count up from there (The motion hardware, Homing). - Beam detector (
head/beam_detect_analog, 0 to 65535;head/beam_detect_digital, 0 or 1): the analog and digital outputs of the beam detector in the head. It is a scatter detector in the beam path ahead of the mirror that turns the beam down to the work, so it sees the beam and not the material. ForgeFIRM reads the analog output as the live emission witness during a job (see The laser). Measured on the bench reference: near 1834 dark, and 2600 to 2890 during fire atS300toS400. Its response at low fire energies is unmeasured. How the detector works inside the head is below. - Accelerometer: the head carries an ST LIS2HH12, bound to the mainline
st_acceldriver. It is the machine's motion witness: the step drives are open loop, so position counters are never proof of motion, and it is the sensor ForgeFIRM's motion-liveness gate, crash watch and commissioning lens finder all use (see forgectrl, the cooling engine, and Motion hardware for what it reads).head/accel_irqreports whether it set its IRQ. The part's own interrupt generator is below.
The head accelerometer¶
The same part (ST LIS2HH12) sits in three places: the head at i2c-3 address 0x1e, the board at i2c-3 address 0x1d, and the lid at i2c-0 address 0x1e. Resolve them by bus path, never by index.
The head one carries a full on-chip interrupt generator, and that generator is the factory's head crash detector:
- Per-axis 8-bit thresholds (
IG_THS_X1/Y1/Z1, registers 0x32 to 0x34), a duration counter (IG_DUR1, 0x35), a per-axis event register (IG_SRC1, 0x31), full scale of ±2, ±4 or ±8 g (CTRL4), and two independent generators, IG1 and IG2. - The threshold LSB is full scale / 256. The datasheet states no LSB for
IG_THS(only forACT_THS, which is full scale / 128), so this was settled on the bench: at ±2 g a threshold of 100 trips on the 1.03 g gravity reading and 150 does not, which brackets gravity at full scale / 256 and would be impossible at full scale / 128. The factory's own values agree: under full scale / 128 its travel-abort threshold of 133 would be 4.16 g at ±4 g, past the measurable range, an abort that could never fire. - The factory arms it per job from the pulse header's
HA*tags, which map bit-exactly onto these registers (per-axis threshold toIG_THS, duration toIG_DUR1, full scale toCTRL4, period and decimator toCTRL5and the output data rate), and reads trips by pollingIG_SRC1over the accelerometer's own bus, in two tiers: an alert pauses, an abort fails. So theHA*values are register values at the scale theHAsrtag sets, not numbers in an unknown unit. - What the factory actually ships, across the 23 captured headers: hunts
arm nothing (every threshold zero); travel files are abort-only (
HAar133 at ±4 g, about 2.08 g); a cut job is alert-only (HAxr132, about 2.06 g;HAyr112, about 1.75 g).HAz*and every idle threshold are zero in every header. The factory never arms Z, because gravity rides it, and never arms the idle state. - The INT pin does not reach the SoC as a host interrupt. It wires to the
head MCU's GPIO and surfaces only as a flag bit there (below). Neither the
factory device tree nor ForgeFIRM's gives the accelerometer an
interruptsproperty. - The generator only samples at a running output data rate, and
st_accelleaves the part powered down between one-shot reads. Anything that arms the generator has to set the rate itself and re-assert it after any liveness read. The registers coexist with the bound driver overi2c-dev:IG_SRC1polls at about 166 Hz whilest_accelraw reads keep working.
For scale: normal commanded motion reads under 0.2 g, and a rail strike reads 1.8 g and up, so the factory's roughly 2 g band sits where it should.
The head MCU, and what the head IRQ really is¶
The head carries a Kinetis KL17 at i2c-3 address 0x47. It is I²C-slave-only to the SoC and never talks to the accelerometer itself.
Once per main-loop pass it samples four head-local GPIO input levels into the
read-only flag register 0x05: bit 0 hall_sensor (the lens Z home), bit 1
the head accelerometer's INT pin (a bare level, not a bus read), bit 2 the raw
beam-detect comparator output, and a fourth, unidentified input the driver
does not expose (a candidate second hall sensor, or a head-present line). A
fifth flag, bit 7, is the processed beam-detect verdict (below). No GPIO pin
interrupts are configured anywhere in its firmware; every input is polled.
The head-attention line the SoC sees (gpio-keys code 7, factory pad name HEAD_IRQ, GPIO3_22) is the MCU's own output driven back. It is level-driven and mirrors register 0x02, the latched IRQ status, being nonzero. Register 0x02 latches edges on the 0x05 bits, but only those the SoC arms through registers 0x03 (rising) and 0x04 (falling), and it is read-to-clear. So the SoC chooses which head events raise the line, answers by reading 0x02 to identify and clear, and reads 0x05 for live levels.
ForgeFIRM writes neither 0x03 nor 0x04 and reads neither 0x02 nor the two undecoded flags, so the head IRQ is dormant by construction: the line sits idle low with a healthy head, pulses while the head MCU reboots (hence the 60 ms debounce in the device tree), and floats to the SoC's pull-up with no head. That is why the raw level is not a presence signal. Presence is the head answering at address 0x47 (Buses).
Beam detect inside the head MCU¶
The head's beam detector is more than the two attributes the driver exposes:
- The sensor reaches the MCU's ADC on pin PTE16, and its raw analog level
is register 0x16, published as
head/beam_detect_analog. - A float EWMA and CUSUM over five coefficients (registers 0x22 to 0x2a:
LAMBDA_K0x07ae,LAMBDA_T0x1999,THETA_R0x20,THETA_T0x28,E_T0x60) feeds an N-of-M sliding-window verdict into flag 0x05 bit 7. ForgeFIRM's head probe writes those five coefficients, but they are the firmware's own power-on defaults. - A DAC (register 0x1e, default 0x3ff) sets an analog comparator threshold
whose raw output is flag 0x05 bit 2, published as
head/beam_detect_digital.
The driver exposes the raw comparator and the raw analog level, not the processed verdict at bit 7. Whether the factory enables beam detect in production is unknown: its 2.6.0 application carries a complete but config-gated subsystem, with separate printing and idle enables, three severities, a level-or-edge trigger option and a fault-report upload, and an invalid severity defaults to disabled.
Safety-chain readbacks¶
The lid switches, the button, the interlock loop, the two latches, the charge-pump watchdog, LASER_ON and LASER_PGOOD are all readable. They are monitoring only; enforcement is the hardware AND gate. The inputs, their pins and their Linux exposure are tabulated on The safing chain; the attribute and switch reference is on the kernel module and forgectrl.