TinyML on ESP32 with Edge Impulse: Training and Deploying On-Device Machine Learning Models
TinyML is machine learning that runs entirely on a microcontroller — no cloud round-trip, no internet dependency, and inference measured in milliseconds on a chip that costs a few dollars. This guide covers the general TinyML workflow on ESP32 using Edge Impulse, the free platform most hobbyists use to go from raw sensor data to a deployed model without writing a training pipeline from scratch. This is distinct from vision-based projects like ESP32-CAM face detection — the technique here applies to any sensor stream: accelerometer gesture recognition, audio keyword spotting, vibration anomaly detection, or simple environmental classification.
Why TinyML on ESP32 Makes Sense
The ESP32 (and especially the S3 variant with its vector instructions) has enough RAM and clock speed to run small neural networks — typically under 100KB of model weights — fast enough for real-time classification, while drawing little enough power to run from a battery for weeks. Compare this to sending raw sensor data over WiFi to a cloud model: TinyML eliminates the network dependency, the latency, and the privacy concern of streaming raw audio or motion data off-device. The trade-off is model size and complexity — you're not running a large language model here, you're running a compact classifier trained on a narrow, well-defined task.
The Edge Impulse Workflow
- Create a project and choose your target. Edge Impulse supports ESP32 (Arduino and ESP-IDF), ESP32-S3, and dozens of other boards directly, with built-in latency and memory estimates for each.
- Collect data directly from the device. The Edge Impulse CLI (edge-impulse-daemon) connects to your ESP32 over serial and streams live sensor readings straight into the web dashboard, so you're labeling real data from your real hardware, not a substitute dataset.
- Label your samples. For a gesture classifier this might be "shake," "tap," "idle." For audio keyword spotting it might be "wake_word," "noise," "silence." Aim for at least 3–5 minutes of labeled data per class, captured across different conditions (different people, different orientations, different background noise) or the model will overfit to your specific test setup.
- Build an impulse. This is Edge Impulse's term for the processing pipeline: a signal processing block (spectral analysis for motion, MFCC or spectrogram for audio) feeding a learning block (usually a small neural network, though classical ML models are also available for very constrained targets).
- Train and evaluate. The dashboard shows accuracy, a confusion matrix, and — critically for embedded targets — the estimated RAM, flash, and per-inference latency on your specific chip before you ever flash anything.
- Deploy as an Arduino library or C++ SDK. Edge Impulse exports a ready-to-include library with a single run_classifier()-style API, so integrating inference into an existing ESP32 sketch is a handful of lines, not a machine learning implementation.
Two Concrete Project Shapes
ProjectSensorProcessing BlockTypical Model Size Gesture recognition (shake/tap/flip)MPU6050 or built-in accelerometerSpectral Analysis10–30 KB Wake-word / keyword spottingINMP441 I2S microphoneMFCC (Mel-Frequency Cepstral Coefficients)20–60 KB Vibration anomaly detection (motor/bearing health)ADXL345 or ADXL345-class accelerometerSpectral Analysis + Anomaly Detection (K-means)15–40 KB Simple sound classification (glass break, alarm tone)I2S microphoneSpectrogram30–80 KBAll four of these reuse the same pipeline shape — collect, label, extract features, train a small network, deploy — and all four run comfortably within the ESP32's constraints, which is really the entire pitch of TinyML: you're not scaling a project down from a cloud model, you're picking a task that was always small enough to fit.
Common Pitfalls
- Class imbalance. If your "idle" or "noise" class has 10x the samples of your target class, the model will happily predict "idle" most of the time and still score well on accuracy while being useless in practice. Watch the confusion matrix, not just the top-line accuracy number.
- Data collected in one static setup. A gesture model trained with the board taped to a table in one orientation will fall apart the moment it's handheld. Vary orientation, distance, and background conditions during collection.
- Skipping the live classification test. Edge Impulse's dashboard lets you stream live inference results before deploying anything — use it. A model that looks great on the training/test split can still behave differently on genuinely live, real-time data.
- Ignoring the EON Compiler memory estimate. Edge Impulse's EON Compiler shows RAM and flash usage before deployment; if it's tight against your ESP32 variant's available memory (especially if WiFi and other libraries are also loaded), simplify the model or reduce the window size before you're debugging a mysterious crash on-device.
Where This Fits With ESP32-CAM Vision Projects
If you've already worked through ESP32-CAM face detection or motion detection on this site, the underlying idea is the same — deploy Edge Impulse or TensorFlow Lite Micro inference on-device — but the sensor and preprocessing are completely different. Vision models need a camera, more RAM, and typically more flash for the frame buffer and model weights; accelerometer and audio TinyML models are far lighter and will run comfortably on a plain ESP32 without the camera module at all, making this a good on-ramp if you want to understand the embedded ML workflow before committing to the more resource-hungry vision case.
Related Guides
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- How to Hack Wi-Fi and Bluetooth with the Flipper Zero and Wi-Fi Marauder
- ESP32: Setting Up for Arduino IDE
- Automated Plant Watering System with Raspberry Pi
- Reading Sensors over I2C with Raspberry Pi