Building a Mesh Network with ESP-NOW

Difficulty Level: Intermediate

ESP-NOW is a lightweight, wireless communication protocol developed by Espressif for ESP8266 and ESP32 boards. Unlike traditional Wi-Fi, ESP-NOW enables direct device-to-device communication without requiring a router or access point, making it perfect for low-power IoT applications like sensor networks or home automation systems. In this tutorial, we’ll guide you through building a mesh network using multiple ESP8266 modules to exchange data using ESP-NOW.

Components Required (With eBay Links)

- Multiple ESP8266 modules (e.g., NodeMCU, ESP-01): ESP8266 NodeMCU on eBay
- Breadboard: Breadboard on eBay
- Jumper Cables: Jumper Cables on eBay
- Breadboard Power Supply (optional for stable voltage): Breadboard Power Supply on eBay
- USB-to-Serial Adapter (for programming ESP-01, if used): USB-to-Serial Adapter on eBay

Install the ESP8266 Core for Arduino IDE

To program the ESP8266, you’ll need the ESP8266 core installed in the Arduino IDE. Here’s how to set it up:

1. Open the Arduino IDE and go to File > Preferences.
2. In the "Additional Board Manager URLs" field, add:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
3. Navigate to Tools > Board > Boards Manager, search for "ESP8266," and install the latest version.
4. Select your ESP8266 board (e.g., NodeMCU 1.0) under Tools > Board.

Include ESP-NOW Library

The ESP-NOW library is bundled with the ESP8266 core, so no additional installation is typically required. To verify:

1. Go to Sketch > Include Library > Manage Libraries.
2. Search for "ESP8266" and ensure the core library is installed. If you encounter issues, update the Arduino IDE and the ESP8266 core.

Understanding ESP-NOW Basics

Before diving into the code, let’s clarify how ESP-NOW works:

- Peer-to-Peer Communication: Devices communicate directly using MAC addresses.
- Roles: Devices can be controllers (senders), slaves (receivers), or combos (both).
- Range: Typically 200-250 meters in open space, depending on the environment and antenna.
- Data Payload: Up to 250 bytes per message.

To find the MAC address of an ESP8266, upload this simple sketch:

#include 
void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.println(WiFi.macAddress());
}
void loop() {}
        

Run it on each module and note the MAC addresses for later use.

Set Up Peer-to-Peer Communication

Let’s start with a basic example of two ESP8266 modules—one sender and one receiver.

Sender Code

#include 
#include 

// Replace with the receiver's MAC address
uint8_t broadcastAddress[] = {0x24, 0x6F, 0x28, 0xA4, 0xB6, 0x7F};

// Callback function to check delivery status
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Delivery Status: ");
  Serial.println(sendStatus == 0 ? "Success" : "Fail");
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA); // Set device as a Wi-Fi Station

  // Initialize ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); // Sender role
  esp_now_register_send_cb(OnDataSent); // Register callback
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0); // Add peer
}

void loop() {
  String message = "Hello, ESP-NOW!";
  uint8_t data[250];
  message.getBytes(data, 250); // Convert string to byte array

  esp_now_send(broadcastAddress, data, sizeof(data)); // Send data
  delay(2000); // Send every 2 seconds
}
        

Receiver Code

#include 
#include 

// Callback function to process received data
void OnDataRecv(uint8_t *mac_addr, uint8_t *incomingData, uint8_t len) {
  char message[len + 1];
  memcpy(message, incomingData, len);
  message[len] = '\0'; // Null-terminate the string
  Serial.print("Received: ");
  Serial.println(message);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA); // Set device as a Wi-Fi Station

  // Initialize ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE); // Receiver role
  esp_now_register_recv_cb(OnDataRecv); // Register callback
}

void loop() {
  // Keep listening for incoming messages
}
        

Set Up a Mesh Network

For a true mesh network, each node should act as both a sender and receiver, forwarding messages to other nodes as needed. Here’s an example of a combined sender-receiver node:

Mesh Node Code

#include 
#include 

// Replace with MAC addresses of other nodes in the network
uint8_t peer1[] = {0x24, 0x6F, 0x28, 0xA4, 0xB6, 0x7F};
uint8_t peer2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // Add more peers as needed

// Structure to hold message data
typedef struct {
  char message[200];
  int hopCount;
} Message;

Message myMessage;

// Callback for sent data
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Sent to ");
  for (int i = 0; i < 6; i++) Serial.printf("%02X", mac_addr[i]);
  Serial.println(sendStatus == 0 ? ": Success" : ": Fail");
}

// Callback for received data
void OnDataRecv(uint8_t *mac_addr, uint8_t *incomingData, uint8_t len) {
  memcpy(&myMessage, incomingData, sizeof(myMessage));
  Serial.printf("Received from ");
  for (int i = 0; i < 6; i++) Serial.printf("%02X", mac_addr[i]);
  Serial.printf(": %s (Hops: %d)\n", myMessage.message, myMessage.hopCount);

  // Forward message if hop count is below a threshold
  if (myMessage.hopCount < 3) {
    myMessage.hopCount++;
    esp_now_send(NULL, (uint8_t *)&myMessage, sizeof(myMessage)); // Broadcast to all peers
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.println("MAC Address: " + WiFi.macAddress());

  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_set_self_role(ESP_NOW_ROLE_COMBO); // Both sender and receiver
  esp_now_register_send_cb(OnDataSent);
  esp_now_register_recv_cb(OnDataRecv);

  // Add peers
  esp_now_add_peer(peer1, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
  esp_now_add_peer(peer2, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {
  // Send a test message every 5 seconds
  static unsigned long lastSend = 0;
  if (millis() - lastSend > 5000) {
    strcpy(myMessage.message, "Mesh Test Message");
    myMessage.hopCount = 0;
    esp_now_send(NULL, (uint8_t *)&myMessage, sizeof(myMessage)); // Broadcast
    lastSend = millis();
  }
}
        

Key Enhancements:

- Message Structure: Uses a struct to include a hop count, preventing infinite loops.
- Broadcasting: Sending to NULL broadcasts to all registered peers.
- Hop Limiting: Messages stop forwarding after a set number of hops (e.g., 3).

Expand to Multiple Nodes

1. Assign unique MAC addresses to each node and update the peer arrays in the code.
2. Upload the mesh node code to all ESP8266 modules.
3. Test by placing nodes within range (e.g., 50-100 meters indoors) and monitoring serial outputs.

Testing and Debugging

- Serial Monitor: Open the Serial Monitor (115200 baud) for each node to verify sent/received messages.
- Range Testing: Move nodes apart to test the effective range and reliability.
- Interference: Avoid crowded 2.4 GHz environments (e.g., near Wi-Fi routers).

Common Issues and Fixes:

- "Error initializing ESP-NOW": Ensure the ESP8266 core is updated and the board is functional.
- No Data Received: Double-check MAC addresses and ensure nodes are powered correctly.
- Message Corruption: Reduce payload size or add error-checking (e.g., CRC).

Practical Applications

- Sensor Network: Attach sensors (e.g., DHT11 for temperature) to nodes and relay data.
- Home Automation: Control lights or relays across multiple nodes.
- Data Logging: Add an SD card module to one node to log messages.

Example: Adding a Sensor

Modify the sender code to include sensor data:

#include 
#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

void loop() {
  float temp = dht.readTemperature();
  String message = "Temp: " + String(temp) + "C";
  uint8_t data[250];
  message.getBytes(data, 250);
  esp_now_send(NULL, data, sizeof(data));
  delay(5000);
}
        

Conclusion

With ESP-NOW, you’ve built a flexible, low-power mesh network using ESP8266 modules. This setup is scalable, efficient, and ideal for IoT projects requiring decentralized communication. Experiment with additional features like encryption (ESP-NOW supports it with a key) or dynamic peer discovery to enhance your network further.

Further Reading

Want to dive deeper into ESP-NOW and related projects? Check out these resources on our site:

- Getting Started with ESP32 and ESP-NOW: Learn how to adapt this tutorial for the more powerful ESP32.
- Building a Wi-Fi Sensor Network with ESP8266: Explore combining ESP-NOW with traditional Wi-Fi for hybrid networks.
- IoT Home Automation Projects: See how to integrate ESP-NOW into smart home systems.
- Optimizing Power Consumption in ESP8266 Projects: Tips for making your mesh network battery-powered and energy-efficient.

FAQ

Here are answers to some common questions about building a mesh network with ESP-NOW:

What’s the maximum number of nodes I can have in an ESP-NOW mesh network?
ESP-NOW supports up to 20 peers per device, but in a mesh setup, this depends on your forwarding logic and range. Practically, 5–10 nodes work well without significant performance drops.
Can I use ESP-NOW with ESP32 instead of ESP8266?
Yes, the ESP32 is well suited for ESP-NOW mesh networks. It offers better performance than the ESP8266 and includes additional features like Bluetooth support.
Why aren’t my nodes communicating?
Ensure MAC addresses are correct, nodes are within range (up to 200m in open space), and there’s no interference from other 2.4 GHz devices. Also, check that your power supply is stable.
How do I add encryption to ESP-NOW messages?
ESP-NOW supports encryption using a 16-byte key. Use esp_now_add_peer() with the key parameter. Refer to the official Espressif documentation for details.
Can ESP-NOW coexist with Wi-Fi on the same device?
Yes, but you’ll need to manage both carefully. Set your Wi-Fi to a fixed channel that matches ESP-NOW’s channel (typically channel 1) to avoid conflicts.

Contact Us

If you have any questions or inquiries, feel free to reach out to us at Microautomation.no@icloud.com .

Follow our Socials for the newest updates!