← Back to Academy

There is a special circle of hell reserved for IoT makers who just want to send a single button press from one room to another, but instead spend three hours debugging Wi-Fi credentials, fighting with DHCP leases, and trying to figure out why their MQTT broker crashed again. Sometimes, you don’t need a middleman. Sometimes, you just need two microcontrollers to look at each other and communicate via aggressive, high-speed telepathy.

Welcome to ESP-NOW. It’s a connectionless Wi-Fi communication protocol developed by Espressif that allows multiple ESP boards (ESP32, ESP8266, etc.) to talk directly to each other without needing a local Wi-Fi network or router. It’s essentially walkie-talkies for microcontrollers.

In this lesson, we are going to strip away the overhead and learn how to send data from one ESP32 to another with virtually zero latency.


Why Use ESP-NOW?

If you already know how to set up a Local Web Server or use MQTT, you might be wondering why you’d bother with ESP-NOW.

  1. Zero Router Required: If you are building an RC car or a drone out in a field, there is no Wi-Fi. ESP-NOW works anywhere.
  2. Blazing Fast: Standard Wi-Fi requires a complex handshake process. ESP-NOW just broadcasts the data. Latency is often under 5 milliseconds.
  3. Low Power: Because the boards don’t have to maintain a continuous connection to a router, they can wake up from Deep Sleep, blast a message via ESP-NOW, and go right back to sleep in a fraction of a second.

What You Need

For this lesson, you will need two ESP32 boards. If you need a solid recommendation, you can’t go wrong with these:

The Basics of ESP-NOW

ESP-NOW uses MAC Addresses to identify devices. Every network device in the world has a unique Media Access Control (MAC) address stamped on it at the factory.

To make Board A (the Sender) talk to Board B (the Receiver), Board A needs to know Board B’s MAC address. It’s like knowing your friend’s phone number.

Step 1: Find the Receiver’s MAC Address

Before we write the main code, upload this tiny sketch to the board you want to use as the Receiver. Open the Serial Monitor at 115200 baud, and it will print its MAC address.

#include "WiFi.h"

void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
}

void loop(){}

Write down the MAC Address printed in the monitor (e.g., 24:6F:28:1A:2B:3C). You will need it for the sender code.


Step 2: The Data Structure

When sending data via ESP-NOW, it’s best to package it into a struct. A struct is just a custom data type that groups different variables together. Both the sender and the receiver need to use the exact same struct definition.

typedef struct struct_message {
  char a[32]; // A string
  int b;      // An integer
  float c;    // A float
  bool d;     // A boolean
} struct_message;

Step 3: The Sender Code

Here is the code for the Sender ESP32. Don’t forget to replace the broadcastAddress with the MAC address you found in Step 1!

#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH THE MAC Address of your receiver 
uint8_t broadcastAddress[] = {0x24, 0x6F, 0x28, 0x1A, 0x2B, 0x3C};

// Create a struct to hold our data
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  bool d;
} struct_message;

struct_message myData;

esp_now_peer_info_t peerInfo;

// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

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

  // Register the send callback
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  // Set values to send
  strcpy(myData.a, "Hello from Sender!");
  myData.b = random(1, 20);
  myData.c = 1.2;
  myData.d = false;

  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(2000); // Send every 2 seconds
}

Step 4: The Receiver Code

Now, upload this code to your Receiver ESP32. It listens for incoming packets and prints the data to the Serial Monitor.

#include <esp_now.h>
#include <WiFi.h>

// Must match the sender structure
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    bool d;
} struct_message;

struct_message myData;

// Callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("Bool: ");
  Serial.println(myData.d);
  Serial.println();
}
 
void setup() {
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Register for receiving data
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {
  // The loop is empty! The ESP32 does everything in the background callback.
}

Next Steps

Fire up both boards. Look at the Serial Monitor for the Receiver, and you should see the data packets pouring in every two seconds. No routers, no passwords, no IP addresses. Just raw, unfettered microcontroller communication.

In our next lesson, we will look at expanding this into a mesh network, but for now, enjoy the absolute speed of ESP-NOW!