Architecture¶
This page is for people who want to understand or hack on the firmware. For build and test commands see Building & Testing.
The firmware is written in Rust, targeting xtensa-esp32-espidf on the ESP-IDF
framework. At its heart is a state-machine loop with a dual-core design.
Dual-core design¶
Core 1 (main): runs the ATM state machine —
Idle → CountingCoins → WithdrawReady.Core 0 (background): runs the BlockClock/OrangeClock mempool data fetcher when enabled, spawned via the FreeRTOS
xTaskCreatePinnedToCoreAPI.
The application state is the AppState enum (state.rs): Idle,
CountingCoins(u64) and WithdrawReady(u64).
Modules¶
Module |
Responsibility |
|---|---|
|
The |
|
Offline LNURL-withdraw generation — the core crypto. Implements the LNbits FOSSA protocol: AES-256-CBC encryption with EVP_BytesToKey-style key derivation (MD5 rounds) and bech32-encoded output. |
|
The |
|
The |
|
Pulse-counting coin detection via a debounced GPIO interrupt. |
|
NVS (non-volatile storage) persistence for board type, display type, the LNbits connection and BlockClock settings. NVS keys must be ≤ 15 chars. |
|
|
|
The WiFi access-point configuration portal (HTML form via |
|
Fetches Bitcoin network data (block height, price, fees, difficulty,
mempool stats) from the mempool.space API with a fallback endpoint. The
background fetcher runs on Core 0 with |
|
Bitmap icon data for the BlockClock display. |
|
Re-exports |
Key design constraints¶
The
espfeature flag gates all ESP32-specific dependencies. The[lib]target built with--no-default-featurescompiles the platform-independent modules for x86 testing (see Building & Testing).The
epd-wavesharecrate is pinned to a specific git revision (1e525bb) for unreleased display-driver support.The espflash runner erases the NVS config partition on every flash (
--erase-parts nvsin.cargo/config.toml), so the device returns to the config portal after a flash.The configuration portal is triggered by holding GPIO 0 (the BOOT button, physically inside the case) during startup.