A distributed mesh-networked home automation system built on ESP32 microcontrollers using ESP-NOW for communication. This system consists of a master controller with OLED display and multiple slave devices that control relays and collect sensor data throughout your home.
The ESP32 Home Automation System is a scalable, wireless solution for controlling and monitoring your home environment. The system uses a master-slave architecture:
- Master Controller: Central hub with OLED display and button interface for monitoring and controlling all devices
- Slave Devices: Distributed nodes that control relays and collect environmental data
The system uses ESP-NOW for direct device-to-device communication, creating a mesh network where messages can be relayed between devices, allowing for extended range and reliability.
- Mesh Networking: ESP-NOW based peer-to-peer communication
- Auto-discovery: No manual pairing required
- Scalable: Support for up to 10 devices (configurable)
- Centralized Control: Manage all slave devices from one interface
- Visual Dashboard: OLED display shows device status and sensor readings
- Button Interface: Simple control for device selection and relay toggling
- Real-time Monitoring: See temperature, humidity, light levels, and presence detection
- Auto-detection: Automatically discovers and monitors all slaves on the network
- Heartbeat System: Detects and displays disconnected devices
- Relay Control: Control up to 4 relays per device
- Local Control: Physical buttons for manual relay control
- Sensor Integration:
- Temperature and humidity monitoring (DHT11/DHT22)
- Human presence detection (PIR sensor)
- Ambient light sensing (LDR)
- State Persistence: Relay states persist through power cycles
- Status Indication: Heartbeat LED with different patterns showing system status
- Message Relaying: Forward commands to other devices in the network
- ESP32 development board
- SSD1306 OLED display (128x64 resolution)
- 5 push buttons (1 for device selection, 4 for relay control)
- Jumper wires and resistors
- ESP32 development board
- Relay module (up to 4 relays)
- 4 momentary push buttons
- DHT11 or DHT22 temperature and humidity sensor
- PIR motion sensor
- Light-dependent resistor (LDR)
- LED for heartbeat indication
- Resistors and wiring components
The system consists of several core components:
- main.ino: Main application logic for the master controller
- config.h: Configuration parameters for the master
- sensors.h: Data structures for sensor information exchange
- main.ino: Main application logic for slave devices
- config.h: Configuration parameters for slaves
- sensors.h: Sensor API declarations
- sensors.cpp: Sensor handling implementation
- Arduino IDE or PlatformIO
- ESP32 board support
- Required libraries:
- ESP32 WiFi and ESP-NOW
- Adafruit GFX
- Adafruit SSD1306
- DHT sensor library
- Wire library
- Clone this repository
- Install required libraries through the Arduino Library Manager
- Configure your devices:
- Assign a unique
DEVICE_ID
to each slave (1-254) - Set
DEVICE_ID
to0xFF
for the master controller
- Assign a unique
- Connect the hardware according to the pin configurations
- Flash the appropriate firmware to each device:
- Master firmware to the master controller
- Slave firmware to the slave devices
Edit config.h
for the master device:
#define DEVICE_ID 0xFF // Master ID
// OLED I2C Configuration
#define OLED_SDA 21
#define OLED_SCL 22
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Buttons
#define SELECT_BUTTON_PIN 32
#define RELAY_BUTTON_PINS { 33, 25, 26, 27 }
// ESP-NOW
#define CHANNEL 1
#define MAX_PEERS 10
#define HEARTBEAT_TIMEOUT 5000
Edit config.h
for each slave device:
// Unique ID for this device (must be different for each slave)
#define DEVICE_ID 0x03
// Pin Configuration
#define RELAY_PINS { 5, 18, 19, 21 }
#define BUTTON_PINS { 13, 14, 27, 26 }
#define HEARTBEAT_LED_PIN 2
// Sensor Pins
#define HUMAN_SENSOR_PIN 32
#define LDR_SENSOR_PIN 33
#define DHT_PIN 4
// Features toggle
#define ENABLE_HEARTBEAT true
#define ENABLE_DHT true
#define ENABLE_HUMAN_SENSOR true
#define ENABLE_LUX_SENSOR true
// ESP-NOW
#define CHANNEL 1
#define MAX_PEERS 10
The system uses two main data structures for communication:
struct Message {
uint8_t targetID; // ID of the target slave
uint8_t relayState[4]; // Desired states for the 4 relays
};
struct SlaveSensorData {
uint8_t id; // Slave identifier
bool human; // Human presence detection
float temp; // Temperature reading
float hum; // Humidity reading
int lux; // Light level reading
bool relayStates[4]; // Current relay states
unsigned long timestamp; // Last update timestamp (set by master)
};
-
Slave Device Status Updates:
- Slave devices read sensors and button states periodically
- They package this data into
SlaveSensorData
structures - The data is broadcast to the network using ESP-NOW
-
Master Device Monitoring:
- The master receives
SlaveSensorData
updates from all slaves - It maintains a registry of all slaves and their latest data
- The data is displayed on the OLED screen, rotating through devices
- The master receives
-
Relay Control Commands:
- User presses buttons on the master to toggle relays
- Master creates a
Message
with the target slave ID and desired relay states - The message is broadcast to the network using ESP-NOW
-
Command Relaying:
- When a slave receives a message, it checks the targetID
- If the message is for this slave, it updates its relay states
- If the message is for another slave, it relays the message forward
-
Flash and Configure Devices:
- Flash the master firmware to one ESP32 with OLED display
- Flash the slave firmware to other ESP32s with appropriate sensors
- Ensure each slave has a unique ID
-
Power On the Network:
- Power on the master controller
- Power on slave devices
- The master will automatically discover slaves as they come online
-
Navigating Between Slaves:
- Press the SELECT button to cycle through connected slave devices
- The display shows the current slave ID at the top
-
Reading Sensor Data:
- Temperature and humidity are shown numerically
- Light level is displayed as a bar graph
- Human presence is shown as a filled/empty circle
-
Controlling Relays:
- Select the desired slave using the SELECT button
- Press the corresponding relay button (1-4) to toggle a relay
- The display will update to show the new relay state
-
Manual Control:
- Press the physical buttons connected to the slave to toggle relays
- The heartbeat LED indicates the current state of the device
-
Reading Status:
- The heartbeat LED blinks at different rates to show the device status:
- Slow blink (2000ms): Disconnected
- Medium blink (1000ms): Idle
- Fast blink (300ms): Receiving data
- Very fast blink (100ms): Relaying data
- The heartbeat LED blinks at different rates to show the device status:
-
Master Controller:
- ESP32 with connected OLED display (SDA to pin 21, SCL to pin 22)
- 5 buttons: Select button on pin 32, Relay buttons on pins 33, 25, 26, 27
-
Slave Device Example:
- ESP32 with 4-channel relay module on pins 5, 18, 19, 21
- 4 buttons on pins 13, 14, 27, 26
- DHT11 sensor on pin 4
- PIR sensor on pin 32
- LDR sensor on pin 33
- Heartbeat LED on pin 2
When powered on, the system performs these steps:
- Master initializes the OLED display and ESP-NOW
- Each slave initializes its sensors, relays, and ESP-NOW
- Slaves begin broadcasting their status
- Master discovers and displays each slave as it comes online
Slave devices store relay states in non-volatile memory:
// Save current relay states
void saveRelayStates() {
for (int i = 0; i < RELAY_COUNT; i++) {
Preferences prefs;
prefs.begin("relays", false);
prefs.putBool(String(i).c_str(), relayStates[i]);
prefs.end();
}
}
// Load saved relay states
void loadRelayStates() {
for (int i = 0; i < RELAY_COUNT; i++) {
Preferences prefs;
prefs.begin("relays", true);
relayStates[i] = prefs.getBool(String(i).c_str(), false);
digitalWrite(relayPins[i], relayStates[i]);
prefs.end();
}
}
-
Define New Sensor Pin:
#define NEW_SENSOR_PIN 15
-
Update SlaveSensorData Structure:
struct SlaveSensorData { // Existing fields... float newSensorValue; // Add your new sensor data field };
-
Add Initialization and Reading Code:
void initSensors() { // Existing initialization... pinMode(NEW_SENSOR_PIN, INPUT); } void readSensors() { // Existing readings... newSensorValue = analogRead(NEW_SENSOR_PIN); }
-
Update Display Code on Master:
void updateDisplay() { // Existing display code... display.printf("New: %.1f\n", s.newSensorValue); }
You can implement automatic relay control based on sensor readings:
void automaticControl() {
// Example: Turn on relay 1 when it's dark and someone is present
if (getLux() < 300 && isHumanPresent()) {
setRelayState(0, true);
} else {
setRelayState(0, false);
}
// Example: Turn on relay 2 when temperature exceeds threshold
if (getTemperature() > 28.0) {
setRelayState(1, true);
} else {
setRelayState(1, false);
}
}
To add more than 10 peers:
- Modify the
MAX_PEERS
definition inconfig.h
- Adjust memory allocation as needed
- Consider using a more structured relay approach to avoid network congestion
Problem | Possible Cause | Solution |
---|---|---|
No devices appear | Slaves not powered | Check power to slave devices |
ESP-NOW initialization failed | Check initialization messages on serial | |
Incorrect channel | Ensure all devices use the same channel | |
Display not working | I2C connection issue | Check SDA/SCL connections |
Incorrect display address | Verify the display address is 0x3C | |
Buttons not responding | Incorrect pin configuration | Check button pin definitions |
Faulty pull-up resistors | Ensure buttons have proper pull-up resistors |
Problem | Possible Cause | Solution |
---|---|---|
LED not blinking | Heartbeat disabled | Check ENABLE_HEARTBEAT setting |
Incorrect LED pin | Verify the LED pin definition | |
Relays not responding | Power supply issue | Check relay power supply |
Incorrect pin configuration | Verify relay pin definitions | |
Failed relay | Test relay with direct voltage | |
Sensor readings incorrect | Wiring issue | Check sensor connections |
Sensor not initialized | Verify sensor initialization code | |
Feature disabled | Check feature toggle definitions |
Problem | Possible Cause | Solution |
---|---|---|
Intermittent communication | Distance too great | Add more devices to relay messages |
Interference | Change the ESP-NOW channel | |
Message not relayed | Relay path broken | Add more devices to ensure coverage |
Busy network | Reduce update frequency |
Create a bridge device that:
- Connects to WiFi and MQTT
- Forwards ESP-NOW messages to/from MQTT
- Enables integration with Home Assistant
Add an ESP32 with microphone module to:
- Process voice commands locally or via cloud
- Convert commands to ESP-NOW messages
- Control devices via voice
Implement a time-based controller:
- Add an RTC module to a slave or master
- Create a scheduling system
- Trigger events at specific times
Develop a bridge to smartphone control:
- Create a WiFi-enabled ESP32 gateway
- Develop a mobile app that communicates with the gateway
- Convert app commands to ESP-NOW messages
// Add or update a slave in the registry
void addOrUpdateSlave(const SlaveSensorData& newData);
// Send relay control message to a slave
void sendRelayUpdate(uint8_t index);
// Update the OLED display with current information
void updateDisplay();
// Draw visual elements on the display
void drawBarGraph(int x, int y, int w, int h, int v, int max);
void drawPresenceIcon(int x, int y, bool present);
// Update the heartbeat LED based on current state
void updateHeartbeatLED();
// Save relay states to non-volatile storage
void saveRelayStates();
// Load relay states from non-volatile storage
void loadRelayStates();
// Set a relay to a specific state
void setRelayState(uint8_t index, bool state);
// Read data from all enabled sensors
void readSensors();
// Get sensor readings
bool isHumanPresent();
float getTemperature();
float getHumidity();
int getLux();
// Master callback for receiving data
void OnDataRecv(const uint8_t* mac, const uint8_t* data, int len);
// Slave callback for receiving commands
void OnDataRecv(const uint8_t* mac, const uint8_t* incomingData, int len);
This project is released under the MIT License.
- ESP-NOW library developers
- Adafruit for their display and sensor libraries
- DHT sensor library contributors
- ESP32 community for their invaluable resources and examples
Contributions to this project are welcome. Please feel free to:
- Report bugs
- Suggest features
- Submit pull requests
- v1.0.0 - Initial release
- v1.1.0 - Added OLED display support for master controller
- v1.2.0 - Implemented message relaying for extended range
- v1.3.0 - Added persistent storage for relay states