Page 1 of 1

Problems with SoftAP mode versus Station mode with ESP-NOW

Posted: Fri Aug 09, 2024 2:04 pm
by jigglebit
I've got a project where I've got a setup of 4 ESP32 boards in a room. 1 is acting like a master giving commands to the other 3 acting as slaves using ESP-NOW to communicate. The master also is setup as a soft wifi access point with a web-server to allow external devices (like a phone or a tablet) to connect to the ESP network and use the web-server to trigger commands for the master to send to the 3 slaves.

I had this working in Station mode perfectly, but I when I turn everything off and on again, sometimes the wifi/ESP-NOW channel used by the master and slaves wasn't the same, and communication wouldn't work. To solve that problem, I switched to the soft wifi access point, but when I did that I started getting interface errors in the form of "ESP_ERR_ESPNOW_IF," even though I can confirm the wifi and ESP-NOW channels are exactly the same for the master and all the slaves. Is there some trick to setting up wifi, a web-server, and ESP-NOW on the ESP32 acting as the master in SoftAP mode that keeps everything working? Here's the master code I'm using (please excuse all the debugging statements):

Code: Select all

#include <WiFi.h>
#include <esp_now.h>
#include <WebServer.h>
#include <esp_wifi.h> // Include the ESP-IDF Wi-Fi header

// SoftAP credentials
const char* ssid = "MYSSID";
const char* password = "Password1234";
const int wifi_channel = 6; // Fixed Wi-Fi channel

// Create a web server object
WebServer server(80);

// ESP-NOW peer information
uint8_t slaveAddress1[] = {0x78, 0x21, 0x84, 0x8c, 0xCC, 0xE4};
uint8_t slaveAddress2[] = {0x78, 0x21, 0x84, 0x88, 0x58, 0xC8};
uint8_t slaveAddress3[] = {0x78, 0x21, 0x84, 0x8e, 0x52, 0xD0};

typedef struct struct_message {
    char a[32];
} struct_message;

struct_message myData;

// Function to handle incoming data
void onDataReceive(const esp_now_recv_info* info, const uint8_t *data, int len) {
    memcpy(&myData, data, sizeof(myData));
    Serial.print("Message received: ");
    Serial.println(myData.a);
}

// Function to handle the root path and serve the web form
void handleRoot() {
    String html = "<html><body>"
                  "<h1>ESP32 Web Form</h1>"
                  "<form action=\"/submit\" method=\"post\">"
                  "<button name=\"button\" value=\"A\" type=\"submit\">Button A</button>"
                  "<button name=\"button\" value=\"B\" type=\"submit\">Button B</button>"
                  "</form></body></html>";
    server.send(200, "text/html", html);
}

// Function to handle form submission and send ESP-NOW messages
void handleSubmit() {
    if (server.hasArg("button")) {
        String button = server.arg("button");
        if (button == "A") {
            strcpy(myData.a, "Signal X");
            Serial.println("Button A pressed, sending Signal X");
        } else if (button == "B") {
            strcpy(myData.a, "Signal Y");
            Serial.println("Button B pressed, sending Signal Y");
        }

        // Send to all three slaves and print detailed error codes
        esp_err_t result;
        
        result = esp_now_send(slaveAddress1, (uint8_t *) &myData, sizeof(myData));
        if (result == ESP_OK) {
            Serial.println("Message sent successfully to Slave 1");
        } else {
            Serial.print("Error sending message to Slave 1: ");
            printEspNowError(result);
        }

        result = esp_now_send(slaveAddress2, (uint8_t *) &myData, sizeof(myData));
        if (result == ESP_OK) {
            Serial.println("Message sent successfully to Slave 2");
        } else {
            Serial.print("Error sending message to Slave 2: ");
            printEspNowError(result);
        }

        result = esp_now_send(slaveAddress3, (uint8_t *) &myData, sizeof(myData));
        if (result == ESP_OK) {
            Serial.println("Message sent successfully to Slave 3");
        } else {
            Serial.print("Error sending message to Slave 3: ");
            printEspNowError(result);
        }

        server.send(200, "text/html", "Message sent!");
    } else {
        server.send(400, "text/html", "Invalid request");
    }
}

// Function to interpret ESP-NOW errors
void printEspNowError(esp_err_t err) {
    switch (err) {
        case ESP_OK:
            Serial.println("ESP_OK: Success");
            break;
        case ESP_ERR_ESPNOW_NOT_INIT:
            Serial.println("ESP_ERR_ESPNOW_NOT_INIT: ESP-NOW is not initialized");
            break;
        case ESP_ERR_ESPNOW_ARG:
            Serial.println("ESP_ERR_ESPNOW_ARG: Invalid argument");
            break;
        case ESP_ERR_ESPNOW_INTERNAL:
            Serial.println("ESP_ERR_ESPNOW_INTERNAL: Internal error");
            break;
        case ESP_ERR_ESPNOW_NO_MEM:
            Serial.println("ESP_ERR_ESPNOW_NO_MEM: Out of memory");
            break;
        case ESP_ERR_ESPNOW_NOT_FOUND:
            Serial.println("ESP_ERR_ESPNOW_NOT_FOUND: Peer not found");
            break;
        case ESP_ERR_ESPNOW_IF:
            Serial.println("ESP_ERR_ESPNOW_IF: Interface error");
            break;
        default:
            Serial.print("Unknown error: ");
            Serial.println(err);
            break;
    }
}

// Initialize ESP-NOW
void initESPNow() {
    if (esp_now_init() != ESP_OK) {
        Serial.println("Error initializing ESP-NOW");
        return;
    }
    esp_now_register_recv_cb(onDataReceive);
    Serial.println("ESP-NOW initialized");

    // Add peers
    esp_now_peer_info_t peerInfo;
    memset(&peerInfo, 0, sizeof(peerInfo));

    memcpy(peerInfo.peer_addr, slaveAddress1, 6);
    peerInfo.channel = wifi_channel;  
    peerInfo.encrypt = false;
    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("Failed to add peer 1");
        return;
    }
    Serial.println("Added peer 1");

    memcpy(peerInfo.peer_addr, slaveAddress2, 6);
    peerInfo.channel = wifi_channel;  
    peerInfo.encrypt = false;
    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("Failed to add peer 2");
        return;
    }
    Serial.println("Added peer 2");
    
    memcpy(peerInfo.peer_addr, slaveAddress3, 6);
    peerInfo.channel = wifi_channel;  
    peerInfo.encrypt = false;
    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("Failed to add peer 3");
        return;
    }
    Serial.println("Added peer 3");
}

void setup() {
    Serial.begin(115200);

    // Initialize Wi-Fi as SoftAP
    WiFi.softAP(ssid, password, wifi_channel);
    Serial.print("AP started, IP address: ");
    Serial.println(WiFi.softAPIP());

    // Ensure the channel is set correctly
    esp_wifi_set_promiscuous(true);
    esp_wifi_set_channel(wifi_channel, WIFI_SECOND_CHAN_NONE);
    esp_wifi_set_promiscuous(false);

    // Initialize the web server
    server.on("/", handleRoot);
    server.on("/submit", HTTP_POST, handleSubmit);
    server.begin();
    Serial.println("Web server started");

    // Initialize ESP-NOW after SoftAP and web server are set up
    initESPNow();

    Serial.print("ESP-NOW and AP initialized on channel: ");
    Serial.println(wifi_channel);
}

void loop() {
    // Handle web server
    server.handleClient();
}
And here's the slave code I'm using:

Code: Select all

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

// Master device's MAC address
uint8_t masterAddress[] = {0x78, 0x21, 0x84, 0x8D, 0x02, 0x40};
int wifi_channel = 6; // Set to match master's Wi-Fi channel

// Setting pins and colors for NeoPixels
const int INDICATOR_LED = 21;
Adafruit_NeoPixel indicatorLed = Adafruit_NeoPixel(1, INDICATOR_LED, NEO_GRB + NEO_KHZ800);
uint32_t orange = indicatorLed.Color(255,140,0);
uint32_t blue = indicatorLed.Color(0,0,255);
uint32_t off = indicatorLed.Color(0,0,0);

typedef struct struct_message {
    char a[32];
} struct_message;

struct_message incomingData;
struct_message ackData;

void allLedsOff() {
    indicatorLed.setPixelColor(0, off);
    indicatorLed.show();
}

void onDataReceive(const esp_now_recv_info* info, const uint8_t *data, int len) {
    memcpy(&incomingData, data, sizeof(incomingData));
    Serial.print("Message received: ");
    Serial.println(incomingData.a);
    
    // Correct string comparison
    if (strcmp(incomingData.a, "Signal X") == 0) {
        indicatorLed.setPixelColor(0, orange);
        indicatorLed.show();
        delay(5000);
        allLedsOff();
    } else if (strcmp(incomingData.a, "Signal Y") == 0) {
        indicatorLed.setPixelColor(0, blue);
        indicatorLed.show();
        delay(5000);
        allLedsOff();
    }

    // Prepare acknowledgment data
    strcpy(ackData.a, "ACK");

    // Send acknowledgment back to master
    esp_err_t result = esp_now_send(masterAddress, (uint8_t *) &ackData, sizeof(ackData));
    if (result == ESP_OK) {
        Serial.println("Acknowledgment sent successfully");
    } else {
        Serial.println("Error sending acknowledgment");
    }
}

void setup() {
    Serial.begin(115200);
    Serial.println("Starting...");

    indicatorLed.begin();
    indicatorLed.show();
    allLedsOff();

    // Initialize Wi-Fi
    WiFi.mode(WIFI_STA);
    esp_wifi_set_promiscuous(true); // Enable promiscuous mode for Wi-Fi
    esp_wifi_set_channel(wifi_channel, WIFI_SECOND_CHAN_NONE); // Set channel
    esp_wifi_set_promiscuous(false); // Disable promiscuous mode

    Serial.print("WiFi initialized on channel: ");
    Serial.println(wifi_channel);

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

    // Register callback function for when data is received
    esp_now_register_recv_cb(onDataReceive);
    Serial.println("Receive callback registered");

    // Add master as a peer
    esp_now_peer_info_t peerInfo;
    memset(&peerInfo, 0, sizeof(peerInfo));
    memcpy(peerInfo.peer_addr, masterAddress, 6);
    peerInfo.channel = wifi_channel;  
    peerInfo.encrypt = false;
    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("Failed to add peer");
        return;
    }
    Serial.print("Master peer added on channel: ");
    Serial.println(peerInfo.channel);
}

void loop() {
    // Nothing to do here, all work is done in the callback
}