Thursday, May 4, 2023

ESP NOW Bidirectional data passage between ESP 8266 and ESP 32


The Random Nerd Tutorials tutorials (here and here) for bidirectional communications using ESP NOW have a bit of unnecessary added complexity, in adding humidity sensors, and in the case of ESP32 a OLED screen.  This post presents pared down sketches demonstrating bidirectional data exchange between an ESP 8266 and ESP32.  These are the same development boards used on the previous ESP NOW post, where each processor is installed on a NODEMCU type breakout board.

As in the previous post, I got the MAC address of each device using the sketch provided on the Random Nerd Tutorial links above.  

These MAC addresses are needed for each board to know where to send messages.  Besides this, the Random Nerd Tutorial sketches were changed to replace the humidity sensors with a potentiometer (and remove the screen).  This allows simplification of the data structure, and I added a little logic to trigger a couple LEDs based on the potentiometer position.  This way I don't need to be connected to serial monitor to know that the sketch is working.

Each board takes input from the wiper of their respective potentiometer and the data read is sent to the other board using ESP NOW.  Two LEDs are connected to each board, one for the local pot and one for that connected to the other board.  

ESP 8266 wiring is as follows:

  • 3.3 V -> Pot high side (red wire)
  • GND -> Pot low side (black wire)
  • A0 -> Pot wiper (yellow wire)
  • D6(GPIO12) -> Green LED anode (green wire)
  • Green LED Cathode -> 150 Ohm Resistor -> GND
  • D7(GPIO13) -> Red LED Anode (white wire)
  • Red LED Cathode -> 150 Ohm Resistor -> GND
ESP 32 wiring is similar:

  • 3.3 V -> Pot high side (red wire)
  • GND -> Pot low side (black wire)
  • G34(GPIO 34) -> Pot wiper (yellow wire)
  • G16(GPIO16) -> Green LED anode (green wire)
  • Green LED Cathode -> 150 Ohm Resistor -> GND 
  • G17(GPIO17) -> Red LED Anode (white wire)
  • Red LED Cathode -> 150 Ohm Resistor -> GND 

The LEDs illuminate if the pot wiper is past the halfway point.  Below is when the ESP 32 pot is turned up:


... and when the ESP 8266 wiper is turned up:


Maybe I will make a video later, as the images aren't terribly exciting.

Below is the ESP 8266 sketch:

/*
  This sketch is rework of a sketch first written by Rui Santos and presented on:
  https://RandomNerdTutorials.com/esp-now-two-way-communication-esp8266-nodemcu/
  
  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 <esp8266wifi.h>
#include <espnow.h>


// REPLACE WITH THE MAC Address of your receiver 
uint8_t broadcastAddress[] = {0x58, 0xBF, 0x25, 0x31, 0x9D, 0xE0};

int arriving_data=0;
int local_data=0;
const long interval = 200; 
unsigned long previousMillis = 0;

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
    int reading;
} struct_message;

// Create a struct_message to hold sensor readings
struct_message outbound;

// Create a struct_message to hold incoming sensor readings
struct_message inbound;

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}

// Callback when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&inbound, incomingData, sizeof(inbound));
  Serial.print("Bytes received: ");
  Serial.println(len);
  arriving_data = inbound.reading;
}

void read_pot(){
  local_data = analogRead(A0);
  Serial.print("Pot reading: ");
  Serial.println(local_data);
}

void printIncomingReadings(){
  // Display Readings in Serial Monitor
  Serial.println("INCOMING READINGS");
  Serial.println(arriving_data);
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  pinMode(12,OUTPUT);
  pinMode(13,OUTPUT);
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

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

  // Set ESP-NOW Role
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
  
  // Register for a callback function that will be called when data is received
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // save the last time you updated the DHT values
    previousMillis = currentMillis;

    //Get pot readings
    read_pot();

    //Set values to send
    outbound.reading = local_data;

    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &outbound, sizeof(outbound));

    // Print incoming readings
    printIncomingReadings();
    
  }
  if(arriving_data>2048)
  {
  digitalWrite(12, HIGH);
  } 
  else
  {
    digitalWrite(12, LOW);
  }
  if(local_data>512)
  {
    //Serial.println("writing local led high");
    digitalWrite(13, HIGH);
  }
  else
  {
    //Serial.println("writing local led low");
    digitalWrite(13, LOW);
  }
}
...and the ESP32 sketch:
/*
  This sketch is rework of a sketch first written by Rui Santos and presented on:
  https://RandomNerdTutorials.com/esp-now-two-way-communication-esp32/
  
  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 <esp_now.h>
#include <wifi.h>

// REPLACE WITH THE MAC Address of your receiver 
uint8_t broadcastAddress[] = {0xBC, 0xFF, 0x4D, 0x4A, 0x4D, 0x6A};

int arriving_data=0;
int local_data=0;
const long interval = 200; 
unsigned long previousMillis = 0;

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
    int reading;
} struct_message;

// Create a struct_message to hold sensor readings
struct_message outbound;

// Create a struct_message to hold incoming sensor readings
struct_message inbound;

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");
}

// Callback when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&inbound, incomingData, sizeof(inbound));
  Serial.print("Bytes received: ");
  Serial.println(len);
  arriving_data = inbound.reading;
}

void read_pot(){
  local_data = analogRead(34);
  Serial.print("Pot reading: ");
  Serial.println(local_data);
}

void printIncomingReadings(){
  // Display Readings in Serial Monitor
  Serial.println("INCOMING READINGS");
  Serial.println(arriving_data);
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  pinMode(16,OUTPUT);
  pinMode(17,OUTPUT);
 
  // 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;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  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;
  }
  // Register for a callback function that will be called when data is received
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) 
  {
    previousMillis = currentMillis;
    
  read_pot();
 
  // Set values to send
    outbound.reading = local_data;

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

  }
    if(arriving_data>512)
  {
  digitalWrite(16, HIGH);
  } 
  else
  {
    digitalWrite(16, LOW);
  }
  if(local_data>2048)
  {
    //Serial.println("writing local led high");
    digitalWrite(17, HIGH);
  }
  else
  {
    //Serial.println("writing local led low");
    digitalWrite(17, LOW);
  }
}

Surely this circuit could be made even simpler.  As a learning exercise, the novice may consider replacing the potentiometer with a switch and  taking a digital reading rather than powering the local LED by GPIO.