Meshtastic PIR Motion Detection Security Network
Heltec WiFi LoRa 32 + PIR Sensor Project
Project Overview
Create a distributed motion detection system using Meshtastic mesh network. When any PIR sensor detects motion, it immediately notifies all other nodes in the mesh, creating a comprehensive security monitoring system.
Hardware Required
Per Node:
- Heltec WiFi LoRa 32 V3 (or V2)
- PIR Motion Sensor (HC-SR501 or similar)
- LED (Red for motion alert)
- Buzzer (optional for audio alerts)
- 220Ω resistor (for LED)
- Jumper wires
- Breadboard
- LiPo battery (for portable deployment)
- USB-C cable for programming
Hardware Setup
PIR Sensor (HC-SR501) Connections
- VCC → 3.3V
- GND → GND
- OUT → GPIO 21 (Digital input with pulldown)
Alert LED
- Long leg (Anode) → GPIO 2 through 220Ω resistor
- Short leg (Cathode) → GND
Buzzer (Optional)
- Positive → GPIO 26
- Negative → GND
Built-in Components (No wiring needed)
- LoRa Radio for mesh communication
- OLED Display for status
- WiFi/Bluetooth for phone app
Pin Layout Diagram
Heltec WiFi LoRa 32
┌─────────────────────┐
│ USB-C │
│ │
│ GPIO 21 ←──PIR OUT │
│ GPIO 2 ←──LED+ │
│ GPIO 26 ←──BUZZER+ │
│ 3.3V ──────→PIR VCC │
│ GND ───────→PIR GND │
│ →LED GND │
│ →BUZ GND │
│ │
│ [OLED Display] │
└─────────────────────┘
Software Implementation
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoJson.h>
#include <time.h>
// Pin definitions
#define PIR_PIN 21
#define LED_PIN 2
#define BUZZER_PIN 26
#define OLED_SDA 4
#define OLED_SCL 15
#define OLED_RST 16
// Display object
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
// Motion detection variables
bool motionDetected = false;
bool lastPirState = false;
unsigned long lastMotionTime = 0;
unsigned long lastHeartbeat = 0;
String nodeId;
int totalDetections = 0;
// Network status
struct NetworkStatus {
bool meshConnected = false;
int connectedNodes = 0;
unsigned long lastMessageTime = 0;
};
NetworkStatus networkStatus;
void setup() {
Serial.begin(115200);
Serial.println("PIR Security Node Starting...");
// Initialize pins
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Get unique node ID
nodeId = "NODE_" + WiFi.macAddress().substring(9);
nodeId.replace(":", "");
// Initialize I2C for OLED
Wire.begin(OLED_SDA, OLED_SCL);
// Initialize OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c)) {
Serial.println("OLED initialization failed!");
}
// Show startup screen
showStartupScreen();
// Allow PIR to stabilize
Serial.println("PIR warming up (30 seconds)...");
delay(30000);
Serial.println("Security system ARMED");
updateDisplay();
}
void loop() {
// Check PIR sensor
bool currentPirState = digitalRead(PIR_PIN);
// Detect motion (rising edge)
if (currentPirState && !lastPirState) {
motionDetected = true;
lastMotionTime = millis();
totalDetections++;
Serial.println("MOTION DETECTED!");
// Send immediate alert
sendMotionAlert();
// Activate local alerts
activateAlerts();
}
lastPirState = currentPirState;
// Handle ongoing motion
if (motionDetected && (millis() - lastMotionTime > 5000)) {
motionDetected = false;
deactivateAlerts();
}
// Send periodic heartbeat
if (millis() - lastHeartbeat > 60000) { // Every minute
sendHeartbeat();
lastHeartbeat = millis();
}
// Check for incoming messages
checkIncomingMessages();
// Update display
updateDisplay();
delay(100);
}
void sendMotionAlert() {
// Create JSON alert message
StaticJsonDocument<300> alert;
alert["type"] = "MOTION_ALERT";
alert["node"] = nodeId;
alert["timestamp"] = millis();
alert["location"] = "Zone_1"; // Can be customized per node
alert["detection_count"] = totalDetections;
String alertString;
serializeJson(alert, alertString);
// Send via Meshtastic
Serial.println("MESH_SEND:" + alertString);
Serial.println("Motion alert sent to mesh network");
}
void sendHeartbeat() {
StaticJsonDocument<200> heartbeat;
heartbeat["type"] = "HEARTBEAT";
heartbeat["node"] = nodeId;
heartbeat["timestamp"] = millis();
heartbeat["status"] = "ACTIVE";
heartbeat["uptime"] = millis() / 1000;
String heartbeatString;
serializeJson(heartbeat, heartbeatString);
Serial.println("MESH_SEND:" + heartbeatString);
}
void activateAlerts() {
// Flash LED rapidly
for (int i = 0; i < 10; i++) {
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 1000, 100);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
// Keep LED solid during motion
digitalWrite(LED_PIN, HIGH);
}
void deactivateAlerts() {
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
}
void checkIncomingMessages() {
// Check for incoming Meshtastic messages
if (Serial.available()) {
String message = Serial.readString();
message.trim();
if (message.startsWith("MESH_RECV:")) {
String jsonData = message.substring(10);
processIncomingMessage(jsonData);
}
}
}
void processIncomingMessage(String jsonData) {
StaticJsonDocument<300> doc;
DeserializationError error = deserializeJson(doc, jsonData);
if (error) {
Serial.println("JSON parsing failed");
return;
}
String messageType = doc["type"];
String senderNode = doc["node"];
if (messageType == "MOTION_ALERT" && senderNode != nodeId) {
// Remote motion detected
Serial.println("REMOTE MOTION: " + senderNode);
// Brief alert for remote detection
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 500, 200);
delay(200);
digitalWrite(LED_PIN, LOW);
networkStatus.lastMessageTime = millis();
}
else if (messageType == "HEARTBEAT" && senderNode != nodeId) {
// Update network status
networkStatus.meshConnected = true;
networkStatus.lastMessageTime = millis();
}
}
void showStartupScreen() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("SECURITY SYSTEM");
display.println("===============");
display.println();
display.print("Node: ");
display.println(nodeId);
display.println();
display.println("Initializing...");
display.println("PIR warming up");
display.display();
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
// Header
display.println("SECURITY NODE");
display.println("=============");
// Node info
display.print("ID: ");
display.println(nodeId.substring(5)); // Show short ID
// Status
display.print("Status: ");
if (motionDetected) {
display.println("MOTION!");
} else {
display.println("ARMED");
}
// Detection counter
display.print("Alerts: ");
display.println(totalDetections);
// Network status
display.print("Mesh: ");
if (networkStatus.meshConnected &&
(millis() - networkStatus.lastMessageTime < 120000)) {
display.println("ONLINE");
} else {
display.println("OFFLINE");
}
// Uptime
display.print("Up: ");
display.print(millis() / 60000);
display.println("min");
display.display();
}
Project Extensions
1. Multi-Zone Security
// Add zone identification
struct SecurityZone {
String zoneId;
String description;
bool isArmed;
int sensitivity;
};
SecurityZone currentZone = {"ZONE_A", "Front Door", true, 1};
2. Advanced Alert System
- Different alert tones for different zones
- Escalating alerts (soft beep → loud alarm)
- Silent mode for covert monitoring
3. Time-Based Arming
// Auto-arm/disarm based on schedule
bool isArmedTime() {
// Get current time and check schedule
// Return true if should be armed
return true;
}
4. Camera Integration
- Add ESP32-CAM for motion-triggered photos
- Send images over mesh when motion detected
5. Mobile App Integration
- Use Meshtastic phone app for notifications
- Remote arming/disarming commands
Deployment Scenarios
1. Classroom Security
- Monitor multiple rooms
- Alert when someone enters after hours
- Track movement patterns
2. Home Security System
- Portable battery-powered nodes
- Place at doors, windows, hallways
- Mesh provides redundant coverage
3. Wildlife Monitoring
- Outdoor weatherproof deployment
- Solar charging for remote locations
- Log animal activity patterns
Learning Objectives
Technical Skills
- Digital I/O: Reading PIR sensor states
- Interrupt Handling: Efficient motion detection
- Mesh Communication: Real-time alert propagation
- JSON Formatting: Structured message protocols
- State Machines: Managing armed/disarmed states
Security Concepts
- Distributed Monitoring: Multiple detection points
- Redundancy: Mesh network resilience
- Alert Escalation: Appropriate response levels
- False Positive Reduction: Smart detection algorithms
Testing and Experiments
1. Detection Range Testing
- Measure PIR effective range at different angles
- Test sensitivity adjustments
- Document detection patterns
2. Network Performance
- Measure alert transmission time across mesh
- Test with increasing number of nodes
- Analyze message routing efficiency
3. Power Consumption Analysis
- Monitor battery life under different alert frequencies
- Optimize sleep modes between detections
- Calculate deployment duration
4. False Positive Study
- Document environmental triggers (wind, temperature)
- Implement filtering algorithms
- Compare indoor vs outdoor performance
Advanced Features
1. Machine Learning Detection
- Classify motion types (human vs animal vs environment)
- Learn normal activity patterns
- Reduce false alarms
2. Mesh Network Visualization
- Display network topology on OLED
- Show message routing paths
- Monitor node health status
3. Data Analytics
- Log detection timestamps
- Generate activity reports
- Identify security patterns
Assessment Rubric
- Hardware Assembly (25%): Correct wiring and connections
- Code Implementation (25%): Working PIR detection and alerts
- Mesh Integration (25%): Successful multi-node communication
- Documentation (25%): Clear explanation of system operation
This project teaches practical security system concepts while demonstrating the power of mesh networks for distributed monitoring applications.
https://www.silabs.com/software-and-tools/usb-to-uart-bridge-vcp-drivershttps://www.silabs.com/software-and-tools/usb-to-uart-bridge-vcp-drivers