MicroPython: Wi-Fi Manager with ESP32 and ESP8266

In this user guide, we will learn how to incorporate Wi-Fi manager with our ESP32/ESP8266 boards. WiFi manager let us connect ESP32 or ESP8266 to a network without having to hardcode our network credentials in a script. We will be using a previous example when we learned how to build a web server (ESP32/ESP8266 MicroPython Web Server – Control Outputs) and include the wi-fi manager in it for better functionality.

MicroPython Wi-Fi Manager with ESP32 and ESP8266

Introducing Wi-Fi Manager

With the addition of the Wi-fi manager library in MicroPython, we no longer have to separately provide the SSID and password of the local network. No hard coding would be required. We will be able to connect through other networks (Access Points) with our ESP32/ESP8266 boards automatically without any hassle of manually entering the network credentials every time. Multiple SSID connections, as well as additional custom variables, can also be implemented through this.

The Process of Integrating Wi-Fi Manager

The following flow chart taken from https://github.com/tayfunulu/WiFiManager shows the process behind the execution of wi-fi managers with Esp boards in MicroPython.

WiFi Manager micropython diagram ESP32 ESP8266
  • Initially, our Esp32 or Esp8266 will be set up as an Access Point when it starts.
  • Next, to connect to your ESP board which acts as an AP, we will go to the IP address 192.164.4.1.
  • A web page containing different network will be shown from which we will choose the one which we want to configure.
  • These network credentials of the network we chose, will get saved in our ESP32/8266.
  • Then we will set up a new SSID and password for our selected network. The ESP32/8266 will restart and now will be able to connect with the network we selected. In this phase the ESP board acts in the Station mode.
  • If the connection is not successful, the ESP board goes back to AP mode and we will set the SSID and password again.

Wi-Fi Manager Library in MicroPython

MicroPython does not contain the library for the wi-fi manager so we will need to upload it ourselves. We will learn how to do this by using two different IDE for MicroPython:

import network
import socket
import ure
import time

ap_ssid = "WifiManager"
ap_password = "tayfunulu"
ap_authmode = 3  # WPA2

NETWORK_PROFILES = 'wifi.dat'

wlan_ap = network.WLAN(network.AP_IF)
wlan_sta = network.WLAN(network.STA_IF)

server_socket = None


def get_connection():
    """return a working WLAN(STA_IF) instance or None"""

    # First check if there already is any connection:
    if wlan_sta.isconnected():
        return wlan_sta

    connected = False
    try:
        # ESP connecting to WiFi takes time, wait a bit and try again:
        time.sleep(3)
        if wlan_sta.isconnected():
            return wlan_sta

        # Read known network profiles from file
        profiles = read_profiles()

        # Search WiFis in range
        wlan_sta.active(True)
        networks = wlan_sta.scan()

        AUTHMODE = {0: "open", 1: "WEP", 2: "WPA-PSK", 3: "WPA2-PSK", 4: "WPA/WPA2-PSK"}
        for ssid, bssid, channel, rssi, authmode, hidden in sorted(networks, key=lambda x: x[3], reverse=True):
            ssid = ssid.decode('utf-8')
            encrypted = authmode > 0
            print("ssid: %s chan: %d rssi: %d authmode: %s" % (ssid, channel, rssi, AUTHMODE.get(authmode, '?')))
            if encrypted:
                if ssid in profiles:
                    password = profiles[ssid]
                    connected = do_connect(ssid, password)
                else:
                    print("skipping unknown encrypted network")
            else:  # open
                connected = do_connect(ssid, None)
            if connected:
                break

    except OSError as e:
        print("exception", str(e))

    # start web server for connection manager:
    if not connected:
        connected = start()

    return wlan_sta if connected else None


def read_profiles():
    with open(NETWORK_PROFILES) as f:
        lines = f.readlines()
    profiles = {}
    for line in lines:
        ssid, password = line.strip("\n").split(";")
        profiles[ssid] = password
    return profiles


def write_profiles(profiles):
    lines = []
    for ssid, password in profiles.items():
        lines.append("%s;%s\n" % (ssid, password))
    with open(NETWORK_PROFILES, "w") as f:
        f.write(''.join(lines))


def do_connect(ssid, password):
    wlan_sta.active(True)
    if wlan_sta.isconnected():
        return None
    print('Trying to connect to %s...' % ssid)
    wlan_sta.connect(ssid, password)
    for retry in range(100):
        connected = wlan_sta.isconnected()
        if connected:
            break
        time.sleep(0.1)
        print('.', end='')
    if connected:
        print('\nConnected. Network config: ', wlan_sta.ifconfig())
    else:
        print('\nFailed. Not Connected to: ' + ssid)
    return connected


def send_header(client, status_code=200, content_length=None ):
    client.sendall("HTTP/1.0 {} OK\r\n".format(status_code))
    client.sendall("Content-Type: text/html\r\n")
    if content_length is not None:
      client.sendall("Content-Length: {}\r\n".format(content_length))
    client.sendall("\r\n")


def send_response(client, payload, status_code=200):
    content_length = len(payload)
    send_header(client, status_code, content_length)
    if content_length > 0:
        client.sendall(payload)
    client.close()


def handle_root(client):
    wlan_sta.active(True)
    ssids = sorted(ssid.decode('utf-8') for ssid, *_ in wlan_sta.scan())
    send_header(client)
    client.sendall("""\
        <html>
            <h1 style="color: #5e9ca0; text-align: center;">
                <span style="color: #ff0000;">
                    Wi-Fi Client Setup
                </span>
            </h1>
            <form action="configure" method="post">
                <table style="margin-left: auto; margin-right: auto;">
                    <tbody>
    """)
    while len(ssids):
        ssid = ssids.pop(0)
        client.sendall("""\
                        <tr>
                            <td colspan="2">
                                <input type="radio" name="ssid" value="{0}" />{0}
                            </td>
                        </tr>
        """.format(ssid))
    client.sendall("""\
                        <tr>
                            <td>Password:</td>
                            <td><input name="password" type="password" /></td>
                        </tr>
                    </tbody>
                </table>
                <p style="text-align: center;">
                    <input type="submit" value="Submit" />
                </p>
            </form>
            <p>&nbsp;</p>
            <hr />
            <h5>
                <span style="color: #ff0000;">
                    Your ssid and password information will be saved into the
                    "%(filename)s" file in your ESP module for future usage.
                    Be careful about security!
                </span>
            </h5>
            <hr />
            <h2 style="color: #2e6c80;">
                Some useful infos:
            </h2>
            <ul>
                <li>
                    Original code from <a href="https://github.com/cpopp/MicroPythonSamples"
                        target="_blank" rel="noopener">cpopp/MicroPythonSamples</a>.
                </li>
                <li>
                    This code available at <a href="https://github.com/tayfunulu/WiFiManager"
                        target="_blank" rel="noopener">tayfunulu/WiFiManager</a>.
                </li>
            </ul>
        </html>
    """ % dict(filename=NETWORK_PROFILES))
    client.close()


def handle_configure(client, request):
    match = ure.search("ssid=([^&]*)&password=(.*)", request)

    if match is None:
        send_response(client, "Parameters not found", status_code=400)
        return False
    # version 1.9 compatibility
    try:
        ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!")
        password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!")
    except Exception:
        ssid = match.group(1).replace("%3F", "?").replace("%21", "!")
        password = match.group(2).replace("%3F", "?").replace("%21", "!")

    if len(ssid) == 0:
        send_response(client, "SSID must be provided", status_code=400)
        return False

    if do_connect(ssid, password):
        response = """\
            <html>
                <center>
                    <br><br>
                    <h1 style="color: #5e9ca0; text-align: center;">
                        <span style="color: #ff0000;">
                            ESP successfully connected to WiFi network %(ssid)s.
                        </span>
                    </h1>
                    <br><br>
                </center>
            </html>
        """ % dict(ssid=ssid)
        send_response(client, response)
        try:
            profiles = read_profiles()
        except OSError:
            profiles = {}
        profiles[ssid] = password
        write_profiles(profiles)

        time.sleep(5)

        return True
    else:
        response = """\
            <html>
                <center>
                    <h1 style="color: #5e9ca0; text-align: center;">
                        <span style="color: #ff0000;">
                            ESP could not connect to WiFi network %(ssid)s.
                        </span>
                    </h1>
                    <br><br>
                    <form>
                        <input type="button" value="Go back!" onclick="history.back()"></input>
                    </form>
                </center>
            </html>
        """ % dict(ssid=ssid)
        send_response(client, response)
        return False


def handle_not_found(client, url):
    send_response(client, "Path not found: {}".format(url), status_code=404)


def stop():
    global server_socket

    if server_socket:
        server_socket.close()
        server_socket = None


def start(port=80):
    global server_socket

    addr = socket.getaddrinfo('0.0.0.0', port)[0][-1]

    stop()

    wlan_sta.active(True)
    wlan_ap.active(True)

    wlan_ap.config(essid=ap_ssid, password=ap_password, authmode=ap_authmode)

    server_socket = socket.socket()
    server_socket.bind(addr)
    server_socket.listen(1)

    print('Connect to WiFi ssid ' + ap_ssid + ', default password: ' + ap_password)
    print('and access the ESP via your favorite web browser at 192.168.4.1.')
    print('Listening on:', addr)

    while True:
        if wlan_sta.isconnected():
            return True

        client, addr = server_socket.accept()
        print('client connected from', addr)
        try:
            client.settimeout(5.0)

            request = b""
            try:
                while "\r\n\r\n" not in request:
                    request += client.recv(512)
            except OSError:
                pass

            print("Request is: {}".format(request))
            if "HTTP" not in request:  # skip invalid requests
                continue

            # version 1.9 compatibility
            try:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
            except Exception:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
            print("URL is {}".format(url))

            if url == "":
                handle_root(client)
            elif url == "configure":
                handle_configure(client, request)
            else:
                handle_not_found(client, url)

        finally:
            client.close()

Uploading wiFi Manager Library in uPyCraft IDE

Click on the New file icon in the tools section and a file will open up.

uPyCraft IDE create new file

Write down all the code available at GitHub by clicking here. This code initializes the wi-fi manager library.

micropython wifi manager library upload upycraft ide 1

Click the Save button, and set wifimgr.py as the name of the file. You can save this file by giving in your preferred directory.

micropython wifi manager library upload upycraft ide 2

When you press the save button, the following window will appear:

micropython wifi manager library upload upycraft ide 3

To upload the wifi manager library, click the Download and Run button. You will see the wifimgr library under device option.

micropython wifi manager library upload upycraft ide 5

We have successfully installed the wifi manager library in uPyCraft IDE.

micropython wifi manager library upload upycraft ide 4

Uploading Wi-Fi Manager in Thonny IDE

Open a new file and write the library code available at GitHub which can be accessed from here.

Click on the Save button and set the file name as wifimgr.py

micropython wifi manager library upload thonny IDE 1

When you click on the save button, you will see the following window will appear. Select, MicroPython device to upload the library to ESP32 or ESP8266.

Note: The Device options in the menu bar to upload scripts are available in the previous versions of Thonny IDE.

micropython wifi manager library upload thonny IDE 2

After following these steps, we have successfully installed the Wi-Fi manager library in Thonny IDE.

Wi-Fi manager with ESP32 and ESP8266

As we have set up the wi-fi manager library in our respective IDEs, we can now see how to configure the wi-fi manager with our ESP boards. We will be using a previous example where we created a webpage that controlled the output GPIO pins of ESP32 and ESP8266. You can view that tutorial here.

The web page contains an ON and an OFF button indicating the status of an LED connected to the GPIO of the ESP boards. Instead of manually hard coding the network credentials, we will provide the SSID and password of our WiFi router to our ESP boards with the wi-fi manager instead.

MicroPython Script

We will first open a new file and save it as main.py. Save it in the same directory as wifimgr.py. Now copy the code which is given below in your main.py file.

import wifimgr     # importing the Wi-Fi manager library
from time import sleep     
import machine
import gc
try:
  import usocket as socket
except:
  import socket
led = machine.Pin(2, machine.Pin.OUT)
wlan = wifimgr.get_connection()        #initializing wlan
if wlan is None:
    print("Could not initialize the network connection.")
    while True:
        pass  
print("ESP OK")
led_state = "OFF"
def web_page():
    html = """<html>

<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css"
     integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    <style>
        html {
            font-family: Arial;
            display: inline-block;
            margin: 0px auto;
            text-align: center;
        }

        .button {
            background-color: #ce1b0e;
            border: none;
            color: white;
            padding: 16px 40px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 16px;
            margin: 4px 2px;
            cursor: pointer;
        }

        .button1 {
            background-color: #000000;
        }
    </style>
</head>

<body>
    <h2>ESP MicroPython Web Server</h2>
    <p>LED state: <strong>""" + led_state + """</strong></p>
    <p>
        <i class="fas fa-lightbulb fa-3x" style="color:#c81919;"></i>
        <a href=\"?led_2_on\"><button class="button">LED ON</button></a>
    </p>
    <p>
        <i class="far fa-lightbulb fa-3x" style="color:#000000;"></i>
        <a href=\"?led_2_off\"><button class="button button1">LED OFF</button></a>
    </p>
</body>

</html>"""
    return html


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
    try:
        if gc.mem_free() < 102000:
            gc.collect()
        conn, addr = s.accept()
        conn.settimeout(3.0)
        print('Received HTTP GET connection request from %s' % str(addr))
        request = conn.recv(1024)
        conn.settimeout(None)
        request = str(request)
        print('GET Rquest Content = %s' % request)
        led_on = request.find('/?led_2_on')
        led_off = request.find('/?led_2_off')
        if led_on == 6:
            print('LED ON -> GPIO2')
            led_state = "ON"
            led.on()
        if led_off == 6:
            print('LED OFF -> GPIO2')
            led_state = "OFF"
            led.off()
        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        conn.close()
    except OSError as e:
        conn.close()
        print('Connection closed')

How the Code works?

The web server part of this code is taken from our previously published project here (ESP32/ESP8266 MicroPython Web Server – Control Outputs). You can refer to that for a code explanation of the web server part.

We will be importing the Wi-Fi manager library in the main.py file so that we will be able to use the wi-fi manager functionality.

import wifimgr

Then, we will be handling the Wi-fi manager by initializing WLAN. This will automatically set our ESP board in the station mode as it uses the instance ‘network.WLAN(STA_IF).’ The get_connection() function waits for the user to enter the ssid and password of your network.

wlan = wifimgr.get_connection()
if wlan is None:
    print("Could not initialize the network connection.")
    while True:
        pass  

We will be using try and except statements so that we are not left with an open socket. This occurs when the ESP board boots initially. Next, we will bind the socket to an IP address and a port number. This is achieved by using the bind() method. In our case, we will pass an empty string followed by ‘80’ to the bind() function. The empty string portrays the default IP address of our Esp32/Esp8266 board and ‘80’ specifies the port number set for the Esp web servers.

try:
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  s.bind(('', 80))
  s.listen(5)

 The ESP board would reset if there is a failure and we get an OS error. This shows that there was an open socket left. With the help of the reset() function, the open socket would be deleted.

except OSError as e:
machine.reset()

Once we set the network credentials using the Wifi manager, the ESP board sets as a station instead of an access point and connects through the wi-fi manager.

MicroPython Wi-Fi Manager Demo

To see Wi-Fi manager demo, upload wifimgr.py and main.py files to ESP32 or ESP8266. After uploading press the reset button on your ESP board:

ESP32 enable reset button

After pressing the reset button, you will see this message on shell console. Thats means ESP32/ESP8266 board has setup as a access point.

micropython wi-fi manager access point IP address

Now connect to soft access point using either your desktop computer or mobile phone. Open Wi-Fi settings in your mobile and you will see WifiManager as wireless connection.

Connect to soft access point 2

Connect to WiFi manager by using a default password tayfunulu.

Connect to soft access point 1

After successfully connecting with WiFiManager, open any web browser and type the IP address 192.168.4.1. You wil see the following page og Wi-Fi manager:

WiFi manager

Select your network and enter the password in password window. After that press enter button. You will get the message that the ESP has successfully connected to a Wi-Fi network.

WiFi manager successfully connected to network

That means our ESP board has succesfully connected to WiFi network and it has setup in station mode. Now ESP32/ESP8266 will work in station mode as soon as you press the reset button. When you press the reset button, it will print the ESP web server IP address in station mode.

WiFi manager micropython shell console output

Now again connect your desktop computer or mobile phone with your local network that is router and paste the IP address in your web browser.

You will see this web page in your web browser which control GPIO2 of ESP32.

esp32 web server micropython station with wifi manager

In summary

By using the Wi-Fi manager library, we do not have to hardcode our network credentials in our MicroPython script. WiFi-Manager library sets the ESP32/ESP8266 in soft access point mode and displays available network connections on a web page. It will let you select your network and connect to it using its name and password. Once your ESP board connected to the network, it will automatically work in station mode.

If you find this tutorial useful, you can read our MicroPython web server project here:

3 thoughts on “MicroPython: Wi-Fi Manager with ESP32 and ESP8266”

  1. I’ve used a couple of different esp boards on your tutorials a lot of failures and no successes. In this particular tutorial I used a esp8266 and its worked on previous projects. But during the upload with Thonny, Arduino Miropython, uPyCraft, Just about every IDE available and I still can seem to order or make my boards work with these methods. PLEEEEEAASSSEEE contact me with any help available.

    Reply
  2. One question – how would you add a field to the initial screen and save that – structure of the returned variable? E.g. add a single input field to display and be updated above the list of available networks. The contents of this field to be saved to a separate file. The file reading/writing isn’t an issue, but how do you retrieve the value from the returned webpage form?

    Reply
  3. ANy pointers on how to get additional fields added to the initial WiFi selection screen, and transferred back to the program? Particularly the structure of the return field response/request. Thanks

    Reply

Leave a Comment