image: drop the python3 meta-package and the GnuTLS stack; pin forgectrl and grblHAL

The release rootfs carried python3-modules (tkinter, idle, 2to3, pydoc,
ensurepip, venv, asyncio, multiprocessing, xmlrpc) through the python3
meta-package, and libgnutls30, nettle, libgmp10, libunistring5 and
libtasn1 through libmicrohttpd (https) and ulfius (GnuTLS). Nothing on
the image uses either. Each Python recipe declares the module packages it
imports; forgetest declares its own, so the dev image carries no module
the release image lacks.

Pins: forgectrl 0e907f7db54b7a4c90b660f198d49e5c66b667b5 (armed shown from a
fresh report only, zero smoke phase for a dark session), grblhal-glowforge
fa9ed7834faab6f48367dc1ca5a5109b474f788d (rail enable only standalone).

BRINGUP: the lid IR lamp response measured over the full range; no camera
register file under /data; the rail policy and the cool status items
closed; laser.armed-kill stays in its domain; the debug-kernel drill
constraints.

Platform change (layer content): the full campaign is owed on the built
image.
This commit is contained in:
ScottW514
2026-08-31 09:04:37 -04:00
parent 431f0f29fa
commit b334c4cc33
7 changed files with 64 additions and 37 deletions
+29 -29
View File
@@ -960,8 +960,10 @@ is committed.
UAPI "best guess" linear formula was 3–5 °C high and everything derived from UAPI "best guess" linear formula was 3–5 °C high and everything derived from
it had to be re-derived. it had to be re-derived.
- **The four `pic/lid_ir_*` channels are first of all a photometer for the lid - **The four `pic/lid_ir_*` channels are first of all a photometer for the lid
lamp.** Measured against `lid_led`: 0 → `2 2 1 2`, 8 → `2 2 3 2`, 131 → lamp.** Measured against `lid_led` (sysfs brightness, 0 to 1023): all four
`54 55 61 62`, 255 → `172 171 190 188`. Against that lamp-set level, a channels follow it as a straight line, 2 counts dark, 32 to 35 at 128, 54 to
61 at 256, 96 to 105 at 512, 131 to 143 at 768 and 161 to 177 at 1023;
channels 3 and 4 read about 7 percent above 1 and 2. Against that lamp-set level, a
full-power cut raises them only +4 to +6 counts and a candle burning on the full-power cut raises them only +4 to +6 counts and a candle burning on the
bed +3 to +6, with ±3 counts of ambient noise and ~+22 counts of day-to-day bed +3 to +6, with ±3 counts of ambient noise and ~+22 counts of day-to-day
drift. forgectrl's camera engine drives `pic/lid_led` for every lid capture, drift. forgectrl's camera engine drives `pic/lid_led` for every lid capture,
@@ -1132,17 +1134,17 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
thresholds in every pulse header, and the numbers do not look lamp-naive: thresholds in every pulse header, and the numbers do not look lamp-naive:
baseline 3 counts on all four channels, alert at 275 and critical at 688 on baseline 3 counts on all four channels, alert at 275 and critical at 688 on
the first quartile, 374 and 1022 on the second, with the third and fourth the first quartile, 374 and 1022 on the second, with the third and fourth
left at zero. Measured lamp response on this bench is 0 counts to 2, 131 to left at zero. The lamp response (facts bank) puts a fully lit lamp at 161
58, 255 to 180, so the factory's alert sits above the reading a fully lit to 177 counts on every channel and the dark floor at 2, so the factory's
lamp produces and its baseline matches the lamp-off floor. If that holds, alert sits above the lamp and its baseline matches the floor: the factory
the factory rides out the lamp by choosing thresholds above it rather than rides out the lamp by choosing thresholds above it rather than by tracking
by tracking it, and the watch could be re-armed on fixed numbers after all. it, and the watch could be re-armed on fixed numbers after all. Still
Unproven: it assumes the header's quartiles map onto the raw channels and unproven: that the header's quartiles map onto the raw channels and share
share their units. Confirm both by reading the four channels while stepping their units (all four channels behave alike, while the header leaves the
the lamp, then compare against the header the next cloud job carries. By third and fourth quartiles at zero). Confirm against the header the next
decision those header thresholds (`IR??`) are the prior for this redesign cloud job carries. By decision those header thresholds (`IR??`) are the
and nothing else: the cloud client declares them ignored, and the watch prior for this redesign and nothing else: the cloud client declares them
stays disabled until it is lamp-aware. ignored, and the watch stays disabled until it is lamp-aware.
5. **Limit-switch homing.** The planned second homing method (`$22` stays 0 5. **Limit-switch homing.** The planned second homing method (`$22` stays 0
until it lands); printable brackets are in `3d-models/`. Also: calibrate until it lands); printable brackets are in `3d-models/`. Also: calibrate
`gfcloud_home_x/y` against a jog to a known reference if the factory corner `gfcloud_home_x/y` against a jog to a known reference if the factory corner
@@ -1157,9 +1159,12 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
calibration**, an OmniVision LENC register file the factory pushes into the calibration**, an OmniVision LENC register file the factory pushes into the
sensor at every stream start (`load_cam_regs.sh` → a `regs` sysfs attribute sensor at every stream start (`load_cam_regs.sh` → a `regs` sysfs attribute
its driver adds; OV8858 `0x58xx` addresses remapped to the OV8856's its driver adds; OV8858 `0x58xx` addresses remapped to the OV8856's
`0x59xx`). The files are per-machine data, not in the factory rootfs — look `0x59xx`). The files are per-machine data: not in the factory rootfs, and
for them under `/data` on a machine booted into the factory slot before not under `/data` on the bench machine (the partition is shared between the
deciding whether to reimplement the mechanism. Finally the deferred emulator slots and holds no camera register file). Whether the factory app fetches
them from the service at run time is the open question, so a factory-slot
session with the app running comes before deciding whether to reimplement
the mechanism. Finally the deferred emulator
homing-image smoke, now that the emulator can be pointed at live snapshots. homing-image smoke, now that the emulator can be pointed at live snapshots.
7. **Cloud mode.** A print is no longer capped by the ring: the client holds 7. **Cloud mode.** A print is no longer capped by the ring: the client holds
the compressed body, fills the ring before the button, and tops it up as it the compressed body, fills the ring before the button, and tops it up as it
@@ -1208,10 +1213,6 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
hardware themselves while the engine suspends its writes; the check hardware themselves while the engine suspends its writes; the check
parameters are already shared (`cool.h`), so what remains is folding the parameters are already shared (`cool.h`), so what remains is folding the
tools into the engine and retiring the suspend/resume dance. tools into the engine and retiring the suspend/resume dance.
- **Rail policy** (the one `[contract]` item left in SERVICES.md). The GRBL
driver still writes `cnc/enable` at init and at homing resume —
idempotent, since the rail is already up, so this is tidiness rather than
a bounce source.
- **Busy-state arbitration under one lock.** The idle/busy gates (`POST - **Busy-state arbitration under one lock.** The idle/busy gates (`POST
/settings`, `/mode`, diagnostics start, upload/apply) each cross-check /settings`, `/mode`, diagnostics start, upload/apply) each cross-check
`machine_is_idle()` and `update_job_running()` at their own call sites. `machine_is_idle()` and `update_job_running()` at their own call sites.
@@ -1223,17 +1224,18 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
`ensure_engine` `popen()`s should move out of the HTTP callback so a slow `ensure_engine` `popen()`s should move out of the HTTP callback so a slow
media-ctl cannot stall the request thread. Changing the MHD start flags media-ctl cannot stall the request thread. Changing the MHD start flags
touches the streaming model, so this wants a bench slot of its own. touches the streaming model, so this wants a bench slot of its own.
- **`/cool/status` cosmetics.** The endpoint echoes the last reported `armed`
flag even when that report is stale (`report_age_s` tells the truth), and a
gfcloud homing session reports every motion as a job, so the engine cycles
run → smoke → idle per motion. Both are silent and safe.
9. **Physical-evidence negatives still open.** A present head answering I²C 9. **Physical-evidence negatives still open.** A present head answering I²C
badly (the K-11 runtime case) and a failed head capture leaving the measure badly (the K-11 runtime case) and a failed head capture leaving the measure
laser off — both need the head connected and a fault injected. Opportunistic: laser off — both need the head connected and a fault injected. Opportunistic:
`STATE_FAULT` recovery via `enable` the next time a DRV8825 fault line `STATE_FAULT` recovery via `enable` the next time a DRV8825 fault line
actually trips. actually trips.
10. **Debug-kernel checks.** Module load/unload under `CONFIG_DEBUG_MUTEXES` 10. **Debug-kernel checks.** Module load/unload under `CONFIG_DEBUG_MUTEXES`
and a forced `-EPROBE_DEFER` unwind still need a debug kernel build. and a forced `-EPROBE_DEFER` unwind still need a debug kernel build. Both
drills cycle what the rail policy avoids: a module unload powers the 40 V
rail off (a stepper driver can come out of the power-up unserviceable),
and a forced defer needs the 40 V regulator or the SDMA device unbound
under the module's probe. This is a bench slot with the rail-cycle gamble
accepted, not a quick check.
11. **Wi-Fi SDIO CRC watch.** The uSDHC pads now carry the factory-exact values 11. **Wi-Fi SDIO CRC watch.** The uSDHC pads now carry the factory-exact values
and ship in every image. Watch `dmesg | grep -c "sdio .* failed"` across and ship in every image. Watch `dmesg | grep -c "sdio .* failed"` across
sessions (baseline: 1 event in 49 min of uptime). Effect if one lands sessions (baseline: 1 event in 49 min of uptime). Effect if one lands
@@ -1249,10 +1251,8 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
unit-tested but not yet driven from the page. Two catalog gaps from the unit-tested but not yet driven from the page. Two catalog gaps from the
tool's own plan, `cooling.confirm-escalate` and tool's own plan, `cooling.confirm-escalate` and
`cooling.fire-gate-blocks-arm`, are not ported (both need the pump `cooling.fire-gate-blocks-arm`, are not ported (both need the pump
switched by hand mid-run, so they are bench-tab material first), and switched by hand mid-run, so they are bench-tab material first). From
whether `laser.armed-kill` belongs in the always-required core rather the coverage maps: splitting gfutilities' `websocket.py`
than its domain is still an open call (the core carries the emission
witness). From the coverage maps: splitting gfutilities' `websocket.py`
into transport and transfer helpers would take websocket-transport into transport and transfer helpers would take websocket-transport
changes off the offline tests (a gfutilities refactor, not a map). changes off the offline tests (a gfutilities refactor, not a map).
Tools that genuinely need a second host (LAN flood, remote auth probes) Tools that genuinely need a second host (LAN flood, remote auth probes)
@@ -23,10 +23,12 @@ S = "${WORKDIR}/git"
inherit cmake pkgconfig inherit cmake pkgconfig
# curl backs the default-ON WITH_CURL client-request API (find_package REQUIRED). # curl backs the default-ON WITH_CURL client-request API (find_package REQUIRED).
DEPENDS = "gnutls jansson libmicrohttpd curl orcania yder" DEPENDS = "jansson libmicrohttpd curl orcania yder"
# No systemd journald on the forgefirm image. # No systemd journald on the forgefirm image. forgectrl serves plain HTTP
EXTRA_OECMAKE += "-DWITH_JOURNALD=off" # and no websockets: no GnuTLS (which keeps nettle, gmp, libunistring and
# libtasn1 off the rootfs) and no websocket support, which depends on it.
EXTRA_OECMAKE += "-DWITH_JOURNALD=off -DWITH_GNUTLS=off -DWITH_WEBSOCKET=off"
# ulfius builds itself with -Werror -Wconversion; under Yocto's arm32 # ulfius builds itself with -Werror -Wconversion; under Yocto's arm32
# time64 ABI (-D_TIME_BITS=64) a long-long-to-long time conversion in # time64 ABI (-D_TIME_BITS=64) a long-long-to-long time conversion in
@@ -2,5 +2,5 @@
# only SRCREV and PV here - the image manifest leaves *-pin.inc out of the # only SRCREV and PV here - the image manifest leaves *-pin.inc out of the
# layer content hash because the component entry already identifies the # layer content hash because the component entry already identifies the
# pinned source (forgefirm-image-manifest.bbclass). # pinned source (forgefirm-image-manifest.bbclass).
SRCREV = "fac30a4388f96f9f5786a936cd2759b1144ca1a3" SRCREV = "0e907f7db54b7a4c90b660f198d49e5c66b667b5"
PV = "0.1.0" PV = "0.1.0"
@@ -25,7 +25,25 @@ inherit python3-dir update-rc.d forgefirm-manifest
# it identifies the campaign, never a test fingerprint). # it identifies the campaign, never a test fingerprint).
FORGEFIRM_MANIFEST_SRC = "${WORKDIR}/forgetest/forgetest" FORGEFIRM_MANIFEST_SRC = "${WORKDIR}/forgetest/forgetest"
RDEPENDS:${PN} = "python3 forgectrl" # The standard-library packages the tool imports (the page server, the
# catalog, the bench scripts), not the python3 meta-package: the dev image
# must carry no module the release image lacks, or a missing module in
# the release rootfs would pass the campaign unseen.
RDEPENDS:${PN} = " \
python3-core \
python3-compression \
python3-crypt \
python3-fcntl \
python3-io \
python3-json \
python3-logging \
python3-math \
python3-netclient \
python3-netserver \
python3-shell \
python3-statistics \
forgectrl \
"
INITSCRIPT_NAME = "forgetest" INITSCRIPT_NAME = "forgetest"
INITSCRIPT_PARAMS = "start 95 2 3 4 5 . stop 70 0 1 6 ." INITSCRIPT_PARAMS = "start 95 2 3 4 5 . stop 70 0 1 6 ."
@@ -2,5 +2,5 @@
# changes; keep only SRCREV and PV here - the image manifest leaves # changes; keep only SRCREV and PV here - the image manifest leaves
# *-pin.inc out of the layer content hash because the component entry # *-pin.inc out of the layer content hash because the component entry
# already identifies the pinned source (forgefirm-image-manifest.bbclass). # already identifies the pinned source (forgefirm-image-manifest.bbclass).
SRCREV = "06965b6639f6ef15d25683786252bb1bdbc2be50" SRCREV = "fa9ed7834faab6f48367dc1ca5a5109b474f788d"
PV = "0.1.0" PV = "0.1.0"
@@ -14,9 +14,13 @@ DESCRIPTION = "OpenGlow/ForgeFIRM image for Glowforge"
# costs 8.7 MB, most of it libmagic and its database, which nothing else # costs 8.7 MB, most of it libmagic and its database, which nothing else
# here uses. The dev image blanks FORGEFIRM_RELEASE_TRIM to keep it (a # here uses. The dev image blanks FORGEFIRM_RELEASE_TRIM to keep it (a
# removal spec is expanded when applied, so the variable, not the removal, # removal spec is expanded when applied, so the variable, not the removal,
# is what a requiring recipe can override). # is what a requiring recipe can override). python3 is the meta-package:
# it installs every standard-library module (tkinter, idle, 2to3, pydoc,
# ensurepip, venv, the debugger, asyncio, multiprocessing, xmlrpc), which
# nothing here imports. Each Python recipe declares the module packages it
# imports (python3-core plus the few it uses), so the meta-package goes.
FORGEFIRM_RELEASE_TRIM ?= "nano" FORGEFIRM_RELEASE_TRIM ?= "nano"
IMAGE_INSTALL:remove = "gfui-client ${FORGEFIRM_RELEASE_TRIM}" IMAGE_INSTALL:remove = "gfui-client python3 ${FORGEFIRM_RELEASE_TRIM}"
# grblhal-glowforge: the grblHAL motion controller (Grbl over TCP:23). # grblhal-glowforge: the grblHAL motion controller (Grbl over TCP:23).
# forgectrl: the ForgeFIRM machine-services daemon (HTTP :8080): controller # forgectrl: the ForgeFIRM machine-services daemon (HTTP :8080): controller
@@ -0,0 +1,3 @@
# forgectrl serves plain HTTP on the LAN: no TLS in the HTTP library, so
# the rootfs carries no GnuTLS stack for it.
PACKAGECONFIG:remove = "https"