#pragma once #include #include #include namespace fgc { // Structured decode of a firmware DUMP block (the text between "DUMP BEGIN" and // "DUMP END"). Mirrors firmware/tools/decode_dump.py, which is authoritative for // the on-wire field names and the TMC status-register bit tables. struct DumpAxis { char axis = '?'; // 'Y' (yaw) / 'P' (pitch) std::string state_name = "?"; // BOOT/RESET/HOMING/READY/ERROR bool enabled = false; bool has_encoder = false; long lim_neg = 0; long lim_pos = 0; long soft_margin = 0; // counts kept clear of each hard limit long center_count = 0; // count the firmware anchored the homed midpoint to long hold_target = 0; long speed = 0; // Motor current, as the firmware believes it applied it. GLOBALSCALER and // IHOLD_IRUN are write-only on the TMC5160, so this echo is the only way the // host can confirm a CURRENT command landed. // // -1 means the field was absent, i.e. the firmware predates the command - // which is a different thing from "applied a current of zero" and has to stay // distinguishable, because one is a version skew and the other is a fault. long global_scaler = -1; long run_irun = -1; long homing_irun = -1; // The rest of what the host declares and the firmware is told. Same -1 // convention and the same reason: absent means a firmware that predates the // config handshake, which is a version skew, not a value of zero. long steps_per_rev = -1; long enc_deviation = -1; long homing_speed = -1; long center_speed = -1; long sw_mode = -1; // Bitmask over the firmware's CfgKey: which required keys have arrived. // -1 when absent. long cfg_received = -1; // Is the axis running the firmware's limp-home fallback rather than anything // this host declared? USE DEFAULTS fills cfg_received with the whole required // mask, so that field alone cannot tell the two apart - a bench board reads as // fully configured. -1 when absent, i.e. a firmware predating the flag. long limp_home = -1; // The chip-select and enable pins the running image was BUILT with. -1 when // absent, i.e. a firmware that predates the pin echo. long cs_pin = -1; long en_pin = -1; // Encoder-tracking health accumulated by the firmware hold corrector. long hold_corrections = 0; // # of hold resyncs since firmware boot/RESET long hold_peak_dev = 0; // largest deviation seen while holding (counts) long hold_cumulative_dev = 0; // sum of corrected deviations (counts) int hsub = 0; // Raw register hex strings as received, keyed by name (GCONF, DRV_STATUS...). std::map regs; // Decoded status registers + their set-bit names. unsigned drv_status = 0; unsigned gstat = 0; unsigned ramp_stat = 0; int cs_actual = 0; // (DRV_STATUS >> 16) & 0x1F int sg_result = 0; // DRV_STATUS & 0x3FF std::vector drv_flags; std::vector gstat_flags; std::vector ramp_flags; }; struct DumpData { bool valid = false; std::string build; long uptime_ms = 0; // Why the MCU last restarted, as the firmware reported it. // // `mcusr` is the RAW value and means DIFFERENT THINGS per board: on AVR it is // the MCUSR bitmask (bit 0 power-on, bit 1 external, ...); on RP2040 it is // rp2040.getResetReason(), a plain enum where 3 is one value, not two bits. // Decoding one as the other is silently wrong - it reported an RP2040 soft // reset as "PORF (power-on), EXTRF (external)" on tower 121. // // So the firmware now NAMES it in the dump (`reset=`), and that text is // authoritative because only the board knows its own encoding. reset_flags is // the legacy AVR bit decode, kept for firmware that predates the field. unsigned mcusr = 0; std::string reset_text; // empty => firmware did not name it long free_ram = 0; // SPI pins the image was built with - RP2040 only, where they are a choice. // -1 when absent: a Leonardo, or firmware predating the echo. long spi_sck = -1, spi_mosi = -1, spi_miso = -1; std::vector reset_flags; std::vector axes; }; // Parse a raw DUMP block. Returns {valid=false} for empty/malformed input (no // "DUMP BEGIN .. DUMP END" pair). DumpData parseDump(const std::string& block); // Human-readable lines for a decoded dump (console-reusable pretty print). std::vector formatDump(const DumpData& d); } // namespace fgc