Web server on both interfaces in WIFI_AP_STA mode?

I'm on an ESP32-DevKitC-VIE kit (WROVER-IE module). I configure the wifi as WIFI_AP_STA and spin up a webserver on port 80.

It connects to the local network fine and serves webpages over that, but when I connect to the device AP directly, the webserver doesn't respond (which is reasonable of course, as no other configuration has been done.)

I've seen a few threads where people are struggling with this (many on ESP8266) but I can't find any answers for my device/code. Looking at the APIs there doesn't seem to be any provision for specifying the interface to use.

Any ideas? Ideally I would have one webserver respond to both interfaces, but if I have to spin up a separate webserver I think that would be acceptable as well.

Should I be using a different library to achieve this?

Current code (summary):


#include <Arduino.h>
#include <WiFi.h>
#include "esp_wifi.h"

const char* wifi_network_ssid     = "localssid";
const char* wifi_network_password = "password";
const char *soft_ap_ssid          = "ap_netname";
const char *soft_ap_password      = NULL; // NULL for no password
const int   channel        = 10;    // WiFi Channel number between 1 and 13
const bool  hide_SSID      = false; // To disable SSID broadcast -> SSID will not appear in a basic WiFi scan
const int   max_connection = 2;     // Maximum simultaneous connected clients on the AP

WiFiServer espServer(80);

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

    WiFi.mode(WIFI_AP_STA);

    Serial.println("\n[*] Creating ESP32 AP");
    WiFi.softAP(soft_ap_ssid, soft_ap_password, channel, hide_SSID, max_connection);
    Serial.print("[+] AP Created with IP Gateway ");
    Serial.println(WiFi.softAPIP());

    IPAddress ip(192,168,3,23);   
    IPAddress gateway(192,168,3,1);   
    IPAddress subnet(255,255,255,0);   
    WiFi.config(ip, gateway, subnet);

    WiFi.begin(wifi_network_ssid, wifi_network_password);
    Serial.println("\n[*] Connecting to WiFi Network");

    while(WiFi.status() != WL_CONNECTED)
    {
        Serial.print(".");
        delay(100);
    }

    Serial.print("\n[+] Connected to the WiFi network with local IP : ");
    Serial.println(WiFi.localIP());

    Serial.println("Starting webserver");
    espServer.begin();
    Serial.println("Started webserver");
}

void loop()
{
  WiFiClient client = espServer.available();
  if (client) handleWebClient(client);
}

WebServer(IPAddress addr, int port = 80);

the IP address defines the network interface to use

Thanks Juraj! -- was just coming back to post that I found that, but it didn't seem to work? If I do:

espServer = WiFiServer(IPAddress(192,168,3,23), 80);
espServer.begin();

...it works (this is the STA IP). But if instead I do:

espServer = WiFiServer(IPAddress(192,168,4,1), 80);
espServer.begin();

...and then I connect to the AP and try http://192.168.4.1, it does not work.

Should I be using WebServer instead of WiFiServer? What is the difference? I found a little bit online but it didn't quite explain the pros/cons... EDIT: I see, WiFiServer doesn't speak http, WebServer does. But does it matter which I use in terms of using the AP vs STA?

WebServer has WiFiServer inside and passes the IP address to it.

Thanks again -- based on your advice and this example code, I tried to get this working, but I'm still having trouble.

Below is my current code -- do you see anything obvious that I am doing wrong?

I can connect on the STA interface via http://esp32.local and http://esp32.local:8080 and I get the expected responses ("dual" vs "STA" server responses.) But when I connect to the soft AP, I get nothing on either http://esp32.local or http://esp32.local:8080... or if I use the IP (e.g. http://192.168.4.1:8080).

Terminal output is as expected:

[+] Static IP forced: 192.168.3.23

[*] Connecting to WiFi Network
.......
[+] Connected to the WiFi network with local IP : 192.168.3.23

[*] Creating ESP32 AP
[+] AP Created with IP Gateway 192.168.4.1
Soft AP SSID: "esp32", IP address: 192.168.4.1
MDNS started.
Starting webserver
Started webservers
#include <Arduino.h>
#include <WiFi.h>
#include "WebServer.h"
#include <ESPmDNS.h>

const char* wifi_network_ssid     = "mynetwork";
const char* wifi_network_password = "mypassword";
const char *soft_ap_ssid          = "apnetname";
const char *soft_ap_password      = NULL; // NULL for no password
const int   channel        = 10;    // WiFi Channel number between 1 and 13
const bool  hide_SSID      = false; // To disable SSID broadcast -> SSID will not appear in a basic WiFi scan
const int   max_connection = 2;     // Maximum simultaneous connected clients on the AP

WebServer *webserver, *APwebserver, *STAwebserver;

void handleWebRequest()
{
  webserver->send(200, "text/plain", "this is the dual webserver");
}

void handleAPWebRequest()
{
  APwebserver->send(200, "text/plain", "this is the AP webserver");
}

void handleSTAWebRequest()
{
  STAwebserver->send(200, "text/plain", "this is the STA webserver");
}

void handleNotFound(WebServer *server)
{
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server->uri();
  message += "\nMethod: ";
  message += (server->method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server->args();
  message += "\n";
  for (uint8_t i = 0; i < server->args(); i++) {
    message += " " + server->argName(i) + ": " + server->arg(i) + "\n";
  }
  server->send(404, "text/plain", message);
}

void handleDualNotFound()
{
  handleNotFound(webserver);
}
void handleNotFoundAP()
{
  handleNotFound(APwebserver);
}
void handleNotFoundSTA()
{
  handleNotFound(STAwebserver);
}

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

    WiFi.mode(WIFI_STA);

    IPAddress ip(192,168,3,23);
    IPAddress gateway(192,168,3,1);
    IPAddress subnet(255,255,255,0);
    WiFi.config(ip, gateway, subnet);

    Serial.print("\n[+] Static IP forced: ");
    Serial.println(WiFi.localIP());

    WiFi.begin(wifi_network_ssid, wifi_network_password);
    Serial.println("\n[*] Connecting to WiFi Network");

    while(WiFi.status() != WL_CONNECTED)
    {
        Serial.print(".");
        delay(100);
    }

    Serial.print("\n[+] Connected to the WiFi network with local IP : ");
    Serial.println(WiFi.localIP());

    Serial.println("\n[*] Creating ESP32 AP");
    if (!WiFi.softAP(soft_ap_ssid, soft_ap_password, channel, hide_SSID, max_connection))
    {
        Serial.println("failed to start softAP");
    }
    Serial.print("[+] AP Created with IP Gateway ");
    Serial.println(WiFi.softAPIP());
    Serial.print("Soft AP SSID: \"");
    Serial.print(soft_ap_ssid);
    Serial.print("\", IP address: ");
    Serial.println(WiFi.softAPIP());

    if (MDNS.begin("esp32")) Serial.println("MDNS started.");
    else Serial.println("Error! MDNS not started.");

    Serial.println("Starting webserver");
    webserver = new WebServer(80); // responds to either AP or STA connections
    APwebserver = new WebServer(WiFi.softAPIP(),8080);
    STAwebserver = new WebServer(WiFi.localIP(), 8080);
    webserver->on("/", handleWebRequest);
    APwebserver->on("/", handleAPWebRequest);
    STAwebserver->on("/", handleSTAWebRequest);
    webserver->onNotFound(handleDualNotFound);
    APwebserver->onNotFound(handleNotFoundAP);
    STAwebserver->onNotFound(handleNotFoundSTA);
    webserver->begin();
    APwebserver->begin();
    STAwebserver->begin();
    Serial.println("Started webservers");  
}

void loop()
{
  webserver->handleClient();
  APwebserver->handleClient();
  STAwebserver->handleClient();
}

I world not test all with one sketch. test the AP version as the only webserver in the sketch.

MDNS has its own problems with STA/AP. so remove it from the sketch and test only with the IP address.

Argh, got it figured out -- when the phone connects to the IP it pops up saying "mynetname has no internet access -- tap for options" which I was just dismissing because, yeah, that's as expected, no problem. But if I tap that, it says "no internet access -- do you want to stay connected?" Mind you, it stays connected to the AP whether you dismiss this or click "yes", but if you don't click yes on this pop-up, it silently fails to work. Tap "yes" and everything is fine. Thanks for the help!