Project Overview
This project creates a wireless motion notification system that uses ESP-NOW protocol to send alerts between ESP32 boards without requiring a Wi-Fi network. It’s perfect for monitoring remote locations like workshops, sheds, or entry gates where Wi-Fi may not reach.
Difficulty: Intermediate
Estimated time: 2-3 hours
Estimated cost: $20-30 (two ESP32 boards + PIR sensors)
How It Works
Two ESP32 boards communicate via ESP-NOW, a low-power, connectionless protocol developed by Espressif. The transmitter unit has a PIR sensor. When motion is detected, it sends a packet to the receiver unit. The receiver unit can display an alert via LED, buzzer, or LCD, and can also send notifications to a phone via Wi-Fi if available.
ESP-NOW works without Wi-Fi, has very low latency, and can achieve ranges of 50-100 meters line-of-sight.
Materials Needed
- ESP32 development boards (2) – one for transmitter, one for receiver
- HC-SR501 PIR sensor (1)
- LED (for alert indication)
- Buzzer (optional)
- LCD 16×2 with I2C (optional, for display)
- Resistors (220Ω for LEDs)
- Jumper wires
- Power supplies (5V USB or batteries)
- Enclosures (for weatherproofing if outdoor)
Circuit Diagram
Transmitter Unit Connection Table
| Component | Pin | ESP32 Pin | PIR Sensor | VCC | 3.3V | PIR Sensor | GND | GND | PIR Sensor | OUT | GPIO 4 | Status LED | Anode | GPIO 2 (through 220Ω) | Status LED | Cathode | GND |
|---|
Receiver Unit Connection Table
| Component | Pin | ESP32 Pin | Alert LED | Anode | GPIO 2 (through 220Ω) | Alert LED | Cathode | GND | Buzzer | Positive | GPIO 5 | Buzzer | Negative | GND | LCD (I2C) | VCC | 3.3V | LCD (I2C) | GND | GND | LCD (I2C) | SDA | GPIO 21 | LCD (I2C) | SCL | GPIO 22 |
|---|
ESP-NOW Setup: Get MAC Addresses
First, upload this code to both ESP32 boards to get their MAC addresses:
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop() {}
Note the MAC address of the receiver board. You’ll need it for the transmitter code.
Transmitter Code
// ESP-NOW Motion Sensor Transmitter
#include <esp_now.h>
#include <WiFi.h>
const int pirPin = 4;
const int ledPin = 2;
// Replace with receiver's MAC address
uint8_t receiverAddress[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
// Message structure
typedef struct struct_message {
int motionDetected;
int sensorId;
unsigned long timestamp;
} struct_message;
struct_message motionData;
unsigned long lastSendTime = 0;
const unsigned long sendCooldown = 5000; // 5 seconds between sends
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("Send status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
digitalWrite(ledPin, status == ESP_NOW_SEND_SUCCESS ? HIGH : LOW);
delay(100);
digitalWrite(ledPin, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(pirPin, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_send_cb(OnDataSent);
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, receiverAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
Serial.println("Transmitter Ready");
Serial.print("Receiver MAC: ");
for (int i = 0; i < 6; i++) {
Serial.printf("%02X", receiverAddress[i]);
if (i < 5) Serial.print(":");
}
Serial.println();
delay(60000); // PIR warm-up
}
void loop() {
bool motion = digitalRead(pirPin) == HIGH;
if (motion && (millis() - lastSendTime > sendCooldown)) {
motionData.motionDetected = 1;
motionData.sensorId = 1;
motionData.timestamp = millis();
esp_now_send(receiverAddress, (uint8_t *) &motionData, sizeof(motionData));
lastSendTime = millis();
Serial.println("Motion detected - sending alert");
}
delay(100);
}
Receiver Code
// ESP-NOW Motion Sensor Receiver
#include <esp_now.h>
#include <WiFi.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int alertLedPin = 2;
const int buzzerPin = 5;
unsigned long lastAlertTime = 0;
const unsigned long alertDuration = 5000; // 5 seconds alert indication
bool alertActive = false;
int lastMotionCount = 0;
// Message structure (must match transmitter)
typedef struct struct_message {
int motionDetected;
int sensorId;
unsigned long timestamp;
} struct_message;
struct_message motionData;
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&motionData, incomingData, sizeof(motionData));
Serial.print("Motion alert from sensor ");
Serial.print(motionData.sensorId);
Serial.print(" at ");
Serial.println(motionData.timestamp);
lastMotionCount++;
lastAlertTime = millis();
alertActive = true;
// Activate indicators
digitalWrite(alertLedPin, HIGH);
tone(buzzerPin, 2000, 500);
// Update LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Motion detected!");
lcd.setCursor(0, 1);
lcd.print("Sensor ");
lcd.print(motionData.sensorId);
lcd.print(" Count: ");
lcd.print(lastMotionCount);
}
void setup() {
Serial.begin(115200);
pinMode(alertLedPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
digitalWrite(alertLedPin, LOW);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("ESP-NOW Monitor");
lcd.setCursor(0, 1);
lcd.print("Ready");
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_recv_cb(OnDataRecv);
Serial.println("Receiver Ready");
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop() {
if (alertActive && (millis() - lastAlertTime > alertDuration)) {
digitalWrite(alertLedPin, LOW);
alertActive = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("System Ready");
lcd.setCursor(0, 1);
lcd.print("Total alerts: ");
lcd.print(lastMotionCount);
}
delay(100);
}
Multiple Sensors Network
To add more sensors, modify the transmitter code with a unique sensor ID for each unit:
// For sensor #2 (different ID)
motionData.sensorId = 2;
// For sensor #3
motionData.sensorId = 3;
The receiver will display which sensor triggered.
Range Extension Tips
- Use external antennas on ESP32 boards (some boards have IPEX connectors).
- Place units with line-of-sight when possible.
- Use 2.4GHz Wi-Fi channel 1 for better performance.
- Add external power (not USB) to ensure consistent transmission power.
Power-Saving Version (Deep Sleep)
For battery-powered remote sensors, add deep sleep:
// Add to transmitter code
#include <esp_sleep.h>
void setup() {
// ... existing setup ...
esp_sleep_enable_ext0_wakeup((gpio_num_t)pirPin, 1);
}
void loop() {
// After sending, go to deep sleep
esp_deep_sleep_start();
}
With deep sleep, battery life can extend to months or even years.
Installation Steps
- Get MAC addresses: Upload MAC scanner to both boards, record receiver MAC.
- Program transmitter: Update receiver MAC address and upload code.
- Program receiver: Upload code and test.
- Test communication: Place boards within range, trigger motion, verify receiver alerts.
- Mount sensors: Place transmitter unit at desired monitoring location (mailbox, gate, shed).
- Place receiver: Keep receiver unit where you can see/hear alerts.
- Power up: Use USB power or batteries.
Project Extensions
- Wi-Fi gateway: Add Wi-Fi to receiver to forward alerts to phone via Telegram/Blynk.
- Data logging: Add SD card module to receiver to log all events.
- Temperature sensor: Add DS18B20 to transmitter to send temperature data.
- Battery monitoring: Add voltage divider to transmitter to send battery level.
- Meshing: Add multiple receivers or repeaters for larger coverage area.
Troubleshooting
- No communication: Check MAC address. Ensure boards are within range. Try swapping transmitter/receiver roles.
- Intermittent connection: Reduce distance. Use external antennas.
- False alerts: Adjust PIR sensitivity. Ensure sensor not facing heat sources.
- Receiver not displaying: Check LCD wiring and I2C address.
Conclusion
This ESP-NOW wireless notification system provides a low-power, long-range solution for monitoring remote locations without Wi-Fi. It’s ideal for property perimeter monitoring, mailbox alerts, and workshop security.
