VoriaSolutions · 2026

An AMOLED watch display driver

Part of porting the open-source ZSWatch to a Waveshare ESP32-S3 AMOLED board on upstream Zephyr: the board itself, its production test, and a display driver rebuilt around DMA.

Goal

ZSWatch is an open-source smartwatch built around a Nordic nRF5340. The goal was to run it on a cheaper off-the-shelf ESP32-S3 board with a 410×502 AMOLED panel, on upstream Zephyr, without breaking the original nRF builds.

What I built

Board support, and a rule for the port

Device trees, pin control and configuration for the new board, and one rule that kept the port clean: the nRF sources stay untouched, and the new board gets sibling backends selected through Kconfig and the device tree.

Production test

The factory test flow, brought up check by check: I²C scan, display, power-management chip, battery, IMU, touch, real-time clock, audio codec and microphone, microSD card, Wi-Fi scan and BLE advertising.

The display driver

The CO5300 panel needs a one-line command and address followed by four-line pixel data in a single transaction, which Zephyr's SPI driver could not produce. I went from bit-banged GPIO to the ESP32-S3's hardware QSPI through Espressif's HAL, then fed it with GDMA in 4 KB chunks, and finally waited on the DMA-complete interrupt instead of spinning, so other threads run during transfers.

Driver diagram: pixels go from an LVGL frame through GDMA and a 40 MHz QSPI bus to the CO5300 AMOLED panel; the driver moved from bit-banged GPIO to hardware QSPI to QSPI with DMA.
The pixel path, and how the driver got there.

The rest of the watch

Brightness and vibration backends for the new hardware, a battery backend on the AXP2101 power chip, the Bluetooth and Gadgetbridge layer moved to upstream Zephyr's stack, and UI screens laid out for the 410×502 panel.

Result

Bit-banged, a full screen took 200–400 ms. With hardware QSPI the per-second redraw took 2.2 ms, and with DMA 0.9 ms, at about 14 MB/s and with the CPU free during transfers. All 16 production-test checks pass on the board.

What I learned

  • Reading a display controller's datasheet closely enough to get its command protocol exactly right.
  • ESP32-S3 peripherals below the RTOS: QSPI, GDMA and the GPIO matrix, through Espressif's HAL alongside Zephyr drivers.
  • Porting a large Zephyr application to a new board without forking it.
  • Bringing up a board one peripheral at a time, with a production test as the checklist.