ST7789 TFT display¶
A 1.9" 320×170 colour IPS display can show track metadata over a full-colour bitmap
background, with a progress bar and elapsed/remaining time. Rendering uses
LVGL 9 with esp_lvgl_port.

ESP32-S3 with PSRAM required
This driver is not viable on the original ESP32. LVGL 9 plus the AirPlay audio pipeline plus WiFi exceeds the internal SRAM budget, and a Wrover's 4 MB flash is too small once the audio stack, SPIFFS partition and LVGL assets are accounted for.
idf_component.yml enforces this: LVGL and esp_lvgl_port are only declared as
dependencies when the target is esp32s3. Non-S3 builds are unaffected — the managed
components are never downloaded and the OLED driver keeps working.
Tested on an ESP32-S3 N16R8 (16 MB flash, 8 MB PSRAM) with ESP-IDF 5.5.3.
Wiring¶
| Display pin | ESP32-S3 GPIO | Function |
|---|---|---|
| SCL / CLK | 18 | SPI clock |
| SDA / MOSI | 17 | SPI data |
| CS | 15 | Chip select |
| DC / RS | 16 | Data / command select |
| RES / RST | 21 | Reset |
| BLK / BL | 38 | Backlight |
| VCC | 3.3 V | Power |
| GND | GND | Ground |
Enabling¶
The display is disabled by default.
idf.py menuconfig
# AirPlay Receiver → Display Configuration → Enable display
# Select driver: ST7789 TFT (320×170 landscape)
CONFIG_DISPLAY_ENABLED=y
CONFIG_DISPLAY_DRIVER_ST7789=y
CONFIG_DISPLAY_SPI_CLK=18
CONFIG_DISPLAY_SPI_MOSI=17
CONFIG_DISPLAY_SPI_CS=15
CONFIG_DISPLAY_SPI_DC=16
CONFIG_DISPLAY_SPI_RST=21
CONFIG_DISPLAY_BL_GPIO=38
Background image¶
At startup the driver loads a full-screen background from /spiffs/bg/background.bin into
PSRAM and draws all widgets on top of it. With no file present the screen falls back to
solid black and everything still renders correctly.
No background ships with the firmware — one costs 106 KB of SPIFFS, which does not fit alongside the web UI on a 4 MB board, and most supported boards have no display at all. To add one:
- Design your image and export a PNG. Any size works — it gets resized.
- Convert it from the project root:
Brightness ranges from
python3 components/display/make_background.py <source.png> [brightness]0.4to0.6. Start at0.5; the ST7789 backlight is considerably brighter than a monitor. - The script writes
data/bg/background.bin. Get it onto the device either by reflashing SPIFFS over serial withidf.py flash, or over WiFi:Then reboot — the new background loads on next boot.curl -X POST "http://<device-ip>/api/fs/upload?path=/spiffs/bg/background.bin" \ --data-binary @data/bg/background.bin
There is no web UI for background uploads; the device's web interface covers WiFi setup and configuration only.
Colour banding¶
The panel is RGB565, so red and blue carry 32 levels and green 64, down from 256 each. Subtle dark gradients show visible banding, which is a hardware limit with no complete software fix. Bold contrast and distinct colour regions render well. The conversion script applies Floyd–Steinberg dithering, which helps a little.
If colours come out washed out, swapped or psychedelic, the byte order is wrong rather than the driver — see the colour bar test in the display component README.
Cover art¶
Album artwork is disabled by default because it can stall the audio pipeline. If you have a TFT display and want it, see AirPlay tuning.
Implementation notes¶
Non-obvious integration requirements for ESP-IDF + LVGL 9 + esp_lvgl_port — rotation
ordering, LVGL task core affinity, and DMA buffer placement — are documented alongside the
code in the
display component README.