Search code examples
wifiesp32

ESP NOW failing using WIFI_AP_STA and WiFi.begin but working without the WiFi.begin


I am using code derived from Rui Santos https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/

I am using ESP NOW to send readings from an ESP32 sender to an ESP32 receiver then using the ESP32 receiver to send an html message to a ESP32 web server. Per Rui's instructions I need to start WiFi with WIFI_AP_STA to allow both wifi connection methods.

The sender and receiver code is below.

If I run the code as is, i.e. the receiver setup as WIFI_AP_STA but with the WiFi.begin line commented out, I get a sender status of: Send success, and a receive status of:Receive status. SO there is no problem sending an ESP NOW message from the sender to the receiver (also works with WIFI_STA).

If I use WIFI_AP_STA and uncomment the line in the receiver "WiFi.begin(SSIS, PASSWORD)" so that I can send a message to the ESP32 web server, I get a send status of:Send fail, and a receive status of:Receive status with failed send. The send fails but the receive is still successful. Same fail if I use WIFI_AP. It seems that in WIFI_AP_STA mode with a WiFi.begin, the receiver sends an incorrect status back to the sender.

In summary, on the receiver, using wifi mode WIFI_AP_STA without a WiFi.begin, works for sending an ESP NOW message from sender to receiver, as it should.

Using wifi mode WIFI_AP_STA and WiFi.begin on the receiver, the sender fails when sending an ESP NOW message. When I implement the web code the web html message send works. However the issue can be reproduced using the simplified code below.

Using [email protected].

I've run out of ideas, is anyone able to point me at further investigation areas?

My sender code is:

#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
// Rui Santos https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
uint8_t broadcastAddress[] = {0x24, 0x6F, 0x28, 0xAA, 0x84, 0x10};
typedef struct struct_message
{
  char ESP32NowText[33];
} struct_message;
struct_message ESP32NowMessage;
//
String text = "AAA000010000200003000040000500006";
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");
}
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
  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;
  }
}
void loop()
{
  strncpy(ESP32NowMessage.ESP32NowText, text.c_str(), text.length());
  Serial.println("Msg to send:" + String(ESP32NowMessage.ESP32NowText));
  Serial.println("Snd Len:" + String(sizeof(ESP32NowMessage)));
  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&ESP32NowMessage, sizeof(ESP32NowMessage));
  if (result == ESP_OK)
  {
    Serial.println("Sent with success");
  }
  else
  {
    Serial.println("Error sending the data");
  }
  delay(2000);
}

My receiver code is:

#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
 // Rui Santos https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
typedef struct struct_message
{
  char ESP32NowValues[33];
} struct_message;
struct_message ESP32NowMessage;
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len)
{
  memcpy(&ESP32NowMessage, incomingData, sizeof(ESP32NowMessage));
  Serial.println("Bytes received: " + String(len));
  Serial.println("Values:" + String(ESP32NowMessage.ESP32NowValues));
  Serial.println("---------------------------------------");
}
const char WiFiSSID[] = "SSID";
const char WiFiPassword[] = "PASSWORD";
//
void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_AP_STA);
  // WiFi.begin(WiFiSSID, WiFiPassword);
  // Init ESP-NOW
  if (esp_now_init() != ESP_OK)
  {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  esp_now_register_recv_cb(OnDataRecv);
}
void loop()
{
}

Solution

  • You need to send data to the receiver using the same channel it is using on the WiFi gateway. The channel is set on the receiver after running WiFi.begin(WiFiSSID, WiFiPassword);, according to the WiFi gateway configuration.

    Method 1

    You can find the channel by adding this line WiFi.printDiag(Serial); after WiFi.begin(WiFiSSID, WiFiPassword); in your receiver code. Then set the channel manually in the sender code with:

    esp_wifi_set_promiscuous(true);
    esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
    esp_wifi_set_promiscuous(false);
    

    Where channel is the integer you need to replace.

    Method 2

    You can save the WiFiSSID in the sender code and use it to find the channel without having to write it down in case it changes over time:

    cost char *WiFiSSID = "YOUR SSID";
    
    int32_t getWiFiChannel(const char *ssid) {
        if (int32_t n = WiFi.scanNetworks()) {
            for (uint8_t i=0; i<n; i++) {
                if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
                    return WiFi.channel(i);
                }
            }
        }
        return 0;
    }
    

    In your setup() function, include the following lines:

    int32_t channel = getWiFiChannel(WiFiSSID);
    esp_wifi_set_promiscuous(true);
    esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
    esp_wifi_set_promiscuous(false);
    

    Method 3 (recommended)

    You can use the callback function to offset the channel until ESP-NOW communications are confirmed:

    uint8_t channel = 1;
    
    void tryNextChannel() {
      Serial.println("Changing channel from " + String(channel) + " to " + String(channel+1));
      channel = channel % 13 + 1;
      esp_wifi_set_promiscuous(true);
      esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
      esp_wifi_set_promiscuous(false);
    }
    
    
    void onDataSent(const uint8_t* mac_addr, esp_now_send_status_t status) {
      if (!channelFound && status != ESP_NOW_SEND_SUCCESS) {
        Serial.println("Delivery Fail because channel" + String(channel) + " does not match receiver channel.");
        tryNextChannel(); // If message was not delivered, it tries on another wifi channel.
      }
      else {
        Serial.println("Delivery Successful ! Using channel : " + String(channel));
        channelFound = true;
      }
    }
    

    When initializing ESP-NOW, just add this line: esp_now_register_send_cb(onDataSent);.

    If you would like to see a basic example of ESP-NOW communication that works with a WiFi gateway, you can take a look at my Github repository: https://github.com/lukalafaye/ESP-NOW-WiFi-Gateway.