README
esp32-clock
Standalone firmware for the Spotpear ESP32-S3 1.28" BOX (the round "ESPHome Xiaozhi AI Voice Chat Robot Ball"). Turns the device into a desk companion with an animated face, analog clock, time-aware colour themes, a pomodoro timer, and plant soil moisture monitoring.
<p align="center"> <img src="docs/face-day.png" width="160" alt="Daytime face with clock hands"/> <img src="docs/face-night.png" width="160" alt="Night mode sleeping face"/> <img src="docs/date-screen.png" width="160" alt="Date info screen"/> <img src="docs/pomo-running.png" width="160" alt="Pomodoro timer countdown"/> </p>
Features
- Animated smiling face with blinking eyes, rosy cheeks, and R2-D2 chirp reactions to voice
- Analog clock hands overlaid on the face, always visible during the day
- Time-of-day colour themes — sunrise orange, daytime yellow, sunset coral, dusk purple, night blue
- Night mode — sleeping face with closed eyes, dim backlight, wakes briefly on noise
- Date info screen — tap to see day/date, tap again for pomodoro
- Pomodoro timer — 5-60 minutes with pie-slice countdown and alarm beeps
- Plant soil moisture — polls a GraphQL backend and shows a wilting plant when soil is dry
- WiFi captive portal — easy first-time WiFi setup from any phone or laptop
Hardware
Board: Spotpear ESP32-S3 N16R8 (16 MB flash, 8 MB PSRAM)
| Peripheral | Interface | Key GPIOs |
|---|---|---|
| GC9A01 240x240 round LCD | SPI | SCLK=4, MOSI=2, CS=5, DC=47, RST=38 |
| Backlight | LEDC PWM (inverted) | 42 |
| ES8311 audio codec | I2C bus 0 + I2S | SDA=15, SCL=14, MCLK=16, BCLK=9, WS=45 |
| Speaker / Mic | I2S via ES8311 | DIN=10, DOUT=8, PA=46 |
| CST816D touch | I2C bus 1 | SDA=11, SCL=7, RST=6, INT=12 |
| Battery ADC | ADC | 1 |
| Charge detect | GPIO | 41 |
Prerequisites
- ESP-IDF v5.5.2 or later
- The device connects as
/dev/ttyACM0on Linux (built-in USB JTAG/serial)
Build and Flash
# Activate ESP-IDF environment
source ~/esp/esp-idf/export.sh
# Build
idf.py build
# Flash
idf.py -p /dev/ttyACM0 flash
# Monitor serial output (Ctrl+] to exit)
idf.py -p /dev/ttyACM0 monitor
Dependencies
Managed automatically by the ESP-IDF component manager (declared in main/idf_component.yml):
| Component | Version |
|---|---|
espressif/esp_lcd_gc9a01 | ^2.0.1 |
lvgl/lvgl | ~9.2 |
espressif/esp_lvgl_port | ~2.4 |
espressif/esp_codec_dev | ~1.5.4 |
espressif/esp_lcd_touch_cst816s | ^1.0.6 |
WiFi Setup
On first boot (or when saved credentials fail), the device creates an open WiFi network named display-face. Connect to it from your phone or laptop — a captive portal will appear. Select your home network, enter the password, and submit. The device saves the credentials and restarts.
To force re-provisioning, erase flash and reflash:
idf.py -p /dev/ttyACM0 erase-flash
idf.py -p /dev/ttyACM0 flash
Configuration
Timezone — auto-detected from your IP address on each boot using the ip-api.com service. No configuration needed. If the API is unreachable, falls back to UK time (GMT/BST). To override, edit the tz_posix fallback string in wifi_task() in main/main.c and rebuild.
Soil moisture backend — configured via defines at the top of main/main.c:
BACKEND_URL— GraphQL endpoint returning{ devices { friendlyName latestPayload } }SOIL_THRESHOLD— moisture % below which the wilting plant appears (default: 20)
daylight_cb().Partition Table
Custom layout with a 2 MB app partition to fit WiFi + HTTP stack:
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 0x200000
License
MIT