TinyML: Edge Impulse Workflow
The manual pipeline from chapters 4-8 gives you full control. Edge Impulse trades that control for speed: from data collection to deployed firmware in an afternoon, without writing the preprocessing code yourself.
What Edge Impulse Is
Edge Impulse is a cloud platform (as of 2026, with a generous free tier) that wraps the TinyML pipeline behind a browser UI. You handle:
- Choosing the sensor and labeling strategy
- Reviewing data quality
- Selecting the model architecture
- Setting the deployment target
Edge Impulse handles:
- Feature extraction (MFCC, spectral features, image resizing)
- Model training with hyperparameter tuning
- Quantization and optimization
- Generating ready-to-compile Arduino/ESP-IDF/Zephyr libraries
It is worth knowing because many real projects use it, and because its generated code is instructive even when you don't use it.
Creating a Project
Sign up at studio.edgeimpulse.com and create a new project.
Project type: Classification
Device: Arduino Nano 33 BLE Sense (or choose your target)
The device selection changes default DSP block choices and memory budget estimates.
Connecting the Device for Live Collection
Edge Impulse has a CLI that turns your board into a streaming data source:
npm install -g edge-impulse-cli
edge-impulse-daemon
# Follow the prompts: paste your API key, select the project
# The CLI flashes a data forwarder sketch to the board automatically
After connecting, you see the device in the Edge Impulse dashboard under Devices. Click Start sampling to record labeled samples directly into the project without writing a Python collector.
For custom sensors or serial data formats, the edge-impulse-data-forwarder command accepts CSV lines on stdin:
# Forward data from a custom sketch that prints "aX,aY,aZ" to Serial
edge-impulse-data-forwarder --frequency 100 --port /dev/ttyACM0
Building an Impulse
An "impulse" is the processing pipeline from raw data to classification.
Navigate to Impulse Design in the left sidebar:
- Input block: set window size (1000 ms) and window increase (200 ms for 5 Hz inference).
- Processing block: choose the DSP block.
- For accelerometer: "Spectral Analysis" or "Raw features"
- For audio: "MFCC" or "MFE" (mel filterbank energy, no DCT)
- For image: "Image" (resize + normalize)
- Learning block: choose "Classification (Keras)" for standard classification.
- Click Save Impulse.
Configuring the DSP Block
For gesture classification with accelerometer data, use "Spectral Analysis":
Scale axes: 1
Input decimation sampling: 1
Type: FFT
FFT length: 16
Overlap: 50%
Noise floor: -52 dB
Click Save parameters, then Generate features. The dashboard shows a feature visualization (a scatter plot of all samples by class). Well-separated clusters predict good model accuracy.
Training the Model
Navigate to NN Classifier (or Transfer Learning for image models):
Number of training cycles: 100
Learning rate: 0.0005
Validation set size: 20%
Auto-balance dataset: enabled
Click Start training. Training runs on Edge Impulse's servers and typically finishes in 1-5 minutes for sensor models.
After training, the results page shows:
Accuracy: 94.8%
Loss: 0.18
RAM (peak): 3.4 KB
Flash (program): 16.1 KB
Latency (Nano): 2.3 ms
The RAM and flash estimates are measured on the target hardware, not estimated. This is one of Edge Impulse's most useful features: you know whether the model fits before flashing.
Exporting as an Arduino Library
Navigate to Deployment:
- Select Arduino library
- Click Build
- Download the generated
.zipfile
Install it in Arduino IDE:
arduino-cli lib install --zip ~/Downloads/ei-gesture-project-arduino-1.0.4.zip
The library contains the full inference pipeline: DSP feature extraction, the TFLite model, and a C++ class that wraps run_classifier(). The sketch is minimal:
// ei_gesture.ino
#include <gesture-project_inferencing.h>
#include <Arduino_LSM6DSOX.h>
float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];
int feature_ix = 0;
int raw_feature_get_data(size_t offset, size_t length, float* out_ptr) {
memcpy(out_ptr, features + offset, length * sizeof(float));
return 0;
}
void setup() {
Serial.begin(115200);
IMU.begin();
}
void loop() {
if (!IMU.accelerationAvailable()) return;
float ax, ay, az;
IMU.readAcceleration(ax, ay, az);
features[feature_ix++] = ax;
features[feature_ix++] = ay;
features[feature_ix++] = az;
if (feature_ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) return;
feature_ix = 0;
signal_t signal;
numpy::signal_from_buffer(features,
EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
ei_impulse_result_t result;
EI_IMPULSE_ERROR err = run_classifier(&signal, &result, false);
if (err != EI_IMPULSE_OK) return;
// Print top prediction
for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
Serial.print(result.classification[i].label);
Serial.print(": ");
Serial.println(result.classification[i].value, 3);
}
Serial.println();
}
The run_classifier() function does the DSP and inference in one call. The Arduino library includes the normalization, windowing, feature extraction, and TFLite Micro inference under the hood.
Continuous Motion Recognition
Edge Impulse has a special mode for streaming inference that handles the sliding window automatically. Enable it by selecting Continuous motion recognition in the deployment options.
The generated library provides run_classifier_continuous() which you call with each new sensor reading. It maintains the internal ring buffer and feature pipeline:
void loop() {
if (!IMU.accelerationAvailable()) return;
float ax, ay, az;
IMU.readAcceleration(ax, ay, az);
// Buffer single sample
float buf[3] = {ax, ay, az};
signal_t signal;
int err = numpy::signal_from_buffer(buf, 3, &signal);
if (err != 0) return;
ei_impulse_result_t result;
bool debug = false;
run_classifier_continuous(&signal, &result, debug);
if (result.bounding_boxes_count > 0) {
// If using object detection: skip for classification
} else {
// Classification result
ei_classifier_smooth_print_results(&result, SMOOTH_LABELS,
EI_CLASSIFIER_LABEL_COUNT, 0.8f);
}
}
When to Use Edge Impulse vs. Manual Pipeline
| Situation | Recommendation |
|---|---|
| Prototyping a new gesture or keyword | Edge Impulse: faster iteration |
| Need custom DSP not in Edge Impulse | Manual pipeline |
| Production with strict size limits | Manual + custom MicroMutableOpResolver |
| Multiple people collecting data | Edge Impulse's UI handles this well |
| Need to understand the full stack | Manual pipeline at least once |
| Deploying to Zephyr or bare metal | Edge Impulse SDK or manual TFLite Micro |
Edge Impulse's generated code is a good reference even when you write your own. The DSP implementations in edge-impulse-sdk/dsp/ are readable and correct.
Exporting the Model for Use Outside Edge Impulse
If you want the trained model without the Edge Impulse wrapper:
- Navigate to Impulse Design → NN Classifier → Export model
- Select TensorFlow Lite (int8 quantized)
- Download
model.tflite
From there you can follow the manual deployment steps in chapter 7. The model is a standard TFLite flat buffer.
Next Steps
Continue to 11-optimization.md to reduce inference latency, model size, and power consumption using pruning, quantization-aware training, and op selection.