/dev/ has Debug Builds and the Debug Console (builds and the token, the console and its protocol, files and screenshots, driving the UI, crashes and Safe Mode, the command reference), Build, test and release (including how an update works), the architecture decisions and the milestone plans. Generated from the repository by site/tools/gen_dev_docs.py: the ADRs, the milestones, the README's sections, and the command reference, read from the firmware's own `help` text. The pages are committed (Zola cannot read outside its folder); the Site workflow checks they are current, and now also runs when src/main.cpp changes. M0, M1 and CONTEXT.md are not published. README: the gnss commands that the table lacked. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
3.7 KiB
+++ title = "Files, screenshots and the SD card" description = "Copy files to and from the card, take a screenshot, fetch a core dump and restart the device, all over Wi-Fi, with checksums." weight = 3 [extra] tag = "Console" +++
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 answersput: ready <size>orput: 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
.partfile, 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, orput: 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.)