A hands-on TinyML project. Deployed a neural network on an STM32 Nucleo-L476RG that classifies motion (idle, moving, shaking, tapping) from a live accelerometer feed at 100 Hz.
Built this to get past the theory and actually feel what deploying AI on embedded hardware is like. Wanted real experience with the full pipeline, from wiring up a sensor to running inference on the chip.
demo.mp4
| Component | Role |
|---|---|
| STM32 Nucleo-L476RG | ARM Cortex-M4, 80 MHz, 128 KB RAM, 1 MB flash |
| MPU6050 (GY-521) | 3-axis accelerometer over I2C |
| MPU6050 | Nucleo |
|---|---|
| VCC | 3.3V |
| GND | GND |
| SDA | PB9 (D14, I2C1_SDA) |
| SCL | PB8 (D15, I2C1_SCL) |
Development setup: Nucleo-L476RG with MPU6050 secured via tape (no breadboard needed for a 4-wire I2C connection)
- STM32CubeIDE for building, flashing, debugging
- STM32CubeMX for peripheral config (I2C1, USART2)
- STM32 HAL, bare-metal, no RTOS
- Peripheral setup in C (HAL), converted to C++ (required by the Edge Impulse SDK)
- Edge Impulse for data collection, training, and model export
- CMSIS-PACK to integrate the model into the STM32 project
Note: Claude was used as a coding assistant throughout the project.
- Input: 1000ms window of 3-axis accelerometer data at 100 Hz (300 samples)
- DSP: spectral features (63 features)
- Classifier: small dense NN (20 → 10 → 4), float32
- Classes: idle, moving, shaking, tapping
- Training accuracy: 99.5%
- Test accuracy: 91.26%
- Memory: 1.7 KB RAM, ~16.6 KB flash for the model
- Inference: 5 ms (4 ms DSP, 1 ms classifier)
View the model, data, and training on Edge Impulse
- Wired up the MPU6050, got I2C talking, read WHO_AM_I to confirm the sensor was alive, then streamed raw accel over UART
- Collected labeled motion samples across a few orientations using the Edge Impulse Data Forwarder
- Trained the model in Edge Impulse Studio
- Exported as a CMSIS-PACK, dropped it into the STM32 project
- Wrote the inference loop: fill a 500ms window, run the classifier, print the class only when it changes
- AI is unstable, needs a lot of data, and never actually hits 100%. If it does, something's wrong (usually the test set is too easy or the model is overfitting).
- The exact same setup has to be used for training and deployment. Sample rate, sensor orientation, window size, all of it. Mismatch and predictions becomes insanely wrong.
- On a chip with 128 KB of RAM, every design choice is a trade-off. Bigger window means better accuracy but no memory. Smaller window fits, but short events like taps get diluted.
- Even though the model can't fully replace hand-coded logic, it saved me from having to hand-tune filters and thresholds for every motion type. That's the real value of TinyML and it's easier to simply detect new motion, for example, waves, circular motions etc...
- Edge Impulse - Continuous Motion Recognition Tutorial
- Edge Impulse - Run CubeMX CMSIS-PACK
- STM32 Nucleo-64 User Manual (UM1724)
- STM32L4 HAL Driver Manual (UM1884)
- MPU6050 Register Map
/AccSense- main STM32 project running the model/AccelSampling- separate firmware used only to stream accel data for training