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 is 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
How It Works
Two ESP32 boards communicate via ESP-NOW, a low-power, connectionless protocol. The transmitter unit has a PIR sensor. When motion is detected, it sends a packet to the receiver unit. The receiver unit displays an alert via LED, buzzer, or LCD, and can also send notifications to a phone via Wi-Fi if available.
Materials Needed
- ESP32 development boards (2)
- HC-SR501 PIR sensor (1)
- LED for alert indication
- Buzzer (optional)
- LCD 16×2 with I2C (optional)
- 220Ω resistors for LEDs
- Jumper wires
- Power supplies (5V USB or batteries)
- Enclosures for weatherproofing
Get MAC Addresses
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 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};
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;
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
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);
esp_now_init();
esp_now_register_send_cb(OnDataSent);
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, receiverAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
delay(60000);
}
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();
}
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;
bool alertActive = false;
int lastMotionCount = 0;
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));
lastMotionCount++;
lastAlertTime = millis();
alertActive = true;
digitalWrite(alertLedPin, HIGH);
tone(buzzerPin, 2000, 500);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Motion detected!");
lcd.setCursor(0, 1);
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.print("ESP-NOW Monitor");
lcd.setCursor(0, 1);
lcd.print("Ready");
WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_register_recv_cb(OnDataRecv);
}
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: ");
lcd.print(lastMotionCount);
}
delay(100);
}
Power-Saving Deep Sleep Version
#include <esp_sleep.h>
void setup() {
esp_sleep_enable_ext0_wakeup((gpio_num_t)pirPin, 1);
}
void loop() {
// Send notification then deep sleep
esp_deep_sleep_start();
}
Installation Steps
- Get MAC addresses of both boards
- Update receiver MAC address in transmitter code
- Upload transmitter and receiver code to respective boards
- Test communication within range
- Mount transmitter unit at desired monitoring location
- Place receiver unit where you can see/hear alerts
Project Extensions
- Add Wi-Fi gateway to forward alerts to phone
- Add SD card module to log events
- Add DS18B20 to send temperature data
- Add voltage divider to send battery level
- Add multiple receivers for larger coverage
Conclusion
This ESP-NOW wireless notification system provides a low-power, long-range solution for monitoring remote locations without Wi-Fi.
