Edge AI Handwritten Digit Recognizer on Embedded Microcontrollers
Deploy lightweight quantized convolutional neural networks onto an embedded microcontroller to perform real-time classification of handwritten digits (0-9) without relying on internet or cloud APIs.
1. Aim of the Project
Project Objective
The aim is to train a 28x28 grayscale MNIST convolutional neural network in Python using TensorFlow, convert and quantize the model parameters into 8-bit integers (INT8) using TensorFlow Lite for Microcontrollers (TFLM), embed the static C byte array into firmware, and execute inference in under 30 milliseconds directly on edge silicon.
2. Interactive 3D Assembly & Circuit Wiring Model
Rotate the 3D model 360 degrees, zoom in/out, disassemble/explode parts to inspect individual hardware layers, toggle realistic circuit wires, and click any component to inspect its engineering specifications.
3. Laboratory Video Masterclass Tutorial
Step-by-step video walkthrough covering breadboard circuit assembly, wiring verification, and testing. Enrolled students and instructors can access video streaming below.
2. Learning Outcomes
Model Quantization (INT8)
Learn how floating-point 32-bit weights are compressed into 8-bit integers to fit inside strictly constrained microcontroller SRAM.
Zero-Latency Edge Inference
Understand the security, power, and latency advantages of running machine learning offline directly on sensor endpoints.
3. Technologies Learned
4. Required Components
| Component Name | Quantity | Specification | Interface |
|---|---|---|---|
| Arduino Nano 33 BLE / ESP32 | 1 | Arm Cortex-M4 or Xtensa dual-core with >256KB RAM | USB / 3.3V |
| 0.96 inch I2C OLED Display | 1 | 128x64 SSD1306 Graphic Display | I2C (SDA, SCL) |
| Touchpad or Camera Module | 1 | OV7670 camera or capacitive input pad | Parallel / I2C |
5. Complete Arduino Edge AI Code
/*
* Project: Edge AI Handwritten Digit Recognizer
* Author: ElectronLab STEM Curriculum
* Framework: TensorFlow Lite for Microcontrollers
*/
#include <TensorFlowLite.h>
#include <tensorflow/lite/micro/all_ops_resolver.h>
#include <tensorflow/lite/micro/micro_error_reporter.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include "digit_model_data.h" // Quantized model byte array
namespace {
tflite::ErrorReporter* error_reporter = nullptr;
const tflite::Model* model = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
TfLiteTensor* input = nullptr;
TfLiteTensor* output = nullptr;
constexpr int kTensorArenaSize = 60 * 1024; // 60KB Arena
uint8_t tensor_arena[kTensorArenaSize];
}
void setup() {
Serial.begin(115200);
while (!Serial);
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = µ_error_reporter;
// Load the quantized TFLite flatbuffer model
model = tflite::GetModel(g_digit_model_data);
static tflite::AllOpsResolver resolver;
static tflite::MicroInterpreter static_interpreter(
model, resolver, tensor_arena, kTensorArenaSize, error_reporter);
interpreter = &static_interpreter;
// Allocate memory from tensor arena for model tensors
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
return;
}
input = interpreter->input(0);
output = interpreter->output(0);
Serial.println("Edge AI Digit Recognizer Initialized!");
}
void loop() {
// Feed normalized 28x28 image buffer into input tensor
// (In real testing, populate input->data.f from sensor or serial)
// Run inference
TfLiteStatus invoke_status = interpreter->Invoke();
if (invoke_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed!");
return;
}
// Find predicted class with highest confidence
int bestDigit = -1;
float maxConfidence = 0.0;
for (int i = 0; i < 10; i++) {
float confidence = output->data.f[i];
if (confidence > maxConfidence) {
maxConfidence = confidence;
bestDigit = i;
}
}
Serial.print("Recognized Digit: ");
Serial.print(bestDigit);
Serial.print(" (Confidence: ");
Serial.print(maxConfidence * 100.0, 1);
Serial.println("%)");
delay(3000);
}
6. Working Principle
The convolutional neural network applies 3x3 kernel matrix convolutions across the 28x28 pixel grid, extracting spatial features like vertical strokes, loops, and intersections. The flattened feature maps pass through dense fully connected layers, culminating in a 10-node softmax output representing probabilities for digits 0 through 9.
7. Troubleshooting Guide
Arena Allocation Failed
Your microcontroller ran out of RAM. Increase kTensorArenaSize if supported by your board, or prune model filter counts during Python training.