Updating the firmware¶
The console updates its four chips by itself — RP1, RP2, RP3 and the ESP32 — from the micro-SD card and the network, with no PC and without taking the card out. This page says how it works, how a version is published, and what has to be done once on a board that never had partitions.
What the user sees¶
When the dashboard starts with Wi-Fi up, the console reads the published manifest
(/api/firmware) and compares its build with the lowest build its chips report.
If it is higher, a line shows on the menu: V… DISPONIBLE : MISE A JOUR. On the
MISE A JOUR screen:
- the four installed builds (what each chip says it runs: its boot banner, the ESP's hello), the version online, the one on the card;
- TELECHARGER: the images go to
papydeck/firmware/(<chip>-<build>.bin), each checked against its sha256;manifest.jsonis written last — a card with a manifest is a complete card; - INSTALLER, after confirmation: ESP32, RP2, RP3, then RP1, which reboots. About two minutes. Every step is shown; a chip already at that build is skipped, which is how an interrupted update resumes.
Nothing beyond the check is automatic: downloading and installing are choices.
Why it cannot brick¶
The three RP2350s have A/B partitions and the bootrom's try before you buy
(RP2350 datasheet §5.1.17): the new image is written to the partition not in
use, read back and hashed, then the bootrom boots it once. It must then
"buy" itself (rom_explicit_buy) after its self-tests — for RP1: RP2 and RP3
came up, video runs, the ESP answers, the card reads; for RP2/RP3: RP1 judges
(trial banner, then VSYNC or console) and orders the buy. Any reset before the
buy — power cut, crash, watchdog, 45 s without passing self-tests — brings the
old image back, no persistent state having changed. The ESP32 has the
equivalent: two OTA slots, and its bootloader keeps the new image only if it
declares itself valid (at RP1's first command).
The rollback was provoked on the bench: an image that does not buy itself comes back to the old one within 45 s; a chip that gives no banner is put back on its old image by RP1.
Publishing a version¶
From papyDeck_FW, clean tree:
export IDF_PYTHON_ENV_PATH=$HOME/.espressif/python_env/idf5.3_py3.13_env
source ~/esp/esp-idf/export.sh
python3 scripts/fw-publish.py # --no-upload to only assemble
The script builds the TBYB variants of the three RP2350s (build-tbyb,
-DPAPYDECK_TBYB=ON: the bit that makes an image "to be tried") and the ESP,
copies the images as dist/fw/<chip>-<build>.bin, computes the sha256s, writes
the manifest and sends everything to the server, which serves it under
GET /api/firmware. Images first, manifest last.
The manifest is flat, under a kilobyte:
{ "format": 1, "version": "v0.5.0-3-gabc1234", "build": 412, "board": "rev8",
"rp1": { "file": "rp1-412.bin", "sha256": "…", "size": 440176 },
"rp2": { "file": "rp2-412.bin", "sha256": "…", "size": 31596 },
"rp3": { "file": "rp3-412.bin", "sha256": "…", "size": 33584 },
"esp": { "file": "esp-412.bin", "sha256": "…", "size": 1356224 },
"notes": "", "signature": "" }
build is git rev-list --count HEAD: the monotonic integer the console
compares, and the image version the bootrom compares between A and B. version
is git describe, for the eyes. signature is reserved (Ed25519 to come):
today integrity rests on the sha256 and on HTTPS, whose certificate the ESP
verifies.
Once per board: the partition tables¶
A new board, or one from before September 2026, has no partitions. The migration is
done at a PC, once; never again afterwards. See papyDeck_FW/README.md and
CONTRIBUTING.md for the exact commands (RP1 through picotool: the table,
then the program at 0x10002000; RP2 and RP3 through their probe, with RP1 put
in BOOTSEL first; the ESP through scripts/esp-flash.py, which lays the OTA
table).
Two traps that cost an evening: picotool load -p 0 <uf2> does not relocate the
image into the partition, it writes it at 0x10000000, over the table; and an
image that embeds the table is booted as the table, outside the A/B choice.
The bench¶
Without the server: scripts/usb-file.py <image> papydeck/firmware/x.bin puts a
file on the card over USB (Ctrl-Y), and on RP1's console
Ctrl-P fw:<chip>:<path>:<sha256>:<build> runs one step (esp takes an HTTP
path). Ctrl-L says part=A|B and tbyb= for RP1, b<build> pA|pB [essai]
for the peers.