Watch Face

A three-mode smartwatch UI β€” digital, analog, and tilt-level β€” backed by the PCF85063 RTC, QMI8658 IMU raise-to-wake, and tap-to-cycle touch.

PCF85063 RTC QMI8658 IMU CST816T Touch Raise-to-wake Arduino_Canvas 3 watch faces

What it does

The PCF85063 RTC keeps accurate time across power cycles. On first boot (or if the oscillator-stopped flag is set), the sketch seeds the RTC from the compile-time __DATE__ / __TIME__ macros.

Tap the screen to cycle through three faces: Digital (large HH:MM, seconds bar, date), Analog (swept hands with gold bezel and tick marks), and Info (time + live pitch/roll level bars). Tilting the board upward triggers raise-to-wake; the display sleeps after 10 s of inactivity.

# Watch Faces

FaceIndexKey elements
Digital0HH:MM at size-6, seconds in gold at size-3, animated seconds progress bar, day + date + month string.
Analog160-tick bezel with gold double-ring, 12 hour numerals, thick hour/minute hands, red second hand with tail, date window at 6 o'clock position.
Info / Level2Compact time at top, two horizontal progress bars showing live roll and pitch angles. Bar turns green within Β±3Β°, orange beyond.

# Interaction Model

InputBehaviour
Tap (CST816T gesture 0x01)Cycles faceIdx 0β†’1β†’2β†’0, wakes display, resets 10 s sleep timer.
Raise-to-wake (IMU)Detects rising-edge on Z-axis acceleration crossing 0.4β†’0.7 g (wrist lift). Wakes display, resets timer.
Inactivity timeoutAfter 10 000 ms with no touch or raise event, backlight is pulled LOW (display off).

# PCF85063 RTC

Registers used

0x00  Control_1 β€” STOP/START bit
0x04  Seconds  (7 bytes, BCD)
0x04  Seconds β€” OS flag (bit 7)
      β†’ non-zero means clock lost power

Auto-seed logic

if (OS flag set) {
  stop RTC (0x00 = 0x20)
  write __DATE__ / __TIME__
  start RTC (0x00 = 0x00)
}

Only runs once after a cold start or battery loss β€” subsequent boots preserve stored time.

# IMU β€” Raise-to-Wake

The QMI8658 accelerometer is read every loop (~50 ms). A simple edge detector watches the Z-axis value (gravity component) cross from below 0.4 g to above 0.7 g β€” the signature of a wrist-lift. A low-pass filter (az = azΓ—0.8 + prevΓ—0.2) smooths the signal before the threshold check.

For the Info face, pitch and roll are derived from all three accelerometer axes:

pitch = atan2(ay, sqrt(axΒ² + azΒ²)) Γ— 180/Ο€
roll  = atan2(βˆ’ax, az)              Γ— 180/Ο€

# Rendering Pipeline

All drawing targets an Arduino_Canvas (off-screen framebuffer in PSRAM). Each face calls canvas->flush() once at the end to push the full frame via SPI DMA β€” eliminating tearing artifacts.

Canvas setup

Arduino_Canvas *canvas =
  new Arduino_Canvas(240, 280, gfx);

PSRAM-backed; the 240Γ—280Γ—2 byte buffer (~131 KB) fits comfortably in the 8 MB OPI PSRAM.

thickLine() helper

for d in [-thick/2 .. thick/2]:
  offset perp to line by d px
  drawLine(offset copy)

Renders bold clock hands without hardware stroke-width support.

# Source

Full sketch in the ESP32-S3-LCD repo β€” sketches/watch_face/watch_face.ino.

git clone https://github.com/binRick/ESP32-S3-LCD
bash flash-sketch-003-watch-face.sh