An ESP32 WiFi mesh network lets you extend IoT sensor coverage across your entire home, office, or campus without worrying about WiFi range limitations. Using the ESP-MESH protocol built into the ESP-IDF framework, multiple ESP32 nodes self-organise into a mesh topology where each node acts as both a sensor and a relay, passing data through the network until it reaches a root node connected to the internet. This guide shows you how to build a complete mesh network from scratch.
Table of Contents
- Why Mesh Networks for IoT?
- Understanding ESP-MESH Protocol
- Hardware Requirements
- Building with painlessMesh Library
- Practical: Temperature Sensor Mesh
- Root Node as Internet Gateway
- Optimising Mesh Performance
- Frequently Asked Questions
Why Mesh Networks for IoT?
Traditional WiFi networks use a star topology — every device connects directly to the router. This creates two problems for IoT deployments:
- Range limitation: A typical WiFi router covers 15-30 metres indoors. Sensors in far rooms, the terrace, or the garden may not reach the router.
- Single point of failure: If the router fails, all devices go offline.
A mesh network solves both problems. Each ESP32 node communicates with its nearest neighbours, and data hops through the network node-by-node until it reaches the root node (which has internet access). Benefits include:
- Self-healing: If one node fails, the network automatically reroutes around it
- Extended range: Each node extends the network by another 15-30 metres
- No router dependency: Nodes communicate peer-to-peer without a central access point
- Scalability: Add nodes as needed — the network adapts automatically
Understanding ESP-MESH Protocol
ESP-MESH is built on top of the WiFi protocol and creates a tree-like network topology:
- Root Node: The topmost node. Connected to the external WiFi router for internet access. Only one root node exists per mesh network.
- Parent Nodes: Intermediate nodes that relay data between leaf nodes and the root.
- Leaf Nodes: End nodes that only send and receive data, do not relay for other nodes.
Key characteristics of ESP-MESH:
- Supports up to 1000 nodes (ESP-IDF) or ~50 nodes (painlessMesh)
- Up to 6 layers deep (root → 5 hops)
- Automatic parent selection based on signal strength
- Each node can connect to the mesh AND run as a WiFi access point simultaneously
- Messages are JSON-formatted and can be broadcast or sent to specific nodes
Hardware Requirements
For a basic 4-node mesh network you need:
- 4x ESP32 development boards (any variant with WiFi)
- 4x USB cables for power
- Sensors for each node (DHT22, LDR, PIR, etc.)
- USB power banks or phone chargers for deployment
Total cost: approximately ₹1,200-2,000 for a 4-node network (₹300-500 per ESP32 board).
Building with painlessMesh Library
The painlessMesh library simplifies ESP-MESH programming enormously. Install it from the Arduino Library Manager (search for “painlessMesh” by GitLab).
Basic Mesh Node
#include <painlessMesh.h>
#define MESH_PREFIX "ZboticMesh"
#define MESH_PASSWORD "mesh_password_123"
#define MESH_PORT 5555
painlessMesh mesh;
// Called when a message is received
void receivedCallback(uint32_t from, String &msg) {
Serial.printf("Received from %u: %sn", from, msg.c_str());
}
// Called when mesh topology changes
void changedConnectionCallback() {
Serial.printf("Mesh changed. Nodes: %dn",
mesh.getNodeList().size() + 1);
}
// Called when a new node connects
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("New node connected: %un", nodeId);
}
void setup() {
Serial.begin(115200);
mesh.setDebugMsgTypes(ERROR | STARTUP);
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
mesh.onReceive(&receivedCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNewConnection(&newConnectionCallback);
Serial.print("Node ID: ");
Serial.println(mesh.getNodeId());
}
void loop() {
mesh.update();
// Send a message every 10 seconds
static unsigned long lastSend = 0;
if (millis() - lastSend > 10000) {
String msg = "Hello from node " + String(mesh.getNodeId());
mesh.sendBroadcast(msg);
Serial.println("Broadcast: " + msg);
lastSend = millis();
}
}
Flash this same code to all ESP32 boards. They will automatically discover each other and form a mesh. The MESH_PREFIX and MESH_PASSWORD must be identical on all nodes.
Practical: Temperature Sensor Mesh
Here is a complete example with each node reading a DHT22 sensor and broadcasting temperature/humidity data in JSON format:
#include <painlessMesh.h>
#include <DHT.h>
#include <ArduinoJson.h>
#define MESH_PREFIX "ZboticMesh"
#define MESH_PASSWORD "mesh_password_123"
#define MESH_PORT 5555
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define ROOM_NAME "Living Room" // Change per node
painlessMesh mesh;
DHT dht(DHT_PIN, DHT_TYPE);
void receivedCallback(uint32_t from, String &msg) {
// Parse incoming JSON
JsonDocument doc;
deserializeJson(doc, msg);
Serial.printf("[%s] Temp: %.1fC, Humidity: %.1f%%n",
doc["room"].as(),
doc["temp"].as(),
doc["humidity"].as());
}
void changedConnectionCallback() {
Serial.printf("Network: %d nodes connectedn",
mesh.getNodeList().size() + 1);
}
void setup() {
Serial.begin(115200);
dht.begin();
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
mesh.onReceive(&receivedCallback);
mesh.onChangedConnections(&changedConnectionCallback);
}
void loop() {
mesh.update();
static unsigned long lastRead = 0;
if (millis() - lastRead > 30000) { // Every 30 seconds
float temp = dht.readTemperature();
float humidity = dht.readHumidity();
if (!isnan(temp) && !isnan(humidity)) {
JsonDocument doc;
doc["nodeId"] = mesh.getNodeId();
doc["room"] = ROOM_NAME;
doc["temp"] = temp;
doc["humidity"] = humidity;
doc["uptime"] = millis() / 1000;
String msg;
serializeJson(doc, msg);
mesh.sendBroadcast(msg);
Serial.printf("Sent: %s: %.1fC, %.1f%%n",
ROOM_NAME, temp, humidity);
}
lastRead = millis();
}
}
Change the ROOM_NAME variable for each node before flashing: “Living Room”, “Bedroom”, “Kitchen”, “Terrace”, etc. Each node will broadcast its readings and display readings from all other rooms.
Root Node as Internet Gateway
To send mesh data to the internet (for a web dashboard or MQTT broker), designate one node as the root and connect it to your WiFi router:
#include <painlessMesh.h>
#include <WiFiClient.h>
#define MESH_PREFIX "ZboticMesh"
#define MESH_PASSWORD "mesh_password_123"
#define MESH_PORT 5555
#define WIFI_SSID "YourHomeWiFi"
#define WIFI_PASS "YourWiFiPassword"
painlessMesh mesh;
void receivedCallback(uint32_t from, String &msg) {
Serial.printf("Gateway received from %u: %sn", from, msg.c_str());
// Forward to HTTP server
WiFiClient client;
if (client.connect("api.thingspeak.com", 80)) {
// Parse and forward sensor data
// ... HTTP POST implementation
client.stop();
}
}
void setup() {
Serial.begin(115200);
mesh.setDebugMsgTypes(ERROR | STARTUP);
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
mesh.onReceive(&receivedCallback);
// Connect mesh to WiFi router
mesh.stationManual(WIFI_SSID, WIFI_PASS);
mesh.setHostname("MeshGateway");
mesh.setRoot(true); // This node is the root
mesh.setContainsRoot(true); // Tell mesh a root exists
}
void loop() {
mesh.update();
}
Optimising Mesh Performance
- Reduce broadcast frequency: Sending data every 30-60 seconds is ideal. More frequent broadcasts cause congestion in larger networks.
- Use targeted messages: Use
mesh.sendSingle(nodeId, msg)instead of broadcast when you know the destination. This reduces network traffic. - Limit network depth: Keep the mesh to 3-4 layers. Each hop adds latency. Place nodes so that every sensor is within 2-3 hops of the root.
- Power management: For battery-powered nodes, use ESP32 light sleep between sensor readings. The painlessMesh library supports task scheduling via the built-in task scheduler.
- Channel selection: The mesh uses WiFi channel 1 by default. In Indian apartments with many WiFi networks, change to channel 6 or 11 to avoid interference.
Frequently Asked Questions
How many ESP32 nodes can be in one mesh?
The painlessMesh library practically supports up to 50 nodes. The ESP-IDF native mesh implementation supports up to 1000 nodes. For most home and small office applications, 10-20 nodes provide excellent coverage.
What is the range between mesh nodes?
Each ESP32 node can communicate up to 30 metres indoors through walls, and 100+ metres in open areas. The mesh extends range by relaying — a 5-node chain can cover 150 metres of building.
Can I mix ESP32 and ESP8266 in the same mesh?
Yes, painlessMesh supports both ESP32 and ESP8266. However, ESP8266 has limited memory and can only handle smaller networks. Use ESP32 for parent nodes and ESP8266 for simple leaf nodes.
Does the mesh work without internet?
Yes. The mesh network is completely independent of the internet. Nodes can communicate with each other without any WiFi router. The internet connection is only needed if you want to send data to cloud services.
How does the mesh handle power failures?
When a node loses power and comes back, it automatically rejoins the mesh. If the root node fails, another node can be promoted to root. The network is self-healing by design.
Conclusion
Building an ESP32 WiFi mesh network is the most cost-effective way to deploy IoT sensors across a large area. With nodes costing under ₹500 each and no central infrastructure required, you can cover your entire home, hostel floor, office building, or warehouse with temperature, humidity, motion, and air quality sensors. The painlessMesh library makes setup incredibly simple — flash the same code to all boards and they auto-organise into a network.
Start building your mesh network today. Explore our range of ESP32 development boards at Zbotic.in — we stock 30-pin, 38-pin, and specialty ESP32 boards with fast delivery across India.
Add comment