mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-20 12:29:45 -05:00
Fix the runout insert path and TOOL=ALL staging; drop SAFE-MODE for MOD_UI
A2: _FF_INSERT's body was entirely inside {% if kind == 'switch' %}, yet
insert_gcode is wired on all four fm_ex* with KIND=motion. A motion insert --
which is what clearing a clog looks like, filament moving again -- produced no
output at all, on exactly the path where the operator most needs the next
step. Adds the motion branch the comment already described.
A3: TOOL_OFFSET_CALIBRATE staged each tool with set_nozzle() inside the loop,
so a gap-check failure on a later tool left earlier tools pending for
SAVE_CONFIG -- while raising an error that says "nothing saved". True for
TOOL=<n>, false for TOOL=ALL, and told to the operator at the moment they
decide whether to save. Staging now happens after every tool has passed.
A8: SAFE-MODE is removed rather than repaired. It never worked: the settle
check cleared the counter 90s after every boot on a condition
(`[ -s $CHOICE ]`) that cannot be false, since $CHOICE is written two lines
earlier and "none" is not empty. Nothing checked whether helix-screen was
alive, so the latch only fired on three reboots inside 90 seconds -- not the
crash-loop it was written for.
In its place, MOD_UI in anvil.conf, alongside MOD_WEB/MOD_CAM/MOD_SSH/MOD_WIFI
and templated by patch.sh the same way. It stops the UI being started; it does
not pretend to repair one. The docs said `touch SAFE-MODE` was how you recover
a blank screen. Nothing recovers a blank screen on the machine: HelixScreen is
the only UI, and getting one back means flashing a package. Said plainly now,
in the README, the recovery section and the troubleshooting table -- along with
what is genuinely unaffected, which is ssh, Mainsail, Klipper and printing.
48 passed, 5 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e5bd171cf8
commit
b2c651b11f
11 files changed
+62
-58
No files matched your search
@@ -102,10 +102,13 @@ installs the same way — stick in, power on.
|
||||
A few things are deliberately built in so a bad flash cannot cost you the
|
||||
machine:
|
||||
|
||||
* **A crashing UI cannot lock you out.** If HelixScreen cannot start or
|
||||
crashes repeatedly, the printer latches SAFE-MODE and boots *headless* —
|
||||
no UI at all — instead of looping. Delete `/usr/data/anvil/SAFE-MODE` over
|
||||
ssh to try again.
|
||||
* **A crashing UI cannot lock you out.** The screen is not how you reach the
|
||||
printer: ssh and Mainsail come up from their own services, before and
|
||||
independently of the UI, so a dead screen costs you the screen and nothing
|
||||
else — you can still print. It is not repairable on the machine, though:
|
||||
HelixScreen is the only UI there is, and getting a working screen back means
|
||||
flashing a package again. `MOD_UI=0` in `/usr/data/anvil/anvil.conf` stops
|
||||
it being started at all.
|
||||
* **ssh stays up** even when the screen does not, so you can get in and look.
|
||||
* **Your `printer.cfg` is never overwritten** — it is not a file this
|
||||
firmware ships, and it is where your changes belong. The configs the mod
|
||||
|
||||
+5
-4
@@ -27,10 +27,10 @@ fi
|
||||
# above -- still overrides any of them.
|
||||
#
|
||||
# FlashForge's firmwareExe is REPLACED, not kept: HelixScreen is the only UI.
|
||||
# If it crash-loops, S80ui latches SAFE-MODE after 3 failed boots and the
|
||||
# printer boots headless instead of looping. ssh and Mainsail are your
|
||||
# recovery path if the screen is dark, and a USB stick with the STOCK
|
||||
# FlashForge package on it is the uninstall (proven by `make test-recovery`).
|
||||
# If it will not start, set MOD_UI=0 in /usr/data/anvil/anvil.conf to boot
|
||||
# headless. ssh and Mainsail are your recovery path if the screen is dark, and
|
||||
# a USB stick with the STOCK FlashForge package on it is the uninstall (proven
|
||||
# by `make test-recovery`).
|
||||
BUILD_KLIPPER="${BUILD_KLIPPER:-fork}"
|
||||
BUILD_TOOLCHANGE="${BUILD_TOOLCHANGE:-1}"
|
||||
BUILD_MAINSAIL="${BUILD_MAINSAIL:-1}"
|
||||
@@ -38,6 +38,7 @@ BUILD_MOONRAKER="${BUILD_MOONRAKER:-1}"
|
||||
BUILD_HELIX="${BUILD_HELIX:-1}"
|
||||
MOD_SSH="${MOD_SSH:-1}"
|
||||
MOD_WEB="${MOD_WEB:-1}"
|
||||
MOD_UI="${MOD_UI:-1}"
|
||||
|
||||
# Third-party payload pieces (Mainsail, HelixScreen, Moonraker). They are
|
||||
# downloaded on demand rather than vendored, so the repo carries no binaries
|
||||
|
||||
@@ -288,6 +288,7 @@ cp -f payload/init.d/S* "$MP/init.d/"
|
||||
chmod +x "$MP/init.d"/S*
|
||||
sed -e "s/^MOD_WEB=.*/MOD_WEB=${MOD_WEB:-1}/" \
|
||||
-e "s/^MOD_CAM=.*/MOD_CAM=${MOD_CAM:-1}/" \
|
||||
-e "s/^MOD_UI=.*/MOD_UI=${MOD_UI:-1}/" \
|
||||
-e "s/^MOD_SSH=.*/MOD_SSH=${MOD_SSH:-1}/" \
|
||||
-e "s/^MOD_WIFI=.*/MOD_WIFI=${MOD_WIFI:-1}/" \
|
||||
payload/anvil.conf > "$MP/anvil.conf"
|
||||
|
||||
@@ -102,9 +102,10 @@ screen and there is nothing to fall back to:
|
||||
`firmwareExe` — 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-MODE` latches after 3 boots and the printer
|
||||
boots **headless** — no UI started at all — rather than crash-looping. ssh
|
||||
and Mainsail stay up. Delete `/usr/data/anvil/SAFE-MODE` to try again.
|
||||
- If the UI misbehaves, set `MOD_UI=0` in `/usr/data/anvil/anvil.conf` and
|
||||
reboot: the printer comes up **headless** — no UI started at all. ssh and
|
||||
Mainsail are unaffected either way. There is no automatic latch; nothing
|
||||
stops the UI unless you do.
|
||||
|
||||
**Go/no-go, in this order — with the emergency stop within reach:**
|
||||
|
||||
@@ -223,14 +224,22 @@ every flash. Each of those files opens with a header saying as much.
|
||||
`moonraker.conf` is the one exception — it has no include-and-override seam, so
|
||||
an edited copy is kept and the new version lands beside it as `.mod-new`.
|
||||
|
||||
**Recovering from a bad UI, in increasing severity:**
|
||||
**A bad UI is not repairable on the printer.** There is no fallback interface:
|
||||
FlashForge's binary is replaced and HelixScreen is the only thing that draws on
|
||||
the screen. ssh tells you *why* it is dark, but nothing you can set on the
|
||||
machine will make it light up again:
|
||||
|
||||
```sh
|
||||
ssh root@PRINTER
|
||||
/usr/data/anvil/init.d/S80ui status # what did it choose, and why
|
||||
touch /usr/data/anvil/SAFE-MODE # boot headless next time (ssh + Mainsail only)
|
||||
reboot
|
||||
# chose "none" -> MOD_UI=0, or HelixScreen is not installed
|
||||
# chose "helix" -> it was started and failed on its own; see its log
|
||||
```
|
||||
If ssh is gone too, flash the stock FlashForge package for your model.
|
||||
|
||||
`MOD_UI=0` only stops it being started at all — useful to silence a UI that
|
||||
interferes with something else, not a repair. The actual fix is to flash a
|
||||
package again: the mod, if you have a corrected build, or the stock FlashForge
|
||||
package for your model to go back to FlashForge's own interface.
|
||||
|
||||
---
|
||||
|
||||
@@ -238,7 +247,7 @@ If ssh is gone too, flash the stock FlashForge package for your model.
|
||||
|
||||
| Symptom | Do this |
|
||||
|---|---|
|
||||
| Printer boots, screen blank | ssh in; `/usr/data/anvil/init.d/S80ui status`; `touch /usr/data/anvil/SAFE-MODE`; reboot |
|
||||
| Printer boots, screen blank | ssh in; `/usr/data/anvil/init.d/S80ui status` says whether the UI was even started. No on-device repair — reflash the mod, or flash the stock package to get FlashForge's UI back. ssh, Mainsail and printing are unaffected |
|
||||
| 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) |
|
||||
|
||||
@@ -43,7 +43,7 @@ S50wifi wlan0 + wpa_supplicant + udhcpc
|
||||
S60web nginx (Mainsail) + moonraker
|
||||
S65camera mjpg-streamer on :8080 (nginx proxies it at /webcam/)
|
||||
S70klipper Klipper
|
||||
S80ui decides whether the UI runs; owns SAFE-MODE
|
||||
S80ui decides whether the UI runs (MOD_UI, HelixScreen installed?)
|
||||
```
|
||||
|
||||
Everything the mod installs lives under `/usr/data/anvil` on the **data**
|
||||
|
||||
+3
-3
@@ -234,9 +234,9 @@ Tests that cannot fail are worse than no tests, because they read as coverage:
|
||||
to it. Its allowlist had drifted to nine entries that excused nothing. The
|
||||
absolute-path half survives, because `S50wifi`'s binaries sit on a branch
|
||||
no simulation reaches.
|
||||
- `lint-danger`'s structural greps were satisfiable by comments —
|
||||
`firmwareExe:44` and `S80ui:16` *mention* `helix` and `SAFE-MODE`, so the
|
||||
checks passed with the logic deleted.
|
||||
- `lint-danger`'s structural greps were satisfiable by comments — the
|
||||
comments in `firmwareExe` and `S80ui` *mention* `helix` and (at the time)
|
||||
`SAFE-MODE`, so the checks passed with the logic deleted.
|
||||
- `case-ui` went with the helix-only decision.
|
||||
|
||||
## Bugs these caught
|
||||
|
||||
@@ -14,6 +14,10 @@ MOD_WEB=1
|
||||
#MOD_CAM_PORT=8080
|
||||
MOD_CAM=1
|
||||
|
||||
# Touchscreen UI: HelixScreen, started by the firmwareExe wrapper.
|
||||
# 0 = boot headless (blank screen); ssh and Mainsail are unaffected.
|
||||
MOD_UI=1
|
||||
|
||||
# SSH: dropbear on :22
|
||||
MOD_SSH=1
|
||||
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ if [ -d $MODDIR/init.d ]; then
|
||||
fi
|
||||
|
||||
# ---- the UI, in the foreground so the stock watchdog can see us ----------
|
||||
# S80ui already handled SAFE-MODE and crash counting and wrote its verdict
|
||||
# here: "helix" to start the UI, "none" to stay headless.
|
||||
# S80ui already decided and wrote its verdict here: "helix" to start the UI,
|
||||
# "none" to stay headless (MOD_UI=0, or HelixScreen not installed).
|
||||
UI_CHOICE=`cat $MODDIR/.ui-choice 2>/dev/null || echo none`
|
||||
echo "ui: choice is $UI_CHOICE"
|
||||
|
||||
|
||||
+9
-33
@@ -8,19 +8,14 @@
|
||||
# foreground, so it only chooses.
|
||||
#
|
||||
# HelixScreen is the only UI: FlashForge's firmwareExe is retired and there is
|
||||
# nothing to fall back to. So the safety rules encoded here are what stands
|
||||
# between a crash-looping UI and losing the machine:
|
||||
# * ssh comes from the stock /etc/init.d/S50dropbear, which busybox init
|
||||
# already ran long before this script. It is up regardless of what
|
||||
# happens here, and so are S60web (Mainsail) and S70klipper.
|
||||
# * A UI that crash-loops latches SAFE-MODE after MAX_UI_FAILURES boots and
|
||||
# the printer then boots HEADLESS -- no UI at all -- rather than looping.
|
||||
# * Delete /usr/data/anvil/SAFE-MODE (over ssh) to try the UI again.
|
||||
# nothing to fall back to. There is no crash counter and no automatic headless
|
||||
# latch -- the UI is started whenever it is installed and MOD_UI is not 0.
|
||||
# To stop it, set MOD_UI=0 in /usr/data/anvil/anvil.conf and reboot; ssh comes
|
||||
# from the stock /etc/init.d/S50dropbear, which busybox init ran long before
|
||||
# this script, so it is up regardless of what happens here, and so are S60web
|
||||
# (Mainsail) and S70klipper.
|
||||
MODDIR=/usr/data/anvil
|
||||
[ -f $MODDIR/anvil.conf ] && . $MODDIR/anvil.conf
|
||||
MAX_UI_FAILURES="${MOD_MAX_UI_FAILURES:-3}"
|
||||
FAILFILE=$MODDIR/ui-failures
|
||||
SAFEFILE=$MODDIR/SAFE-MODE
|
||||
CHOICE=$MODDIR/.ui-choice
|
||||
|
||||
have_helix() { [ -x $MODDIR/helixscreen/bin/helix-launcher.sh ] || [ -x $MODDIR/helixscreen/bin/helix-screen ]; }
|
||||
@@ -28,20 +23,8 @@ have_helix() { [ -x $MODDIR/helixscreen/bin/helix-launcher.sh ] || [ -x $MODDIR/
|
||||
start() {
|
||||
want=helix
|
||||
|
||||
if [ -f "$SAFEFILE" ]; then
|
||||
echo "ui: SAFE-MODE latched -- staying headless (delete $SAFEFILE to retry)"
|
||||
want=none
|
||||
fi
|
||||
|
||||
failures=`cat "$FAILFILE" 2>/dev/null || echo 0`
|
||||
case "$failures" in ''|*[!0-9]*) failures=0 ;; esac
|
||||
|
||||
# Every boot increments the counter; the UI clears it once it has been
|
||||
# alive for a while (see the settle check below). A crash-loop therefore
|
||||
# walks the counter up until SAFE-MODE latches.
|
||||
if [ "$failures" -ge "$MAX_UI_FAILURES" ]; then
|
||||
echo "ui: $failures consecutive short boots -- latching SAFE-MODE"
|
||||
: > "$SAFEFILE"
|
||||
if [ "${MOD_UI:-1}" = 0 ]; then
|
||||
echo "ui: MOD_UI=0 in anvil.conf -- staying headless"
|
||||
want=none
|
||||
fi
|
||||
|
||||
@@ -56,12 +39,6 @@ start() {
|
||||
|
||||
echo "$want" > "$CHOICE"
|
||||
echo "ui: chose $want"
|
||||
|
||||
# Arm the failure counter, then clear it in the background once the UI has
|
||||
# survived long enough to be considered healthy.
|
||||
echo `expr $failures + 1` > "$FAILFILE"
|
||||
( sleep 90
|
||||
if [ -s "$CHOICE" ]; then rm -f "$FAILFILE"; fi ) &
|
||||
}
|
||||
stop() {
|
||||
killall helix-screen helix-watchdog helix-splash 2>/dev/null
|
||||
@@ -72,7 +49,6 @@ case "$1" in
|
||||
stop) stop ;;
|
||||
restart) stop; sleep 2; start ;;
|
||||
status) echo "ui choice: `cat $CHOICE 2>/dev/null || echo unset`";
|
||||
echo "failures: `cat $FAILFILE 2>/dev/null || echo 0`";
|
||||
[ -f "$SAFEFILE" ] && echo "SAFE-MODE: latched" || echo "SAFE-MODE: clear" ;;
|
||||
echo "MOD_UI: ${MOD_UI:-1}" ;;
|
||||
*) echo "usage: $0 {start|stop|restart|status}"; exit 1 ;;
|
||||
esac
|
||||
@@ -155,7 +155,8 @@ gcode:
|
||||
# Called by the sensors' insert_gcode (Klipper only fires it while idle, so
|
||||
# this never interrupts a moving print). The app logged "insert" and, for
|
||||
# the motion sensors, cleared its clog flag; we just tell the user what to
|
||||
# do next.
|
||||
# do next. A switch insert means filament is present again; a motion insert
|
||||
# means it is MOVING again, which is what clearing a clog looks like.
|
||||
# ---------------------------------------------------------------------------
|
||||
[gcode_macro _FF_INSERT]
|
||||
gcode:
|
||||
@@ -167,4 +168,8 @@ gcode:
|
||||
{% elif not printer.pause_resume.is_paused %}
|
||||
{ action_respond_info("Filament detected in T%d: LOAD_FILAMENT TOOL=%d to feed it." % (tool, tool)) }
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if printer.pause_resume.is_paused and tool == printer.ff_toolchange.current_tool %}
|
||||
{ action_respond_info("T%d is moving filament again: RESUME to continue (or CANCEL_PRINT)." % tool) }
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -545,12 +545,17 @@ class FFToolOffset:
|
||||
self.last['t%d' % tool] = (cx, cy, zp)
|
||||
gcmd.respond_info("T%d: offset = (%.4f, %.4f, %.4f)"
|
||||
% (tool, cx, cy, zp))
|
||||
if save:
|
||||
self.tools[tool].set_nozzle(cx, cy, zp)
|
||||
if release:
|
||||
self._run('TOOLCHANGE_PARK')
|
||||
self._wait_moves()
|
||||
|
||||
# Staged only once every tool has passed its guards, so that a failure
|
||||
# part-way through TOOL=ALL really does leave nothing saved -- which is
|
||||
# what the guard messages above promise.
|
||||
if save:
|
||||
for tool, (cx, cy, zp) in results.items():
|
||||
self.tools[tool].set_nozzle(cx, cy, zp)
|
||||
|
||||
# the app's exit block: heater off for the tool(s), Z15
|
||||
for tool in tools:
|
||||
self._run('M104 S0 T%d' % tool)
|
||||
|
||||
Reference in new issue
Block a user