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 xTaskCreatePinnedToCore API.

The application state is the AppState enum (state.rs): Idle, CountingCoins(u64) and WithdrawReady(u64).

Modules

Module

Responsibility

state.rs

The AppState enum that drives the main loop.

lnurl.rs

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.

display.rs

The AtmDisplay trait with per-resolution implementations for the 1.54”, 2.13” and 2.7” Waveshare e-paper displays (epd-waveshare + embedded-graphics). Also renders the BlockClock screens.

board.rs

The BoardType enum (Generic / Waveshare) mapping to the GPIO pin assignments — the source of truth for Wiring & Pinout.

coins.rs

Pulse-counting coin detection via a debounced GPIO interrupt. COIN_MAP maps a pulse count to a cent value; the ISR uses an AtomicU32 for lock-free pulse counting.

config.rs

NVS (non-volatile storage) persistence for board type, display type, the LNbits connection and BlockClock settings. NVS keys must be ≤ 15 chars.

util.rs

LNBitsConnection parsing from the device string and QR-code matrix generation.

wifi.rs

The WiFi access-point configuration portal (HTML form via include_str!) and WifiStation for the BlockClock’s station-mode connection.

mempool.rs

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 Arc<Mutex<Option<MempoolData>>> shared state.

orangeclock_icons.rs

Bitmap icon data for the BlockClock display.

lib.rs

Re-exports coins, lnurl, mempool, state and util for host-side unit testing.

Key design constraints

  • The esp feature flag gates all ESP32-specific dependencies. The [lib] target built with --no-default-features compiles the platform-independent modules for x86 testing (see Building & Testing).

  • The epd-waveshare crate 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 nvs in .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.