ESP32: ESP-MESH, a self-healing Wi-Fi network
Build a multi-hop Wi-Fi network with multiple ESP32 boards. Self-healing, automatic routing, no central router needed.
ESP-MESH is the protocol that lets multiple ESP32 boards form a self-healing Wi-Fi network. Each board is a node; the network automatically routes messages between them. If a node goes offline, the network reroutes around it.
This is the protocol for sensor networks across a building, garden, or farm. You scatter ESP32 nodes; they find each other; they form a mesh. No router needed for the data path (though a router can be involved for the root node’s external connectivity).
ESP-MESH is Espressif-specific. Arduino Uno, Raspberry Pi, and Pico do not support it.
What you need
- 3 or more ESP32 boards (more is better; the mesh needs nodes to be useful)
- A USB cable per board
The mesh architecture
ESP-MESH has two types of nodes:
- Root node: connects to the router. One per mesh.
- Child nodes: connect to the root or to other children. Form the mesh topology.
The root acts as the gateway to the outside world. Children forward packets through the mesh to reach the root, which forwards them to the internet (or wherever they need to go).
Install the library
The ESP-MESH library is part of the ESP32 Arduino core. No additional
install needed. The headers are in painlessMesh.h if you want a
higher-level API:
Sketch >> Include Library >> Manage Libraries >> search for
painlessMesh by cochrane. Install it.
The code: root node
#include <painlessMesh.h>
#define MESH_PREFIX "ctrlaltbrian"
#define MESH_PASSWORD "meshpassword123"
#define MESH_PORT 5555
painlessMesh mesh;
void setup() {
Serial.begin(115200);
delay(1000);
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
mesh.onReceive(&onReceive);
mesh.onNewConnection(&onNewConnection);
mesh.onChangedConnections(&onChangedConnections);
}
void loop() {
mesh.update();
}
void onReceive(uint32_t from, String &msg) {
Serial.printf("Received from %u: %s\n", from, msg.c_str());
}
void onNewConnection(uint32_t nodeId) {
Serial.printf("New connection: %u\n", nodeId);
}
void onChangedConnections() {
Serial.printf("Connections changed: %d\n", mesh.getNodeList().size());
}
The code: child node
#include <painlessMesh.h>
#define MESH_PREFIX "ctrlaltbrian"
#define MESH_PASSWORD "meshpassword123"
#define MESH_PORT 5555
painlessMesh mesh;
void setup() {
Serial.begin(115200);
delay(1000);
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
mesh.onReceive(&onReceive);
}
unsigned long lastSend = 0;
void loop() {
mesh.update();
// Send a sensor reading every 5 seconds
if (millis() - lastSend > 5000) {
lastSend = millis();
String msg = "{\"temp\":22.5,\"node\":\"";
msg += ESP.getEfuseMac();
msg += "\"}";
mesh.sendBroadcast(msg);
}
}
void onReceive(uint32_t from, String &msg) {
Serial.printf("Received from %u: %s\n", from, msg.c_str());
}
Upload the root node to one ESP32, the child node to others. Open Serial Monitor on the root. After a few seconds, you should see “New connection” messages as the children find the root.
The “any node can be root” pattern
In a real deployment, you cannot predict which node will have access to power and the internet. The standard pattern is to designate any node as root and have the others fall back automatically.
bool isRoot = (digitalRead(4) == LOW); // hold a button at boot to be root
void setup() {
// ...
if (isRoot) {
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA);
// ... connect to router, etc.
} else {
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_STA);
}
}
Hold the button at boot to make a node the root. Release the button to make it a child.
The message types
Three message patterns:
- Broadcast:
mesh.sendBroadcast(msg): every node receives. - Single node:
mesh.sendSingle(nodeId, msg): only one node. - Specific nodeId: the destination node’s ID.
Node IDs are assigned automatically when nodes join the mesh. Get the list:
auto nodes = mesh.getNodeList();
for (auto nodeId : nodes) {
Serial.printf("Node: %u\n", nodeId);
}
Throughput
ESP-MESH is not a high-throughput protocol. It is designed for low-rate sensor data (a few KB per minute). For high-throughput applications (video, audio), use regular Wi-Fi.
Typical throughput: a few hundred bytes per second per node. Enough for sensor readings, MQTT messages, control commands.
Range and node count
Each ESP32 board has the same Wi-Fi range (200m+ line of sight). In a mesh, the effective range is the sum of the hops. A 5-node mesh across a 1 km area is realistic with line-of-sight placement.
Maximum nodes: depends on the network topology and traffic. The painlessMesh library handles 30+ nodes; the official ESP-MESH handles 1000+ (in theory).
Power consumption
Mesh nodes need to keep Wi-Fi active to participate in the mesh. Power consumption is high (50-100 mA active). For battery-powered mesh nodes, use deep sleep between messages:
void loop() {
mesh.update();
if (millis() - lastSend > 60000) {
lastSend = millis();
String msg = "{\"temp\":22.5}";
mesh.sendBroadcast(msg);
// Sleep for 1 minute
esp_sleep_enable_timer_wakeup(60 * 1000000);
esp_deep_sleep_start();
}
}
The mesh can tolerate sleeping nodes; messages are buffered for a few seconds before being dropped.
Common projects
- Whole-home sensor mesh. One node per room. All publish to a central MQTT broker through the root.
- Garden monitoring. Nodes scattered across a garden, all reporting back to a root near the house.
- Industrial monitoring. Nodes on machines across a factory floor, forming a mesh that survives machine outages.
- Disaster-tolerant networks. Mesh networks continue to function when individual nodes fail.
What you learned
- ESP-MESH forms a self-healing multi-hop Wi-Fi network.
- One root node connects to the router; children forward packets.
- painlessMesh library is the easiest way to use ESP-MESH.
- Throughput is low (low-rate sensor data is the target).
When something breaks
- Children cannot find the root. Too far apart, or wrong MESH_PREFIX / MESH_PASSWORD.
- Messages dropped. Mesh congested; reduce broadcast frequency.
- Network keeps rebuilding. Some node is dropping out; check power supply.
- Throughput much lower than expected. Mesh is many hops; reduce hop count by adding more root nodes.
What to build next
- The ESP-NOW tutorial covers the lower-level peer-to-peer protocol that ESP-MESH uses internally.
- The ESP32 MQTT tutorial combines with mesh for sending data to external services.
- The book ESP32 Mesh Networks covers multi-hop routing with custom topology.