Skip to content

External Spool Mapping #55

Description

@TwoTenPvP

Bug report: bpm.print_3mf_file fails with 07FF_8012 "Failed to get AMS mapping table" when printing from the external spool (no AMS)

  • Library: bambu-printer-manager (bpm), >=1.0.2
  • Affected call: BambuPrinter.print_3mf_file(...)
  • Symptom: Print never starts; printer reports HMS error 07FF_8012"Failed to get AMS mapping table".
  • Affected hardware: Single-nozzle printers run without an AMS (external spool), printing a single plate. Confirmed against our P2S / H2S / X1.
  • Workaround status: We had to stop using print_3mf_file and build/publish the project_file MQTT command ourselves. Details below.

What we expected

Calling print_3mf_file to print one plate of an uploaded .3mf from the external spool (no AMS attached) should start the print.

We tried what the API suggests for "no AMS":

self._printer.print_3mf_file(
    name=remote,            # absolute SD path, e.g. "/magprint_job.gcode.3mf"
    plate=1,
    bed=PlateType.AUTO,
    use_ams=False,
    ams_mapping="",         # empty -> bpm omits the field entirely
    bedlevel=bed_level,
    flow=flow_calibration,
    timelapse=timelapse,
)

What actually happens

The print does not start. The printer raises HMS 07FF_8012 "Failed to get AMS mapping table".

Root cause is how the firmware interprets the AMS mapping fields in the project_file command, and bpm does not encode them correctly for external-spool / single-nozzle printing:

  1. ams_mapping (flat array). The firmware rejects raw tray IDs (254 / 255) in the flat ams_mapping. For external-spool printing it must be [-1]. Passing a non-empty mapping makes the firmware consult an AMS mapping table that does not exist without an AMS, and it pauses. Passing "" (so bpm omits the field) is also not sufficient on current firmware.

  2. ams_mapping2 (per-nozzle). This must carry the correct ams_id:

    • Single-nozzle printers (P2S, H2S, X1, A1, P1, …) must use ams_id: 255 — the main/virtual tray.
    • True dual-nozzle printers (H2D / H2D Pro / H2C / X2D) use ams_id: 254 — the deputy/left nozzle fed by the external spool.

    bpm hardcodes a single value that is wrong for one of these classes, which is what produces 07FF_8012.

  3. H2-family field typing. H2-family firmware (H2D, H2D Pro, H2C, X2D, and the single-nozzle H2S) wants integer 0/1 — not JSON booleans — for the calibration / leveling fields (bed_leveling, flow_cali, layer_inspect, vibration_cali). X1 / P1 / A1 / P2S accept real JSON booleans. use_ams and timelapse stay boolean on all models. bpm does not differentiate.

Note on serial families: H2S shares the 094 serial + firmware family with H2D, but H2S is single-nozzle. So family detection alone is not enough — ams_id selection (254 vs 255) must be driven by nozzle count, not by firmware family.

Our workaround

We stopped calling print_3mf_file and instead build the project_file command from bpm.bambucommands.PRINT_3MF_FILE and publish it directly over MQTT. The essential parts:

import copy, json
from bpm.bambucommands import PRINT_3MF_FILE
from bpm.bambutools import PrinterSeries, getPrinterSeriesByModel

DUAL_NOZZLE_MODELS = {"h2d", "h2d pro", "h2dpro", "h2c", "x2d"}
H_FAMILY_MODELS    = DUAL_NOZZLE_MODELS | {"h2s"}

cmd = copy.deepcopy(PRINT_3MF_FILE)
p = cmd["print"]

# subtask_name = basename without .3mf/.gcode
# url scheme: file:///sdcard{remote} for A1/P1, ftp://{remote} otherwise
series = getPrinterSeriesByModel(printer.config.printer_model)
p["url"]  = f"file:///sdcard{remote}" if series in (PrinterSeries.A1, PrinterSeries.P1) else f"ftp://{remote}"
p["file"] = remote
p["param"] = p["param"].replace("#", "1")     # single plate

p["use_ams"]      = False
p["ams_mapping"]  = [-1]                        # <-- firmware rejects raw 254/255 here
p["ams_mapping2"] = [{"ams_id": 254 if is_dual_nozzle else 255, "slot_id": 0}]

fmt = (lambda v: (1 if v else 0)) if is_h_family else (lambda v: v)
p["bed_leveling"]   = fmt(bed_level)
p["flow_cali"]      = fmt(flow_calibration)
p["layer_inspect"]  = fmt(p["layer_inspect"])
p["vibration_cali"] = fmt(p["vibration_cali"])
p["timelapse"]      = timelapse

printer.client.publish(f"device/{printer.config.serial_number}/request", json.dumps(cmd))

This starts prints reliably across our single- and dual-nozzle, with and without an AMS attached.

Suggested fix in bpm

In print_3mf_file, when use_ams=False (external spool):

  1. Set the flat ams_mapping to [-1] rather than passing raw tray IDs or omitting it.
  2. Set ams_mapping2 ams_id based on nozzle count: 255 for single-nozzle printers, 254 for true dual-nozzle printers — not based on serial/firmware family (H2S is single-nozzle despite the 094/H2 family).
  3. Encode the calibration/leveling fields (bed_leveling, flow_cali, layer_inspect, vibration_cali) as integer 0/1 for H2-family firmware, JSON booleans otherwise.

Reproduction

  1. Single-nozzle Bambu printer (e.g. P2S or H2S), no AMS attached, filament on the external spool.
  2. Upload a single-plate .3mf to the SD card.
  3. Call print_3mf_file(name=<abs path>, plate=1, bed=PlateType.AUTO, use_ams=False, ams_mapping="", ...).
  4. Expected: print starts. Actual: HMS 07FF_8012 "Failed to get AMS mapping table"; print never starts.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions