FitnessAI Watch

An open-hardware smartwatch on a $3 ESP32-C3 — heart rate, steps, and GPS tracked on-device, then synced to the Fitness AI Agents platform for analysis. No proprietary ecosystem.

Projects  ·  Fitness AI Agents  ·  FitnessAI Watch


The Build


Most wearables lock your own vitals inside someone else’s app. This one does the opposite: a hand-wired watch built from commodity I2C modules around an ESP32-C3 SuperMini, running firmware written in C/C++ against the Arduino framework. It counts steps and beats on-device, keeps its own clock over NTP, and uploads workouts straight to the same /ingest endpoint the phone app uses — so the AI analysis on the platform works on data the watch itself produced.

It is also the second life of a competition entry. The first version was built for PhysTech 2026 and was the first hardware Matt had ever built. After that submission the surrounding project pivoted to a multi-source data platform, and the firmware was restarted from scratch in June 2026 on the lessons from the first build.

Hardware


Everything shares one I2C bus, which is what makes the wiring tractable and also what caused the worst bugs.

ESP32-C3 SuperMini

MCU and Wi-Fi radio on one ~$3 board. Chosen because it has Wi-Fi built in, so the watch needs no phone to reach the internet.

SSD1306 128×64 OLED

Shared I2C bus at address 0x3C. An LCD1602 with a PCF8574 backpack is supported as an alternative panel on the same pins.

MPU6050

Accelerometer and gyroscope at 0x68. Drives step counting and, in the current firmware, auto-rotates the display so it stays upright.

MAX30102

Heart rate and SpO₂ at 0x57, plus a die-temperature reading. Polled every loop rather than using its interrupt line.

NEO-6M GPS (optional)

Route tracking over UART1. Optional — the watch is fully functional without it.

Board

A KiCad schematic and PCB layout exist in the repo alongside the firmware, so the hand-wired prototype has a route to a real board.


What Works Today


Milestones 1–5 plus auto-orientation are done, and the firmware compiles clean for esp32:esp32:esp32c3 at 46% flash and 14% RAM. On the device that means:


Not Built Yet


The battery and power stage is designed and the firmware side (power.h / power.cpp) is written and compiling, but the hardware is not built. Until the divider is soldered, PIN_BATT_ADC ships as -1 so an unwired pin cannot float and report a fictional battery percentage; the status bar keeps reading USB. The watch currently runs from USB power.

The power design does have one genuinely non-obvious trap already worked out on paper: on the SuperMini the 5V pin is USB VBUS, so tying a TP4056 charger input to it while injecting a boost converter creates a loop that charges the battery from itself. It never terminates, the cell drains faster than with no charger at all, and nothing visibly fails — which is what makes it worth documenting before building it.

Bugs Worth Keeping


Two bring-up failures cost more time than the rest of the firmware combined, and both are recorded in the repo so they are not rediscovered:

The display driver. The 0.96″ SSD1306 must be initialised as NONAME, not ALT0. The ALT0 init makes a sparse test pattern look completely fine, then interleaves the rows with real text so every UI line is squashed on top of the next — a fault that only appears once you draw something realistic.

The bus clock. The shared I2C bus has to run at 100 kHz, but MAX30105.begin(Wire, I2C_SPEED_FAST, ...) silently raises it back to 400 kHz. The OLED then flickers or blanks for reasons that look nothing like a clock-speed problem. The clock is now re-asserted after the sensor init block.

What to Inspect Next



View the firmware on GitHub