Tomato32 is a Pomodoro timer for the Waveshare ESP32-S3-Touch-LCD-3.49 development board.
tomato32_demo_video.mp4
- Touchscreen interface
- Three preset timer profiles, each with customizable durations
- You can adjust the durations for focus time, short break, long break, and the number of focus sessions before a long break.
- Various settings to make it fit your workflow.
- Custom background support (see Custom Backgrounds)
- Battery life of ~14-24 hours with always-on display, depending on screen brightness used (using the 18650 battery).
- Battery life can be extended if screen dimming is enabled.
- CMake
- SDL2
brew install cmake sdl2- Waveshare ESP32-S3-Touch-LCD-3.49 development board
- Python 3
- Docker (used by the ESP32 build/flash script)
Note
The simulator has only been tested on macOS. If you get it working on Linux or Windows, a PR adding setup instructions would be welcome.
cmake -B build
cmake --build build
./build/platform/simulator/pomodoro_simcd platform/esp32
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
./build.sh clean
./build.sh flash /dev/cu.usbmodem101Note
Replace /dev/cu.usbmodem101 with the actual serial port of your ESP32 board. Run ls /dev/cu.* before and after plugging in the board to identify the correct port.
Tomato32 has two boot modes you can enter:
| Mode | How to enter |
|---|---|
| Normal | Hold the PWR button until you see the Tomato32 splash screen. |
| Config mode | Hold the PWR button for one second. While still holding PWR, immediately press and hold the BOOT button until the Tomato32 splash screen displays. |
The serial monitor is available in config mode only.
./build.sh monitor /dev/cu.usbmodem101Enter config mode (see instructions in How to boot?). Connect the device to your computer with a USB-C cable, a drive will appear on your computer. Open TOMATO32_CONFIG.conf on that drive and fill in your credentials:
WIFI_SSID=your_network
WIFI_PASS=your_password
TZ=UTC0
See TZ format examples (JST-9, CET-1CEST,M3.5.0/2,M10.5.0/3, etc.).
Eject the drive, then power-cycle the device. The new settings take effect on the next boot.
Note
Why do I need Wi-Fi for a Pomodoro timer?
Wi-Fi is only used to sync the date and time via NTP and update the RTC. This is only needed for the daily "Focused today" statistic. Providing Wi-Fi credentials is completely optional. Without them, the timer will still work, but the "Focused today" stat may not reset at the correct local midnight.
Press the BOOT button to open Settings. Press it again to return to the timer.
Tomato32 has three presets (A, B, C). Pick one on the left, tap Edit durations to configure it, then Use profile to go back to the timer.
| Setting | Description |
|---|---|
| Focus session | Focus phase length (minutes). |
| Short break | Short break length (minutes). |
| Long break | Long break length (minutes). |
| Rounds | Focus sessions before a long break is triggered. |
| Option | Description |
|---|---|
| Auto | Next phase starts automatically when the current one ends. |
| Manual | You have to manually start the next phase. |
| Setting | Options | Description |
|---|---|---|
| Theme | Light / Dark | Colour scheme. |
| Visual pulse | On / Off | Pulsing background color when the timer ends. |
| Custom backdrop | On / Off | Shows a custom background on the timer screen (see Custom Backgrounds). |
| Setting | Options | Description |
|---|---|---|
| Default brightness | 0–100 % | Screen brightness during normal use. |
| Smart dim brightness | 0–100 % | Brightness when smart dim is enabled. |
| Visual pulse opacity | 0–100 % | Opacity of the visual pulse color. |
| Smart dim | On / Off | Dims to the smart dim level after 60 seconds of inactivity while the timer runs. Tap anywhere to restore. |
| Smart sleep | On / Off | Turns the screen off after 60 seconds of inactivity instead of dimming. Tap to wake up. |
| Setting | Options | Description |
|---|---|---|
| Sound | On / Off | Chime at the end of each phase. |
| Volume | 0–100 % | Chime volume. |
| Setting | Options | Description |
|---|---|---|
| Date source | NTP / Manual | Select between synchronizing time via NTP or setting it manually. NTP requires Wi-Fi credentials to be set up (see Wi-Fi credentials via config drive). |
| Set date | - | If Date source is set to Manual, set the date. |
| Set time | - | If Date source is set to Manual, set the time. |
Shows Focused today and Total focus time (all-time total).
To access the screen, tap and hold the phase label (Focus / Break / Long Break) text in the main timer screen for 1 second to open.
Shows the current datetime, Wi-Fi SSID, IP address, and free heap.
To access the screen, tap the main timer text (MM:SS) 10 times within 5 seconds to open it.
To use a custom background image on the timer, go to System Settings → UI → Custom Backdrop and set it to On.
Place your image files (640×172 PNG) in the app/ directory, then rebuild and flash.
Tomato32 uses the most specific matching file available. Name your files accordingly:
| Specificity | File name example | When it's used |
|---|---|---|
| Preset + theme | custom_background_b_dark.png |
Preset B, dark mode only |
| Preset only | custom_background_b.png |
Preset B, both themes |
| Theme only | custom_background_dark.png |
Any preset, dark mode |
| (fallback) | Default solid color |
Valid preset suffixes are _a, _b, and _c. Valid theme suffixes are _dark and _light.
Important
Background images are embedded into the firmware at build time. Whenever you add or remove PNG files in app/, run a clean build so CMake picks up the changes:
cd platform/esp32
./build.sh clean
./build.sh flash /dev/cu.usbmodem101This project vendors LVGL under lvgl/.
- Project-level notices:
THIRD_PARTY_NOTICES.md - LVGL license:
lvgl/LICENCE.txt - LVGL third-party attributions:
lvgl/COPYRIGHTS.md
The typeface used in the UI is Inter, licensed under the SIL Open Font License 1.1.
The sound effect used for timer end is sourced from freesound.org and licensed under Creative Commons 0.
- Photo by Federico Respini on Unsplash.
- Photo by Alex Machado on Unsplash.





