ESP8266 – Switch automatically between 2 access points


Sometimes it is necessary to switch the ESP8266’s connection between two WiFi access points. For example if you have a second AP in your house as a backup if your main AP fails.

In my case I stumbled over a connectivity problem with my ESP8266 devices. On my lot I needed to have 2 WiFi access points, because the WiFi modem/router I got from my ISP has a really lousy WiFi range (internal antenna). So I added a second WiFi access point in my backyard to be able to connect the devices located there to the same network. Some of the devices are on the range border of both AP’s and one or the other AP disappears in the ESP8266 frequently.

So I worked on a solution that allows the ESP8266 to detect the loss of WiFi connection and switch to the other AP automatically.

Luckily the ESP8266-Android framework has build in callbacks for WiFi events like ‘connected‘ ‘IP assigned‘ and ‘connection lost‘. In this small tutorial I show how to use this callbacks and how to switch between two known access points.

Development environment:

I use Visual Studio Code with PlatformIO. But if you prefer ArduinoIDE it is not difficult to transfer the code from the .cpp files into a single .ino file.


The complete code is available in my Bitbucket repository.

The tutorial code does not need any additional libraries beside of what is in the ESP8266 framework.

For easier maintenance the code is split into several header and code files

  • wifiLib.cpp and wifiLib.h contains all functions and definitions for the WiFi connection, detection of disconnection and acquiring a list of available AP’s
  • main.cpp and main.h contains the setup() and loop() functions
  • ledLib.cpp and ledLib.h contains all functions to control some optical feedback by lighting or flashing the onboard LED’s. I am using Adafruit ESP8266 HUZZAH boards, which have two onboard LED’s, a red one (I call it activity LED) and a blue one (I call it the communication LED). If you have a different board or want to use other GPIO’s to signal the status, you need to adapt these files.
Check for available networks

Very first step before connecting to the primary or secondary AP is to check if the networks are available. The results are in the global variable usePrimAP. If true, the first AP we try to connect to is the primary AP, if false the secondary AP is used. The variable canSwitchAP indicates if both AP’s were found. If false only one of the defined AP’s was found, so there is no possibility to switch.
The scan of WiFi networks is done with the WiFi library function WiFi.scanNetworks(). After we got the list of networks we check if the defined primary and secondary AP’s are found. The result is saved in the variables bool foundPrim which tells us if the defined primary AP is found and byte foundAP which tells if one or both defined AP’s were found

In the next step we check the conditions. If we didn’t find any of the defined AP’s an error is returned.

If we found only the primary AP or the secondary AP the condition is stored in the variables usePrimAP and canSwitchAP.

If we found both primary and secondary AP we check which of the AP’s has the stronger signal. The first connection will use the AP with the stronger signal.

During the WiFi scan the two LED’s blink fast, but as the whole scan takes not more than 2 seconds, this is barely visible.

How to get a notification if the WiFi connection changes

The ESP8266WiFi library provides 3 callback functions for the WiFi station mode that can be used to call your own code in case

  • the connection gets lost – WiFiEventStationModeDisconnected
  • a connection is established – WiFiEventStationModeConnected
  • an IP address was assigned – WiFiEventStationModeGotIP

The use of these callback functions is quite easy. First we need to declare them

Then we need to tell the WiFi library which functions should be assigned to the callbacks This is done in the function connectInit()

After initializing this function scans for the available AP’s and then calls the function which starts the connection.

To handle the WiFi connection/disconnection/re-connection sequences I defined 4 state which are stored in the variable connStatus. This variable is defined to start in the status CON_LOST, which here means as well that there was never a WiFi connection established

This connStatus is handled by the function checkWiFiStatus, which I will explain later. Here are the three callback functions:

lostCon() is called when WiFi got disconnected

As you can see there is not much happening here. First we check if the last status was CON_GOTIP or CON_START. That tells us that we had a WiFi connection established already or tried to connect and that we lost this connection.

If this is the case, we send a short debug message over the serial port and switch the connection status to CON_LOST, which indicates the loss of a connection. For visual confirmation of this disconnected state I switch on both LED’s permanently.

gotCon() is called when the AP was found and the connection is established, but at this point there is no IP address assigned to the ESP8266. I added this state, because sometimes it can be that you find the AP but the ESP8266 doesn’t get an IP address assigned.

Here we check if the previous status was CON_INIT, which means that we try to connect to an AP. If this was the case, the connStatus is set to CON_START, some debug information is sent over serial and to show this status visually the activity LED is switched off and only the communication LED stays on.

And last, the gotIP() function. This is called when the ESP8266 got an IP address assigned by the AP.

Again, there is not much happening inside. We check if the last status was CON_START, which shows that we are trying to connect to an AP. If this is the case, some debug messages are sent and the status is set to CON_GOTIP, which means that the connection can be used now. And to show the status, the activity LED is turned off and the communication LED is starting to blink with a 0.5s frequency to show that the ESP8266 has established a working connection.

Connection Status callbacks


We have defined the callbacks that show the different status of the connection process. Here is the code that checks the different status and reacts to them. All of this is done in the function checkWiFiStatus.

The first check is if the initiating of the connection takes too long. On my network the connection including assignment of the IP address takes usually not more than 5 seconds. So the first check is we are waiting for a connection more than 10 seconds. If this is the case we initiate the connection to the alternative WiFi AP if possible or retry on the same AP.

Next check is if we lost the connection. If this is the case we try to connect to the alternative AP if possible or retry on the same AP

Two more functions left for the WiFi stuff.

The first one is connectWiFi() which will start the connection to one of the two AP’s.

First WiFi.disconnect() is called. This looks awkward, but is necessary if an unsuccessful connection attempt is still ongoing. Then the WiFi is put into STA mode which means we attempt to connect to an AP. We disable automatic reconnecting here, as this code is handling the re-connection by itself.

The next part set the status to CON_INIT to indicate that we started a connection attempt. Then it checks to which of the defined AP’s the connection should be established and then starts the connection attempt with WiFi.begin().

The last function is initOTA() which prepares the device for over-the-air updates. This is quite standard stuff. There are only 2 parts I want to highlight here.

The first is the creation of a unique mDNS name for the OTA update. I use a part of the devices MAC address to create a unique name for the device.

The second one is that I add the board name to the mDNS service text to make it easier to see what type of device it is

You can either check the complete code here or just jump to the next page to the setup() and loop() functions of the tutorial.

And then there is one file I am not sharing on the Bitbucket repository because in this file I define the two AP’s and their passwords. As the file contains my network credentials, I do not share it with you.

You have to create this file or make the definitions inside the wifiLib.h header file. The structure of the definitions is like



setup() and loop() functions

The setup() and loop() functions are quite simple.

In setup() we just initialize the LED’s (see next page for more info), the serial connection and then connectInit() is called. As explained before, connectInit() checks the available AP’s and initiates a connection.

For the loop() function one additional flag is needed: bool otaInitDone. This flag shows if the OTA (over the air update) functionality is already initialized. This can be done only after the WiFi connection was established.

Inside loop() we check the flag otaInitDone and call initOTA() if required. Next we check if an OTA update request was received. And last we call checkWiFiStatus() to see if a WiFi connection is available or if the connection was interrupted.

Here is an example log of the ESP8266 switching between the two AP’s

LED control

To control the LED’s I wrote a kind of small library with standard functions to let the LED’s flash in different frequencies. In this tutorial I do not use all of the functions, but I thought it might be interesting for you.

To flash the LED’s I use three different Tickers. Ticker is a standard library on the ESP8266 that makes it easy to have specific functions repeated frequently or just once after a defined time (But that’s part of another tutorial).

First we define the three Ticker and the default GPIO’s for the blue and red LED of my Adafruit ESP8266 HUZZAH boards.

The function initLeds() can be called either without parameters, then the default GPIO’s are used or with the GPIO numbers for the two LED’s in case they are not on the Adafruit HUZZAH standard. This achieved by declaring the function as

initLeds() then intializes the GPIO’s as output and switches off both LED’s.

The next three functions starts flashing of either one LED or both LED’s with opposite on/off status. The functions are called with a parameter that defines the on and off time in seconds. For example to flash the red LED with 1Hz frequency the call would be actLedFlashStart(0.5). Inside the functions the Ticker is activated by attaching a function to the Ticker and defining the time it should be called.

The functions attached to the Ticker only toggles the LED’s from on to off and back 

And last not least there are functions to stop the flashing. These functions switch off the LED and detach the toggle function from the Ticker

That’s all folks. You can find the whole source code on my Bitbucket repository.




  • Klaus

    Nice code, it really works perfectly after a small correction in the scanNetworks section, line 15 and 20.
    It should read:

    if (strcmp((const char*) &ssid[0], ssidPrim) == 0)
    if (strcmp((const char*) &ssid[0], ssidSec) == 0)

    The strcmp() funtion returns zero when the strings match.
    Without the correction, the function finds all networks except the own ones, which results in a random connection to either of the two defined networks.

    • beegee1962

      Sorry for the late reply.
      Thanks for pointing to this mistake. I corrected it.


Leave a Reply to beegee1962 Cancel reply

Your email address will not be published. Required fields are marked *

Free Link Directory