Back to ESP32 Desk Companion

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
See the User Guide for a walkthrough of daily use.

Hardware

Board: Spotpear ESP32-S3 N16R8 (16 MB flash, 8 MB PSRAM)

PeripheralInterfaceKey GPIOs
GC9A01 240x240 round LCDSPISCLK=4, MOSI=2, CS=5, DC=47, RST=38
BacklightLEDC PWM (inverted)42
ES8311 audio codecI2C bus 0 + I2SSDA=15, SCL=14, MCLK=16, BCLK=9, WS=45
Speaker / MicI2S via ES8311DIN=10, DOUT=8, PA=46
CST816D touchI2C bus 1SDA=11, SCL=7, RST=6, INT=12
Battery ADCADC1
Charge detectGPIO41

Prerequisites

  • ESP-IDF v5.5.2 or later
  • The device connects as /dev/ttyACM0 on Linux (built-in USB JTAG/serial)

Build and Flash

bash
# 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):

ComponentVersion
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:

bash
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)
Night mode schedule — 20:00 to 07:15, hardcoded in daylight_cb().

Partition Table

Custom layout with a 2 MB app partition to fit WiFi + HTTP stack:

shell
nvs,      data, nvs,     0x9000,  0x6000
phy_init, data, phy,     0xf000,  0x1000
factory,  app,  factory, 0x10000, 0x200000

License

MIT

© 2026 Jonathan Leahy · v0.8.1-31-g196fa14