ZeptoWatch — STM32-based Smartwatch with Python Script Runtime
Overview
ZeptoWatch is a from-scratch smartwatch project, developed as the DIY capstone for an analog circuits lab course. The core concept was to build a wearable device powered by an ordinary microcontroller (STM32F4) that could install and run user-written Python apps — bridging the gap between fixed-firmware fitness bands and full smartwatch operating systems. The Python interpreter (PikaScript) was embedded directly into the firmware, allowing users to write scripts, load them via USB, and execute them as apps.
Key Features
- Embedded Python interpreter: PikaScript runs user
.pyscripts stored on the device’s FAT file system. - USB mass storage: Plug into a computer — the watch appears as a USB drive; drag in Python scripts to install apps. Verified on Windows and Ubuntu.
- Touch display: CST816 capacitive touch chip (I²C) + GC9A01 round LCD (SPI + DMA), with smooth LVGL animations.
- Rich peripherals: EEPROM, IMU (MPU6050), microphone (I²S + DMA), vibration motor, Bluetooth module, battery voltage ADC.
- FreeRTOS multi-tasking: Separate tasks for UI, sensor reading, and script execution with Mutex protection for LVGL thread safety.
- FAT file system: FatFs on 64 KB EEPROM; supports file read/write from both device firmware and USB host.
- Custom PCB: Four design iterations using KiCad / LCEDA; 0402 SMD components, two-layer stacked board for screen placement.
Technical Details
Hardware (Ver 3.0 — final):
- STM32F4 as main controller; CST816 (touch), GC9A01 (display), MPU6050 (IMU), M24512 (EEPROM).
- Two-board stacked design: top board holds the LCD, bottom board contains all active components connected via magnetic pogo pins.
- Type-C connector for both charging (lithium battery management IC) and Full-speed USB 2.0 data.
Firmware Architecture:
- Board-level drivers: Custom I²C bit-bang drivers for touch, EEPROM, IMU; hardware SPI + DMA for display; hardware I²S + DMA for microphone; ADC + DMA for battery voltage.
- FreeRTOS: Multi-task architecture; LVGL resources protected by Mutex to prevent race conditions between tasks.
- LVGL: Embedded GUI framework for all system UI (clock face, date/time settings, app launcher, dropdown menus). Extended
pika_lvglbindings to expose LVGL APIs to Python scripts. - PikaScript: Lightweight Python 3 subset interpreter. Custom extension packages written to expose hardware APIs (IMU, motor, display, timer) to user scripts.
- App examples: Calculator, spectrum analyzer (FFT via ARM DSP library), gravity simulation (accelerometer-driven physics), electronic Muyu (wooden fish tapping), and more.
Challenges
- USB mass storage not recognized: CubeMX-generated USB code was broken until a library update resolved it — a week-long debugging ordeal.
- FatFs on small EEPROM: FatFs blocked formatting below a minimum sector count; resolved by patching the source. Windows failed to recognize the volume for an extended period (eventually fixed itself — suspected to be FAT16/FAT32 auto-detection logic).
- LVGL thread safety: The most persistent crash cause was concurrent LVGL access from multiple FreeRTOS tasks. Adding a Mutex lock resolved all unexplained freezes.
- PikaScript instability: As an early-stage open-source project, PikaScript lacked a crash handler; contributed
__platform_panicoverride to enable graceful recovery from script crashes without rebooting the whole system. - PCB soldering issues: 0402 components and near-BGA spring pins required careful soldering; cold joints caused intermittent issues throughout development.
Reflection and Insights
ZeptoWatch was the most complex embedded project undertaken at the undergraduate level — spanning schematic design, PCB layout, hardware bring-up, driver development, RTOS integration, GUI framework, file system, USB stack, and a scripting language runtime. The most impactful lesson was that system-level correctness requires holistic thinking: a thread-safety bug in LVGL manifested as random freezes everywhere else, and no amount of isolated debugging found it until the root cause was understood. The project also instilled the habit of reading official documentation and library changelogs carefully — the USB bug was silently fixed in a CubeMX update that would have been caught earlier with regular updates.
Team and Role
- Team: Four-person team; responsibilities split between hardware design, firmware, and testing.
- My Role: Contributed to firmware architecture design, peripheral driver development (serial communication, GY-25 interfacing), and Python app development; co-led system integration and debugging.