[Media] Ratatui rendering to a physical ILI9341 over SPI — embedded espresso-machine firmware in Rust on the ESP32
![[Media] Ratatui rendering to a physical ILI9341 over SPI — embedded espresso-machine firmware in Rust on the ESP32](https://i.redd.it/d8fwm88wse8h1.jpeg)
I'm fairly new to Rust — long-time backend/fullstack dev, but this is my first real project in the language. I picked it on purpose: I'd been impressed by Ratatui for terminal UIs, then found out people had pushed it onto embedded displays, and I wanted to combine that with my home-automation setup. My espresso machine (a Lelit MaraX) streams telemetry over UART, so it became the target: firmware that parses that stream and renders a dashboard onto a 320×240 ILI9341 TFT, then feeds the data into Home Assistant.
The part that might interest this sub: the whole UI is Ratatui, rendered to the physical display via mousefood — an embedded-graphics backend for Ratatui. So it's the normal Ratatui model (layout solver, widgets, tui-widgets big-text, a rolling chart) but instead of a terminal the frames land on a screen bolted to a coffee machine. Ratatui's immediate-mode redraw maps cleanly onto a small refresh-on-change display, and I got to reuse the whole widget ecosystem on bare-ish metal.
Stack:
esp-idf-svc(so std, notno_std— I wanted threads, MQTT and Wi-Fi without reimplementing them)ratatui+mousefoodfor the UI,ili9341+display-interface-spifor the panel- UART parse of the machine's telemetry frames into a typed struct
rumqttcout to MQTT, with an optionalhome-assistantfeature that publishes MQTT Discovery configs straight from the firmware- a host simulator: same UI code, but mousefood renders through
embedded-graphics-simulator(SDL2) instead of SPI. Cargo features (device/simulator) swap the backend, so I develop the entire interface on my laptop and only flash when the logic is right
That simulator was the big quality-of-life win, and honestly the thing that made the project survivable as a beginner. The UI is backend-agnostic by construction — the same draw() runs against a real ILI9341 and against an SDL2 window, because both are just embedded-graphics targets under mousefood. I could iterate on layout and widgets at laptop speed and only deal with hardware when the logic was actually ready.
It started from the well-known MaraX serial mod and an existing ESPHome integration, but it's a ground-up rewrite — new UI, custom PCB, MQTT discovery for Home Assistant. Source, screenshots and the simulator: https://github.com/ololonly/maratui
I'd genuinely welcome a critical read of the code given I'm new to the language.