ESP32 + ESP-NOW with auto-pairing (loop through peer list help)

I am playing around with a sketch to use an ESP32 (main/sender board) to auto-pair with all the ESP32 (receiver) boards in the area.

  • When I try to look up the official espressif.com docs.. the syntax is still a bit overwhelming for me.. so I am not clear on how to actually 'execute' some of these functions/methods I see.
    (all the & and * and _t stuff.. still throws me off) :frowning: - enlightenment in that area would also be nice/helpful! :slight_smile:

Anywho...

Looking for some help to loop through the 'peer' list after all auto-paring is complete.

I have the auto-pairing done..and I do get the (updated) total number of peers after each auto-pair completion.. but I am not clear on how to access/display the mac addresses saved in this peer list?

I'm basically just stuck at the loop now?

//esp 'peer' info
esp_now_peer_info_t slave;


bool addPeer(const uint8_t *peer_addr) {      // add pairing
  memset(&slave, 0, sizeof(slave));
  const esp_now_peer_info_t *peer = &slave;
  memcpy(slave.peer_addr, peer_addr, 6);
  
  slave.channel = chan; // pick a channel
  slave.encrypt = 0; // no encryption
  // check if the peer exists
  bool exists = esp_now_is_peer_exist(slave.peer_addr);
  if (exists) {
    // Slave already paired.
    Serial.println("Already Paired");
    return true;
    
  }else{
    esp_err_t addStatus = esp_now_add_peer(peer);
    
    if (addStatus == ESP_OK) {
      // Pair success
      Serial.println("Pair success");

      //esp_err_t currentPeerCount = esp_now_peer_num.total_num;
      esp_now_peer_num_t pn;
      esp_now_get_peer_num(&pn);
      Serial.print("Total Peer Count: ");
      Serial.println(pn.total_num);
      
      Serial.print("MAC Address Added To List: ");
      printMAC(peer_addr);
      Serial.println("");

      //output peer list
      Serial.print("Current Peer List: ");
      Serial.println("");

      if((pn.total_num) > 0){        
        for(i=0; i < pn.total_num; i++){
          //Serial.println("");        
          Serial.print("MAC ADDRESS ");
          Serial.print(i);
          Serial.print(": ");
          Serial.println(slave.peer_addr[i]);
          //Serial.println(peer.peer_addr[i]);
          //Serial.println("");
        }      
      }

    }
  }
}

but my current out put is -not- mac addresses?

Current Peer List: 
MAC ADDRESS 0: 8 
MAC ADDRESS 1: 209

I tried to use peer vs slave.. (but it threw an error?)

Wrong array/object? Wrong approach to access it?

I saw the esp_now_fetch_peer () function.. but not really clear how its executed (returns status..not clear on how to get info?) (or if its even needed?)

Any (real/helpful) feedback is appreciated!

Thanks

Please post your full sketch rather than just a snippet of it

None of the rest is relevant is it... that function IS the focus. (just the loop part.. wrong object/array being referenced? wrong syntax?)

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


//esp 'peer' info
esp_now_peer_info_t slave;
int chan; 

enum MessageType {PAIRING, DATA,};
MessageType messageType;

//variable/counter placeholder
int counter = 0;

// Structure example to receive data
// Must match the sender structure
typedef struct struct_pairing {       
    uint8_t msgType;
    uint8_t id;
    uint8_t macAddr[6];
    uint8_t channel;
} struct_pairing;
//define instance of pairing struct
struct_pairing pairingData;


// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  uint8_t msgType;
  uint8_t id;
  float temp;
  float hum;
  unsigned int readingId;
} struct_message;
//define instance of message struct
struct_message incomingReadings;
struct_message outgoingSetpoints;


// ---------------------------- esp_ now -------------------------
void printMAC(const uint8_t * mac_addr){
  char macStr[18];
  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);
}

bool addPeer(const uint8_t *peer_addr) {      // add pairing
  memset(&slave, 0, sizeof(slave));
  const esp_now_peer_info_t *peer = &slave;
  memcpy(slave.peer_addr, peer_addr, 6);
  
  slave.channel = chan; // pick a channel
  slave.encrypt = 0; // no encryption
  // check if the peer exists
  bool exists = esp_now_is_peer_exist(slave.peer_addr);
  if (exists) {
    // Slave already paired.
    Serial.println("Already Paired");
    return true;
    
  }else{
    esp_err_t addStatus = esp_now_add_peer(peer);
    
    if (addStatus == ESP_OK) {
      // Pair success
      Serial.println("Pair success");

      //esp_err_t currentPeerCount = esp_now_peer_num.total_num;
      esp_now_peer_num_t pn;
      esp_now_get_peer_num(&pn);
      Serial.print("Total Peer Count: ");
      Serial.println(pn.total_num);
      
      Serial.print("MAC Address Added To List: ");
      printMAC(peer_addr);
      Serial.println("");

       //output peer list
      Serial.print("Current Peer List: ");
      Serial.println("");

      if((pn.total_num) > 0){        
        for(i=0; i < pn.total_num; i++){
          //Serial.println("");        
          Serial.print("MAC ADDRESS ");
          Serial.print(i);
          Serial.print(": ");
          Serial.println(slave.peer_addr[i]);
          //Serial.println(peer.peer_addr[i]);
          //Serial.println("");
        }      
      }
      return true;
      
    }else {
      Serial.println("Pair failed");
      return false;
    }
  }
} 

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("Sending Data Status: ");
  Serial.print(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success to " : "Delivery Fail to ");
  printMAC(mac_addr);
  Serial.println();
}

void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { 
  //Serial.print(len);
  //Serial.print(" bytes of data received from : ");
  Serial.println();
  Serial.print("Message received from: ");
  printMAC(mac_addr);
  Serial.println();
  
  //String payload;
  uint8_t type = incomingData[0];       // first message byte is the type of message 

  //parse incoming data
  switch (type) {
  
    case DATA :                           // the message is data type
      memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));      
      break;
    
    case PAIRING:                            // the message is a pairing request 
      memcpy(&pairingData, incomingData, sizeof(pairingData));

      Serial.print("Pairing Message Type: ");
      Serial.println(pairingData.msgType);

      Serial.print("Pairing Request Device ID: ");
      Serial.println(pairingData.id);
      
      Serial.print("Pairing Request Device MAC Address: ");
      printMAC(mac_addr);
      Serial.println();

      Serial.print("Pairing Channel: ");
      Serial.println(pairingData.channel);

      // do not replay to server itself
      if(pairingData.id > 0){     
        
        if(pairingData.msgType == PAIRING){ 
          pairingData.id = 0;       // 0 is server
          // Server is in AP_STA mode: peers need to send data to server soft AP MAC address 
          WiFi.softAPmacAddress(pairingData.macAddr);   
          pairingData.channel = chan;
          Serial.println("Send Pairing Reponse To Requesting Device - ");
          Serial.println();
          esp_err_t result = esp_now_send(mac_addr, (uint8_t *) &pairingData, sizeof(pairingData));
          addPeer(mac_addr);
        }  
      }      
      //end switch statement
      break; 
  }
}

void initESP_NOW(){
    // Init ESP-NOW
    if (esp_now_init() != ESP_OK) {
      Serial.println("Error initializing ESP-NOW");
      return;
    }
    esp_now_register_send_cb(OnDataSent);
    esp_now_register_recv_cb(OnDataRecv);
} 

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);

  Serial.println();
  Serial.print("CORE Server MAC Address:  ");
  Serial.println(WiFi.macAddress());

  // Set the device as a Station and Soft Access Point simultaneously
  WiFi.mode(WIFI_AP_STA);
 
  chan = WiFi.channel();
  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Wi-Fi Channel: ");
  Serial.println(WiFi.channel());
  Serial.println();

  initESP_NOW();
  
}

void loop() {
  static unsigned long lastEventTime = millis();
  static const unsigned long EVENT_INTERVAL_MS = 5000;
  if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
    
    lastEventTime = millis();
    // do whatever
  }
}

All of the code is relevant. Your original snippet referenced global variables and functions whose definitions were not included.

Hi @xl97,

for support specific to your sketch please consider @UKHeliBob 's post!

Actually if you google for "ESP NOW Arduino" you'll get lots of links ...

I'd like to recommend this one
https://wolles-elektronikkiste.de/en/esp-now

Regarding "(all the & and * and _t stuff.. still throws me off)" you might buy a book on C++ ... Or (at least) have a look at this

https://www.geeksforgeeks.org/cpp-pointer-operators/

?? I did post it? the whole sketch WAS posted before you responded?

Ummm ?? yeah there are tons of ESP/ESP-NOW links.. (not understanding your point? your link looks to same as MANY others? Still saw nothing that is focused on my question here though?)
Did I miss something?

Thanks.. I'll check it out!

It was posted.

You posted the code while I was preparing mine and I didn't check what was going on in between. I even used the word "please" ...

The link I suggested goes to a page that really goes step by step and explains a lot of details. Much better than many others.

So I don't think there's any valid reason to get angry with me ...
:wink:

Angry? huh? why think that? (not angry at all)

If I get a post saying to post my code.. (but it already is).. I'm going to respond as such.
Requesting something that -is- there means it was missed.

I have seen a lot of the links.. they are all good/decent.. but I have a specific question. I dont see this being addressed/done in any of these tutorials.

This is my only questions/request for help (this topic only for now)

        for(i=0; i < pn.total_num; i++){
          //Serial.println("");        
          Serial.print("MAC ADDRESS ");
          Serial.print(i);
          Serial.print(": ");
          Serial.println(slave.peer_addr[i]);
          //Serial.println(peer.peer_addr[i]);
          //Serial.println("");
        }      
      }

Which is not outputting a MAC address, only this:

Current Peer List: 
MAC ADDRESS 0: 8 
MAC ADDRESS 1: 209

A mac address consists of 6 bytes so try this


     printMAC(slave.peer_addr);
     printMAC(peer.peer_addr);

using this function ,,,

// ---------------------------- esp_ now -------------------------
void printMAC(const uint8_t * mac_addr){
  char macStr[18];
  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);
}

However I do not think that you can go through the peer list like you tried ...

"slave.peer_addr[i]" is element i of the mac address array but not the i. element in the list ...

Edit:

I have not tried it but this seems to be the function to read from the list:

  • Start with "from_head = true;" to get the first entry
  • Check the return status
  • if ESP_OK -> Evaluate peer.peer_addr
  • Go on with "from_head = false" for the next entry until ESP_ERR_ESPNOW_NOT_FOUND

Again not tested:

      if((pn.total_num) > 0){        
        boolean from_head = true;
        int i = 1;
        while (esp_now_fetch_peer(from_head, slave) == ESP_OK){
          from_head = false;
          Serial.print("MAC ADDRESS ");
          Serial.print(i);
          Serial.print(": ");
          printMAC(slave.peer_addr);
          i++;
        }      
      }

(Sorry a curly bracket was missing after the while-condition...)

1 Like

Thanks..

I have tried this: (got error with it last night)

 printMAC(slave.peer_addr);

Thank you for clarifying.. .I was thinking same thing.. but wasnt sure how to verify? (as I dont see those values in any part of my 'receiver' CORE mac addresses?

Which technically is: 08:D1:F9:D0:5D:94 of the 'receiver' board. Actually (duuuh) looking at it again.. D1 (hex) converted to INT does in fact = 209... so it IS just the indexes of the same/single mac address. (thank you talking this out with me..been a couple days.. and things are confusing) LOL

So.. what is the proper solution/way to access this then?

I did post about the esp_now_fetch_peer() function.. but wasnt clear on how to properly use it.

I am going to try your solution now (appreciate actually reading my focus/question)..

Before I try your solution.. can I ask for clarification on the from_head = true/false? I read that it sets the pointer to the beginning? and false makes it go onto the next one? (was that a reference to the internal /idex of the same mac address? or pointing to the next (full) mac address (next devcie) in the list? I'm thinking its (now) more of the first.. and not the later?

Thanks
I'll try now.. and post back in a few.

The second argument of esp_now_fetch_peer() needs to be a pointer to a variable of type esp_now_peer_info_t. From esp_now.h:

/**
  * @brief     Fetch a peer from peer list. Only return the peer which address is unicast, for the multicast/broadcast address, the function will ignore and try to find the next in the peer list.
  *
  * @param     from_head  fetch from head of list or not
  * @param     peer  peer information
  *
  * @return
  *          - ESP_OK : succeed
  *          - ESP_ERR_ESPNOW_NOT_INIT : ESPNOW is not initialized
  *          - ESP_ERR_ESPNOW_ARG : invalid argument
  *          - ESP_ERR_ESPNOW_NOT_FOUND : peer is not found
  */
esp_err_t esp_now_fetch_peer(bool from_head, esp_now_peer_info_t *peer);
1 Like

I was just chasing that issue also :wink:

As "slave" is not a pointer it should read "address of slave":

if((pn.total_num) > 0){        
        boolean from_head = true;
        int i = 1;
        while (esp_now_fetch_peer(from_head, &slave) == ESP_OK){
          from_head = false;
          Serial.print("MAC ADDRESS ");
          Serial.print(i);
          Serial.print(": ");
          printMAC(slave.peer_addr);
          i++;
        }      
      }

Ok?

1 Like

The first call with that parameter set to 'true' copies the information struct for the first peer into the variable of type esp_now_peer_info_t that's pointed to by the pointer that you supply. Subsequent calls with the bool set to false work their way down the list.

FYI, this calling mechanisam is analogous to that used by strtok().

2 Likes

I understand the function like this:

  • from_head = true sets the index in the internal list to "top of list"
  • from_head= false: The function searches the next entry from where the internal index is at the moment
  • With each succesful return a complete esp_now_peer_info_t structure is copied to "peer" (or in the snippet of post #13 it is "slave")
typedef struct esp_now_peer_info {
    uint8_t peer_addr[ESP_NOW_ETH_ALEN];     
    uint8_t lmk[ESP_NOW_KEY_LEN];            
    uint8_t channel;                         
    wifi_interface_t ifidx;  
    bool encrypt;
    void *priv; 
} esp_now_peer_info_t;
1 Like

There is a ready to use solution on random nerd tutorials

Thank you to both:
@gfvalvo & @ec2021 for you time/effort and response! (I appreciate it) +1 for each post/reply.

The updated loop -is- in fact outputting things exactly how I wanted it to.

Thank you for educating me on the header/peer stuff.

Just so I believe I am understanding how things work..

Later on.. (lets just say button press).. I want to be able to grab a specific 'mac address' from this list.

*whether its a random one.. of the 1st or 3rd or 5th one in this list. (I want to get/return the full mac address)

current set-up (used in the add peer function/output).. but will later be used outside of this.

if((pn.total_num) > 0){        
    boolean from_head = true;
    int i = 1;
    while (esp_now_fetch_peer(from_head, &slave) == ESP_OK){
        from_head = false;
        Serial.print("MAC ADDRESS ");
        Serial.print(i);
        Serial.print(": ");
        printMAC(slave.peer_addr);
        i++;
    }      
}
  • the peer/slave list is available at any time....correct? (its just a list built during this auto-paring process and is accessible/available at any time?) - just want to make sure I'm not misleading myself here.

  • the 'while' loop is the part that is collecting 'each' (index? maybe not correct term?) of the single mac address of that current 'slave' (pointer).....correct? (the while/loop part is throwing me off a little still?) or is this just grabbing the FULL mac address initially?

So header = true is starting at top of 'full/all' mac address listings.. NOT the start/top of the 'index' in a single mac address? and then setting it to false.. meas focus on the 'next' in the full list of mac address.... (the while() loop and only seeing the (single) MAC ADDRESS: output in serial monitor is still a bit confusing)?.. I only get '2' MAC ADDRESS outputs currently (because there is only 2 receiver devices connected... so it -is- correct)

Moving forward a bit.. as I want to (ultimately) make this a standalone function I guess..

so I can just be like:


function getPeerAddress(targetDevice){
}

and pass in a 'target' index of the list... (if that makes sense?)
targetDevice param is -not- to be a specifically known mac address.. just to provide a target index for what to grab form the list of mac address.

function getPeerAddress(1){
}

should grab the first mac address in the list.

function getPeerAddress(6){
}

should grab the '6th' address in the list..etc (or maybe I just use the 'total', and set it a random # within the max/total)..

because this current testing is being done within the addPeer() function there is already reference to the 'current' slave.peer_addr being added.

If this was a stand alone function.. how would I pass/reference this slave order?

I need to pass in a specific &slave param in the function?

esp_now_fetch_peer(from_head, &slave)

Would that be like:

esp_now_fetch_peer(from_head, &slave[0])

or something?

thanks!

@StefanL38

That is the tutorials I started from.. where in that link answers the question posted here? (accessing/outputting the mac address from the peer/slave list.. in one looped output?. not the random output of an address as it was sent as a param or something. The standalone peer/slave list.. accessing it directly to get a full list output?)

I dont recall seeing anything on that aspect? (perhaps I missed something?)

This has been answered in thanks to @gfvalvo & @ec2021 already... but am curious as to where in the tutorials this answer is you saw?

As of now.. I'm taking the info given to me (still parsing it in my head!) LOL... and am going to work towards a standalone function to return any 1 specific mac address from the list.

  • In the end.. I want my main/sender board to be able to:

  • send an FF, FF, FF, FF, FF (broadcast) message to all devices.
    or

  • grab a mac address from this peer/slave list.. and send a message directly to THAT mac address.

So I'm working out my smaller steps to build toward that behavior.

Thanks everyone!

No I don't think you missed it. It simply works different. Which means your solution is smarter than the one on RNT

I just saw "auto-pairing" and remembered the RNT-website

If you managed to make it work it would be a great contribution to the community if you post the working code in the introductiony tutorial sub-forum

best regards Stefan

I don't think you yet understand what the esp_now_fetch_peer() function does. It doesn't fetch / return a MAC address. It fills in a esp_now_peer_info_t struct using the pointer that you provide.

No. You can't directly access random elements of the list. You must iterate through from the beginning. If you want the 5th list element, then you must call esp_now_fetch_peer() five times.

I haven't looked at the code in detail, but I'd guess the peer information is stored in a dynamically-allocated linked (or doubly linked) list.