Skip to content
foonerd edited this page Oct 18, 2026 · 8 revisions

Volumio ESP Companion

Now playing, transport, volume and album art on an ESP32 touch panel, talking to a Volumio player over the network. Firmware written in Rust for the family of ESP32 display boards: the pages, the player state and the Volumio client are written once and know nothing of the board; a board contributes its pins, its bring-up and a record of what it has; a controller chip has one driver however many vendors solder it down.

It grows out of rpi-waveshare28, the Raspberry Pi runtime for the Waveshare 2.8" SPI LCD. Peter of PIXIS, who tested that runtime on the PIXIS CB-1, asked for the same on the Waveshare ESP32-C5-Touch-LCD-2.8 and sent the first board. Thank you, Peter.

Where the project stands. Pre-alpha, and running on its first board. Every layer exists and is tested on a desktop: the pages, the application loop, the Volumio client, the album-art decoder, the drivers, the ESP-IDF platform crate. On the Waveshare ESP32-C5-Touch-LCD-2.8 the firmware boots, takes its network, its name and its player from a microSD card (Card setup), joins on 2.4 or 5 GHz, shows the player's face with its album art, and answers to a tap and a swipe. Not yet: sound, the settings page, a second board. Install takes you from the release to the face in ten minutes; Building is for building it yourself.

Start here

Page What it is for
Install From nothing to the player's face: download, flash from Windows, macOS or Linux, write the card. Start here.
Connections How the panel meets the player: the network, the optional serial cable, the board's headers.
Boards The boards the project targets, what each carries, the pin map, and where each stands.
Building The toolchain, the build, the image and the flash command.
Card setup How to give the device its network and its player: one text file on a microSD card.

How it is put together

flowchart LR
    subgraph LAN
        V[Volumio player<br/>REST on :3000, mDNS _Volumio._tcp] <-- Wi-Fi --> B
        subgraph B[ESP32 display board]
            direction TB
            FW[firmware/&lt;vendor&gt;-&lt;model&gt;<br/>pins, bring-up, capabilities] --> RT[runtime<br/>loop, gestures, Volumio client, art gate]
            RT --> C[core<br/>pages, surface, state]
            FW --> D[drivers<br/>touch, expander]
            FW --> P[platform/esp-idf<br/>Wi-Fi, NVS, HTTP]
            FW --> A[art<br/>fetch + decode thread]
            C -- DrawTarget --> LCD[(panel)]
            D -- hal::TouchSource --> RT
        end
    end
Loading
Layer Where Holds
core core/ The pages, the drawing surface, the player state model and the Volumio protocol types. No operating system, no chip, no vendor.
hal hal/ The contract between the shared code and a device: the traits the core and the runtime talk to, and the capabilities record a firmware fills in.
drivers drivers/<chip>/ One crate per controller chip, over embedded-hal, used by every board that carries the chip. Today the CH32V003 IO expander, the CST3530 touch controller and the ES8311 codec.
runtime runtime/ The application: the loop as a state machine the device steps, the gesture derivation, the Volumio client, the album-art gate, the settings. no_std, generic over hal; the device owns threads, clock and I/O and calls in.
art art/ Album art fetched and decoded (JPEG, PNG) on a thread, for any device with std and the memory. The simulator and an ESP32 on ESP-IDF share it.
platform platform/esp-idf/ What every ESP board shares: the HTTP client, the configuration store over NVS, Wi-Fi and the network status the status page shows.
firmware firmware/<vendor>-<model>/ The device. One crate per board, with its own target, toolchain and chip configuration; its build is the image you flash.
sim sim/ The runtime on a desktop, in a window or straight to a PNG, talking to a real player.

All of it exists today, with its tests; core, hal, runtime and the drivers also build for bare-metal RISC-V, which is how the check proves they are no_std. Finding the player by mDNS is not written yet: the player's address is a setting.

flowchart TB
    core[core · no_std + alloc · pages, surface, state, protocol types]
    hal[hal · no_std + alloc · traits + capabilities]
    drivers[drivers/&lt;chip&gt; · no_std · embedded-hal]
    runtime[runtime · no_std + alloc · the application]
    art[art · std · the decode thread]
    platform[platform/esp-idf · std + esp-idf-svc]
    firmware[firmware/&lt;vendor&gt;-&lt;model&gt; · the device, the image]
    sim[sim · the runtime on a desktop]

    core --> hal
    runtime --> core
    runtime --> hal
    art --> hal
    drivers --> hal
    platform --> hal
    firmware --> runtime
    firmware --> art
    firmware --> drivers
    firmware --> platform
    sim --> runtime
    sim --> art
Loading

Arrows read "depends on". Nothing above platform knows ESP-IDF; nothing in core, hal, runtime or the drivers knows std.

Talking to the player

The player and the panel on one network

The companion is a client of the player over the LAN, as the Volumio apps are: it polls the player's state over its REST interface on port 3000 and sends transport and volume the same way, at the address you give it (finding it by mDNS, _Volumio._tcp, is on the list). Nothing is installed on the player. Connections has the detail and the one cable you might run.

Clone this wiki locally