Sending between 2 esp32

Hi everybody

I see the code of @Idahowalker https://forum.arduino.cc/t/esp32-is-not-connecting-to-wifi-router/978117/4?u=alex_al , Can I use this code to send text like "Hello" from esp32 to router and then receive this text from another esp32 ? to make small network ? like :
esp32(1)......>Router.......>esp32(2)
What is the code of receiving the message?

Is the router a necessary component?
If not, you can message directly between ESP32s using ESP Now.

No, I just want to send and receive a value or a text from esp32(1) to esp32(2). So I thought the router would solve the problem.

Google ESP Now. It's easy to use. I have external antennae on mine and they cover the distance of my 1000' driveway.

That is very good! how to use that by wifi or? How to receive the messages? In fact, I have no
idea how to send and receive and what I need to do that

1 Like

Words like "Random Nerds tutorial ESP NOW" into an internet search thingy could provide some answer to the "I do not know how's"? Give it a try.

1 Like

here is a demo-code that demonstrates Tx / Rx with ESP-NOW.

It has a lot of debug-output to analyse if some details are wrong

// keep this variable on top to have an easy to remember place to look up
// which board shall have this code-version /a short name is easier to remember as
// a MAC-Adress
char BoardName[] = "Bonny"; 

unsigned long SendDataInterval;
int Summand;

// at the end of this file there is an explanation that uses an analogy with 
// sending / receiving postal letters to explain the several parts of ESP-NOW
// so if you would like to have an easy to understand overview read this
// explanation first.

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

// For sending / receiving ESP-NOW-data on both sides sender and receiver;
// a structured variable has to be defined with the EXACT SAME structure

// the information that shall be sended/received is transmitted bytewise
// the ESP-NOW-functions know nothing about what the bytes mean
// it just transfers a specified number of bytes
// If the structures do not match 100% the data gets MIS-interpreted on the receiver-side
// in this demo-code a structure with an Array Of Char and an integer is used
// it is defined as follows

// ESP-NOW data-structure-definition
typedef struct MyESP_NOW_Data_type {
  char MyESP_NOW_MsgStr[128];
  int  MyESP_NOW_Int;
} MyESP_NOW_Data_type;

// After defining the structure two variables 
// one for receiving one for sending data
// this demo wants to demonstrate send AND receive in both directions
MyESP_NOW_Data_type my_received_ESP_NOW_Data;      
MyESP_NOW_Data_type my_READYtoSEND_ESP_NOW_Data;   


//#############################################################################################
// list of MAC-Adresses of all receivers:

// important note: ESP-NOW sending is based on the RECEIVERS Mac-adress.
// this means for every ESP-modul that shall receive an ESP-NOW-Messages
// you have to execute register a peer in the Setup-function

// Mac-Adress must be stored in an array of uint8_t
uint8_t ESP_NOW_MAC_adrOfRecv[] = {0x24, 0x6F, 0x28, 0x22, 0x62, 0xFC };// Board 0x90 sends to Board 0xFC
//uint8_t ESP_NOW_MAC_adrOfRecv[] = {0x24, 0x6F, 0x28, 0x22, 0xB6, 0x90 }; // Board 0xFC sends to Board 0x90


char MAC_adrOfRecv_as_AoC[18];
char Own_MacAdr_AoC[18];  //suffix _AoC for easier remembering variable-type is ArrayOfChar

char MySelfHeader[]   = "Myself: ";
char ReceivedHeader[] = "-------------->"; 
//##############################################################################################

//ESP_NOW_functions

// callback function that will be executed when data is received
// Parameters:
// mac:          mac-adress of sender
// incomingData: the bytes that are received
// NoOfBytesRcv: number of bytes received

//void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t NoOfBytesRcv) { 
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int NoOfBytesRcv) {
  char MacAdr[18];
  Serial.print(ReceivedHeader);
  Serial.print("ESP-NOW-Data received from Board with MAC-Adress#");
  Serial.print( strupr(hex02str(mac[0])));
  Serial.print(":");
  Serial.print( strupr(hex02str(mac[1])));
  Serial.print(":");
  Serial.print( strupr(hex02str(mac[2])));
  Serial.print(":");
  Serial.print( strupr(hex02str(mac[3])));
  Serial.print(":");
  Serial.print( strupr(hex02str(mac[4])));
  Serial.print(":");
  Serial.print( strupr(hex02str(mac[5])));
  Serial.print(":");
  Serial.println("#");
  
  // copy data bytewise from variable incomingData to variable my_received_ESP_NOW_Data
  memcpy(&my_received_ESP_NOW_Data, incomingData, sizeof(my_received_ESP_NOW_Data));

  Serial.print(ReceivedHeader);
  Serial.print("No of Bytes received: ");
  Serial.println(NoOfBytesRcv);

  //these lines must match the variables inside the ESP_NOW-data-structure
  Serial.print(ReceivedHeader);
  Serial.print("Received Msg: #"); // leading "#"
  Serial.print(my_received_ESP_NOW_Data.MyESP_NOW_MsgStr);
  Serial.println("#"); // trailing "#" makes it easy to see which bytes where received
  
  Serial.print(ReceivedHeader);
  Serial.print("Int: ");
  Serial.println(my_received_ESP_NOW_Data.MyESP_NOW_Int);

  Serial.println();
}


// callback when data is sent. Gets executed when sending data has finished
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.println();
  Serial.println();
  Serial.print(MySelfHeader);
  Serial.print(" OnDataSent Send Status: ");
  if (status == ESP_NOW_SEND_SUCCESS) {    
    Serial.println("Success'");
    Serial.println();
  }
  else {
    Serial.println("Failed'");
    Serial.println();
  }  
}

// attention! for some unknown strange reasons the variable 
// for the peer-info has to be global otherwise you will 
// get the error "ESPNOW: Peer Interface is invalid"
esp_now_peer_info_t MyPeerInfo; 

void ESP_Now_setup() {
  WiFi.mode(WIFI_STA);
  Serial.println("WiFi.mode(WIFI_STA); done");
  WiFi.disconnect(); // for strange reasons WiFi.disconnect() makes ESP-NOW work
  Serial.println("WiFi.disconnect(); done");
  
  // Init ESP-NOW  
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }  
  Serial.println("esp_now_init() was successful");
    
  // register callback-function that will be executed each time 
  // function esp_now_send() has finished
  esp_now_register_send_cb(OnDataSent);
  Serial.println("esp_now_register_send_cb(OnDataSent); done");
  
  // register callback-function that will be executed each time
  // ESP-NOW-Data is received
  esp_now_register_recv_cb(OnDataRecv);  
  Serial.println("esp_now_register_recv_cb(OnDataRecv); done");

  // the ESP-NOW-Sender needs to "fill out" a list with informations about each receiver
  // this is called peer. Therefore you have to create a variable of type esp_now_peer_info_t
  //esp_now_peer_info_t MyPeerInfo;
  // then "fill out" peer-data-form
  memcpy(MyPeerInfo.peer_addr, ESP_NOW_MAC_adrOfRecv, 6);
  MyPeerInfo.channel = 0;  
  MyPeerInfo.encrypt = false;
  
  // after setting up peer-info, add peer        
  if (esp_now_add_peer(&MyPeerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  Serial.println("esp_now_add_peer(&peerInfo) was successful");
  // this setup peer-info and add peer has to be repeated for each receiver 
  // that shall receive from this sender

  for (int i = 0;   i < 6;  i++) {
    strcat (MAC_adrOfRecv_as_AoC, hex02str(ESP_NOW_MAC_adrOfRecv[i])   );
    if (i < 6) {
      strcat (MAC_adrOfRecv_as_AoC, ":" );    
    }  
  }  
  MAC_adrOfRecv_as_AoC[17] = 0;
  strupr(MAC_adrOfRecv_as_AoC); // make letters UPPERCASE
  Serial.print("MAC-Adress of Receiver is ");
  Serial.println(MAC_adrOfRecv_as_AoC);
}


void ESP_NOW_SendData()
{
  char AoC[10];
  // Set values to send
  strcpy(my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr, "HI I'M SENDING EVERY ");
  itoa(SendDataInterval,AoC,10);
  strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr,AoC );
  strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr, " Milli-SECONDS COUNTiNG UP ");
  itoa(Summand,AoC,10);
  strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr,AoC );
  my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int = my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int + Summand; 

  // Send message via ESP-NOW  
  esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data));
  Serial.print(MySelfHeader);
  Serial.println("esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data)); done");
  Serial.print(MySelfHeader);
  Serial.print("I am the board named '");
  Serial.print(BoardName);
  Serial.print("' with the MAC-Adress ");
  Serial.println(Own_MacAdr_AoC); 
  Serial.print(MySelfHeader);
  Serial.print("and I try to send my ESP-NOW-Data to the board with MAC-Adress #");
  Serial.print(MAC_adrOfRecv_as_AoC); 
  Serial.println("#"); 
  
  // if sending has finished function OnDataSent is called
}


/* nonblocking timing based on millis()
this function returns true each time the TimePeriod has expired and immediately 
starts a new TimePeriod. So this function
example-use:
unsigned long myTimer;

  if (TimePeriodIsOver( myTimer,2000) ) {
    //the code here gets executed only every 2000 Milliseconds
  }  
*/  
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

unsigned long SendDataTimer;


/*
everytime the compiler compiles the file new 
the macros named "__FILE__",  "__DATE__", "__TIME__"  
where replaced with what their names say  Filename with path
Date and time of the Computer the IDE is running on 
// so by simply starting the device the uploaded code yells filename date and time at the serial interface
*/
void PrintWiFiMacAdress()
{
  char HexByteDigits[3];
  
  for (uint8_t i = 0; i < 18; i = i + 1)
  { 
    Own_MacAdr_AoC[i] = WiFi.macAddress()[i];
  } 
  Own_MacAdr_AoC[17] = 0; // zero to terminate the string
  Serial.print("ESP32-Board's OWN MAC-Address is:  ");
  Serial.println(Own_MacAdr_AoC);
  
  Serial.println();
  Serial.println("copy the line below and replace the codeline");
  Serial.println("uint8_t ESP_NOW_MAC_adrOfRecv[] = { .... };");
  Serial.println("inside the code with the copied line from the serial monitor");
   
  Serial.println();
  Serial.print("uint8_t ESP_NOW_MAC_adrOfRecv[] = {");
  for (uint8_t i = 0; i < 16; i = i + 3)
  { 
    HexByteDigits[0] = Own_MacAdr_AoC[i];
    HexByteDigits[1] = Own_MacAdr_AoC[i+1];
    HexByteDigits[2] = 0; // zero for terminating the string
    Serial.print("0x");
    Serial.print(HexByteDigits);
    if (i < 14) Serial.print(", ");
  }  
  Serial.println(" };");
  Serial.println();      
}

char* hex02str(uint8_t b)  {
 static char str[]="FF"; // RAM für einen 2-Zeichen string reservieren.
  snprintf(str,sizeof(str),"%02x",b);
  return str;
}


void PrintFileNameDateTime()
{
  Serial.print("Code running comes from file ");
  Serial.println(__FILE__);
  Serial.print(" compiled ");
  Serial.print(__DATE__);
  Serial.print("  ");
  Serial.println(__TIME__);  
}

  
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  Serial.println();  // a carriage return to make sure serial-output begins in colum 1 of a new line
  PrintFileNameDateTime();
  PrintWiFiMacAdress();  

  ESP_Now_setup();

}

void loop() {
  SendDataInterval = 5000;
  Summand = 6;
  // check if timer-intervall is over
  if (TimePeriodIsOver(SendDataTimer,SendDataInterval) )  
    {
     Serial.print(MySelfHeader); 
     Serial.println("SendData");
     ESP_NOW_SendData();
     Serial.print(MySelfHeader);
     Serial.println("SendData done");
    }
}

// explanation how ESP-NOW works:
// sending serial data is as easy as

// Serial.begin(baudrate);
// Serial.print("Hello World");

// This can be coded so easy because a lot of things are fixed
// IO-Pins used by Serial fixed
// Connection to other device: made of wire must not be programmed
// standard serial-connection does not care if receiver is connected
// send data and that's all

// with ESP-NOW more things are NOT fixed = are adjustable and MUST be adjusted
// the receiver is NOT defined by wire but through his MAC-Adress

// Data-transport can do only transporting one or multiple bytes
// That's not a real problem the user can define his own data-pattern (data-structure)

// ESP-NOW offers feedback if a datatransmission was successful or failed

// each ESP-modul can receive data from different senders 
// so an information from WHICH sender the data was sended is useful
// this is identified by the senders MAC-Adress

// Analogy sending / receiving postal letters:

// if you want to SEND out a letter to somebody you have to write 
// the receivers adress on the envelope. 
// similar thing with ESP-NOW: the data is NOT guided by a wire its sended "Up in the air" 
// with ESP-NOW this receiver-adress is the MAC-Adress of the receiver

// if the postman brings you a letter you don't have to be at home. He will put the letter
// into your letter-box and you can pick it out later.

// similar thing with ESP-NOW: there is some kind of "letter-box" the received data will be stored into
// the data-receiving into the "letter-box" runs in the backround similar to an interrupt
// The received data will be catched up and must be copied into some kind of "letterbox"

// any transporting service has some kind of procedure if you want to send a package or a letter
// fill out some kind of form (on paper or onnline) print out and glue a sticker 
// with the transporting informations on the package/envelope put the package / envelope into the
// "send out" box or bring it to a pickup store

// so there are some things to do:

// - build a special designed "package" with multiple special designed compartments
//   where all parts of the data has its own compartment 
//   defining a new variable type "structure"

// - build a "letter-box" designed expecially for the user-defined package 
//   and "mount it in front of your house" so it can be easily found by the postman

// - write a list with receiver-adresses and handover this list to ESP-NOW (the "letter-department" of your "company"

// - setup if data shall be encrypted or not

// - setup transmission channel that shall be used for sending the data wireless

// - design your "sending-form"

// these are the reasons why ESP-NOW needs a bit more than just
// Serial.begin(baudrate);
// Serial.print("My Data");

// Your mobile phone has a lot of functions. It took you some time to learn them all
// Same thing with ESP-NOW it will take some time to learn it

// if you want it fully automated without learning anything 
// use Alexa, Siri or google assistant instead

best regards Stefan

1 Like

This for ESP8266, is the same thing for ESP32? and can I open two sketch one for the receiver, and another for the sender on the "same laptop" ?

For ESP8266 you have to use a different Wifi-library
If you want to watch the serial output of two ESP-modules at the same time
you have to start a second Arduino-IDE.

Second Arduino-IDE means not to open a second file in the same Arduino-IDE
You have to do a double-click on the Arduino-IDE-icon to start a second IDE.
With this second IDE you can adjust the com-port to a different one than the first

best regards Stefan

OK, thank you!

You might try to write code that is identical to both units. Here's something that lets you do that by figuring out which of two MAC addresses to use for talking to another board.

Not your own MAC address, but the other (known) address. Just plug in the MAC addresses of the two units you have, and call unitSelect at some point.

Subsewuently, any braodCast address reference should be changed to select from the small array of broadcast addresses in the program, like

   esp_now_add_peer(broadcastAddress[unitNumber], ESP_NOW_ROLE_COMBO, 1, NULL, 0)

See it here:

unsigned char unitNumber = 0xff;  // no unit selected yet

const String asText[] = {
  "B4:E6:2D:8E:50:A1",
  "B4:E6:2D:8E:67:45",
};

uint8_t broadcastAddress[][6] = {
/* 0 */  {0xb4, 0xe6, 0x2d, 0x8e, 0x50, 0xa1},
/* 1 */  {0xb4, 0xe6, 0x2d, 0x8e, 0x67, 0x45},
};
 
void selectUnit()
{
//	char asChars[20];  // leftover, sry. No need for this.

	unitNumber = 0xff; // no it doesn't

// use one of the following three lines
	
//	String myMACAddress = WiFi.macAddress();	// if you use this for real...

	String myMACAddress = "B4:E6:2D:8E:67:45";  // simulated! will succeed

//	String myMACAddress = "B4:E7:2D:8E:67:45";  // simulated! will fail


	Serial.print("I am "); Serial.println(myMACAddress);

	if (myMACAddress == asText[0]) unitNumber = 1;
	else if (myMACAddress == asText[1]) unitNumber = 0;

	if (unitNumber == 0xff) {
		Serial.println(" MACAddress assignment fail!");
		for (; ; ); // whatever you want to do when you have no partner. Me, I die.
	}
	else {
		Serial.print("I'll talk to unit "); Serial.print(unitNumber);
		Serial.print(" at "); Serial.println(asText[unitNumber]);
  }	
}

void setup() {
	Serial.begin(9600);
	Serial.println("unit selection test");

	selectUnit();
}

void loop() {}

Obvsly determine the MAC addresses for your units and edit accordianly.

So the code on the two units can be identical. You can edit it in one IDE instance and download to one or the other board by switching the port selection in the Tools menu.

HTH

a7

Thanks for all,
After the difficulties, I was able to send from one microcontroller to another, but I have a question. If the structure is global declared, Why I can't I print the array received after this function esp_now_register_recv_cb(OnDataRecv); ?
Because I want to use it inside another function after this esp_now_register_recv_cb().
any advice please??

sender side code:

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

// REPLACE WITH YOUR ESP RECEIVER'S MAC ADDRESS
uint8_t broadcastAddress1[] = {0x24,0x0A,0xC4,0xA6,0x7B,0x20};

typedef struct test_struct {
  int arr[5];
} test_struct;

test_struct test;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  Serial.print("Packet to: ");
  // Copies the sender mac address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print(macStr);
  Serial.print(" send status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  Serial.begin(115200);
 
  WiFi.mode(WIFI_STA);
 
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_register_send_cb(OnDataSent);
   
  // register peer
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  // register first peer  
  memcpy(peerInfo.peer_addr, broadcastAddress1, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
//-------------------------
  for (int i=0; i < 5; i++) {
       test.arr[i]=i;
  }
  esp_err_t result = esp_now_send(0, (uint8_t *) &test, sizeof(test_struct));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(2000);
}
 
void loop() {}

Receiver side code:

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

//Structure example to receive data
//Must match the sender structure
typedef struct test_struct {
  int x;
  int y;
  int rearr[5];
} test_struct;

//Create a struct_message called myData
test_struct 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("rearray=: ");
for(int i=0;i<5;i++){
    Serial.print(myData.rearr[i]);

}
  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;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);

  Serial.print("rearray AFTER esp fun=: ");  //Where the receiver part does not print this part
  for(int i=0;i<5;i++){
    Serial.print(myData.rearr[i]);}
} 
void loop() {}

It doesn't look like you have matching structures.

a7

Sender structure

receiver structure

Is it possible to see the update? where the same problem I can not print anything after the function esp_now_register_recv_cb(OnDataRecv);
I need to use the received value of int arr[5]; in another functions.

Hi Alex,

I don't know what you mean with

if you want real help you have to post the

actual

sender-sketch
and
the

actual

receiver sketch as a

new posting

using this method

There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

Hi Stefan,
I mean I made the structures identical in sender and receiver in the post #13 and that is the actual sender-sketch and receiver sketch that I work on it.
Usually when the questions are on the same thing , they suggested that I do not make them in a new post, so I did not publish this in new post!

Who is meant with "they" here? Other users?

The rules are not to start a new

thread

on the same question.

in opposite modifying

postings

has a chance to make the following answers look like nonsense because the post the answer is related to is modifyed. The only exception to this is correcting typos.

So any follow-up should be done as a

new

entry --- inside the same

thread

If the last sketch you have posted is your actual sketch then there are missing a lot of lines of code that are a must to make it work

My exa,ple code had a lot of serial out that helps to find errors
If you just delete them all you are in the dark.

You will be faster in understanding how it all works if you go back to the working example and then do not delete lines of code just make a part of the code a block-comment
by using

/*  starts block-comment
*/ ends block-comment

just a few lines. Then test again.

The more lines of code you put away the bigger the chance that something important you are missing but you don't know where

If you then would start changing here a bit changing there a bit by guessings
you can waste hours and days of time without proceeding.
This is the reason why in the end it is faster to do

  1. small changes then test
  2. small changes then test
  3. small changes then test
  4. small changes then test
  5. small changes then test
    ...
    only in case you find this boring and doing quick guessings is that what makes the fun for you
    go on. This kind of fun will last much longer if you don't ask but just keep on consequently guessing.

best regards Stefan

When you do such things as change the code, post the changed code in a new post.

1 Like