Portfolio project for an embedded and intelligent systems application: an ESP32-style firmware simulator that runs anomaly detection on industrial sensor data fully offline, with optional MQTT logging and a HAL that can be swapped for real hardware later.
- Edge ML deployment with an embedded-friendly Isolation Forest
- ESP32-style
setup()+loop()application structure - Clean hardware abstraction via
SimulatorHALandESP32HAL - Offline inference with optional MQTT event publishing
- Exported model artifacts for both Python simulation and future C porting
CLI (Click) -> main.py (firmware loop) -> HAL (simulator | esp32)
| |
v v
ml/detector.py hal/simulator.py
|
v
ml/preprocessing.py
|
v
comms/mqtt_logger.py
- The simulator generates ESP32-like sensor readings every 100 ms.
- A sliding window of 20 samples is converted into 12 runtime features.
- The detector scales features and runs Isolation Forest inference locally.
- The app toggles LED and buzzer states on anomalies.
- If MQTT is configured, JSON events are published without changing app logic.
- Algorithm: Isolation Forest
- Dataset:
data/ai4i2020.csv - Window size: 20 samples
- Runtime features: 12
- Exports:
joblibmodel, scaler JSON, tree JSON for future embedded translation
The original requested feature list adds up to 13 values. This implementation keeps the trained model at 12 features to stay aligned with the embedded-size goal:
- rolling mean, max, and delta for process temperature
- rolling mean, max, and delta for vibration
- rolling mean, max, and delta for rotational speed
- torque mean
- torque MAD
- tool wear
The simulator still computes vibration_torque_ratio and exposes it in telemetry, but it is not included in the trained feature vector.
edge-ai-anomaly-detector/
├── assets/demo.gif
├── data/ai4i2020.csv
├── firmware/README.md
├── models/
├── notebooks/training.ipynb
├── src/anomaly_detector/
│ ├── cli.py
│ ├── main.py
│ ├── display.py
│ ├── hal/
│ ├── ml/
│ └── comms/
├── tests/
├── pyproject.toml
└── README.md
cd /Users/bernard/dev/edge-ai-anomaly-detector
python3 -m pip install -e .
anomaly-detector train --data data/ai4i2020.csv
anomaly-detector run -n 200If editable install is blocked by an older local pip setup, run with:
PYTHONPATH=src python3 -m anomaly_detector.cli train --data data/ai4i2020.csv
PYTHONPATH=src python3 -m anomaly_detector.cli run -n 200Run the deployable web dashboard locally:
PYTHONPATH=src python3 -m anomaly_detector.cli serve --host 0.0.0.0 --port 8000Then open:
http://localhost:8000
The live demo runs the simulator continuously in the background and exposes:
/for the dashboard/api/statusfor the latest live sample and detection state/api/historyfor recent inference history/demo.giffor the terminal-style demo preview
Train the model:
anomaly-detector train --data data/ai4i2020.csvRun the simulator:
anomaly-detector run -n 200Run with MQTT:
anomaly-detector run --mqtt-broker localhostUseful runtime options:
anomaly-detector run -n 200 --threshold -0.05 --anomaly-rate 0.05Training produces:
models/model.joblibmodels/scaler_params.jsonmodels/model_trees.json
pytestVerified locally:
- training completed successfully
- model artifacts were created
pytestpassed- simulator run produced anomaly detections and colored terminal output
This repo now includes a deployable FastAPI dashboard, so a public always-live demo can be hosted directly:
- easiest always-on host: Fly.io
- simplest managed alternative: Railway
- good for portfolio visuals but not free-always-on: Hugging Face Spaces
Use Fly.io with the included web UI. It is the cleanest fit if you want a URL that stays up and continuously runs the simulator loop.
Why:
- Fly.io supports keeping app processes available across regions and documents controls for app availability: https://fly.io/docs/apps/app-availability/
- you can run a lightweight Python web process continuously instead of exposing a raw CLI
- it maps well to a small real-time dashboard for portfolio reviewers
- Fly.io Best choice if you want an always-live URL. Use one small machine and keep it running.
- Railway Viable if you deploy a small FastAPI dashboard. Railway documents a Serverless mode that scales to zero after inactivity; inference: for an always-live demo you should avoid that mode and use a normal service instead. Docs: https://docs.railway.com/reference/public-networking#serverless
- Hugging Face Spaces Good if you turn the demo into Gradio or Streamlit, but free hardware sleeps after inactivity. Hugging Face documents sleep behavior and paid options for avoiding it: https://huggingface.co/docs/hub/spaces-gpus#sleep-time
Deploy the included app with:
fly launch --copy-config --no-deploy
fly deployThe included fly.toml keeps one machine running so the demo stays available.
The runtime depends on the abstract interface in src/anomaly_detector/hal/base.py. To move to real hardware later:
- implement the methods in
src/anomaly_detector/hal/esp32.py - instantiate
ESP32HAL(...)instead ofSimulatorHAL(...) - keep the rest of the application unchanged
