README: replace the stalled REST/GUI roadmap with what the firmware does -
GRBL mode over TCP for LightBurn/UGS/cncjs, optional cloud mode, the web
control panel, cameras, A/B install beside the factory firmware - plus the
supported hardware incl. the 8 MP camera limitation and a real roadmap.
kas/README: xvclk is the board's 24 MHz oscillator; the pin/push rule covers
every source repo; camera and motion sections are hardware-validated, with the
OV8856 caveat spelled out; the device-tree item closes with the control_12v
residue named; forgectrl is the machine-services daemon with two supervised
controllers.
LIGHTBURN.md: drop the pre-first-light status line.
BUILD.md: state the NXP firmware licensing and where the EULA lives on the
machine. The image now installs firmware-imx-lic, so the EULA text ships beside
the VPU/EPDC blobs it covers; a bbappend declares that package at parse time,
which is what makes it installable from an image recipe.
Record the finished shared-services state: forgectrl is the one
machine-services daemon behind both controller modes, both controllers
are cooling-engine clients, and the drill and soak coverage that proves
it. Attribute the cooling policy records to the engine's present home.
Add the remaining polish as Next work item 8 - diagnostics as engine
modes, the rail-policy remainder, cloud per-job fan profile
confirmation, /cool/status cosmetics, and button edge detection.
The controller lifecycle belongs to the forgectrl supervisor (it
spawns the selected controller as a direct child); the init script no
longer starts grblHAL and remains only as a manual emergency stop.
BRINGUP item 2: grbl-mode laser software implemented + bench-verified
without fire (record in the gate list); first light pending.
LIGHTBURN.md: arming/button-press operation, S-max 1000, fire gates.
scripts/bench/laser_stream_test.py: host-side stream-dump contract
verification against the null-sink build.
The cloud-action-surface branches are merged: forgectrl builds from main
(same revision), and the forgefirm-app include pins the python3-gfhardware
master head (adds docs/CLOUD.md and the clean-shutdown gfhome).
gfhome.py, ffmachine.py, and gfcloud.py (with the gfcloud init script
and gfhome conf sample) live in the python3-gfhardware repository's
forgefirm-app/ directory. The three recipes fetch that repo through a
shared include (forgefirm-app.inc) carrying a single pinned SRCREV,
replacing the local file:// copies. Package names, dependencies, and
installed paths are unchanged.
ws_connect() returns the running WsClient, so gfhome no longer needs to
build the client by hand to get a clean disconnect; the exit path uses
WsClient.shutdown() to stop and join the socket thread.
Points the forgectrl recipe at the pushed cloud-action-surface commit
(controller_mode=cloud + the compatibility banner) so a test image
carries the cloud-mode UI. Repoint to main once the branch is merged.
Enable the read-only firmware-version probe and point it at
/data/forgefirm/gf-latest.json, where the web-service client records the
latest firmware Glowforge advertises and the version this release was
tested against. forgectrl reads that file for the cloud-mode
compatibility banner.
gfcloud runs the machine under the Glowforge web service (the factory
cloud experience: the app drives homing, framing, printing). Its init
service starts it only when controller_mode = cloud - mutually exclusive
with grblHAL, which already stands down for that mode - so it owns
/dev/glowforge exclusively. Persistent GFUIService loop with a clean
SIGTERM shutdown that safes the hardware; the init script mirrors
grblhal.init. The image installs gfcloud and python3-ffmachine.
Extract the shared-config identity overrides and the forgectrl-routed
ForgectrlMachine from gfhome into an installed module (python3-ffmachine);
gfhome imports it instead of carrying its own copy, so the coming gfcloud
daemon builds the same machine the same way.
Replace the local dispatch table with gfutilities' dispatch_action
(allow_print=False), so the homing runner and GFUIService share one
action surface and cannot drift.
b5c6d9c declares the shared config readers in glowforge_homing.c;
without the declaration the hard-float build read the homing-session
timeout from the wrong register and SIGTERMed every gfcloud homing
runner ~55 ms after spawn.
Bump to the forgectrl revision whose startup pass reloads
regulatory.db (required while the flashed kernel still has built-in
cfg80211 - its boot-time load fails pre-mount and stays failed
without a reload), hints a region only when one is set (unset =
automatic: the AP's 802.11d country, else world; a 00 hint over the
kernel's own world default reports the confusing intersection alias
"country 98"), and pins wlan0 power save off.
BRINGUP.md: bench record for the wireless-regulatory diagnosis and
the live-verified wifi_country / power-save flow; the kernel-batch
note now carries CFG80211/MAC80211=m, DEFAULT_PS off, and the lm75
vs-supply.
Bump to the forgectrl revision that adds the wifi_country setting
(System-tab region picker); the daemon applies it with iw reg set at
startup and on change, so iw joins RDEPENDS. regulatory.db itself
ships via the glowforge-image base (wireless-regdb-static).
Before writing the target slot, the installer now shows what it holds
(factory firmware v<ver>, ForgeFIRM, an unrecognized filesystem, or
unknown/unreadable content). Factory images are archived as before;
anything else requires the operator to type ERASE, since it is
overwritten without a backup. The archive manifest now records the
semantic FIRMWARE_VERSION (ver=), which the update manager displays in
the restore list. Bump forgectrl to the matching GUI change.
/etc/forgefirm/keys ships the ForgeFIRM release-signing public key
and the Glowforge factory keyring (public keys only) - the update
manager verifies release downloads/uploads against the former and
factory archives against the latter. forgectrl SRCREV bumped to the
update-manager commit; runtime deps on ffboot, fwup, the keyring, and
curl made explicit.
The runbook no longer records the bench machine's fuse identity (a
fuse identity cannot be rotated, so a public document must never
carry it); the forgectrl pin follows that repo's history rewrite.
The image's busybox has no blockdev applet, so the grow path silently
skipped. Sector counts now come from /sys/class/block (no external
tool); log lines also go to /dev/kmsg so migration results are visible
in dmesg after boot.
The gf_hostname override is gone (the forgectrl UI no longer offers
it): the hostname is a pure derivation of the serial - base 23 over
the factory consonant alphabet - so a gf_serial override re-derives
MACHINE.HOSTNAME and the fuse derivation stands otherwise. BRINGUP
records the panel rework (units, fuse identity, always-on position)
as offline-verified; board deploy and pin bumps held during the
firmware-upgrade bench testing.
install-forgefirm.sh is now single-stage and never repartitions: run
from factory firmware, it archives every factory slot version plus the
recovery boot partitions to /data/forgefirm/archive (manifest with
md5s), verifies the signed forgefirm.fw against the embedded ForgeFIRM
pubkey (raw 32-byte form for the factory's fwup 0.14.2; dev key until
the production key ceremony), applies it to the INACTIVE slot with the
factory's own fwup, post-verifies the written rootfs, installs
/data/ffboot, and flips the saved env with read-back verification. The
booted factory slot stays installed and bootable; /data is untouched
beyond the archive. Fixed release asset name forgefirm.fw (version in
the fwup metadata and release tag).
slotmigrate (new recipe, rcS before mountall) reclaims the legacy
layout on eMMC-slot boots: deletes p4, grows p3 to the end of the
disk (sfdisk + partx BLKPG - works with a sibling partition as root),
then e2fsck+resize2fs. Every step is keyed off the actual disk state,
so interrupted runs resume and factory-layout disks are a no-op; SD
boots never touch the eMMC.
-l inventories every bootable partition (SD, eMMC slots 1/2, legacy
p4) as machine-parsable key=value lines: firmware type
(forgefirm/factory), version, kernel presence, booted/next markers,
plus the saved-env selection - the shared probe for the installer and
the forgectrl update manager.
Boot switching now writes all four selection variables (mmcdev,
mmchwpart, mmcpart, mmcroot) in one fw_setenv -s transaction and
read-back verifies, falling back from the libubootenv script format
to the classic u-boot-tools format to per-variable writes - the same
script works on factory firmware and ForgeFIRM. mmchwpart was
previously never set and a mixed env could survive a mid-flip power
cut. Switch targets must pass a content probe (rootfs mounts, kernel
present) unless -f. -e picks the newest factory slot by probing,
excluding slots occupied by ForgeFIRM.
The new ffboot recipe installs it as /usr/sbin/ffboot with
/etc/fw_env.config (factory-identical redundant env layout at eMMC
0x80000/0x82000) - previously the image shipped fw_setenv with no
config. Added to forgefirm-image.
The release image now targets the 200 MiB factory eMMC slot: content
plus 40 MiB working space, hard build failure past the slot size. The
raw ext4 is deployed alongside the wic; scripts/mkfw.sh packs it into
a signed .fw with factory-pattern upgrade.a/upgrade.b tasks. fwup
1.16.0 recipe (applies ForgeFIRM and Glowforge-signed archives on
device) is installed in both images. Dev images stay SD-sized with a
256 MiB working margin and no ceiling.
Verified on the 20260808153331 build: release ext4 180.8 MiB; signed
.fw applies byte-exact with fwup 1.16.0 and with the factory's 0.14.2
(raw-format pubkey), and 0.14.2 -V verifies the signature.
Every image writes /etc/forgefirm-version and echoes "ForgeFIRM
<version>" on the serial-console login prompt (/etc/issue, beneath the
OpenGlow banner) and at SSH login (motd). Release images carry
v${FORGEFIRM_RELEASE}; the dev image stamps the build timestamp - the
same DATETIME as the artifact name - tagged (dev) so a bench machine
is never mistaken for a release.
The init script consults controller_mode in /data/forgefirm.conf:
'cloud' (once that mode exists, with its own service reading the same
key) keeps grblHAL down; grbl, unset, or a missing config starts it.
Board-verified both ways: cloud in the conf leaves the controller
stopped with a clear message, grbl starts and serves normally.
forgectrl ad0b441: OpenGlow branding, operational /status dashboard.
grblHAL-glowforge c472a13: position anchor at homing for the status
readers. Runbook updated.
Non-empty gf_serial / gf_password / gf_hostname in /data/forgefirm.conf
(set from the forgectrl GF Cloud tab) are applied with set_cfg before
Machine() is built, so they beat the OCOTP fuse identity - Machine sets
its fuse values with keep_value. The --timeout default comes from
GFHOME_TIMEOUT_S when the controller provides it, so one GUI setting
governs the whole session. Docs: control-panel runbook notes.
The runner's Machine subclass fetches lid/head images from the
forgectrl snapshot endpoint - forgectrl owns the imx-media pipeline
whenever a stream client (LightBurn) is connected, so direct V4L2
grabs fail busy. Head captures request lamp=0 (the cloud's focus
analysis needs torch-off images); the HCil measure-laser LED is still
driven directly. Direct capture remains the fallback when the daemon
is unreachable.
The sample config gains the factory-board settings: the estop motion
gate stays off (the board's estop sense reads low during any motion),
and the forgectrl URL is configurable.
New recipe installing /usr/sbin/gfhome.py and /etc/gfhome.conf.sample
(copied to /data/etc/gfhome.conf on first run). The runner signs the
machine in with its fused identity, opens the WSS control channel with
a held client reference (a clean disconnect is impossible through
ws_connect), and drives the GFUIService dispatch table itself - minus
print - so the service's camera homing sequence (settings -> hunt ->
lid image -> corner move -> lid image) runs against the real hardware
Machine. The service ends the sequence silently, so completion is a
hunt plus at least one motion followed by a configurable quiet window;
the lens is then re-referenced against the hall sensor for a
deterministic Z. Lid/e-stop are checked before the session, print
actions are refused, and exit codes distinguish configuration and
connection failures from an incomplete homing.
Installed in both images; invoked by the grblHAL controller for $H
when homing_mode = gfcloud in /data/forgefirm.conf.