Skip to content

Captive portal control

The default behavior of AutoConnect is to launch the captive portal if 1st-WiFi.begin attempting inside AutoConnect::begin fails. You can change this default behavior through the AutoConnectConfig settings join together with Sketch code that implements to control the WiFi connection attempting.

Captive portal start control

AutoConnect will launch the captive portal based on the AutoConnectConfig settings when the WiFi connection status will become to certain conditions. AutoConnectConfig::autoRise and AutoConnectConfig::immediateStart are concern the conditions to launch the captive portal. Also, the AutoConnectConfig::retainPortal controls the continuity of the captive portal state and allows the Sketch to launch the captive portal dynamically even while in a handleClient loop.

The autoRise allows or disallows the launch of the captive portal. AutoConnect launches the captive portal only if the autoRise is true. If the autoRise is false, the captive portal will not start even if the WiFi connection is lost.

Basically, the captive portal initiation is triggered by the result of 1st-WiFi.begin, but Sketch can control it according to direct the following four actions by configuring AutoConnectConfig with two parameters, the immediateStart and the autoRise.

AutoConnectConfig
::immediateStart
AutoConnectConfig::autoRise
true false
true Skip *1st-WiFi.begin*
ESP module becomes SoftAP and the captive portal starts immediately.
Not attempt WiFi connection.
Only WebServer will start in STA mode.
false Attempts WiFi connection in STA mode.
In some cases, the autoReconnect may restore the connection even if 1st-WiFiBeing fails.
If the connection is completely lost, the captive portal will be launched.
This is the default.
Attempts WiFi connection in STA mode.
In some cases, the autoReconnect may restore the connection even if 1st-WiFiBeing fails.
ESP module stays in STA mode and WebServer will start.

The retainPortal specifies the continuity of the captive portal after AutoConnect::begin, allowing the captive portal would be launched after the Sketch execution has transitioned into the loop function. When AutoConnect establishes a WiFi connection while in the captive portal within AutoConnect::begin, it stops the DNS server to close the captive portal with SoftAP still running. In this situation, if the Sketch loses the established WiFi connection while executing the loop function, it can reopen the captive portal.

However, this behavior can be redundant depending on the Sketch characteristics. In such a case, you can prevent to starting the captive portal during the loop() by autoRise setting to false.

Captive portal start detection

The captive portal will only be activated if 1st-WiFi::begin fails. Sketch can detect with the AutoConnect::onDetect function that the captive portal has started. For example, the Sketch can be written like as follows that turns on the LED at the start captive portal.

AutoConnect Portal;

bool startCP(IPAddress& ip) {
  digitalWrite(BUILTIN_LED, HIGH);
  Serial.println("C.P. started, IP:" + ip.toString());
  return true;
}

void setup() {
  Serial.begin(115200);
  pinMode(BUILTIN_LED, OUTPUT);
  digitalWrite(BUILTIN_LED, LOW);
  Portal.onDetect(startCP);
  if (Portal.begin()) {
    digitalWrite(BUILTIN_LED, LOW);
  }
}

void loop() {
  Portal.handleClient();
}

Captive portal state identification

You can use the AutoConnect::isPortalAvailable function to identify if AutoConnect is in a captive portal state.

Captive-Portal - i.e., just a spoofed response to a DNS lookup for Internet connection verification that occurs on a new connection attempt from the client device; it needs a DNS server and SoftAP to work. AutoConnect implements it with HTTP redirection. The AutoConnect::isPortalAvailable function returns true if all of the following conditions are met.

  • ESP module is in WIFI_AP mode and enable SoftAP.
  • IP address assigned to SoftAP is the equivalent of AutoConnectConfig::apip.
  • AutoConnect is running a DNS server for directing to the captive portal. Through its action, DNS lookups issued by client devices for Internet transparency validation are directed to the ESP module SoftAP.

A similar utility is AutoConnect::portalStatus

See Verify the WiFi connection conditions and AutoConnect::portalStatus function.

Captive portal timeout control

Once AutoConnect has entered the captive portal state due to the above conditions, the default behavior is that AutoConnect::begin will not exit until a WiFi connection is established. Captive portal timeout control prevents AutoConnect from blocking the Sketch progress. It allows Sketch to abort AutoConnect::begin and returns control to Sketch.

AutoConnect has two parameters for timeout control. The first is the timeout value used when trying to connect to the specified AP. It works like a typical timeout control for connection attempts with WiFi.begin. This setting is specified by the AutoConnectConfig::beginTimeout or third argument of the AutoConnect::begin function. The default value is the macro defined by AUTOCONNECT_TIMEOUT in the AutoConnectDefs.h header file.

Another timeout control is for the captive portal itself. It is useful if you want to keep the Sketch offline running even if a WiFi connection is not possible. You can also combine its setting with the immediateStart option to create highly mobile sketches. The timeout of the captive portal is specified by the AutoConnectConfig::portalTimeout as follows.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <AutoConnect.h>

AutoConnect  portal;
AutoConnectConfig  config;

void setup() {
  config.portalTimeout = 60000;  // It will time out in 60 seconds
  portal.config(config);
  portal.begin();
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    // Some sketch code for the connected scene is here.
  }
  else {
    // Some sketch code for not connected scene is here.
  }
  portal.handleClient();
}

Also, if you want to stop AutoConnect completely when the captive portal is timed out, you need to call the AutoConnect::end function. It looks like the following code:

bool acEnable;

void setup() {
  config.portalTimeout = 60000;  // It will time out in 60 seconds
  portal.config(config);
  acEnable = portal.begin();
  if (!acEnable) {
    portal.end();
  }
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    // Some sketch code for the connected scene is here.
  }
  else {
    // Some sketch code for not connected scene is here.
  }
  if (acEnable) {
    portal.handleClient();
  }
}

AutoConnectConfig has another option related to timeouts that you can enable to take advantage of the captive portal feature after a timeout occurrence. The AutoConnectConfig::retainPortal option will not shut down SoftAP and the internal DNS server even though AutoConnect::begin has aborted due to a timeout occurrence. Even after the captive portal times out, you can always try to connect to the AP while keeping the Sketch running offline.

The following sample code is the Sketch above with the retainPortal setting added. As you can see, the implementation for captive portal continuation does not affect the main logic of the Sketch.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <AutoConnect.h>

AutoConnect  portal;
AutoConnectConfig  config;

void setup() {
  config.portalTimeout = 60000;  // It will time out in 60 seconds
  config.retainPortal = true;
  portal.config(config);
  portal.begin();
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    // Some sketch code for the connected scene is here.
  }
  else {
    // Some sketch code for not connected scene is here.
  }
  portal.handleClient();
}

Disable the captive portal

It can also prevent the captive portal from starting even if the connection at the 1st-WiFi.begin fails. In this case, AutoConnect::begin behaves same as WiFi.begin.

For disabling the captive portal, autoRise sets to false with AutoConnectConfig.

AutoConnect       portal;
AutoConnectConfig acConfig;

acConfig.autoRise = false;
portal.config(acConfig);
portal.begin();

Launch the captive portal on demand by external trigger

The default behavior of AutoConnect::begin gives priority to connect to the least recently established access point. In general, We expect this behavior in most situations, but will intentionally launch the captive portal on some occasion.

Here section describes how to launch on demand the captive portal, and suggests two templates that you can use to implement it.

  1. Offline for usual operation, connect to WiFi with an external switch

    You can use this template if the ESP module does not connect to WiFi at an ordinal situation and need to establish by a manual trigger. In this case, it is desirable that AutoConnect not start until an external switch fires. This behavior is similar to the WiFiManager's startConfigPortal function.
    AutoConnectConfig::immediateStart is an option to launch the portal by the SoftAP immediately without attempting 1st-WiFi.begin. Also, by setting the AutoConnectConfig::autoRise option to false, it is possible to suppress unintended automatic pop-ups of the portal screen when connecting to an ESP module SSID.
    To implement this, execute AutoConnect::config within the setup() function as usual, and handle AutoConnect::begin inside the loop() function.

    #define TRIGGER_PIN 5     // Trigger switch should be LOW active.
    #define HOLD_TIMER  3000
    
    AutoConnect       Portal;
    AutoConnectConfig Config;
    
    void setup() {
      pinMode(5, INPUT_PULLUP);
      Config.immediateStart = true;
      // Config.autoRise = false;   // If you don't need to automatically pop-up the portal when connected to the ESP module's SSID.
      Portal.config(Config);
    }
    
    void loop() {
      if (digitalRead(TRIGGER_PIN) == LOW) {
        unsigned long tm = millis();
        while (digitalRead(TRIGGER_PIN) == LOW) {
          yield();
        }
        // Hold the switch while HOLD_TIMER time to start connect.
        if (millis() - tm > HOLD_TIMER)
          Portal.begin();
      }
    
      if (WiFi.status() == WL_CONNECTED) {
        // Here, what to do if the module has connected to a WiFi access point
      }
    
      // Main process of your sketch
    
      Portal.handleClient();  // If WiFi is not connected, handleClient will do nothing.
    }
    

    It will not be automatic reconnect

    The above example does not connect to WiFi until TRIGGER_PIN goes LOW. When TRIGGER_PIN goes LOW, the captive portal starts and you can connect to WiFi. Even if you reset the module, it will not automatically reconnect.

  2. Register new access points on demand

    The following template is useful for controlling the registration of unknown access points. In this case, the ESP module establishes a WiFi connection using WiFi.begin natively without relying on AutoConnect.
    Known access point credentials are saved by AutoConnect, to the ESP module can use the saved credentials to handle WiFi.begin natively. This means that you can explicitly register available access points when needed, and the ESP module will not use unknown access points under normal situations.

    AutoConnect* portal = nullptr;
    
    bool detectSwitch() {
      /*
      Returns true if an external switch to configure is active.
      */
    }
    
    bool connectWiFi(const char* ssid, const char* password, unsigned long timeout) {
      WiFi.mode(WIFI_STA);
      delay(100);
      WiFi.begin(ssid, password);
      unsigned long tm = millis();
      while (WiFi.status() != WL_CONNECTED) {
        if (millis() - tm > timeout)
          return false;
      }
      return true;
    }
    
    void setup() {
      AutoConnectCredential credt;
      station_config_t  config;
      for (int8_t e = 0; e < credt.entries(); e++) {
        credt.load(e, &config);
        if (connectWiFi(config.ssid, config.password, 30000))
          break;
      }
      if (WiFi.status() != WL_CONNECTED) {
        // Here, do something when WiFi cannot reach.
      }
    }
    
    void loop() {
      if (detectSwitch()) {
        AutoConnectConfig config;
        config.immediateStart= true;
        if (!portal) {
          portal = new AutoConnect;
        }
        portal->config(config);
        if (portal->begin()) {
          portal->end();
          delete portal;
          portal = nullptr;
        }
      }
      // Here, ordinary sketch logic.
    }
    

Launch the captive portal on-demand at losing WiFi

If the ESP module loses the established WiFi connection during the loop of handleClient, you can prevent the ESP module from going absolutely standalone by launching the captive portal on demand.

When retainPortal and autoRise settings are enabled, AutoConnect will launch SoftAP and start DNS when it detects a WiFi disconnect with the router during a handleClient loop. This behavior will occur caused by a WiFi disconnect detection even if the WiFi mode is STA.

Since AutoConnect v1.2.0, An improved retainPortal option allows the captive portal to be restarted during a handleClient loop even if it is closed once in AutoConnect::begin. In this case, the Sketch execution stage has already transitioned into the loop function, so the Sketch process seems running concurrently with the captive portal loop. Its captive portal launched from inside handleClient does not block the execution of the Sketch, unlike that launched from AutoConnect::begin.

AutoConnect       Portal;
AutoConnectConfig Config;

void setup() {
  Config.autoRise = true; // It's the default, no setting is needed explicitly.
  Config.retainPortal = true;
  Portal.config(Config);
  Portal.begin();
}

void loop() {
  Portal.handleClient();
}

Need autoRise enabled

Need AutoConnectConfig::autoRise setting enabled to start the captive portal on demand during a handleClient loop.

Although the Sketch above specifies the retainPortal, it does not instruct starts the captive portal always. AutoConnect will try WiFi.begin once in AutoConnect::begin unless the immediateStart option is specified. If AutoConnect fails the 1st-WiFi.begin, the captive portal will be launched at that point and the Sketch execution will stay within the AutoConnect::begin function.

There is also a way to avoid starting the captive portal inside AutoConnect::begin and start the captive portal according to the WiFi connection status after the Sketch execution transitions to a handleClient loop. Adjusting the timing of autoRise activation will allow the captive portal to start only from inside AutoConnect::handleClient function.

AutoConnect       Portal;
AutoConnectConfig Config;

void setup() {
  Config.retainPortal = true;
  Config.autoRise = false;  // Suppresses the launch of the captive portal from AutoConnect::begin.
  Portal.config(Config);    // Don't forget it.
  Portal.begin();
  Config.autoRise = true;   // Enable the launch of the captive portal.
  Portal.config(Config);    // Don't forget it.
}

void loop() {
  Portal.handleClient();
}

The retainPortal option will keep SoftAP even if WiFi has established a connection as a client with the router. Since it has the effect of stopping the DNS server, the phenomenon that the portal screen will not pop up automatically even though SoftAP is in action occur.
This is a legacy behavior to ensure backward compatibility with up to AutoConnect v1.1.7. To stop SoftAP on escape from the on-demand captive portal, you need to explicitly call WiFi.softAPdisconnect(true) and WiFi.enableAP(false) in the Sketch.

AutoConnect       Portal;
AutoConnectConfig Config;

bool  Connected;
unsigned long Elapsed;

void onConnect(IPAddress& clientIP) {
  Connected = true;
  Elapsed = millis();
}

void setup() {
  Config.retainPortal = true;
  Portal.config(Config);
  Portal.onConnect(onConnect);
  Portal.begin();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    connected = false;
    Elapsed = millis();
  }

  if ((WiFi.getMode() & WIFI_AP) && Connected) {
    if (millis() - Elapsed > 30000) {
      WiFi.softAPdisconnect(true);
      WiFi.enableAP(false);
    }
  }

  // Actual sketch process is here.

  Portal.handleClient();
}

The above sketch will shutdown the SoftAP after elapsed time exceeds 30 seconds since the connection was re-established. Its logic is a bit tricky and does not stop SoftAP immediately after the connection established, which has several seconds delay. Doing it ensures that AutoConnect can send the HTML response.

Stopped SoftAP is still displayed

After SoftAP stopped, there is a time lag before it disappears from the detected access points list on the client device.

Shutdown the captive portal

There is some complexity in the conditions under which AutoConnect shuts down the captive portal. Making a sketch that activates SoftAP only when needed can seem tedious.
But there is a reason why. Even if AutoConnect could establish a connection using a captive portal, your cell phone as a client device would still have to keep connected to the ESP module-generated SoftAP in order to send the page for notifying the connection successful to a user. At that point, your client device that opened the captive portal still needs a connection with SoftAP.

What happens, after all, is as follows:

  1. You made a connection to the access point such as WiFi router using the captive portal and took a successful page.
  2. Your sketch will rush into the loop function and starts to works well, hooray!
  3. Oops. Don't celebrate yet. I can see SoftAP ID on my cell phone. But the AutoConnect page never pops up automatically. Why?

Because, for the above reasons, we can not promptly shut down the SoftAP. (However, DNS will stop)

So, If you want to stop SoftAP after connecting to the access point using the captive portal, you need to implement the shutdown process with Sketch explicitly. A template of the basic sketch that can stop the SoftAP after the connection is the following:

AutoConnect Portal;

void setup() {
  if (Portal.begin()) {
    if (WiFi.getMode() & WIFI_AP) {
      WiFi.softAPdisconnect(true);
      WiFi.enableAP(false);
    }
  }
}

void loop() {
  Portal.handleClient();
}

If you stop SoftAP, the connection will be lost

If you stop SoftAP immediately after the AutoConnect::begin successful, will part with the connection and cannot see the result notifying on your client device.
You can expect to receive result notifications if you run handleClient before stopping SoftAP. (although you may not always succeed; it will not work if the WiFi radio signal is weak)

Sketch execution during the captive portal loop

With AutoConnect::begin, once the captive portal is started without being able to connect to a known WiFi access point, control will not return to sketch until the WiFi connection is established or times out. This behavior helps to pin the ESP module's network participation as a WiFi client (that is, AutoConnect::begin is an alternative to WiFi.begin) but it can not rush into the loop function of the Sketch. Therefore, while the ESP module is in the captive portal state and waiting for a connection operation to the access point, the behavior of the Sketch will be restrained by the escape conditions from AutoConnect::begin.

The whileCaptivePortal exit allows the Sketch to continue the process temporarily while the ESP module remains standalone and the captive portal is open. AutConnect::whileCaptivePortal function registers the user's sketch function to be called by AutoConnect::begin or AutoConnect::handleClient during the execution of the captive portal session loop.

The whileCaptivePortal exit can be registered by following:

AutoConnect portal;

bool whileCP(void) {
  bool  rc;
  // Here, something to process while the captive portal is open.
  // To escape from the captive portal loop, this exit function returns false.
  // rc = true;, or rc = false;
  return rc;
}

void setup() {
  ...
  portal.whileCaptivePortal(whileCP);
  portal.begin();
  ...
}

AutoConnect will open the captive portal in the AutoConnect::begin and AutoConnect::handleClient scenes, but the whileCaptive portal exit will be called repeatedly from AutoConnect::begin until exits from it. The whileCaptivePortal exit will be called repeatedly while the captive portal is open until WiFi connected or times out occurs. In the Sketch, returning a FALSE value from the whileCaptivePortal function allows the control to escape from the captive portal loop even before the session elapsed time exceeds the limits.