Two strands, both landing here because they had grown into each other in the
working tree.
The printer replica: the printer's real rootfs.squashfs chrooted under
qemu-mipsel, with /usr/prog and /usr/data from the factory image, so a package
is installed by FlashForge's own updater running on the printer's own busybox,
tar, md5sum and unTar. There are no stubs -- if the real prog partition is
missing the harness refuses to run rather than test against mocks. A release
workflow is blocked on it.
The lanes: a file's directory now says whether it can ever reach a printer.
payload/, assets/ ship
bin/, profiles/ build the package
test/, docs/ neither, ever
* the replica's own infrastructure moved out of bin/ and docker/ and in
beside the harness it belongs to (test/extract-rootfs.sh,
test/build-printer-image.sh, test/printer/Dockerfile{,.full})
* the Makefile has two runners: only RUNSIM gets the docker socket and the
replica's variables, so a build cannot reach the docker daemon at all
* replica settings moved to test.env, which nothing in a package reads
* build-time flags renamed BUILD_* (APPLY, REPORT, KLIPPER, TOOLCHANGE,
MAINSAIL, HELIX). MOD_* now means only the runtime switches written into
anvil.conf and re-read by the printer at every boot
* verify.sh fails if any file in a package is byte-identical to one in
bin/, test/, docker/ or profiles/ -- by content, because a name blacklist
would trip over whatever Mainsail happens to call common.sh
Fixed while mapping it:
* BUILD_REPORT was read only inside the BUILD_APPLY=0 branch, so four
profiles declared it and got nothing. It is honoured on both paths now,
and off outside probe: report.sh copies /etc, passwd and shadow onto the
USB stick, which is a diagnostic, not something to hand out.
* three checks in the brick lint had been quietly dead. Two were guarded on
a payload/boot.sh replaced by the firmwareExe wrapper long ago; the third
was the file glob itself, which matched neither firmwareExe nor the
init.d/S* scripts -- the UI wrapper and every service went unscanned. A
missing file is now a failure rather than a silent pass.
* run-tests.sh overwrote config.env and copied it back, leaving the config
you edited one crashed run away from being a fixture. It uses CONFIG_ENV.
* bin/make-uninstall.sh does not exist and never did; README, run-pre.sh and
full.env all pointed at it. Recovery is the stock package, which is what
test-recovery actually proves.
README is now for someone who wants to flash their printer: what it is, which
file, how to flash, what to do when it goes wrong. The rest moved to
docs/how-it-works.md, docs/building.md and docs/testing.md.
make test: 89 passed, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.8 KiB
Hardware testing: the flash ladder
CI installs the package into a replica of the printer -- the real
rootfs.squashfs running under qemu-mipsel, on the printer's own busybox and
unTar (see printer-replica.md) -- and proves the
machine would still boot. What it cannot do is drive the screen, the MCUs, the
toolchanger or a print. This is the on-hardware procedure, ordered so that each
step is recoverable using what the previous step established.
Rule: never skip a rung. Each one exists because it makes the next one recoverable.
Step zero: check the model gate
Every package carries a gate. Its runFirmwareExe.sh has MACHINE= / PID=
baked in, and compares them against the values app_startup.sh passes from
the firmware already on the printer. A mismatch is refused with
"Firmware does not match machine type" — it will not install, and it is not a
valid recovery image either.
| Model | MACHINE | PID |
|---|---|---|
| Creator 5 | Creator5 |
0028 |
| Creator 5 Pro | Creator5Pro |
0029 |
The gate is not cosmetic: the two models ship different firmwareExe
binaries, so a package must be built from the stock package for its own
model. make release builds both.
Find yours in Settings → About, or in
/usr/data/firmwareRes/config/general.json (machineName), and set
TARGET_MACHINE in config.env. Then:
make full && make verify # one model
make release PROFILE=full # both, into dist/
verify.sh fails loudly on a mismatch. You must start from a stock package
built for your own model — the mod inherits the gate from whatever package
you unpack.
Before the first flash
- Put a copy of the stock FlashForge package for your model on a spare
USB stick and keep it physically separate. That is your recovery image:
flashing it restores every file the mod touches (proven by
make test-recovery). Check it is the right model first — see above. - Note your printer's serial number (Settings → About). The factory image restores a placeholder serial and you may need to put yours back.
- Confirm you can reach the printer's IP.
make testpasses (78 checks).make rootfs && make test-ashpasses — this parses every script that will run on the printer using the printer's own busybox ash.
Two USB sticks, FAT32, both packages at the root of the stick (not in a
folder). Ship both filenames (Creator5-* and Creator5Pro-*): the Pro's
boot script globs Creator5Pro-*.tgz, the non-Pro globs Creator5-*.tgz.
Stage 0 — probe (make probe)
Changes nothing. Reinstalls the stock software component byte-for-byte and writes a diagnostic report to the USB stick.
Flash it, wait for the reboot, pull the stick and read:
| File on the stick | What it tells you |
|---|---|
anvil-report.txt |
free space, partitions, installed versions, init layout, whether nginx/moonraker really exist |
anvil-stock-etc.tar |
the whole /etc, so the init chain can be studied offline |
anvil-stock-nginx.conf |
the stock nginx config (not shipped in any update package) |
anvil-stock-bootscripts.tar |
app_startup.sh, start.sh, passwd, shadow as they exist on YOUR unit |
Go/no-go: the report exists and the printer boots normally. If the stick comes back empty, the package never installed — nothing was changed, and you have learned that before risking anything.
Check in the report:
- free space on
/usr/datais comfortably above the size of your full package moonrakerDmn: yesandnginx binary: yes- the
INIT SYSTEMblock matches expectations (busybox init,/etc/init.d/rcS,S50dropbearpresent)
Stage 1 — ssh (make ssh)
Sets a root password you know. Nothing else changes.
This works because the stock rootfs already ships /usr/sbin/dropbear and an
enabled /etc/init.d/S50dropbear — port 22 is already open on a stock
printer, there is simply no published password.
Set one first:
openssl passwd -6 'your-password' # paste into ROOT_PW_HASH in config.env
Go/no-go:
ssh root@PRINTER
- you get a shell
- the printer still prints normally
From here on you have a real recovery channel, which is what makes every later stage safe.
Stage 2 — web (make web)
Starts nginx (Mainsail on :80) and moonraker (:7125). Klipper and the touchscreen stay stock, so printing behaviour is unchanged.
Go/no-go:
http://PRINTER/loads Mainsail- Mainsail shows the printer as ready (not "Klipper reports: ERROR")
- the touchscreen still works
- a small test print completes from the touchscreen
If Mainsail loads but Klipper errors, stop here and read
/usr/data/logs/printer.log over ssh. Do not continue.
Stage 3 — full (make full)
Replaces the Klipper tree with the fork and installs the toolchanger extras. This is the first stage that changes how the machine moves.
Before flashing, re-read the warnings in creator5-toolchange/README.md
about Z offsets. A wrong offset drives the nozzle into the plate.
Go/no-go — with the emergency stop within reach:
- Klipper starts (
ssh root@PRINTER 'grep -i error /usr/data/logs/printer.log | tail') TOOLCHANGE_STATUSresponds in the Mainsail console- home all axes — watch the first Z move
- one tool change, by hand, at temperature
- a single-tool test print
- a multi-tool test print
The .cfg includes are not wired up automatically — a tuned printer.cfg
is never modified. New files arrive as *.mod-new. Add the includes by hand
in the order given in the toolchanger README, then run
FF_IMPORT_FIRMWARE_CONFIG once.
Stage 4 — helix (make helix)
HelixScreen replaces FlashForge's firmwareExe as the touchscreen UI.
This is the stage where the stock UI stops driving the screen, so understand the safety net before flashing:
- The genuine binary is kept on disk as
firmwareExe.stockand is the fallback. Nothing is deleted. init.d/S70klipperowns Klipper startup, because on stock firmware it wasfirmwareExe— not any init script — that ran/usr/prog/klipper/start.sh. Without this the printer would boot to a working screen and be unable to move.- If the UI dies repeatedly,
SAFE-MODElatches after 3 boots and the printer falls back to the stock UI on its own.
Go/no-go:
- HelixScreen appears on the touchscreen
- Klipper is running (Mainsail says ready) — this is the one people miss
- heaters and motion respond from the touchscreen
- a test print from the touchscreen
Recovering from a bad UI, in increasing severity:
ssh root@PRINTER
/usr/data/anvil/init.d/S80ui status # what did it choose, and why
touch /usr/data/anvil/SAFE-MODE # force the stock UI on next boot
reboot
If ssh is gone too, flash the stock FlashForge package for your model.
If something goes wrong
| Symptom | Do this |
|---|---|
| Printer boots, screen blank | ssh in; touch /usr/data/anvil/SAFE-MODE; reboot |
| No ssh, no screen | flash the stock package for your model |
| Recovery stick does not help | try a newer stock FlashForge package for your model |
| Still broken | factory package (Creator5Pro-factory-*.tgz plus the separate Creator5Pro-factory.tar.xz on the same stick; needs 800 MB free) |
The mod never creates a mount point named like a mod, specifically so that FlashForge's factory-restore package — which greps the mount table for a known community mod's name and refuses to run when it matches — remains usable as a last resort.
Logs worth reading, all on the data partition and all surviving a reboot:
/usr/data/anvil-install.log what the installer did
/usr/data/logs/anvil-boot.log services + UI choice at each boot
/usr/data/logs/printer.log klipper
/usr/data/logs/helixscreen.log helixscreen