POST /printers/{id}/bed-jog takes a signed nozzle-bed gap, documented since it
was written: positive asks for more room between the nozzle and the plate. On
an A1 it did the opposite. The reporter sent distance=5 for clearance and
watched the toolhead come down.
The sign had been flipped on A1 models since the original report on this issue,
where an A1 Mini owner clicked an arrow labelled "move the plate up" and watched
the nozzle dive. That is a labelling problem -- a bed-slinger's plate does not
move in Z at all, so closing the gap shows up as the toolhead descending -- and
it was solved in the transport layer, which turned a parameter documented as
model-independent into one that meant the opposite thing on part of the fleet.
Z is the nozzle-to-bed distance on every Bambu model, by definition of the
coordinate system rather than by convention: G1 Z+ opens the gap whether the bed
drops away from a fixed nozzle (X1/P1/H2, whose end G-code parks with
G1 Z{max_layer_z + 100}) or the nozzle rises off a fixed bed (A1/A2L). The
finish-photo plate restore already relies on exactly that and carries no model
branch. So distance goes onto the wire unchanged and one call means one physical
outcome everywhere: positive is the safe direction on every printer.
Which way an arrow points is a different question, about the machine in front of
the user rather than about G-code, so the printer card answers it and asks for
the gap it wants. The buttons move what you would expect them to move, exactly
as before; on a bed-slinger they now say toolhead rather than plate.
The A2L never had the old fix. It slings its bed the same way the A1 does, but
the inversion listed the A1 names and the A2L was not among them, so its up
arrow has been sending the toolhead at the plate for as long as the machine has
been supported. The new classifier also covers the alternate internal codes
A04 / A11 / A12, which LINEAR_RAIL_MODELS and SINGLE_NOZZLE_FLOW_MODELS both
carry and the old gate did not.
is_bed_slinger is gone from the backend rather than widened: with the route
model-independent it had no caller, and a kinematics helper sitting unused in
the service layer invites the next person to assume the backend handles
direction. It does not, deliberately.
Separately, the soft-endstop comments on both jog routes claimed the firmware
clamps a bare move at the travel limit. It does not, and #2579 measured that:
an H2D at its Z limit ran straight past a clean G91/G1 Z-1.00/G90, while its own
touchscreen refuses the identical move. What #2579 removed was M211 S0, which
disabled the limits globally and took the touchscreen's protection with them.
The jog popover has warned about this correctly the whole time; only the code
comments disagreed with it.
Manual jog could drive an axis past its travel limit into a collision.
Instrumenting the exact G-code to an H2D showed Bambuddy sending a clean
move at the limit (G91 / G1 Z-1.00 F600 / G90, no M211) that the printer
ran straight past, while its own touchscreen refuses the identical move.
This is a Bambu firmware bug: soft endstops are not enforced on G-code
received over MQTT, and no axis position is reported, so the move cannot
be clamped firmware- or client-side from position.
Two changes: (1) jogs no longer wrap moves in M211 S0/S1 — that disabled
the firmware's soft endstops globally, breaking even the touchscreen's
limits until a power cycle; a bare move keeps the touchscreen protected.
(2) The jog panel shows a prominent warning that travel limits are not
enforced during manual moves due to the firmware bug. Client-side
dead-reckoning enforcement is tracked separately.
On A1 / A1 Mini, clicking the "Up" arrow on the printer-card bed-jog
control sent the nozzle straight into the build plate. Reporter
triggered it with the 50 mm step and crashed their nozzle.
Root cause: the bed-jog UI was designed against the X1 / P1 / H2 family
where the bed is the Z-axis and Bambu's firmware homes Z=0 at the top,
so G1 Z- raises the bed toward the toolhead (decreases the nozzle-bed
gap). The frontend maps "Up" to negative distance with that convention
in mind.
A1 / A1 Mini are bed-slingers: bed moves on Y, toolhead moves on X+Z,
firmware uses standard cartesian Z (Z+ = toolhead up). On those models
G1 Z-10 drives the toolhead DOWN 10 mm. There was no model
classification at the bed-jog code path, so every printer got the same
X1-convention G-code.
Fix: new is_bed_slinger(model) helper in printer_manager (sibling to
existing supports_chamber_temp / has_stg_cur_idle_bug, reuses the
already-defined A1_MODELS frozenset which covers display names and
internal codes N1 / N2S). The bed-jog route now inverts the signed
distance before emitting G-code when the printer model is in that set,
so UI "Up" semantics ("decrease nozzle-bed gap") stay consistent
regardless of which physical part moves. Frontend untouched, single
source of truth lives in the backend, keyed off the Printer.model
column. Route Query description and docstring updated to spell out the
new contract: distance is the gap adjustment, not the raw Z value.
Critical safety fix. The bed-jog dialog's "Home Z" button sent a bare
`G28 Z` over gcode_line. On Bambu printers where the Z endstop is at
the top (bed moves UP into it — H2C, H2D, H2S, X1 family), `G28 Z`
skips the toolhead-park step that a full `G28` runs first, so the bed
rises at full speed with nothing getting out of the way. The reporter
only escaped damage because the toolhead happened to be parked on the
purge chute.
The /printers/{id}/home-axes endpoint and BambuClient.home_axes() now
always send bare `G28` regardless of the axes argument, triggering the
firmware's safe multi-step routine (park toolhead → home XY → home Z).
The axes argument is kept for API compat but ignored; invalid values
still return 400.
Frontend retitles the button "Auto Home" and updates the dialog copy
in all 7 locales so users aren't surprised when X/Y motion happens
before Z. Parameterized regression test asserts z/xy/all all produce
bare G28.
Adds a compact "Bed" badge in the printer-card controls row
between print speed and Stop/Pause. Opens a popover with up/down
arrows and a 1 / 10 / 50 mm step selector.
When the Z axis has not been homed since the last print, the
first jog per session opens a Bambu Studio-style modal with
Home Z / Move anyway / Cancel. "Move anyway" bypasses soft
endstops (M211 S0 ... M211 S1) for a single move and is
remembered for the browser session.
Backend:
- POST /printers/{id}/bed-jog?distance=N[&force=bool]
Emits G91 / G1 ZN F600 / G90 (with optional M211 wrap).
Distance validated server-side (non-zero, |N| <= 200 mm).
- POST /printers/{id}/home-axes?axes=z|xy|all
Emits G28 variants.
Both gated behind Permission.PRINTERS_CONTROL.
Frontend:
- New indigo-themed badge + popover in PrintersPage.
- Not-homed confirmation modal with sessionStorage "warned" flag.
- i18n keys under printers.bedJog.* in all 7 locales.
Tests:
- backend/tests/unit/test_bed_jog.py — 13 tests covering
404 / 400 / 500 / success paths for both endpoints, plus
gcode-payload assertions for force on/off.
Docs:
- README feature list, CHANGELOG (0.2.3b4 Unreleased),
printer-control wiki page, website features.html.