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.
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.
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.
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.
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.
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:
If you’re using PlatformIO, add the following lines to the platformio.ini file to add the libraries and change the monitor speed.
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.
Copy the following code to your Arduino IDE (code from the library examples). The code is compatible with both the ESP32 and ESP8266 boards.
CODE:
/*
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 ";
Start by including the painlessMesh library.
#include "painlessMesh.h"
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.
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
Create a painlessMesh object called mesh to handle the mesh network.
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);
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));
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);
}
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.
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.
Here’s the parts required for this example:
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
Giải thích
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
Giải thích
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
Wire the BME280 sensor to the ESP32 or ESP8266 default I2C pins as shown in the following schematic diagrams.
https://randomnerdtutorials.com/esp-mesh-esp32-esp8266-painlessmesh/