Learn how to use ESP-MESH networking protocol to build a mesh network with the ESP32 and ESP8266 NodeMCU boards. ESP-MESH allows multiple devices (nodes) to communicate with each other under a single wireless local area network. It is supported on the ESP32 and ESP8266 boards. In this guide, we’ll show you how to get started with ESP-MESH using the Arduino core.

This article covers the following topics:
- Introducing ESP-MESH
- ESP-MESH Basic Example (Broadcast messages)
- Exchange Sensor Readings using ESP-MESH (broadcast)
Arduino IDE
If you want to program the ESP32 and ESP8266 boards using Arduino IDE, you should have the ESP32 or ESP8266 add-ons installed. Follow the next guides:
- Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux)
- Installing ESP8266 Board in Arduino IDE (Windows, Mac OS X, Linux)
If you want to program the ESP32/ESP8266 using VS Code + PlatformIO, follow the next tutorial:
Introducing ESP-MESH
Accordingly to the Espressif documentation:
“ESP-MESH is a networking protocol built atop the Wi-Fi protocol. ESP-MESH allows numerous devices (referred to as nodes) spread over a large physical area (both indoors and outdoors) to be interconnected under a single WLAN (Wireless Local-Area Network).
ESP-MESH is self-organizing and self-healing meaning the network can be built and maintained autonomously.” For more information, visit the ESP-MESH official documentation.
Traditional Wi-Fi Network Architecture
In a traditional Wi-Fi network architecture, a single node (access point – usually the router) is connected to all other nodes (stations). Each node can communicate with each other using the access point. However, this is limited to the access point wi-fi coverage. Every station must be in the range to connect directly to the access point. This doesn’t happen with ESP-MESH.

ESP-MESH Network Architecture
With ESP-MESH, the nodes don’t need to connect to a central node. Nodes are responsible for relaying each others transmissions. This allows multiple devices to spread over a large physical area. The Nodes can self-organize and dynamically talk to each other to ensure that the packet reaches its final node destination. If any node is removed from the network, it is able to self-organize to make sure that the packets reach their destination.

painlessMesh Library
The painlessMesh library allows us to create a mesh network with the ESP8266 or/and ESP32 boards in an easy way.
“painlessMesh is a true ad-hoc network, meaning that no-planning, central controller, or router is required. Any system of 1 or more nodes will self-organize into fully functional mesh. The maximum size of the mesh is limited (we think) by the amount of memory in the heap that can be allocated to the sub-connections buffer and so should be really quite high.” More information about the painlessMesh library.
Installing painlessMesh Library
You can install painlessMesh through the Arduino Library manager. Go to Tools > Manage Libraries. The Library Manager should open.
Search for “painlessmesh” and install the library. We’re using Version 1.4.5

This library needs some other library dependencies. A new window should pop up asking you to install any missing dependencies. Select “Install all”.

If this window doesn’t show up, you’ll need to install the following library dependencies:
- ArduinoJson (by bblanchon)
- TaskScheduler
- ESPAsyncTCP (ESP8266)
- AsyncTCP (ESP32)
If you’re using PlatformIO, add the following lines to the platformio.ini file to add the libraries and change the monitor speed.
For the ESP32:
monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
ArduinoJson
arduinoUnity
TaskScheduler
AsyncTCP
For the ESP8266:
monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
ArduinoJson
TaskScheduler
ESPAsyncTCP
ESP-MESH Basic Example (Broadcast messages)
To get started with ESP-MESH, we’ll first experiment with the library’s basic example. This example creates a mesh network in which all boards broadcast messages to all the other boards.
We’ve experimented this example with four boards (two ESP32 and two ESP8266). You can add or remove boards. The code is compatible with both the ESP32 and ESP8266 boards.

Code – painlessMesh Library Basic Example
Copy the following code to your Arduino IDE (code from the library examples). The code is compatible with both the ESP32 and ESP8266 boards.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/
This is a simple example that uses the painlessMesh library: https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino
*/
#include "painlessMesh.h"
#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555
Scheduler userScheduler; // to control your personal task
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
void sendMessage() {
String msg = "Hi from node1";
msg += mesh.getNodeId();
mesh.sendBroadcast( msg );
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
Serial.begin(115200);
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
userScheduler.addTask( taskSendMessage );
taskSendMessage.enable();
}
void loop() {
// it will run the user scheduler as well
mesh.update();
}
Before uploading the code, you can set up the MESH_PREFIX (it’s like the name of the MESH network) and the MESH_PASSWORD variables (you can set it to whatever you like).
Then, we recommend that you change the following line for each board to easily identify the node that sent the message. For example, for node 1, change the message as follows:
String msg = "Hi from node 1 ";
How the Code Works
Start by including the painlessMesh library.
#include "painlessMesh.h"
MESH Details
Then, add the mesh details. The MESH_PREFIX refers to the name of the mesh. You can change it to whatever you like.
#define MESH_PREFIX "whateverYouLike"
The MESH_PASSWORD, as the name suggests is the mesh password. You can change it to whatever you like.
#define MESH_PASSWORD "somethingSneaky"
All nodes in the mesh should use the same MESH_PREFIX and MESH_PASSWORD.
The MESH_PORT refers to the the TCP port that you want the mesh server to run on. The default is 5555.
#define MESH_PORT 5555
Scheduler
It is recommended to avoid using delay() in the mesh network code. To maintain the mesh, some tasks need to be performed in the background. Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
Instead, it is recommended to use TaskScheduler to run your tasks which is used in painlessMesh itself.
The following line creates a new Scheduler called userScheduler.
Scheduler userScheduler; // to control your personal task
painlessMesh
Create a painlessMesh object called mesh to handle the mesh network.
Create tasks
Create a task called taskSendMessage responsible for calling the sendMessage() function every second as long as the program is running.
Task taskSendMessage(TASK_SECOND * 1 , TASK_FOREVER, &sendMessage);
Send a Message to the Mesh
The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
String msg = "Hi from node 1";
msg += mesh.getNodeId();
mesh.sendBroadcast( msg );
taskSendMessage.setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));
}
The message contains the “Hi from node 1” text followed by the board chip ID.
String msg = "Hi from node 1";
msg += mesh.getNodeId();
To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
mesh.sendBroadcast(msg);
Every time a new message is sent, the code changes the interval between messages (one to five seconds).
taskSendMessage.setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));
Mesh Callback Functions
Next, several callback functions are created that will be called when specific events happen on the mesh.
The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}
The newConnectionCallback() function runs whenever a new node joins the network. This function simply prints the chip ID of the new node. You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized. It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
setup()
In the setup(), initialize the serial monitor.
void setup() {
Serial.begin(115200);
Choose the desired debug message types:
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
Initialize the mesh with the details defined earlier.
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
Finally, add the taskSendMessage function to the userScheduler. The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask(taskSendMessage);
Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
To keep the mesh running, add mesh.update() to the loop().
void loop() {
// it will run the user scheduler as well
mesh.update();
}
Demonstration
Upload the code provided to all your boards. Don’t forget to modify the message to easily identify the sender node
With the boards connected to your computer, open a serial connection with each board. You can use the Serial Monitor, or you can use a software like PuTTY and open multiple windows for all the boards.
You should see that all boards receive each others messages. For example, these are the messages received by Node 1. It receives the messages from Node 2, 3 and 4.

You should also see other messages when there are changes on the mesh: when a board leaves or joins the network.

Exchange Sensor Readings using ESP-MESH
In this next example, we’ll exchange sensor readings between 4 boards (you can use a different number of boards). Every board receives the other boards’ readings.

As an example, we’ll exchange sensor readings from a BME280 sensor, but you can use any other sensor.
Parts Required
Here’s the parts required for this example:
- 4x ESP boards (ESP32 or ESP8266)
- 4x BME280
- Breadboard
- Jumper wires
Arduino_JSON library
In this example, we’ll exchange the sensor readings in JSON format. To make it easier to handle JSON variables, we’ll use the Arduino_JSON library.
You can install this library in the Arduino IDE Library Manager. Just go to Sketch > Include Library > Manage Libraries and search for the library name as follows:

If you’re using VS Code with PlatformIO, include the libraries in the platformio.ini file as follows:
ESP32
monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
ArduinoJson
arduinoUnity
AsyncTCP
TaskScheduler
adafruit/Adafruit Unified Sensor @ ^1.1.4
adafruit/Adafruit BME280 Library @ ^2.1.2
arduino-libraries/Arduino_JSON @ ^0.1.0
ESP8266
monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
ArduinoJson
TaskScheduler
ESPAsyncTCP
adafruit/Adafruit Unified Sensor @ ^1.1.4
adafruit/Adafruit BME280 Library @ ^2.1.2
arduino-libraries/Arduino_JSON @ ^0.1.0
Circuit Diagram
Wire the BME280 sensor to the ESP32 or ESP8266 default I2C pins as shown in the following schematic diagrams.
ESP32

Recommended reading: ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)
ESP8266 NodeMCU

Recommended reading: ESP8266 with BME280 using Arduino IDE (Pressure, Temperature, Humidity)
Code – ESP-MESH Broadcast Sensor Readings
Upload the following code to each of your boards. This code reads and broadcasts the current temperature, humidity and pressure readings to all boards on the mesh network. The readings are sent as a JSON string that also contains the node number to identify the sender board.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>
// MESH Details
#define MESH_PREFIX "RNTMESH" //name for your MESH
#define MESH_PASSWORD "MESHpassword" //password for your MESH
#define MESH_PORT 5555 //default port
//BME object on the default I2C pins
Adafruit_BME280 bme;
//Number for this node
int nodeNumber = 2;
//String to send to other nodes with sensor readings
String readings;
Scheduler userScheduler; // to control your personal task
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String getReadings(); // Prototype for sending sensor readings
//Create tasks: to send messages and get readings;
Task taskSendMessage(TASK_SECOND * 5 , TASK_FOREVER, &sendMessage);
String getReadings () {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temp"] = bme.readTemperature();
jsonReadings["hum"] = bme.readHumidity();
jsonReadings["pres"] = bme.readPressure()/100.0F;
readings = JSON.stringify(jsonReadings);
return readings;
}
void sendMessage () {
String msg = getReadings();
mesh.sendBroadcast(msg);
}
//Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
JSONVar myObject = JSON.parse(msg.c_str());
int node = myObject["node"];
double temp = myObject["temp"];
double hum = myObject["hum"];
double pres = myObject["pres"];
Serial.print("Node: ");
Serial.println(node);
Serial.print("Temperature: ");
Serial.print(temp);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(hum);
Serial.println(" %");
Serial.print("Pressure: ");
Serial.print(pres);
Serial.println(" hpa");
}
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("New Connection, nodeId = %u\n", nodeId);
}
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
Serial.begin(115200);
initBME();
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
userScheduler.addTask(taskSendMessage);
taskSendMessage.enable();
}
void loop() {
// it will run the user scheduler as well
mesh.update();
}
The code is compatible with both the ESP32 and ESP8266 boards.
How the Code Works
Continue reading this section to learn how the code works.
Libraries
Start by including the required libraries: the Adafruit_Sensor and Adafruit_BME280 to interface with the BME280 sensor; the painlessMesh library to handle the mesh network and the Arduino_JSON to create and handle JSON strings easily.
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>
Mesh details
Insert the mesh details in the following lines.
#define MESH_PREFIX "RNTMESH" //name for your MESH
#define MESH_PASSWORD "MESHpassword" //password for your MESH
#define MESH_PORT 5555 //default port
The MESH_PREFIX refers to the name of the mesh. You can change it to whatever you like. The MESH_PASSWORD, as the name suggests is the mesh password. You can change it to whatever you like. All nodes in the mesh should use the same MESH_PREFIX and MESH_PASSWORD.
The MESH_PORT refers to the the TCP port that you want the mesh server to run on. The default is 5555.
BME280
Create an Adafruit_BME280 object called bme on the default ESP32 or ESP8266 pins.
Adafruit_BME280 bme;
In the nodeNumber variable insert the node number for your board. It must be a different number for each board.
int nodeNumber = 2;
The readings variable will be used to save the readings to be sent to the other boards.
String readings;
Scheduler
The following line creates a new Scheduler called userScheduler.
Scheduler userScheduler; // to control your personal task
painlessMesh
Create a painlessMesh object called mesh to handle the mesh network.
Create tasks
Create a task called taskSendMessage responsible for calling the sendMessage() function every five seconds as long as the program is running.
Task taskSendMessage(TASK_SECOND * 5 , TASK_FOREVER, &sendMessage);
getReadings()
The getReadings() function gets temperature, humidity and pressure readings from the BME280 sensor and concatenates all the information, including the node number on a JSON variable called jsonReadings.
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temp"] = bme.readTemperature();
jsonReadings["hum"] = bme.readHumidity();
jsonReadings["pres"] = bme.readPressure()/100.0F;
The following line shows the structure of the jsonReadings variable with arbitrary values.
{
"node":2,
"temperature":24.51,
"humidity":52.01,
"pressure":1005.21
}
The jsonReadings variable is then converted into a JSON string using the stringify() method and saved on the readings variable.
readings = JSON.stringify(jsonReadings);
This variable is then returned by the function.
return readings;
Send a Message to the Mesh
The sendMessage() function sends the JSON string with the readings and node number (getReadings()) to all nodes in the network (broadcast).
void sendMessage () {
String msg = getReadings();
mesh.sendBroadcast(msg);
}
Init BME280 sensor
The initBME() function initializes the BME280 sensor.
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
Mesh Callback Functions
Next, several callback functions are created that will be called when some event on the mesh happens.
The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback( uint32_t from, String &msg ) {
Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
The message comes in JSON format, so, we can access the variables as follows:
JSONVar myObject = JSON.parse(msg.c_str());
int node = myObject["node"];
double temp = myObject["temp"];
double hum = myObject["hum"];
double pres = myObject["pres"];
Finally, print all the information on the Serial Monitor.
Serial.print("Node: ");
Serial.println(node);
Serial.print("Temperature: ");
Serial.print(temp);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(hum);
Serial.println(" %");
Serial.print("Pressure: ");
Serial.print(pres);
Serial.println(" hpa");
The newConnectionCallback() function runs whenever a new node joins the network. This function simply prints the chip ID of the new node. You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized. It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
setup()
In the setup(), initialize the serial monitor.
void setup() {
Serial.begin(115200);
Call the initBME() function to initialize the BME280 sensor.
initBME();
Choose the desired debug message types:
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
Initialize the mesh with the details defined earlier.
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
Finally, add the taskSendMessage function to the userScheduler. The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask(taskSendMessage);
Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
To keep the mesh running, add mesh.update() to the loop().
void loop() {
// it will run the user scheduler as well
mesh.update();
}
Demonstration
After uploading the code to all your boards (each board with a different node number), you should see that each board is receiving the other boards’ messages.
The following screenshot shows the messages received by node 1. It receives the sensor readings from node 2, 3 and 4.

Wrapping Up
We hope you liked this quick introduction to the ESP-MESH networking protocol. You can take a look at the painlessMesh library for more examples.
We intend to create more tutorials about this subject on a near future. So, write your suggestions on the comments’ section.
You may also like the following articles:
- Getting Started with ESP-NOW (ESP32 with Arduino IDE)
- Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)
- ESP32: ESP-NOW Web Server Sensor Dashboard (ESP-NOW + Wi-Fi)
Learn more about the ESP32 and ESP8266 with our resources:
- Learn ESP32 with Arduino IDE
- Home Automation using ESP8266
- More ESP32 Projects and Tutorials
- More ESP8266 Projects and Tutorials
Thanks for reading.
Hi Sara, incredible stuff you can do with these “IoT” devices!
Just wondering: if you would like to forward the temperature readings (eg with that PHP2MySQL you wrote earlier), would you advice to include that code in each of the nodes in your Mesh (because any of them may or may not be in reach of the actual “working” network with the LAMP server on it – caveat: lots of failed attempt plus if all mesh members succeed, database needs to be instrumented to avoid duplicate uploads), or would you dedicate 1 ESP for “uplink” to the real network?
Thanks for sharing your thoughts!
Hi.
I think it would be better to have one ESP to upload to the database. But it will depend on your project requirements.
I still need to do some more research on this topic to see how to handle different network topologies and requirements.
Regards,
Sara
Hi Sara,
thank you for your suggestion!
I’m wondering if you can “hook up” other devices to this mesh (eg a raspberry pi) – found articles like this: https://github.com/Coopdis/easyMesh/issues/30 so then this pi (which is currently my php/mysql gateway) can simply listen to the mesh 🙂
But indeed, these are other project requirements than initially drawn, but with this mesh, I can try to link to my water well sensor!
Thank you and have a nice day!
Hi Sara,
I did some further reading on this requirement. Apparently this mesh protocol may not be compatible with the wifi modules and TCPIP stacks, so it’s either mesh or wifi, not both at the same time. So I’m not sure on how one “master” ESP32 could “listen” to the mesh and upload the JSON messages towards the wifi (through router).
I found an android app which had a working painlessmesh, however it seems to be broken with the 2020 releases. Then I could at least copy paste the values from the mesh network into my webportal from my phone 🙂
you can send an ID or a timestamp for every different reading of a node.
and set the database to avoid duplicated entries.
Very interesting simple mesh. I would also like to know how to get the data out of the mesh and into MQTT running on another computer, such as a R-pi. I would guess that one of the nodes would be dedicated to that function, but I cannot visualize how to prevent duplicate messages….does the painless mesh library handle that?
Thanks,
Dave K.
Hi Dave.
The painlessMesh library provides an MQTT example.
Here it is: https://github.com/gmag11/painlessMesh/blob/master/examples/mqttBridge/mqttBridge.ino
However, I haven’t experiment it yet.
Regards,
Sara
So I am thinking about a project to attempt, I have a rather long driveway and would like to build a detector.
I know I could easily find purchase one but what fun would that be? So my obstacles are :
Driveway approx 700’
WiFi distance from router around 750’
Using WeMos in conjunction with ESP 8266
I have 2 WeMos and 1 ESP Lolin 8266
Would this work for me given the obstacles and boards I have ?
Great project, could be really useful for me. Would you consider expanding this project to include displaying the readings on a web page running on one of the boards accessible on my home wifi?
Hi.
Yes, we are thinking about writing a tutorial about that.
Regards,
Sara
Can you explain the differences between this painless MESH and ESP-NOW? Are the data passed using ESP-NOW to each node rather than a master-slave scenario? Thanks and please keep up the good work.
Great Tutorial, any way future examples can be done in micropython?
Hi.
Thanks for the suggestion.
Unfortunately, at the moment, ESP-MESH is not implemented in micropython yet.
Regards,
Sara
I have tried painless mesh number of times in the past and recently.
I don’t know if painless mesh uses any official ESP-MESH protocol feature, but it definitely has many custom features?
Since you have quoted official ESP-MESH, I think you should confirm how painless mesh is exactly related to official espredsif ESP-MESH!
I am fairly experienced with ESP devices and have used them successfully for long time, using both WiFi and ESP-NOW protocols. I would also love to use a reliable mesh-networking.
Painless mesh sounds impressive, but I and some others have found it unreliable after running from few hours to few days, even with very few (less than 6) nodes, tried with both ESP8266 and ESP32 and combination. All nodes are within RF range of each other (within 10-20m).
It generally starts OK but after some random time nodes start dropping off from the network and take very long time (minutes) or never to reconnect. This is unreliable and unacceptable.
Please read ‘issues’ on GitLab.
https://gitlab.com/groups/painlessMesh/-/issues
I suggest that you set up a reasonable size (more than 6 nodes) painless mesh network for number of days or longer and then report how reliable it is.
I enjoy your tutorials, have purchased previous courses and use lot of your tutorials as reference.
Best Regards
Hi Jack.
Thanks for sharing your knowledge about this subject.
We’ve only recently started experimenting with ESP-MESH.
To get started, we experimented with 4 nodes and everything seemed to work fine. But, we’ve only did simple experiments.
We’ll experiment different scenarios and see how it goes.
Did you experiment with any other library that supports mesh networking?
Thank you so much for sharing your thoughts about this.
Regards,
Sara
Awesome post!
The range of the MESH network is really good. I tested here with 3 modules in an area where the router coverage doesn’t work very well and, surprisingly, the ESP-MESH bypassed the fisical problems which blocks my router signal (the router signal usually is weak because there is an elevator in the middle).
Thank you very much for this great post!
Hi Eduardo
Did you try anything else with ESP32?
Yes Manuel,
My tests used one ESP32 and two ESP8266. The ESP32 and one ESP8266 were reading sensors (DHT11 on the ESP32 and a BMP280 on the ESP8266) and the other ESP8266 was connected to an OLED display, printing the sensor readings sent by the other two devices. Everything working like a charm!
Regards!
Hi there.
Great post!
Maybe you can post an article about TaskScheduler?
Seems a great tool.
Hi Nuno.
Yes. That is a great topic that many of you might be interested in.
It is already in my to-do list.
Regards,
Sara
The documentation on TaskScheduler is very good and with lots of examples. The basics are pretty simple. I use it in all my projects, have you taken a look at the documentation? I’m not sure an article is needed, although it could use more exposure, it is fantastic.
Good tutorial and also good comments 🙂
Thank you, everyone.
A simple question. Is it possible to automatically number each node? that is, when you insert a new node it recognizes it as “one more” and numbers it.
Hi Manuel.
The mesh recognizes the nodes by the board chip ID.
You might add something to your code that saves the nodes’ chip IDs and numbers the nodes accordingly.
Regards,
Sara
I have a couple of ESP32 scattered around, all linked via my local router and my home network. Each has a fixed IP. I have them talking directly to each other, no problem.
I cannot see how this protocol would work if these ESPs cannot directly contact each other – your comments?
Hi.
Instead of broadcasting, you can send messages to specific boards.
We haven’t explored that topic yet.
Regards,
Sara
Hello Sara and Rui.
How do I only get the message text in the serial monitor?
Without further information.
I’m doing something wrong but don’t understand what I’m doing wrong to get this done.
I only learn very slowly, that’s always my problem.
Please give me a hint
Greetings from the old man in the Netherlands.
Hi Bert,
the easiest way is to try one of the simple “Hello World” samples included in the Arduino IDE. If you can’t see these “Hello World” print-outs on your serial, you are not up to a project like this one 🙂
Greetings from Belgium!
I think I’m misinterpreting it, sorry about that.
How do I change this rule.
Serial.printf (“startHere: Received from% u msg =% s \ n”, from, msg.c_str ());
I only get the text “Hi from node1” in the serial monitor.
And not any other info.
Thanks for your understanding.
Sorry google translate is killing me and me text .
I meant
So that I only get the text “Hi from node1” in the serial monitor.
And not any other info.
Thanks for your understanding.
I could also post in Dutch, but then Sara cannot read it 🙂
Did you upload your code to another ESP device and configure it as node 2? If you only get messages from Node1, you only have 1 node in your mesh.
it works now, after sam studie 🙂
Hello
Thanks again, Sara and Rui.
Do you know if there is any limitations about the number of nodes? can we assume 500 nodes or even 1000?
Thanks
Hi.
I don’t know.
At the moment, we only experimented with 4 nodes.
Regards,
Sara
In the depths of the internet it is said that the number depends only on the RAM and you will probably reach the end at 50…60.
There it is recommended to take off several meshes and to merge them into one mesh … But that’s where I got out of my mind 😉
of course I didn’t put that in the bookmarks …
…sorry 😉
one more question.
If nodes didn’t see each other what happened?
If they don’t “see” each other, they won’t receive its messages.
Regards,
Sara
Oops, typo/copy-paste error in:
void receivedCallback( uint32_t from, String &msg ) {
…
double pres = myObject[“pres”];
…
Serial.print(“Pressure: “);
Serial.print(temp);
Serial.println(” hpa”);
The temp should be pres. The temp shows up twice in the printout sample above.
Hi.
You’re right.
Thanks for pointing that out.
It’s fixed now.
Regards,
Sara
You guys are awesome! Keep doing what you’re doing! Helping us inch into the IoT world!
Thanks 😀
I salute you, I especially wish you health and thank you for the additional study material, but also another problem in my :). But you continue to move on successfully. A lot of good.
But I have another problem. If anyone can advise me. I’ve studied a lot of pages on the Internet. RPI3B and SSD disc mSATA 500GB on the board from GEEKWORM – loading Raspbian. Not from a flash disc (not an SD card). I split the SSD disk, modified the config and another file, but the system does not load. In Raspbian, it can be copied to disk, so it works. I’m sore and thank you.
Hi.
I’m sorry but I haven’t experimented with that and I don’t have any tutorials about that subject.
You’ll get better guidance and help if you post your question on Raspberry Pi related forums.
Regards,
Sara
Unfortunately I have no real success with this example.
I use ESP-01 modules myself, because every chip has its own number anyway, I use this one too – every node has the exact same code.
For sending I decided to use the temperature of a DS18B20 – the integration took longer than expected – but it worked out fine at some point …
– search next sensor
– we are at the end of the list or is this sensor place empty -> to the first sensor
– if this sensor place is occupied -> the conversion time is over -> read sensor, enter values
– start new conversion, remember the time
In the sendMessage(), if a valid sensor was found, it was included in the message – otherwise only the ‘Hello from xyz’.
I also added an output to the terminal, that 5 seconds after the last output a dot is sent (and after 80 the line is wrapped) – so I can at least see that the node hanging on the PC is still alive.
This works for a few minutes, depending on the order in which I supply the nodes – the PC node should be the first one, older nodes I don’t get displayed.
What else I changed in the code:
Access data SSID/PW in separate tab
translated with deepl.com
Translated with http://www.DeepL.com/Translator (free version)
Thank you for the reply.
You have serial use two esp’s to connect to a router. One should be connected to the router and another one should be connected to esp which is connected to router through serial communication protocol
Great work
Can this mesh protocol coexist with wifi for at least one esp32?
thanks
Thanks for a great tutorial.
Hello!
Can each of the boards act as router so that in case the fourth is out of range of the first but within the range of the others, the first can still communicate with the fourth?
You can do that,
However that is not implemented in this tutorial yet. This tutorial only shows broadcast mode.
Regards,
Sara
Thank you for that.
Hello, I got the following error. I don’t understand it. Please help.
‘JSONVar’ was not declared in this scope
…\sketch_dec11a.ino: In function ‘String getReadings()’:
sketch_dec11a:42:3: error: ‘JSONVar’ was not declared in this scope
JSONVar jsonReadings;
^
sketch_dec11a:43:3: error: ‘jsonReadings’ was not declared in this scope
jsonReadings[“node”] = nodeNumber;
^
sketch_dec11a:47:14: error: ‘JSON’ was not declared in this scope
readings = JSON.stringify(jsonReadings);
^
…\sketch_dec11a.ino: In function ‘void receivedCallback(uint32_t, String&)’:
sketch_dec11a:67:3: error: ‘JSONVar’ was not declared in this scope
JSONVar myObject = JSON.parse(msg.c_str());
^
sketch_dec11a:68:14: error: ‘myObject’ was not declared in this scope
int node = myObject[“node”];
^
Multiple libraries were found for “WiFi.h”
…\packages\esp32\hardware\esp32\1.0.2\libraries\WiFi
Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
‘JSONVar’ was not declared in this scope
Hi Larry, check your code and see if this line is missing: “#include <Arduino_JSON.h>”.
If it’s missing, just add it to the beginning of your code and the problem will be solved.
Thank you. Solved.
The one advantage I see with painlessMESH over ESP-NOW is you can mix ESP32 & ESP8266 in the network but I’m wondering about low power and sleep mode and how the MESH might work with that. I’ve been slowing learning and building the code required for a simple 5 node system to monitor 2 water tanks, a septic system and a weather sensors (BME280) using ESP-NOW. The payload is not big but one node will need to be battery(solar charged) powered so obviously low power and sleep mode for that node is important. Have you done any tests with this in mind with the painlessMESH library?
Hi.
Thanks for you comment.
At the moment, we haven’t done any tests regarding power comsumption.
Regards,
Sara
If you don’t need instant readings, you could synchronize time across nodes, then sleep for a set time like 1 hour. The weather sensor could wake more often, save readings and report every hour. It can still take some time for the mesh to form, so might take a minute.
You could divide your nodes into leaf nodes that sleep and always-on intermediate nodes that route packets further than the root router. Then nodes could wake any time, e.g. on a flood sensor.
For lowest power use ESP-NOW on an 8266 (see the RNT tutorials on this) with a programmed wake interval. You’ll have to write your own mesh packet forwarding for nodes too far to reach your base node close to the WiFi router. Mesh is simpler if you preprogram which nodes can pair and which must forward. (The ESP-MESH figures this out on signal strength.) Nodes can sleep until some time, for leaf nodes send a packet and wait for reply, repeat on timeout for some retries. For forwarding nodes, wake and listen for some interval. If no message from a leaf, forward a no-response message. The root node can send a time-sync packet that gets forwarded so nodes can wake within the same second.
Using an ESP-8266 without the USB-serial gives longest battery life. Most of the ESP dev boards connect the serial-USB to the 3.3V CPU power, so drain the battery even when the USB is unplugged.
Thank you for this detailed response Carl.
With what you’ve said it will be better for me to add an extra outdoor node that will be the outdoor “HUB” and is always on. No distances are big and all have unobstructed line of sight. I’ll move the weather sensor to the “HUB” then the only value from the sleeping node will be a tank level which only needs to happen once or twice a day.
Having an always on hub connected to the weather sensors is a good idea. You can just add a bigger solar cell and LiPo (in case of rainy days). I forgot to mention the voltage regulator chips have varying quiescent current and can make a big difference. The lowest ESP32 devboard I noticed had 25uA deep sleep current. So that means you can run your tank CPU for several years on 3 AA or AAA batteries, assuming it sleeps many hours between readings.
You can use google to search for current consumption of various boards. There is a spreadsheet from Andreas Spiess for ESP32 with deep sleep power https://docs.google.com/spreadsheets/d/1Mu-bNwpnkiNUiM7f2dx8-gPnIAFMibsC2hMlWhIHbPQ/edit#gid=0
Rui/Sara, It would be great if the makeradvisor buying guides included deep sleep power. It’s not so easy to measure but important for the battery/solar powered projects.
After a bit more reading (a lot more actually) I now see you are referring to two different mesh libraries that approach the mesh topology differently. ESP-MESH (WiFi) is by Espressif Systems (Shanghai) CO., LTD and follows the more traditional IP sub-net structure which limits leaf to root node to a single path. The “painlessMESH” on the other hand is a true mesh from the sense that if one path is down and a second path exists it will still send the message from leaf to root. In fact this topology is how the military, SWAT and other emergency services do front line comms and video back to command centers. If you are interested have a look at Silvus Technologies.
The caveat as I see it (and it would be nice if I’m wrong) is that if the device is a “painlessMESH” node it can’t be an access point and you’d need to have a second device connected together by serial to create the WiFi gateway? The “painlessMESH” people state:
“painlessMesh subscribes to WiFi events. Please be aware that as a result painlessMesh can be incompatible with user programs/other libraries that try to bind to the same events.”
So my question is have you tried creating a root node WiFi gateway with “painlessMESH” on a single device?? or is it as I have assumed??
We loved this project so much that we featured it in this weeks episode of The Electromaker Show! https://youtu.be/RShENYIlInk?t=1082
Hi.
Thank you so much.
I’m really flattered for those nice words in your video.
I wasn’t expecting that at all.
I’m glad you found the tutorial useful. And thank you for sharing.
We intend to create more tutorials about this subject. Stay tuned.
Keep up the good work.
Regards,
Sara
I’ve taken a look at the API for the Espressif’s Mesh protocol. Is there a way to set the number of layers with the painlessMesh library. I am looking to construct a control system for a large number (500-1000) of PWM outputs. I have previously developed a system with XBee 2 units, but seperate PMW controls were necessary. The ESP32 seems an ideal, low cost solution.
Hi.
I still haven’t taken a look into that.
But, I advise you to take a look at the examples provided in the painlessmesh library and see if there is any clue for what you want to do.
https://github.com/gmag11/painlessMesh/tree/master/examples
Regards,
Sara
Rui and Sara
I just want to repeat my congratulations on your work. Thank you, thank you!
I’m still waiting for a mesh setup also connected to Wifi.
It would be interesting to have some mesh points to store and send data (voltage, GPS information, etc.) and put everything in a single database.
Greetings
Manuel
Hi Manuel.
Mesh network connected to wi-fi is in our big to-do list.
Thanks.
Regards,
Sara
Manuel, this too was on my list to get sorted but I’ve been struggling with serial between to ESP devices to send the updates to a WiFi connected controller. Then I found a library that makes it simple. Multiple formats supported. I tested using software serial both ends and it works a treat. Have a look at this SerialTransfer library https://github.com/PowerBroker2/SerialTransfer NOTE I’ve not tested on ESP only ATMega328, that’s the next step!
Ralph I want to test this library using serial 2 or serial 3 with esp32 boards.
Which pair of files I should use?
There is not enogh information in the read me file but I see three pairs datum, i2c, and spi
Thanks
Philip, I used software serial and just random digital pins. I set up a STX31 Temp and Humidity sensor and sent updates on button push. Just change it to what you want. Also I Used the “Structure” packet method. I haven’t tried the “Array” method yet. I’ve zipped up my test code and shared on my Google drive. Here’s the link. Good luck. https://www.dropbox.com/s/jty8lx09w8zlr30/serialTransfer_example.zip?dl=0
Many thanks Ralph
I will give it a shot
Philip
Thank you for the great tutorial.
I would like to implement the same concept. I have multiple ESP-01 and each has one sensor. I want to create a mesh network to be able to cover a bigger range, then allow one main ESP to connect to a router so that I can send the data from the rest of the ESPs to a website.
How can I have one ESP connect to the mesh and the internet at the same time? So that it can receive data from other nodes and send it to a website
Hi.
I recommend taking a look at the examples provided by the library: https://github.com/gmag11/painlessMesh/tree/master/examples
Regards,
Sara
Hi
Thx for this great job
I have experiment myself and works fine
I have a problem with the wifi band because all nodes act as AP and the band will be saturate. This is amplified by the use off 40Mhz wide. I have not found how to define 20Mhz and change the channel.
I have 2 questions to go further
– do you know if an equivalent library exist based on espnow ?
– how to perform espnow connection between node1 to node2 And have node2 in the painlessmesh network at the same time ?
I will try a base structure network with painlessmesh and espnow connections between the base structure and dedicated sensors.
NodeA, NodeB,NodeC etc painlessmesh
NodeA1 NodeA2 NodeA3 to NodeA with espnow
NodeB1 NodeB2 NodeB3 to NodeB with espnow
Thx for ur reply
Eric