Debug Builds and the Debug Console · Console

Files, screenshots and the SD card

Copy files to and from the card, take a screenshot, fetch a core dump and restart the device, all over Wi-Fi, with checksums.

These are the binary commands: a text header line, then raw bytes. The console task answers them itself, so they keep working when the main loop is stuck. scripts/rdbg.py handles each one on the PC side; the protocol is given too, for your own tools.

get: card to PC

scripts/rdbg.py get /gnss/tracks/20261004-142530.gpx          # saved here, under its own name
scripts/rdbg.py get /captures/lora/capture.pcap my-capture.pcap

The device answers get: data <size>, then exactly <size> bytes, then get: end. A path it cannot open (missing, or a folder) gives get: error cannot open <path>. The script prints the size and the speed.

put: PC to card

scripts/rdbg.py put roro9stack-v0.12.0.ota                     # to /updates/roro9stack-v0.12.0.ota
scripts/rdbg.py put notes.txt /notes/from-the-pc.txt           # to a path you choose

The default destination is /updates/<name>, so this is also the way to install an update from the SD card without touching the device: put the .ota file, then scripts/rdbg.py install /updates/<name>.

The device does a few things you want from a file transfer:

  • the PC sends the size and the SHA-256 first (put <path> <size> <sha256>); the device answers put: ready <size> or put: error <why> (no card, not enough space: it wants the size plus 64 KB free, or a path or size it refuses);
  • it writes to a temporary .part file, creating missing folders, and only renames it into place after the whole file has been read back from the card and its SHA-256 matches: the checksum covers what is on the card, not what arrived;
  • a write the card refuses is retried up to 3 times, cutting the file back to the last good byte, and gives up rather than leave a hole in the middle;
  • it ends with put: done <path> <size> B, or put: error <why> and a closed connection (so the rest of the file is never read as commands). A failed transfer leaves nothing on the card.

Speed is about 300 KB/s. The same transfer exists over USB serial, for a device with no Wi-Fi: scripts/sd_put.sh <file> [card path] (about 30 seconds for 1.6 MB, with the card left in).

screenshot: the screen as a PNG

scripts/rdbg.py screenshot ui.png     # 480x270: the 240x135 screen at 2x

The device sends screenshot: rgb332 <width> <height> and then one byte per pixel: the frame the UI composed off-screen, in RGB332 (RRRGGGBB), the way M5GFX stores an 8-bit sprite. The script expands it and doubles it into a PNG.

  • It is read as it stands, while the UI may be drawing, so it can tear. It is for looking at, not for pixel-exact comparison.
  • It is the real thing: the screenshots on this site, in the user guide and the devlog, were taken this way.
  • Its main use is in a loop: send a key, wait a moment, take a screenshot, look. See Drive the UI.

coredump get: the crash dump

The raw contents of the core dump partition: coredump: data <size>, the bytes, coredump: end, or coredump: none. scripts/rdbg.py coredump fetches and decodes it in one go: see Crashes and Safe Mode.

reset: restart now

scripts/rdbg.py reset

The console prints debug: restarting now and restarts the chip after a moment. It does not go through the main loop, so it works when the loop is stuck. (The text command reboot does go through the main loop: a clean restart. boot other restarts into the other app slot: a manual rollback.)