Public Access
/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
61 lines
3.7 KiB
Markdown
61 lines
3.7 KiB
Markdown
+++
|
|
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
|
|
|
|
```sh
|
|
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
|
|
|
|
```sh
|
|
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
|
|
|
|
```sh
|
|
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](/guide/) and the [devlog](/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](/dev/debug/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](/dev/debug/crashes/).
|
|
|
|
## `reset`: restart now
|
|
|
|
```sh
|
|
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.)
|