Limit on slaves using ESPNow ?

I have an ESP32 master broadcasting to a set of up to 9 ESP32 slaves using ESPNow. When I run the sketch below on the master, it works great up to 4 slaves. When I try to add one additional slave, I see a single ESP_ERR_ESPNOW_NO_MEM message with the first byte sent by esp_now_send() . If I add one more slave, I start seeing a whole sequence of these error messages (with accompanying data drop-outs reported by the slaves) I've done everything I know to reduce memory footprint of the master sketch. Is there anything I can do to solve this problem? Thanks!

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

// Global copy of slave
#define NUMSLAVES 10 
esp_now_peer_info_t slaves[NUMSLAVES] = {};
int SlaveCnt = 0;

#define CHANNEL 1

bool sendCompleted = true;

// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
    Serial.println(F("ESPNow Init Success"));
  } else {
    Serial.println(F("ESPNow Init Failed"));
    ESP.restart();
  }
}

// Scan for slaves in AP mode
void ScanForSlave() {
  int8_t scanResults = WiFi.scanNetworks();
  //reset slaves
  memset(slaves, 0, sizeof(slaves));
  SlaveCnt = 0;
  Serial.println(F(""));
  if (scanResults == 0) {
    Serial.println(F("No WiFi devices in AP Mode found"));
  } else {
    Serial.print(F("Found "));
    Serial.print(scanResults);
    Serial.println(F(" devices "));
    for (uint8_t i = 0; i < scanResults; ++i) {
      // Print SSID and RSSI for each device found
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      String BSSIDstr = WiFi.BSSIDstr(i);

      delay(10);
      // Check if the current device starts with `Slave`
      if (SSID.indexOf("Slave") == 0) {
        // SSID of interest
        Serial.print(i + 1);
        Serial.print(F(": "));
        Serial.print(SSID);
        Serial.print(F(" ["));
        Serial.print(BSSIDstr);
        Serial.print(F("]"));
        Serial.print(F(" ("));
        Serial.print(RSSI);
        Serial.print(F(")"));
        Serial.println(F(""));
        // Get BSSID => Mac Address of the Slave
        int mac[6];

        if (6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5])) {
          for (int ii = 0; ii < 6; ++ii) {
            slaves[SlaveCnt].peer_addr[ii] = (uint8_t)mac[ii];
          }
        }
        SlaveCnt++;
      }
    }
  }

  if (SlaveCnt > 0) {  // check if slave channel is defined
    Serial.print(SlaveCnt);
    Serial.println(F(" Slave(s) found, processing.."));
    // `slave` is defined
    // Add slave as peer if it has not been added already
    manageSlave();
  } else {
    // No slave found to process
    Serial.println(F("No Slave Found"));
  }

  // clean up ram
  WiFi.scanDelete();
}

// Check if the slave is already paired with the master.
// If not, pair the slave with master
void manageSlave() {
  if (SlaveCnt > 0) {
    for (uint8_t i = 0; i < SlaveCnt; i++) {
      Serial.print(F("Processing: "));
      for (uint8_t ii = 0; ii < 6; ++ii) {
        Serial.print((uint8_t)slaves[i].peer_addr[ii], HEX);
        if (ii != 5) Serial.print(F(":"));
      }
      Serial.print(F(" Status: "));
      // check if the peer exists
      bool exists = esp_now_is_peer_exist(slaves[i].peer_addr);
      if (exists) {
        // Slave already paired.
        Serial.println(F("Already Paired"));
      } else {
        // Slave not paired, attempt pair
        esp_err_t addStatus = esp_now_add_peer(&slaves[i]);
        if (addStatus == ESP_OK) {
          // Pair success
          Serial.println(F("Pair success"));
        } else if (addStatus == ESP_ERR_ESPNOW_NOT_INIT) {
          // How did we get so far!!
          Serial.println(F("ESPNOW Not Init"));
        } else if (addStatus == ESP_ERR_ESPNOW_ARG) {
          Serial.println(F("Add Peer - Invalid Argument"));
        } else if (addStatus == ESP_ERR_ESPNOW_FULL) {
          Serial.println(F("Peer list full"));
        } else if (addStatus == ESP_ERR_ESPNOW_NO_MEM) {
          Serial.println(F("Out of memory"));
        } else if (addStatus == ESP_ERR_ESPNOW_EXIST) {
          Serial.println(F("Peer Exists"));
        } else {
          Serial.println(F("Not sure what happened"));
        }
        delay(100);
      }
    }
  } else {
    // No slave found to process
    Serial.println(F("No Slave found to process"));
  }
}

void sendData(byte dataByte) {
  for (uint8_t i = 0; i < SlaveCnt; i++) {
    const uint8_t *peer_addr = slaves[i].peer_addr;
    esp_err_t result = esp_now_send(peer_addr, &dataByte, 1);

    //Serial.print(F("Send Status: "));
    if (result == ESP_OK) {
      //Serial.println(F("Success"));
    } else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
      // How did we get so far!!
      Serial.println(F("ESPNOW not Init."));
    } else if (result == ESP_ERR_ESPNOW_ARG) {
      Serial.println(F("Invalid Argument"));
    } else if (result == ESP_ERR_ESPNOW_INTERNAL) {
      Serial.println(F("Internal Error"));
    } else if (result == ESP_ERR_ESPNOW_NO_MEM) {
      Serial.println(F("ESP_ERR_ESPNOW_NO_MEM"));
    } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
      Serial.println(F("Peer not found."));
    } else {
      Serial.println(F("Not sure what happened"));
    }
  }
  delay(10);  // Online documentation indicates a short delay is required to prevent ESPNOW memory errors
}

void setup() {
  delay(1000);
  Serial.begin(115200);
  delay(1000);
  Serial.println(F("MusicHub ESPNow Master"));
  Serial.flush();
  delay(1000);

  Serial2.begin(31250);  // Receive bytes from MusicHub SoftAP at MIDI rate

  //Set device in STA mode to begin with
  WiFi.mode(WIFI_STA);

  // This is the mac address of the Master in Station Mode
  Serial.print(F("STA MAC: "));
  Serial.println(WiFi.macAddress());

  // Init ESPNow with a fallback logic
  InitESPNow();

  // do an initial scan
  ScanForSlave();
}

void loop() {
  if (Serial.available())  // handle control input from Serial Monitor
  {
    byte readChar = Serial.read();
    if (Serial2.available())  // handle data input from SoftAP controller
    {
      byte readByte = Serial2.read();
      //Serial.print(F("Send: "));
      //Serial.println(readByte);
      
      sendData(readByte);
  }
}

could you use ESP-NOW multicast? e.g.

	
uint8_t broadcastAddress[] = {0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF};

That sounds like a limitation in the ESP_NOW library. Have a look at the code.

Interesting idea, horace! I'm not clear on exactly what you're suggesting though; could you give a bit more detail?

The one thing I found on the web was this: ESP-NOW with ESP32: Send Data to Multiple Boards (one-to-many) | Random Nerd Tutorials which seemed to suggest calling esp_send_now() with a first argument of 0 after all peers are registered. I tried it, but my code issued a series of "internal errors" :frowning:

The ESPNOW docs state clearly that they support 20 pairings, so I don't think that's it.

Have you found any examples of someone successfully using more than four slaves?

1 Like

multicasting transmits the same message to all receiver nodes - usually there is some indicator in the messag, e,.g. a node number, which identifies the specific target or targets

Thanks guys! Looking at the ESPNow docs gave me the clue I needed to fix this. Each send need to complete before the next begins. I'm guessing that flakely looking delay(10) which I got from code on the Web is an improper attempt to handle that.

I ended up adding this callback:

// callback when data is sent from Master to Slave
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  sendCompleted = true;
}

with this in setup() after the call to InitESPNow():

  esp_now_register_send_cb(OnDataSent);

In sendData() I added this code:

    while (!sendCompleted) { delay(1); }
    sendCompleted = false;
    esp_err_t result = esp_now_send(peer_addr, &dataByte, 1);

The caller sets sendCompleted to true before invoking sendData(). Haven't tested extensively yet, but at least I'm not getting send errors with 8 slaves, and the 3 slaves I've checked so far are all getting good data!!

Hope this workaround works in your case. The same Slave error occurs when I restart the master and after some time Slave ESP32 restarts. esp-now_send() returns: "out of memory". Theoretically, all should be restored when the master is up and running, but this is not happening. Most likely some kind of "reset" should be done from the Slave side. Any hints?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.