Smart Camera Intruder Alert System
A camera-based vision project that detects the presence of a person (or unauthorised object) in a monitored area and triggers a local alert. The model runs entirely on the Arduino Q โ no cloud, no internet required.
Camera โ Image Classifier (Edge Impulse) โ Decision (Python) โ Alert (Buzzer / LED)
The Ideaโ
Mount a camera in a fixed position โ pointing at a door, window, or restricted area. The model continuously classifies the camera feed. When it detects a person (or any class you define as an intrusion), it signals the Arduino side via the Bridge to trigger a buzzer or LED alert.
This is a real-world use case: on-device vision for security without sending data to the cloud.
What You'll Buildโ
| Component | Role |
|---|---|
| USB Camera | Captures image frames |
| Edge Impulse Image Classifier | Detects person vs. no person |
Python (main.py) | Runs inference, makes the decision |
| Bridge | Sends alert signal to the MCU |
| Buzzer or LED | Physical alert output |
Hardware Requiredโ
- Arduino UNO Q
- USB Camera (compatible with Arduino App Lab)
- Buzzer or LED + resistor
- USB-C cable
How It Worksโ
- Camera captures frames โ Python reads frames from the USB camera continuously.
- Model runs inference โ the Edge Impulse image classifier processes each frame on-device and returns a confidence score for each class (e.g.
person,empty). - Decision logic in Python โ if the
personclass confidence exceeds a threshold (e.g. 0.8), Python calls the Arduino via the Bridge. - Arduino triggers the alert โ the MCU receives the signal and activates the buzzer or LED.
- Alert resets โ when no person is detected for a set number of frames, the alert is cleared.
Building the Model on Edge Impulseโ
1. Collect Dataโ
You need two classes of images:
personโ photos of a person in the camera's field of view (different angles, distances, lighting).
Collect at least 40โ50 images per class(if you have multiple class). Use your laptop camera or mobile phone QR method in Edge Impulse Studio.
2. Create the Impulseโ
- Input block: Image, 96ร96 px (grayscale or RGB)
- Processing block: Image (flatten or use transfer learning)
- Learning block: Transfer Learning (MobileNetV1 96ร96) โ recommended for image classification on edge
3. Train and Testโ
- Target 85%+ accuracy on the test set.
- Check the confusion matrix โ make sure
personis not being confused withempty. - Use Live classification to test with your camera before deploying.
4. Deployโ
Export as an Arduino library and import it into Arduino App Lab.
The Codeโ
main.py โ Python sideโ
from arduino.app_utils import *
from arduino.app_utils import App
from arduino.app_bricks.web_ui import WebUI
from arduino.app_bricks.video_imageclassification import VideoImageClassification
from arduino.app_peripherals.camera import Camera
from arduino.app_bricks.telegram_bot import TelegramBot
from datetime import datetime, UTC
from io import BytesIO
from PIL import Image
import urllib.request
import requests
import json
import os
ui = WebUI()
# Initialize camera
camera = Camera()
# Pass camera to stream.
# debounce_sec=5.0 replaces the need for sleep(5). It tells the AI to wait 5 seconds before triggering again.
detection_stream = VideoImageClassification(camera=camera, confidence=0.5, debounce_sec=5.0)
bot = TelegramBot()
CHAT_ID = 'CHAT_ID_HERE'
# Safely handle the threshold override so it doesn't crash on startup
def person_detected():
print("โ ๏ธ Person detected! Processing numpy array...")
try:
captured_frame = camera.capture()
if captured_frame is not None:
img = Image.fromarray(captured_frame)
# Note: If your photo colors look weird (e.g., red and blue are swapped),
# uncomment the following line to fix OpenCV's default BGR format:
# img = img.convert("RGB")
output = BytesIO()
img.save(output, format="JPEG")
image_bytes = output.getvalue()
# 1. Try sending the photo without the 'photo=' keyword
try:
bot.send_photo(CHAT_ID, image_bytes, caption="๐จ Intruder Detected!")
Bridge.call("control_led", 1)
print("Photo sent to Telegram successfully (using wrapper)!")
except Exception as e:
print(f"Wrapper failed ({e}). Attempting direct Telegram API call...")
# 2. Bulletproof Fallback: Send it directly to Telegram bypassing the wrapper
try:
token = os.environ.get("TELEGRAM_BOT_TOKEN")
url = f"https://api.telegram.org/bot{token}/sendPhoto"
files = {'photo': ('intruder.jpg', image_bytes, 'image/jpeg')}
data = {'chat_id': CHAT_ID, 'caption': "๐จ Intruder Detected!"}
response = requests.post(url, files=files, data=data)
if response.status_code == 200:
print("Photo sent successfully via direct API!")
else:
print(f"Direct API failed: {response.text}")
bot.send_message(CHAT_ID, "๐จ Intruder Detected! (Photo capture succeeded, but upload failed)")
except Exception as api_e:
print(f"Direct API exception: {api_e}")
bot.send_message(CHAT_ID, "๐จ Intruder Detected! (Network upload failed)")
else:
print("camera.capture() returned None.")
bot.send_message(CHAT_ID, "๐จ Intruder Detected! (Camera capture returned empty)")
except Exception as e:
print(f"Capture method error: {e}")
bot.send_message(CHAT_ID, f"๐จ Intruder Detected! (Camera error: {e})")
def person_not_detected():
Bridge.call("control_led", 0)
detection_stream.on_detect("person", person_detected)
detection_stream.on_detect("non person", person_not_detected)
def send_detections_to_ui(classifications: dict):
if len(classifications) == 0:
return
entries = []
for key, value in classifications.items():
entry = {
"content": key,
"confidence": value,
"timestamp": datetime.now(UTC).isoformat()
}
entries.append(entry)
if len(entries) > 0:
msg = json.dumps(entries)
ui.send_message("classifications", message=msg)
detection_stream.on_detect_all(send_detections_to_ui)
App.run()
sketch.ino โ Arduino sideโ
#include <Arduino_RouterBridge.h>
#define buzzer 10
// This function receives an integer (1 or 0) from Python
void control_led(int state) {
// Clear if/else logic
if (state == 1) {
digitalWrite(LED_BUILTIN, LOW); // Turn LED ON (Active Low)
digitalWrite(buzzer, HIGH);
digital
Monitor.println("LED is ON");
}
else {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED OFF
Monitor.println("LED is OFF");
digitalWrite(buzzer, LOW);
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT)
pinMode(10,OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); // Turn LED OFF (Active Low)
digitalWrite(buzzer, LOW);
Bridge.begin();
Bridge.provide_safe("control_led", control_led);
}
void loop() {
// Bridge runs automatically in the background
}
Event-based Alert The Arduino does nothing on its own โ it only fires when Python sends a signal via the Bridge. This keeps the MCU free and responsive.
Ideas to Extendโ
- Add a timestamp log in Python each time an intrusion is detected.
- Send an email or push notification using a Python library when a person is detected.
- Add a third class (e.g.
animal) to distinguish between types of intrusions. - Stream the camera feed to a web UI served from the Arduino Q.
Your Turnโ
- Collect at least 40 images for each class (
personandempty). - Train a model in Edge Impulse and hit 85%+ test accuracy.
- Deploy the model as an Arduino library.
- Wire up a buzzer or LED to the Arduino Q.
- Run the full system and verify the alert triggers when you step in front of the camera.
- Document each step on Arduino Project Hub.