
DillaDev Notes
August 27, 2026 · Field-tested protocol guide
Starting Bambu A2L prints over LAN with correct AMS Lite mapping
When an uploaded print is accepted but stalls at Preparing, the missing piece may be the A2L's unit-aware AMS mapping—not the file or the heater.
The decisive detail
Send both mapping fields.
"ams_mapping": [0, 3],
"ams_mapping2": [
{ "ams_id": 16, "slot_id": 0 },
{ "ams_id": 16, "slot_id": 3 }
]Diagnosis
The short version
The A2L reports its AMS Lite as physical unit 16. Bambu Studio shows that unit as Q, with trays Q1 through Q4. A flat ams_mapping supplies local zero-based slots; ams_mapping2 preserves the unit identity the A2L needs.
An MQTT acknowledgement with result: "success" only means the command was accepted. It does not prove the project passed mapping validation or reached RUNNING.
Mental Model
There were actually two separate problems
Troubleshoot file indexing and print initialization independently.
Layer 1
Indexed upload
Put the sliced archive directly at /gcodes/<name>.gcode.3mf and verify that the printer can see it.
Layer 2
Unit-aware mapping
Start the indexed project with both flat local slots and A2L AMS unit 16 entries.
Part 1
Upload the sliced .gcode.3mf correctly
LAN Only Mode and Developer Mode enabled
Implicit FTPS on port 990; user bblp
Archive contains Metadata/plate_1.gcode
Upload directly to the final /gcodes filename
curl \
--fail \
--show-error \
--ftp-pasv \
--ssl-reqd \
--insecure \
--user "bblp:$ACCESS_CODE" \
--upload-file "$LOCAL_FILE" \
"ftps://$PRINTER_IP:990/gcodes/$REMOTE_FILE"Validate more than the transfer
Match local and remote byte counts, confirm the exact filename in the /gcodes listing, and wait until it appears under Print Files. Temporary-upload-then-rename patterns were unreliable on the tested A2L.
Part 2
Map Q1–Q4 to AMS unit 16
Q is a Studio-facing label. The wire payload uses numeric local slots plus the physical AMS ID.

For black on Q1 and white on Q4, project filament 0 maps to local slot 0 and project filament 1 maps to local slot 3. Do not pad the arrays to four entries just because the AMS Lite has four trays.
Known-Good Shape
Full working Q1/Q4 payload
{
"print": {
"sequence_id": 0,
"command": "project_file",
"param": "Metadata/plate_1.gcode",
"project_id": "0",
"profile_id": "0",
"task_id": "0",
"subtask_id": "0",
"subtask_name": "two-color-model.gcode.3mf",
"url": "file:///sdcard/gcodes/two-color-model.gcode.3mf",
"bed_type": "auto",
"timelapse": false,
"bed_leveling": true,
"flow_cali": false,
"vibration_cali": false,
"layer_inspect": false,
"use_ams": true,
"ams_mapping": [0, 3],
"ams_mapping2": [
{ "ams_id": 16, "slot_id": 0 },
{ "ams_id": 16, "slot_id": 3 }
]
}
}The URL locates the archive on the printer. The param path locates the sliced G-code inside the archive. They describe different layers and both must be correct.
Telemetry
Do not stop at command accepted
Accepted
project_file reports result=success
Preparing
gcode_state becomes PREPARE
Actually started
gcode_state becomes RUNNING
Subscribe to device/<serial>/report before publishing. Watch gcode_state, mc_percent, print_error, hms, tray_tar, and nozzle/bed targets. On the tested printer, subscribing to the request topic caused the broker to close the connection.
Fast Triage
What the symptoms usually mean
File missing from Print Files
Check the .gcode.3mf suffix, internal Metadata/plate_1.gcode, final /gcodes destination, byte count, and direct-to-final-name upload.
Success, then IDLE
Treat it as project initialization failure. Verify url, param, subtask_name, and both AMS mapping fields.
Stuck at PREPARE, 0%
Inspect live state and tray_tar. A falling or stalled nozzle temperature can be a consequence of the aborted startup—not the root cause.
Wrong tray
Use zero-based slots: Q1=0, Q2=1, Q3=2, Q4=3, with ams_id 16 in every A2L AMS Lite entry.
For Integrators
Keep the unit identity in your data model
A UI that stores only slots 0–3 throws away information newer printer families may need. Store unit plus slot internally, generate model-specific wire payloads, and never apply AMS ID 16 blindly to other Bambu families.
type AmsMapping2Entry = { ams_id: number; slot_id: number };
function buildA2LAmsMapping2(mapping: number[]): AmsMapping2Entry[] {
return mapping.map((slot) =>
slot >= 0
? { ams_id: 16, slot_id: slot }
: { ams_id: 255, slot_id: 255 }
);
}Log model, remote path and byte count, mappings, acknowledgement, state transitions, HMS, and print errors—but never log the LAN access code.
Final Takeaway
If the file prints from the touchscreen but a LAN start silently aborts, inspect the AMS payload first.
On the tested A2L, adding the paired unit-aware ams_mapping2 structure was the difference between an accepted command that returned to IDLE and a print that consistently reached RUNNING.
