t4as

Embedded & Firmware

Yellow Screen Desk Display

A Pomodoro timer and desk dashboard on the ESP32 "Cheap Yellow Display", with touch input, MP3 alerts from SD over I2S, and a Wi-Fi-synced clock.

  • ESP32
  • C++
  • PlatformIO
  • TFT_eSPI
  • I2S audio
  • XPT2046 touch
Yellow Screen Desk Display

What it is

A desk timer and dashboard on the ESP32 “Cheap Yellow Display” board (ESP32 + ST7789 TFT + XPT2046 resistive touch + SD slot). It has two screens:

  • Pomodoro timer: set work and break lengths by touch, with a progress bar. At the end of each session an MP3 from the SD card plays over I2S.
  • Dashboard: Wi-Fi-synced clock, room temperature, and brightness / colour-temperature sliders for a light. The light is simulated for now; the sliders are a placeholder for a smart-light integration.

The Yellow Screen dashboard and timer UI

Structure

A small state machine switches between the dashboard and the timer. Each screen does its own drawing and touch handling.

Class Responsibility
CYD TFT, touch controller and LEDs
PomodoroManager Timer state, timer screen and its touch zones
AudioManager MP3 decode and I2S output
SDManager FAT32 file access

Touch handling

Touch zones are hard-coded rectangles in screen coordinates. While the alarm plays, any touch stops it (PomodoroManager.cpp):

void PomodoroManager::handleTouch(int16_t x, int16_t y) {
    if (m_isAlarmSounding) {
        // Stop alarm on any touch
        m_audio.stop();
        m_isAlarmSounding = false;
        // ... transition to next state ...
        return;
    }

    if (m_isRunning) {
        // Stop button
        if (y >= 190 && y <= 230 && x >= 110 && x <= 210) {
            m_isRunning = false;
            drawInterface();
        }
        return;
    }

    // ... time adjustment and start buttons ...
}

Main loop

Audio playback is non-blocking, so the MP3 decoder has to be fed from the main loop. The UI is serviced every 20 ms, and audioManager.loop() runs on every pass so the I2S buffer doesn’t underrun during playback (main.cpp):

void loop() {
    static unsigned long lastUpdate = 0;
    unsigned long currentTime = millis();

    if (currentTime - lastUpdate >= 20) {
        cyd.update();
        lastUpdate = currentTime;
    }
    audioManager.loop();
}