From 21d3a482c66c6bae52dd5bc594b60c5fc63e3573 Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Sat, 15 Aug 2026 11:03:15 -0400 Subject: [PATCH] Laser safety write-up (docs/SAFETY.md + diagram); bump pin: forgectrl a451e7c docs/SAFETY.md documents the control board's laser safing chain as reverse-engineered and bench-verified - parts, every net with its SoC pin, Linux exposure and polarity, the logic, and the software layers ForgeFIRM stacks on it - with docs/img/safety-chain.svg. Linked from the README. BRINGUP: Next work item 11 records the interlock-latch finding (an open loop never tripped the latch; the kernel now drives INTERLOCK_LATCH_RESET) and the charge-pump watchdog readback, both code-complete and pinned, bench validation on the next image. --- README.md | 4 +- docs/BRINGUP.md | 43 ++- docs/SAFETY.md | 267 ++++++++++++++++++ docs/img/safety-chain.svg | 215 ++++++++++++++ .../recipes-forgefirm/forgectrl/forgectrl.bb | 2 +- 5 files changed, 528 insertions(+), 3 deletions(-) create mode 100644 docs/SAFETY.md create mode 100644 docs/img/safety-chain.svg diff --git a/README.md b/README.md index 585ab45..e530c93 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ 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 the laser safing works](https://github.com/ScottW514/forgefirm/blob/master/docs/SAFETY.md) * [Community Support](https://community.openglow.org) ## What it does @@ -67,7 +68,8 @@ running job unattended — keep a fire extinguisher within reach. Read [Before you cut — safety](docs/LIGHTBURN.md#before-you-cut--safety-read-this-first) before your first job, and the [Regulatory and legal](INSTALL.md#regulatory-and-legal) section before -installing. +installing. [How the laser safing works](docs/SAFETY.md) describes the hardware +safety chain and the software gates ForgeFIRM stacks on it. **A very important warning: this is experimental software. Use of this software could seriously maim or kill you or others, and voids your warranty. It is not diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index 06187ba..b869d67 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -2091,7 +2091,10 @@ accordingly ("Automatic — AP country, else World"). Interlock-trip recovery was exercised in commissioning runs without one — the chain recovers when the condition clears. `cnc/laser_latch` stays write-only (1 = lock), the driver's arm flow unlocks per job, - and `interlock_latch_reset` remains a readback. + and `interlock_latch_reset` remains a readback. **Amended 2026-08-15:** + the *interlock* latch never trips at all in ForgeFIRM — see Next work + item 11; the "recovery" seen in commissioning was the software + safety-door path, not the hardware latch. **Bench items:** open the lid mid-job (expect `Door` at the sender, motion parked, resume on close); jog and `$H` with the lid open (refused); a Pro with an unjumpered interlock connector (expect the @@ -2351,3 +2354,41 @@ accordingly ("Automatic — AP country, else World"). after an underrun cuts at the stale origin unless homing is required (GRBL mode permits unhomed cutting; the underrun itself alarms and unlinks the anchor). +11. **Interlock latch has no hardware trip path in ForgeFIRM (found + 2026-08-15, bench-verified).** With the interlock connector unjumpered + at idle: EV_SW `interlock`=1 (loop open), `interlock_latch`=0 (not + tripped), `cnc/interlock_circuit`=13 (b4 INTERLOCK_RESET=0), + `interlock_latch_reset`=0. This matches the safing schematic: the + interlock latch (U23-2, CD4043B) has RESET = loop-closed and + SET = INTERLOCK_RESET (GPIO4_05) — an open loop only *releases* the + reset, and nothing in ForgeFIRM drives INTERLOCK_RESET (the driver + exposes it as a read-only readback, initialized low; the former + `interlock_reset` LED node that let userspace drive it is gone). So on + a machine with a real external lockout (Pro), an open loop does **not** + cut LASER_ON in hardware; enforcement is the GRBL safety-door hold on + switch code 5 and the cloud client's motion gate. Basic/Plus ship the + loop jumpered. **Decision + fix needed:** drive INTERLOCK_RESET high + whenever the loop is open and hold it until the loop closes, so Q2 + blocks the LASER_ON gate in hardware (the CD4043B is set-dominant, so + the latch stays blocked until the SoC releases SET *and* the loop is + closed). **IMPLEMENTED 2026-08-15 (kernel-module, code-complete, bench + validation pending; kernel-module 015913b, meta-openglow 92d6e20 DTS + + 897c175 pin, forgectrl a451e7c docs, all pushed and pins bumped + 2026-08-15):** `src/cnc_interlock.{c,h}` — an in-kernel input + handler on the gpio-keys switch device (no DT change, GPIO stays with + gpio-keys) drives INTERLOCK_RESET high while EV_SW code 5 reads open, + from probe until the switch device attaches, and if it detaches + (unobservable = open); low only while an attached device reports the + loop closed. Pin init changed to `GPIOF_OUT_INIT_HIGH`. Proof so far: + host test `tests/interlock_test.c` (8 cases, `make -C tests check`, + new CI job `host-tests`) green; module cross-compiled clean against + the staged 6.12.20-fslc kernel with `KCFLAGS=-Werror`, MODPOST silent. + Ships with the next image flash (kernel changes are never hot-swapped); + bench re-run of this exact reading then expects `interlock_latch`=1 / + `interlock_circuit` b4=1 with the loop open, both clearing after it is + closed. Same batch: the charge-pump watchdog readback (`cnc/charge_pump_alive`, + `interlock_circuit` b5; GPIO1_08 = inverted one-shot Q, new + `charge-pump-alive-gpio` + GPIO_8 pad in the linux-fslc DTS — kernel + module and DTB must ship together, the pin is required at probe; DTB + compile-checked with cpp+dtc against the staged kernel). Full write-up + of the chain: `docs/SAFETY.md` (+ `docs/img/safety-chain.svg`). diff --git a/docs/SAFETY.md b/docs/SAFETY.md new file mode 100644 index 0000000..15733f0 --- /dev/null +++ b/docs/SAFETY.md @@ -0,0 +1,267 @@ +# Laser safety: how the machine is kept from firing + +This document describes the laser-safing design of a stock Glowforge running +ForgeFIRM: the discrete hardware chain on the factory control board, what the +i.MX6 can see and drive, and the software layers ForgeFIRM stacks on top. The +hardware chain is the safety boundary; software only ever adds gates in front +of it and never bypasses it. + +Signal names follow the factory board's nets. `GPIOx_yy` is the i.MX6 GPIO; +the Linux name in parentheses is how ForgeFIRM exposes it (device-tree +`gpio-keys` switch, or `glowforge.ko` `/sys/glowforge/cnc` attribute). + +--- + +## 1. Principle + +``` +lid closed (both switches) ─┐ +SoC alive (charge pump) ──┴─▶ HV_ENABLE ─────────────────▶ PSU: HV supply may run + │ +FIRE (per-tick stream bit) ─┐ ▼ +button latch cleared ──┼─▶ LASER_ON ──▶ PSU: tube fires only when BOTH are true +interlock latch cleared ──┘ +``` + +Two independent hardware outputs go to the laser power supply on J1: + +- **HV_ENABLE (J1_16)** — high only while the lid is closed *and* the SoC is + actively retriggering a hardware one-shot ("charge pump"). A hung SoC, a + stuck GPIO or an open lid drops it in hardware. +- **LASER_ON (J1_12)** — the SoC's per-tick FIRE request, AND-gated behind + two hardware latches: the *button latch* (lid state + SoC lock, cleared only + by a physical button press) and the *interlock latch* (set by the SoC, + cleared only while the remote-interlock loop is closed — see §5 for what + that means in ForgeFIRM today). + +The SoC cannot fire the tube by driving one pin. It has to keep the one-shot +alive, release its own lock, wait for a human to press the button while the +lid is closed, and then stream FIRE bits — and any of those conditions going +away kills emission in hardware, not in software. + +--- + +## 2. The hardware chain + +### 2.1 Parts on the control board + +| Ref | Part | Role | +|---|---|---| +| U1 | SN74AHC123A dual retriggerable monostable, R ≈ 499 kΩ / C ≈ 1 µF (t_w ≈ 0.45–0.5 s) | Charge-pump watchdog: Q stays high only while CHG_PUMP keeps arriving; times out ~0.5 s after the last pulse | +| U5, U6 | SN74AHC14 hex Schmitt-trigger inverters | Level inversion / conditioning for every switch line and SoC readback | +| U17 | SN74AHC08 quad 2-input AND | The four gates: DOORS, HV_ENABLE, and the two-stage LASER_ON gate | +| U23 | CD4043B quad R/S latch (NOR type, active-high S/R, output enable tied high) | Latch 1 = button latch, latch 2 = interlock latch | +| U32 | 74AHC1G32 single 2-input OR | Lid-open OR SoC lock → button latch SET | +| U24 | 74AHC1G04 single inverter | E-STOP sense line = ¬HV_ENABLE | +| U18 | i.MX6 Solo | The SoC: drives CHG_PUMP, LATCH_RESET, INTERLOCK_RESET, FIRE; reads everything else | + +### 2.2 Inputs + +| Net | Source | Conditioning | SoC pin | Linux exposure | Meaning | +|---|---|---|---|---|---| +| DOOR_SW1 (L) | J4_13, lid switch pulled to 3.3 V when closed | U5-1 inverts | GPIO4_14 (ball T6) | `gpio-keys` code 0 `door1`, active low → **active = closed** | Left lid switch | +| DOOR_SW2 (R) | J4_12 | U5-2 inverts | GPIO1_06 (T3) | code 1 `door2`, active low → **active = closed** | Right lid switch | +| DOORS | U17-1 = DOOR_SW1 · DOOR_SW2 | U5-3 inverts | GPIO1_00 (T5) | code 3 `doors`, active low → **active = both closed** | The lid term the chain actually uses | +| BUTTON | J5_5, 12 V through the button, 27 kΩ / 8.7 kΩ divider (≈ 2.9 V when pressed) | U5-5 inverts | GPIO4_09 (U6) | code 2 `button`, active low → **active = pressed** | Big front button. Also the RESET input of the button latch | +| INTERLOCK_SW | J8, 12 V through the remote-interlock loop, 432 Ω / 165 Ω divider (≈ 3.3 V when the loop is closed); factory-jumpered on Basic/Plus, brought out on Pro | U6-2 inverts | GPIO1_09 (T2) | code 5 `interlock`, active high → **active = loop OPEN** | Also the RESET input of the interlock latch | +| CHG_PUMP watchdog Q | U1-1 Q (pin 13): /A = GND, /CLR = 3.3 V, B = CHG_PUMP; each rising edge retriggers | U6-6 inverts | GPIO1_08 (R5) | `cnc/charge_pump_alive` (logical), `interlock_circuit` bit 5 (raw, 0 = alive) | watchdog alive | +| Button latch state | U23-1 Q → U5-6 → U6-1 | double inversion | GPIO1_03 (R7) | `cnc/button_latch`, `interlock_circuit` bit 2 | 1 = latch SET (fire blocked / not armed), 0 = armed | +| Interlock latch state | U23-2 Q → U6-3 → U6-4 | double inversion | GPIO1_02 (T1) | code 6 `interlock_latch`, active high → **active = latch SET** | 1 = interlock latch blocking | +| LASER_ON readback | J1_12 net (U17-3 output) | U6-5 inverts | GPIO1_05 (R4) | `cnc/laser_on`, `laser_on_sampled`, `interlock_circuit` bit 0 (raw, active low) | The gated output — the only software-visible proof of emission permission | +| E-STOP | U24 = ¬HV_ENABLE | — | GPIO4_06 (W5) | code 4 `estop`, active high | Sense line, **not** an input: high whenever HV_ENABLE is low (idle), low while HV_ENABLE is alive | +| LASER_PGOOD | J1_14 (the supply's HV_OK line) | — | GPIO4_21 (P24) | `cnc/laser_pgood`, `laser_pgood_sampled` (active low) | Read as "power good" from the laser supply; what the supply actually signals on it is not fully characterized | + +### 2.3 SoC outputs into the chain + +| Net | SoC pin | Driven by | Effect | +|---|---|---|---| +| CHG_PUMP | GPIO3_24 (F22, `charge-pump-gpio`) | `glowforge.ko`: one 0→1→0 pulse at run start, then every 200 ms from a soft hrtimer **only while `state == running`**; forced low on stop, disable, unload and kernel panic | Retriggers U1-1 (t_w ≈ 0.45–0.5 s, so a 200 ms feed holds Q solidly high). No edges → Q falls within ~0.5 s → HV_ENABLE drops | +| LATCH_RESET | GPIO1_07 (R3, `latch-reset-gpio`, init HIGH) | `cnc/laser_latch` (1 = lock). Also drives the FIRE line to high impedance while locked | Into U32 with lid-open; SETs the button latch → LASER_ON blocked until the next button press | +| INTERLOCK_RESET | GPIO4_05 (P5, `interlock-latch-reset-gpio`, init HIGH) | `glowforge.ko`: high whenever the remote-interlock loop reads open, or until a switch device reporting the loop has attached; low only while an attached device reports it closed (in-kernel input handler on the gpio-keys switch, EV_SW code 5). Read back as `interlock_latch_reset` / `interlock_circuit` bit 4 | SET input of the interlock latch → LASER_ON blocked in hardware while the loop is open | +| FIRE (LASER_ENABLE) | GPIO2_30 (E22, `laser-enable-gpio`) | The SDMA script, from bit 4 of each pulse byte; Hi-Z whenever the latch is locked or no run is in flight | One input of the final LASER_ON AND gate | + +Laser *power* (PWM2 on J1_13) is not part of the chain: it sets the tube +current setpoint and is not gated. Emission permission is FIRE ∧ chain; the +laser-off guarantee rests on FIRE, and the kernel drops FIRE within one tick +on end-of-data or underrun. + +### 2.4 Logic + +``` +DOORS_OK = DOOR_SW1 · DOOR_SW2 (U17-1) +WDOG_ALIVE = U1-1 Q, retriggered by every CHG_PUMP rising edge +HV_ENABLE = DOORS_OK · WDOG_ALIVE (U17-4) → J1_16 +E_STOP = ¬HV_ENABLE (U24 inverter) → GPIO4_06 + +Button latch (U23-1): + SET = ¬DOORS_OK + LATCH_RESET (U32 OR) + RESET = BUTTON pressed + Q1 = 1 → fire blocked; 0 → armed + +Interlock latch (U23-2): + SET = INTERLOCK_RESET (SoC: high while the loop reads open or is unobservable) + RESET = interlock loop closed + Q2 = 1 → fire blocked + +LASER_ON = FIRE · ¬Q1 · ¬Q2 (U17-2, U17-3) → J1_12 +``` + +The CD4043B is set-dominant: while SET is high the latch cannot be cleared. +That ordering is what makes the button meaningful — a press only arms the +machine when the lid is closed *and* the SoC has already released its lock. + +![Laser safing chain: lid switches, charge-pump watchdog, button latch, interlock latch and FIRE combine into HV_ENABLE and LASER_ON](img/safety-chain.svg) + +### 2.5 What each condition does, in hardware alone + +| Event | HV_ENABLE | LASER_ON | Recovery | +|---|---|---|---| +| Lid opens (either switch) | drops (DOORS_OK low) | drops immediately: ¬DOORS_OK SETs the button latch | close the lid, SoC lock released, **press the button** | +| SoC asserts LATCH_RESET (kernel `laser_latch=1`) | unchanged | blocked: button latch SET; FIRE line is also Hi-Z | `laser_latch=0`, then a button press | +| SoC stops toggling CHG_PUMP (hang, panic, stop, fault, underrun) | drops within one one-shot period | FIRE is parked by the same paths | next run restarts the feed | +| Button pressed with lid closed and lock released | — | armed (Q1 cleared) | — | +| Button pressed while lid open or lock held | — | stays blocked (SET is dominant) | — | +| Remote-interlock loop opens (Pro) | unchanged | blocked: the kernel drives INTERLOCK_RESET high on the switch edge, setting the interlock latch. Opening the loop by itself only releases the latch's RESET — the board has no direct trip path — so this SoC drive is what makes the interlock a hardware cut (see §3.1); software additionally parks the job on `interlock` | close the loop: the kernel releases INTERLOCK_RESET and the closed loop resets the latch | +| Interlock latch already SET | unchanged | blocked | closing the loop clears it | + +Note the *E-STOP* line: despite the factory name it is an output of this +chain — the inverse of HV_ENABLE. It reads active on a healthy idle machine and +drops for the whole duration of any kernel run, the window in which the charge +pump is fed and HV_ENABLE is alive. Nothing in ForgeFIRM gates on it unless a +machine settings key opts in for a retrofitted circuit. + +--- + +## 3. Software layers on top + +Every layer below sits *in front of* the chain: it can only withhold FIRE, hold +the lock, or starve the charge pump. None can produce emission the hardware +would not allow. + +### 3.1 `glowforge.ko` (kernel) + +- **Laser latch** (`cnc/laser_latch`, write-only): 1 = lock. Locking drives + LATCH_RESET high (button latch SETs) *and* puts the FIRE line in high + impedance so the SDMA stream physically cannot raise it. **Locked by + default; every close of `/dev/glowforge` relocks.** Unlocking never restores + the FIRE drive while a run or ramp is in flight — only run start and the + resume waypoint do, and only if the latch is unlocked at that moment. +- **Charge pump only while running.** The 200 ms retrigger starts with the + run and the callback returns without rearming as soon as the state leaves + `running` (stop, halt, fault, underrun). Stop/disable/unload pin sets force + CHG_PUMP low. +- **FIRE backstop.** At end-of-data and on underrun the SDMA script drops FIRE + and the step lines within one tick; the FIRE line is parked Hi-Z at every + run end and only a latch unlock plus a new run restores it. +- **Interlock latch drive.** The board's interlock latch is reset by a closed + loop but can only be *set* by the SoC's INTERLOCK_RESET line; an open loop + alone does not trip it. The driver owns that line through an in-kernel + input handler on the gpio-keys switch device: it is high (latch set, + LASER_ON blocked) from probe until the switch device attaches, whenever the + loop reads open, and again if the switch device goes away — an + unobservable loop counts as open. Only an attached device reporting the + loop closed releases it, and the set-dominant latch then clears through + its own RESET. The policy is host-tested (`tests/interlock_test.c`). +- **Dead man's switch.** A feeder holds `/dev/glowforge` open with `flock + LOCK_EX`; if that fd closes while a program runs, the driver performs an + emergency stop, puts the head in its safe state and de-energizes the + thermal-loop heat sources. +- **Panic handler.** On a kernel panic the driver stops the EPIT and drives + the pins safe directly: FIRE Hi-Z, CHG_PUMP low, LATCH_RESET asserted, + steppers de-energized — because SDMA and EPIT would otherwise keep playing + the ring with no kernel alive. +- **Readbacks** (`interlock_circuit` bits 0–5, `laser_on[_sampled]`, + `laser_pgood[_sampled]`, `button_latch`, `charge_pump_alive`, + `interlock_latch_reset`) are + monitoring only; the driver enforces nothing from them. Bits 1, 3 and 4 are + driven outputs read back from the data register — bit 3 says what the + driver *commanded*, `laser_on` says what the chain *did*. + +### 3.2 grblHAL controller (`grblHAL-glowforge`) + +- **Operator-armed window.** The first laser-on of a job runs the arm flow on + the protocol thread: coolant fire verdict must be OK, a head must be present + (lens, air assist and beam detector live on it and the chain has no head + term), fans go to the run profile, the latch is unlocked, and the controller + then waits for the physical button (`laser_button_timeout_s`, default 300 s; + a timeout or soft reset relocks and aborts). The hardware button latch is + what the press clears — the software wait exists so the job does not start + streaming FIRE bits into a blocked gate. +- **Disarm.** After `laser_disarm_s` (default 60 s) of no laser use, or on + program end/abort, the controller relocks the latch, turns the button LED + off and stands the cooling profile down. The relock waits for the kernel to + finish the queue tail so a controlled stop can never leave FIRE driven. +- **Coolant fire gates.** The armed window requires a fresh `fire_ok` verdict + from the cooling engine (flow verification, over-temperature, lid-IR + emission witness); a stale or failed verdict relocks in-process. +- **Safety door.** `doors` (lid) and `interlock` (loop open) are the core's + safety-door signal: a running job parks and resumes on close. This is a + motion/UX gate; the lid is *also* cut in hardware by the button latch. +- **Head/motion witnesses.** Position counters are not proof of motion (the + step-stream drives are open loop); the head accelerometer is the motion + witness, and `beam_detect_analog` on the head is the live emission witness. + +### 3.3 forgectrl (machine services) + +- Holds `/dev/glowforge` for its lifetime (pulse-device broker) so controller + handovers never close the device, and **relocks the latch (`cnc/stop` + + `cnc/laser_latch=1`) on every transition out of a running child** — + unexpected death, mode switch, restart. +- The **cooling engine** is the sole owner of the thermal hardware and + publishes the fire verdict the controllers enforce; on a FIRE-class verdict + it writes `cnc/stop` + `cnc/laser_latch=1` itself. +- The **motion-liveness gate** refuses to hand a controller a machine whose + drivers may have wedged (counters running, motors dead) — a laser-safety + corollary of "counters are not motion". +- `/status` reports the switch map and `laser_locked` (`interlock_circuit` + bit 3) for the panel and telemetry; nothing in forgectrl reads the Grbl + socket for machine state. + +### 3.4 Cloud mode + +The factory-experience client runs behind the same kernel latch, charge-pump, +backstop and dead-man rules; the precomputed pulse file it loads is subject to +the same FIRE gating as the live stream. + +--- + +## 4. What is proven, and how + +Verified on the bench with a probe on the PSU-connector LASER_ON pin and the +kernel readbacks (the runbook `BRINGUP.md` holds the drill records): + +- Latch **locked**: 40,000 streamed FIRE bits → PSU pin flat, `laser_enable` + 0 — the lock severs the FIRE drive entirely. +- Latch **unlocked, chain unarmed** (no button press): `laser_enable` 1 + mid-window, PSU pin flat, `laser_on` 0 — the AND gate holds. +- FIRE drop at end-of-data and at true underrun: ≤ 1 tick, both termination + paths. +- A latch unlock inside an acceleration ramp does not restore the FIRE drive + for the in-flight run; a locked latch survives a stop + resume replay. +- Armed kill mid-FIRE: emission tail equals the ring in-flight only + (15–171 ms), the latch relocks, the burn line ends abruptly. +- Switch bits 0–3, 5, 6 verified against physical state; bit 4 (`estop`) + characterized live: high at idle, low through any run. + +--- + +## 5. Not yet established + +Present gaps in the hardware picture. None of them changes the safety +argument (every gap is on the readback/sense side or is a "which part" question), +but each is worth closing: + +- **`estop` toggles seen inside motion windows.** With the one-shot's + measured R/C (≈ 499 kΩ, ≈ 1 µF → t_w ≈ 0.45–0.5 s against a 200 ms feed) + the one-shot cannot gap during a healthy run, so those toggles are most + likely run boundaries (homing and jogs are several short runs) or a feed + late by more than ~250 ms; `charge_pump_alive` sampled through a run + settles it. +- **Interlock latch drive: bench validation pending.** The board itself has + no trip path from the loop to the latch's SET input (bench-verified with the + connector unjumpered: `interlock` open, `interlock_latch` clear while + INTERLOCK_RESET was held low). The kernel drive described in §3.1 closes + that gap; its host test is green and it ships with the next image, where the + expected reading with the loop open is `interlock_latch` set, + `interlock_circuit` bit 4 set, clearing after the loop is closed. +- **`laser_pgood` (HV_OK, J1_14) semantics** are not fully characterized. diff --git a/docs/img/safety-chain.svg b/docs/img/safety-chain.svg new file mode 100644 index 0000000..f269591 --- /dev/null +++ b/docs/img/safety-chain.svg @@ -0,0 +1,215 @@ + + Glowforge laser safing chain as run by ForgeFIRM + Lid switches, the charge-pump watchdog, the button latch, the interlock latch and the FIRE line combine on the factory control board into HV_ENABLE and LASER_ON for the laser power supply; the SoC drives four lines and reads back the rest. + + + + + + + + + Glowforge control board — laser safing chain, as run by ForgeFIRM + + + + PHYSICAL INPUTS + + CONTROL-BOARD SAFING LOGIC (hardware) + + LASER POWER SUPPLY (J1) + + + + LID_SW1J4_13 · closed = 1 + + LID_SW2J4_12 · closed = 1 + + + + ANDU17-1 + + + DOORS_OK (both lid switches closed) + + + + doorsEV_SW 3 · GPIO1_00 + + + + CHG_PUMPGPIO3_24 + 200 ms pulses, only while running + + + one-shotU1-1 · t_w ≈ 0.45 s + + + WDOG_ALIVE + + + + charge_pump_aliveGPIO1_08 (¬Q) + + + ANDU17-4 + + + HV_ENABLE + + + + NOTU24 + + + estopEV_SW 4 · GPIO4_06 · = ¬HV_ENABLE + + + J1_16HV_ENABLE + HV runs only while this + line is alive (pulsed). + + + + + + NOTU5-4 + + lid open + + + LATCH_RESETGPIO1_07 + cnc/laser_latch: 1 = lock (FIRE also Hi-Z) + + + + ORU32 + + + + Button latch + U23-1 · CD4043B + set-dominant + S + R + Q + + + BUTTONJ5 · pressed = 1 + + + + buttonEV_SW 2 · GPIO4_09 + + + + + NOTU5-6 + + ¬Q1 + + + + button_latchGPIO1_03 · = Q1 · 1 = set + + + + INTERLOCK_RESETGPIO4_05 + 1 while loop open or unobserved + + + Interlock latch + U23-2 · CD4043B + set-dominant + S + R + Q + + + INTERLOCK loopJ8 + jumpered on Basic/Plus · closed = 1 + + + + interlockEV_SW 5 · GPIO1_09 · active = open + + + + + NOTU6-3 + + ¬Q2 + + + + interlock_latchEV_SW 6 · GPIO1_02 · = Q2 + + + + ANDU17-2 + + + ANDU17-3 + + + FIREGPIO2_30 (laser_enable) + SDMA pulse-byte bit 4 · Hi-Z when locked or idle + + + + + LASER_ON + + + laser_onGPIO1_05 + + + J1_12LASER_ON + The tube fires only while + this line is high (and HV + is enabled). Power comes + from PWM on J1_13, which + is not part of the chain. + + + HV_ENABLE = DOORS_OK · WDOG_ALIVE + LASER_ON = FIRE · ¬Q1 · ¬Q2 + Button latch: SET = lid open OR LATCH_RESET; RESET = button pressed. Cleared only by a press while the lid is closed and the lock released. + Interlock latch: SET = INTERLOCK_RESET (driven by glowforge.ko while the loop reads open); RESET = loop closed. Both latches are set-dominant. + + + + LEGEND + + physical switch or loop, wired to the board + + line driven by the SoC (glowforge.ko) + + line read by the SoC — gpio-keys switch (EV_SW code) or /sys/glowforge/cnc attribute; monitoring only + + output to the laser power supply + Boxes are logic functions on the control board (part reference below each). Wires carry logic levels: 1 = true as named. + Software can only withhold FIRE, hold LATCH_RESET, drive INTERLOCK_RESET, or stop feeding CHG_PUMP — + every one of those makes the hardware block emission. Nothing on the SoC side can add an emission path. + Also read: door1 / door2 (EV_SW 0 / 1, GPIO4_14 / GPIO1_06), the two lid switches individually; + laser_pgood (GPIO4_21), the supply's HV_OK line on J1_14. + diff --git a/meta-forgefirm/recipes-forgefirm/forgectrl/forgectrl.bb b/meta-forgefirm/recipes-forgefirm/forgectrl/forgectrl.bb index f5f6c66..781302b 100644 --- a/meta-forgefirm/recipes-forgefirm/forgectrl/forgectrl.bb +++ b/meta-forgefirm/recipes-forgefirm/forgectrl/forgectrl.bb @@ -9,7 +9,7 @@ PV = "0.1.0" SRC_URI = "git://github.com/ScottW514/forgectrl.git;protocol=https;branch=main" # Pinned; bump deliberately after pushing forgectrl changes. -SRCREV = "73eda9a5a47c4a94c6df40fa039226129f53d2f9" +SRCREV = "a451e7cd6e6d41cb5ef1d640badca0e8b4b5724b" S = "${WORKDIR}/git"