fbpixel
Tags: ,

In this tutorial, we’ll look at how to configure two ESP32s to establish communication using the ESP-NOW protocol. The ESP32 is a development board integrating Bluetooth and WiFi. It can therefore connect and exchange data with devices connected to the same network.

Hardware

  • Computer
  • NodeMCU ESP32 x2
  • USB A Male to Mini B Male cable x2

Description ESP-NOW

ESP-NOW is a communication protocol developed by Espressif, enabling wireless communication between several devices without the need for a specific network. It enables high-speed exchange of small data packets over 2.4GHz frequency bands, up to 200m away. It requires an initial pairing, but once this is done, communication is persistent and establishes itself when the ESP32 starts up. One of the great advantages of this system, apart from the fact that it uses a dedicated network, is that one or more ESP32 stations can connect to the Wifi in parallel.

EPS-NOW also enables communication between several ESP32 and ESP8266 cards. All you need to do is adapt the code to the type of card.

Code

To test ESP-NOW communication between two ESP32 boards, we’ll use the esp_now.h library, available when you install the board manager. We will define and send a data structure between the master and slave boards. This structure must be identical between the two boards for the data to be understood correctly.

Sender code

For the sending card to communicate with another ESP32 card, it needs its MAC address. You can retrieve this address from the messages sent by the setup() function when the receiving board is started. We’re going to define a function that runs after a message has been sent, to check whether the transmission was successful.

#include <esp_now.h>// https://github.com/espressif/esp-idf/blob/master/components/esp_wifi/include/esp_now.h
#include <WiFi.h>

uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH RECEIVER MAC ADDRESS

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;
struct_message myData;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print(F("\r\n Master packet sent:\t"));
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  // Init Serial Monitor
  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(F("Error initializing ESP-NOW"));
    return;
  }
  Serial.print(F("Transmitter initialized : "));
  Serial.println(WiFi.macAddress());
  
  // Define Send function
  esp_now_register_send_cb(OnDataSent);

  // Register peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  // Add peer
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println(F("Failed to add peer"));
    return;
  }
}

void loop() {
  // Set values to send
  strcpy(myData.a, "data type char");
  myData.b = random(1, 20);
  myData.c = 1.2;
  myData.d = "hello";
  myData.e = 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(F("Sent with success"));
  }
  else {
    Serial.println(F("Error sending the data"));
  }
  delay(1000);
}

N.B.: In some versions, this code does not work. Two possible solutions if you encounter a problem: or

  • place memset(&peerInfo, 0, sizeof(peerInfo)); after the peerInfo declaration
  • declare esp_now_peer_info_t peerInfo before loop()

Receiver code

In the receiver code, we’ll create a function that runs when a message is received. This function is used to process the information received. In this example, we display the data contained in the structure.

#include <esp_now.h>// https://github.com/espressif/esp-idf/blob/master/components/esp_wifi/include/esp_now.h
#include <WiFi.h>

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;
struct_message myData;

// callback 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("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
}

void setup() {
  // Initialize Serial Monitor
  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;
  }
  Serial.print(F("Receiver initialized : "));
  Serial.println(WiFi.macAddress());

  // Define receive function
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
}

When you upload the code for the first time, pay close attention to the setup message containing the MAC address. You need to place it in the transmitter code to ensure successful pairing and communication.

espnow-esp32-get-mac-address Communication between two ESP32s with ESP-NOW

Results

Once the MAC address of the receiving card has been defined and the codes uploaded to each card, communication is established and the structure is sent and decrypted correctly.

espnow-esp32-master-results Communication between two ESP32s with ESP-NOW
espnow-esp32-slave-results Communication between two ESP32s with ESP-NOW

Bonus: Bidirectional communication between two ESP32s

We’ve seen how to send data from one card to another using the ESP-NOW protocol. Now we’ll see how, by slightly modifying the codes, we can obtain two-way communication.

Code Transceiver

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

uint8_t broadcastAddress[] = {0xA4, 0xCF, 0x12, 0x9A, 0x19, 0xAC};// REPLACE WITH OTHER TRANSCEIVER MAC ADDRESS

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;
struct_message myData;
char dataRcv[15];

// callbacks for sending and receiving data
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nMaster packet sent:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&dataRcv, incomingData, sizeof(dataRcv));
  Serial.print("\r\nBytes received: ");
  Serial.println(len);
  Serial.print("From slave: ");
  Serial.println(dataRcv);
  Serial.println();
}
void setup() {
  // Init Serial Monitor
  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(F("Error initializing ESP-NOW"));
    return;
  }
  Serial.print(F("Transceiver initialized : "));
  Serial.println(WiFi.macAddress());
  
  // Define callback functions
  esp_now_register_send_cb(OnDataSent);
  esp_now_register_recv_cb(OnDataRecv);

  // Register peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  // Add peer
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println(F("Failed to add peer"));
    return;
  }
}

void loop() {
  // Set values to send
  strcpy(myData.a, "from Master");
  myData.b = random(1, 20);
  myData.c = 1.2;
  myData.d = "hello";
  myData.e = false;

  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

  delay(1000);
}

N.B.: To obtain the same code for sender and receiver, we use the same data structure for sent and received messages. It is possible to define different structures depending on the origin of the message. Bear in mind, however, that the receiving card needs to know the structure of the received message to be able to decipher it.

Results

For station 0, we define the message

  strcpy(dataSent.a, "Master");
  dataSent.b = random(100, 200);
  dataSent.c = false;

And for station 1:

  strcpy(dataSent.a, "Slave");
  dataSent.b = random(10, 20);
  dataSent.c = false;
espnow-esp32-transceiver0-results Communication between two ESP32s with ESP-NOW
espnow-esp32-transceiver1-results Communication between two ESP32s with ESP-NOW

Sources