Skip to content

Document the five undocumented Cmd 128 payload keys (and: Cmd 324 probe data from a real CC1 + CANVAS)Β #2

Description

@InfiniteBSOD

Authored by Claude (Opus 5 on UltraCode), approved by me - a human 😊


Hi β€” thanks for pycentauri. The provenance notes throughout are unusually good; sdcp.py's
"Reference implementations consulted" header and the PrintSpeedPct note ("Confirmed by reading the
printer's own SPA i18n file at /app/resources/www/assets/i18n/network-en.json") made it possible to
audit what the library actually puts on the wire before trusting it. That care is exactly why one
spot stands out.

(Updated after actually firing Cmd 128 β€” the original version of this issue was written before I
had run it. Empirical results are in Β§2.)

1. The ask: document the five extra keys in start_print()

Printer.start_print() (v0.9.0, client.py:416-424) sends a seven-field payload:

data = {
    "Filename": filename,
    "StartLayer": 0,
    "Calibration_switch": 1 if auto_leveling else 0,
    "PrintPlatformType": 0,
    "Tlp_Switch": 1 if timelapse else 0,
    "slot_map": [],
    "path_prefix": path_prefix,
}

The published SDCP v3 spec documents only Filename and StartLayer for Cmd 128. The other five
carry no comment anywhere in the package β€” unlike almost every other non-obvious constant in the
codebase, which cites where it came from. Concretely, it would help to know:

  1. Calibration_switch β€” what does 1 actually do? A full bed-mesh re-probe, a single
    Z-touch, or something else? This is the one with real consequences: if it is a full re-probe,
    running it with anything left on the plate could persist a corrupted mesh into every later print.
    It also defaults to auto_leveling=True, so the riskier branch is the default. If that is
    deliberate, saying why would be useful.
  2. slot_map β€” sent as [] unconditionally. What is the element shape? (Partial empirical
    answer in Β§2.)
  3. PrintPlatformType β€” always 0. Are there other values (plate types)?
  4. Tlp_Switch β€” presumably timelapse; confirmation that 0 is a clean no-op would be enough.
  5. path_prefix β€” /local vs /usb, alongside the storage argument. Is it redundant with
    Filename, or independently required?

Even a one-line comment each, in the same "confirmed against elegoo_fdm_cc_message_adapter.cpp"
style you already use, would fully resolve this. I'm not asking for a spec β€” just the provenance
you're clearly already tracking.

Why I care rather than just calling it and seeing: I'm wiring a deliberately over-gated remote
start for an unattended printer, and the failure mode is physical (the first act of a start is G28,
which finds Z by driving the nozzle into the plate).

2. Results from actually firing it β€” CC1 + CANVAS, firmware V1.4.46, pycentauri 0.9.0

The seven-field payload works as-is. start_print() returned Ack=0 and the print ran to
completion. So the five undocumented keys are at minimum accepted by CANVAS-era CC1 firmware, with
Calibration_switch: 0, PrintPlatformType: 0, Tlp_Switch: 0, slot_map: [],
path_prefix: "/local". I did not test a spec-minimal {Filename, StartLayer} payload and don't
intend to β€” I only wanted to confirm yours is not rejected.

Partial answer on slot_map: [] for a CANVAS machine. The gcode had T0 baked in (both
M6211 A1 L200 T0 … and a bare T0), i.e. it requests slot 1. The CANVAS's active slot was 2.
The job fed from slot 2 β€” confirmed live from Cmd 324's active_tray_id mid-print.

β‡’ With an empty slot_map, the firmware resolves the feed to the currently active slot and does
not honour the gcode's baked-in tool index.

⚠️ Being precise about what that does and does not show: the active slot was already 2 before the
start, so this demonstrates "the gcode's T0 does not override the active slot" β€” not that an
empty slot_map positively selects anything. Separating those would need a run where the two
disagree in the other direction, which I have not done.

A verification note that may be useful for the library. PrintInfo.Status and
PrintInfo.Filename are unreliable as "did a new job start?" signals β€” the sub-status is sticky
after a job ends. PrintInfo.TaskId is a clean discriminator: empty string "" when idle, a real
UUID while printing. I gate post-send verification on TaskId changing rather than on the
sub-status.

3. Offer: Cmd 324 data from a real CC1 + CANVAS

canvas_status() currently raises, with this rationale (v0.9.0, client.py:265-278):

The SDK lists Cmd 324 (GET_CANVAS_STATUS) for CC1 SDCP but it is unprobed against real CC
firmware β€” and unknown Cmds can crash the CC1's app daemon, so pycentauri won't send it blind.

I have a CC1 on firmware V1.4.46 with a CANVAS attached, and have been sending Cmd 324 from my
own read-only client for weeks. It works, and has never crashed the app daemon β€” including
while a print was running. The reply shape:

{
  "auto_refill": 1,          // 0/1 β€” reflects the touchscreen toggle
  "active_canvas_id": 0,
  "active_tray_id": 1,       // 0-BASED: tray 1 == CANVAS slot 2 in the UI
  "canvas_list": [
    {
      "canvas_id": 0,
      "connected": 1,
      "tray_list": [
        {
          "tray_id": 0,               // 0-based
          "brand": "ELEGOO",
          "filament_type": "PETG",
          "filament_name": "PETG-CF",
          "filament_code": "0x00000",
          "filament_color": "#000000",
          "min_nozzle_temp": 240,
          "max_nozzle_temp": 270,
          "status": 1                 // see caveat below
        }
        // ... 4 trays total
      ]
    }
  ]
}

Notes that might save you time:

  • An empty slot returns garbage rather than nulls β€” mojibake in brand / filament_name,
    min/max_nozzle_temp: 0, and a stale-looking filament_color. Treat 0 temps as the emptiness
    signal, not the strings.
  • filament_code is 0x00000 on every tray here, including loaded ones, so it does not appear
    to carry a usable material identity on non-RFID spools.
  • I would not trust status without more evidence. I have seen 1 for loaded-but-idle slots and
    2 for the active one, but have not established whether it reflects a mechanical sensor or a
    GUI-declared value, and there are conflicting definitions around. Reporting what I observed, not
    asserting semantics.
  • ⚠️ active_tray_id is a firmware pointer, not a measurement. On my machine it kept pointing
    at a slot that had already run empty during a failed auto-refill relay, and selecting a slot in the
    GUI sets it with no check that filament is physically present. Worth a docstring warning if you do
    implement this β€” it is easy to mistake for proof of what is loaded.

Happy to capture any specific frame, run a request/response pair, or open a PR implementing
canvas_status() for the CC1 against this shape β€” whichever is most useful. I'm deliberately not
opening a code PR unprompted since you may prefer a different approach for a command you've been
careful about.

4. Still not probed, and I won't guess

Whether the firmware returns Ack=1 BUSY for Cmd 128 while a job is running. That determines whether
there is a real firmware interlock behind start_print(), and testing it means risking a live print.

Best Regards - InfiniteBSOD


Updated at 2026-08-01

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions