ESP8266 NodeMCU: Create a Wi-Fi Manager (AsyncWebServer library)

In this guide, you’ll create and set up a Wi-Fi Manager with the ESPAsyncWebServer library that you can modify to use with your web server projects or with any project that needs a connection to a Wi-Fi network. The Wi-Fi Manager allows you to connect the ESP8266 board to different Access Points (networks) without hard-coding network credentials (SSID and password) and upload new code to your board. Your ESP will automatically join the last saved network or set up an Access Point that you can use to configure the network credentials.

ESP8266 NodeMCU Create a Wi-Fi Manager AsyncWebServer library

To better understand how this project works, we recommend taking a look at the following tutorials:

How it Works

Take a look at the following diagram to understand how the Wi-Fi Manager we’ll create works.

ESP32 ESP8266 Wi-Fi Manager Web Server How it Works
  • When the ESP first starts, it tries to read the ssid.txt, pass.txt and ip.txt files* (1);
  • If the files are empty (2) (the first time you run the board, the files are empty), your board is set as an access point (3);
  • Using any Wi-Fi enabled device with a browser, you can connect to the newly created Access Point (default name ESP-WIFI-MANAGER);
  • After establishing a connection with the ESP-WIFI-MANAGER, you can go to the default IP address 192.168.4.1 to open a web page that allows you to configure your SSID and password (4);
  • The SSID, password, and IP address inserted in the form are saved in the corresponding files: ssid.txt, pass.txt, and ip.txt (5);
  • After that, the ESP board restarts (6);
  • This time, after restarting, the files are not empty, so the ESP will try to connect to the network in station mode using the settings you’ve inserted in the form (7);
  • If it establishes a connection, the process is completed successfully, and you can access the main web server page that can do whatever you want (control sensor readings, control outputs, display some text, etc.) (9). Otherwise, it will set the Access Point (3), and you can access the default IP address (192.168.4.1) to add another SSID/password combination.

* we also created a gateway field and a gateway.txt file to save the IP address gateway (this is not shown in the diagram).

To show you how to set the Wi-Fi Manager, we’ll set up a web server that controls one output (GPIO2—the built-in LED). You can apply the Wi-Fi Manager to any web server project built with the ESPAsyncWebServer library or to any project that requires the ESP to be connected to a wi-fi network.

Prerequisites

We’ll program the ESP8266 board using Arduino IDE. So make sure you have the ESP8266 board add-on installed.

If you want to program the ESP8266 using VS Code + PlatformIO, follow the next tutorial:

Installing Libraries (Arduino IDE)

You need to install the following libraries in your Arduino IDE to build the web server for this project.

The ESPAsyncWebServer, and ESPAsyncTCP libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.

Installing Libraries (VS Code + PlatformIO)

If you’re programming the ESP8266 using PlatformIO, copy the following to the platformio.ini to include the ESPAsyncWebServer library (it will automatically include any dependencies like the ESPAsyncTCP libraries), change the baud rate to 115200 and set the filesystem to LittleFS:

monitor_speed = 115200
lib_deps = ESP Async WebServer
board_build.filesystem = littlefs

Filesystem Uploader

Before proceeding, you need to have the ESP8266 Uploader Plugin installed in your Arduino IDE.

If you’re using VS Code with PlatformIO, follow the next tutorial to learn how to upload files to the filesystem:

Organizing your Files

To keep the project organized and make it easier to understand, we’ll create four different files to build the web server:

  • Arduino sketch that handles the web server;
  • index.html: to define the content of the web page in station mode to control the output (or any other web page you want to build);
  • style.css: to style the web pages;
  • wifimanager.html: to define the web page’s content to display the Wi-Fi Manager when the ESP is in access point mode.
ESP32 ESP8266 Wi-Fi Manager Project Files

You should save the HTML and CSS files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP8266 filesystem (LittleFS).

You can download all project files:

Creating the HTML Files

For this project, you need two HTML files. One to build the main page that controls the output (index.html) and another to build the Wi-Fi Manager page (wifimanager.html).

index.html

Here’s the text you should copy to your index.html file.

<!DOCTYPE html>
<html>
  <head>
    <title>ESP WEB SERVER</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="style.css">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  </head>
  <body>
    <div class="topnav">
      <h1>ESP WEB SERVER</h1>
    </div>
    <div class="content">
      <div class="card-grid">
        <div class="card">
          <p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 2</p>
          <p>
            <a href="on"><button class="button-on">ON</button></a>
            <a href="off"><button class="button-off">OFF</button></a>
          </p>
          <p class="state">State: %STATE%</p>
        </div>
      </div>
    </div>
  </body>
</html>

View raw code

We won’t explain how this HTML file works because that’s not the purpose of this tutorial. The purpose of this tutorial is to explain the parts related to the Wi-Fi Manager.

wifimanager.html

The Wi-Fi Manager web page looks like this:

ESP32 ESP8266 Wi-Fi Manager Fields

Copy the following to the wifimanager.html file. This creates a web page with a form with four input fields and a Submit button.

<!DOCTYPE html>
<html>
<head>
  <title>ESP Wi-Fi Manager</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div class="topnav">
    <h1>ESP Wi-Fi Manager</h1>
  </div>
  <div class="content">
    <div class="card-grid">
      <div class="card">
        <form action="/" method="POST">
          <p>
            <label for="ssid">SSID</label>
            <input type="text" id ="ssid" name="ssid"><br>
            <label for="pass">Password</label>
            <input type="text" id ="pass" name="pass"><br>
            <label for="ip">IP Address</label>
            <input type="text" id ="ip" name="ip" value="192.168.1.200"><br>
            <label for="gateway">Gateway Address</label>
            <input type="text" id ="gateway" name="gateway" value="192.168.1.1"><br>
            <input type ="submit" value ="Submit">
          </p>
        </form>
      </div>
    </div>
  </div>
</body>
</html>

View raw code

In this HTML file, we create an HTML form that will make an HTTP POST request with the data submitted to the server.

<form action="/" method="POST">

The form contains four input fields and corresponding labels: SSID, password, IP address, and gateway.

This is the input field for the SSID:

<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>

This is the input field for the password.

<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>

There is an input field for the IP address that you want to attribute to the ESP in station mode. As default, we set it to 192.168.1.200 (you can set another default IP address, or you can delete the value parameter—it won’t have a default value).

<input type="text" id ="ip" name="ip" value="192.168.1.200">

Finally, there’s an input field for the gateway address. If the default IP address is 192.168.1.200, the gateway can be 192.168.1.1 by default.

<input type="text" id ="gateway" name="gateway" value="192.168.1.1"><br>

CSS File

Copy the following styles to your style.css file. We won’t explain how these styles work. We have already explained how similar styles work in other ESP Web Server projects.

html {
  font-family: Arial, Helvetica, sans-serif; 
  display: inline-block; 
  text-align: center;
}

h1 {
  font-size: 1.8rem; 
  color: white;
}

p { 
  font-size: 1.4rem;
}

.topnav { 
  overflow: hidden; 
  background-color: #0A1128;
}

body {  
  margin: 0;
}

.content { 
  padding: 5%;
}

.card-grid { 
  max-width: 800px; 
  margin: 0 auto; 
  display: grid; 
  grid-gap: 2rem; 
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.card { 
  background-color: white; 
  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}

.card-title { 
  font-size: 1.2rem;
  font-weight: bold;
  color: #034078
}

input[type=submit] {
  border: none;
  color: #FEFCFB;
  background-color: #034078;
  padding: 15px 15px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  width: 100px;
  margin-right: 10px;
  border-radius: 4px;
  transition-duration: 0.4s;
  }

input[type=submit]:hover {
  background-color: #1282A2;
}

input[type=text], input[type=number], select {
  width: 50%;
  padding: 12px 20px;
  margin: 18px;
  display: inline-block;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

label {
  font-size: 1.2rem; 
}
.value{
  font-size: 1.2rem;
  color: #1282A2;  
}
.state {
  font-size: 1.2rem;
  color: #1282A2;
}
button {
  border: none;
  color: #FEFCFB;
  padding: 15px 32px;
  text-align: center;
  font-size: 16px;
  width: 100px;
  border-radius: 4px;
  transition-duration: 0.4s;
}
.button-on {
  background-color: #034078;
}
.button-on:hover {
  background-color: #1282A2;
}
.button-off {
  background-color: #858585;
}
.button-off:hover {
  background-color: #252524;
} 

View raw code

Setting Up the Web Server

If you’re using VS Code with the platformIO extension, you need to edit the platformio.ini file to look as shown below. If you’re using Arduino IDE, you can ignore this.

platformio.ini ESP8266:

[env:esp12e]
platform = espressif8266
board = esp12e
framework = arduino
monitor_speed = 115200
lib_deps = ESP Async WebServer
board_build.filesystem = littlefs

Code

Copy the following code to your Arduino IDE or to the main.cpp file if you’re using VS Code.

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp8266-nodemcu-wi-fi-manager-asyncwebserver/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncTCP.h>
#include "LittleFS.h"

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
const char* PARAM_INPUT_4 = "gateway";

//Variables to save values from HTML form
String ssid;
String pass;
String ip;
String gateway;

// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
const char* gatewayPath = "/gateway.txt";

IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded

// Set your Gateway IP address
IPAddress localGateway;
//IPAddress localGateway(192, 168, 1, 1); //hardcoded
IPAddress subnet(255, 255, 0, 0);

// Timer variables
unsigned long previousMillis = 0;
const long interval = 10000;  // interval to wait for Wi-Fi connection (milliseconds)

// Set LED GPIO
const int ledPin = 2;
// Stores LED state

String ledState;

boolean restart = false;

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFS mounted successfully");
  }
}


// Read File from LittleFS
String readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path, "r");
  if(!file || file.isDirectory()){
    Serial.println("- failed to open file for reading");
    return String();
  }

  String fileContent;
  while(file.available()){
    fileContent = file.readStringUntil('\n');
    break;
  }
  file.close();
  return fileContent;
}

// Write file to LittleFS
void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, "w");
  if(!file){
    Serial.println("- failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("- file written");
  } else {
    Serial.println("- frite failed");
  }
  file.close();
}

// Initialize WiFi
bool initWiFi() {
  if(ssid=="" || ip==""){
    Serial.println("Undefined SSID or IP address.");
    return false;
  }

  WiFi.mode(WIFI_STA);
  localIP.fromString(ip.c_str());
  localGateway.fromString(gateway.c_str());

  if (!WiFi.config(localIP, localGateway, subnet)){
    Serial.println("STA Failed to configure");
    return false;
  }
  WiFi.begin(ssid.c_str(), pass.c_str());

  Serial.println("Connecting to WiFi...");
  delay(20000);
  if(WiFi.status() != WL_CONNECTED) {
    Serial.println("Failed to connect.");
    return false;
  }

  Serial.println(WiFi.localIP());
  return true;
}

// Replaces placeholder with LED state value
String processor(const String& var) {
  if(var == "STATE") {
    if(!digitalRead(ledPin)) {
      ledState = "ON";
    }
    else {
      ledState = "OFF";
    }
    return ledState;
  }
  return String();
}

void setup() {
  // Serial port for debugging purposes
  Serial.begin(115200);

  initFS();

  // Set GPIO 2 as an OUTPUT
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Load values saved in LittleFS
  ssid = readFile(LittleFS, ssidPath);
  pass = readFile(LittleFS, passPath);
  ip = readFile(LittleFS, ipPath);
  gateway = readFile (LittleFS, gatewayPath);
  Serial.println(ssid);
  Serial.println(pass);
  Serial.println(ip);
  Serial.println(gateway);

  if(initWiFi()) {
    // Route for root / web page
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });
    
    server.serveStatic("/", LittleFS, "/");
    
    // Route to set GPIO state to HIGH
    server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
      digitalWrite(ledPin, LOW);
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });

    // Route to set GPIO state to LOW
    server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request) {
      digitalWrite(ledPin, HIGH);
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });
    server.begin();
  }
  else {
    // Connect to Wi-Fi network with SSID and password
    Serial.println("Setting AP (Access Point)");
    // NULL sets an open Access Point
    WiFi.softAP("ESP-WIFI-MANAGER", NULL);

    IPAddress IP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(IP); 

    // Web Server Root URL
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
      request->send(LittleFS, "/wifimanager.html", "text/html");
    });
    
    server.serveStatic("/", LittleFS, "/");
    
    server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
      int params = request->params();
      for(int i=0;i<params;i++){
        AsyncWebParameter* p = request->getParam(i);
        if(p->isPost()){
          // HTTP POST ssid value
          if (p->name() == PARAM_INPUT_1) {
            ssid = p->value().c_str();
            Serial.print("SSID set to: ");
            Serial.println(ssid);
            // Write file to save value
            writeFile(LittleFS, ssidPath, ssid.c_str());
          }
          // HTTP POST pass value
          if (p->name() == PARAM_INPUT_2) {
            pass = p->value().c_str();
            Serial.print("Password set to: ");
            Serial.println(pass);
            // Write file to save value
            writeFile(LittleFS, passPath, pass.c_str());
          }
          // HTTP POST ip value
          if (p->name() == PARAM_INPUT_3) {
            ip = p->value().c_str();
            Serial.print("IP Address set to: ");
            Serial.println(ip);
            // Write file to save value
            writeFile(LittleFS, ipPath, ip.c_str());
          }
          // HTTP POST gateway value
          if (p->name() == PARAM_INPUT_4) {
            gateway = p->value().c_str();
            Serial.print("Gateway set to: ");
            Serial.println(gateway);
            // Write file to save value
            writeFile(LittleFS, gatewayPath, gateway.c_str());
          }
          //Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
        }
      }
      restart = true;
      request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);
    });
    server.begin();
  }
}

void loop() {
  if (restart){
    delay(5000);
    ESP.restart();
  }
}

View raw code

How The Code Works

Let’s take a look at the code and see how the Wi-Fi Manager works.

The following variables are used to search for the SSID, password, IP address, and gateway on the HTTP POST request made when the form is submitted.

// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
const char* PARAM_INPUT_4 = "gateway";

The ssid, pass, ip, and gateway variables save the values of the SSID, password, IP address, and gateway submitted in the form.

//Variables to save values from HTML form
String ssid;
String pass;
String ip;
String gateway;

The SSID, password, IP address, and gateway when submitted are saved in files in the ESP filesystem. The following variables refer to the path of those files.

// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
const char* gatewayPath = "/gateway.txt";

The station IP address and gateway are submitted in the Wi-Fi Manager form. The subnet is hardcoded but you can easily modify this project with another field to include the subnet, if needed.

IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded

// Set your Gateway IP address
IPAddress localGateway;
//IPAddress localGateway(192, 168, 1, 1); //hardcoded
IPAddress subnet(255, 255, 0, 0);

initWiFi()

The initWiFi() function returns a boolean value (either true or false) indicating if the ESP board connected successfully to a network.

bool initWiFi() {
  if(ssid=="" || ip==""){
    Serial.println("Undefined SSID or IP address.");
    return false;
  }

  WiFi.mode(WIFI_STA);
  localIP.fromString(ip.c_str());
  localGateway.fromString(gateway.c_str());

  if (!WiFi.config(localIP, localGateway, subnet)){
    Serial.println("STA Failed to configure");
    return false;
  }
  WiFi.begin(ssid.c_str(), pass.c_str());

  Serial.println("Connecting to WiFi...");
  delay(20000);
  if(WiFi.status() != WL_CONNECTED) {
    Serial.println("Failed to connect.");
    return false;
  }

  Serial.println(WiFi.localIP());
  return true;
}

First, it checks if the ssid and ip variables are empty. If they are, it won’t be able to connect to a network, so it returns false.

if(ssid=="" || ip==""){

If that’s not the case, we’ll try to connect to the network using the SSID and password saved in the ssid and pass variables and set the IP address.

WiFi.mode(WIFI_STA);
localIP.fromString(ip.c_str());
localGateway.fromString(gateway.c_str());

if (!WiFi.config(localIP, localGateway, subnet)){
  Serial.println("STA Failed to configure");
  return false;
}
WiFi.begin(ssid.c_str(), pass.c_str());

Serial.println("Connecting to WiFi...");

If it cannot connect to Wi-Fi after 20 seconds, it will return false.

delay(20000);
if(WiFi.status() != WL_CONNECTED) {
  Serial.println("Failed to connect.");
  return false;
}

If none of the previous conditions are met, it means that the ESP successfully connected to the network in station mode (returns true).

return true;

setup()

In the setup(), start reading the files to get the previously saved SSID, password, IP address, and gateway.

ssid = readFile(LittleFS, ssidPath);
pass = readFile(LittleFS, passPath);
ip = readFile(LittleFS, ipPath);
gateway = readFile (LittleFS, gatewayPath);

If the ESP connects successfully in station mode (initWiFi() function returns true), we can set the commands to handle the web server requests (or any other code that requires the ESP to be connected to the internet):

if(initWiFi()) {
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });
    
  server.serveStatic("/", LittleFS, "/");
    
  // Route to set GPIO state to HIGH
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
    digitalWrite(ledPin, LOW);
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });

  // Route to set GPIO state to LOW
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request) {
    digitalWrite(ledPin, HIGH);
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });
  server.begin();
}

If that’s not the case, the initWiFi() function returns false. The ESP will set an access point:

else {
  // Connect to Wi-Fi network with SSID and password
  Serial.println("Setting AP (Access Point)");
  // NULL sets an open Access Point
  WiFi.softAP("ESP-WIFI-MANAGER", NULL);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP); 

To set an access point, we use the softAP() method and pass as arguments the name for the access point and the password. We want the access point to be open, so we set the password to NULL. You can add a password if you wish. To learn more about setting up an Access Point, read one of the following tutorials:

When you access the Access Point, it shows the web page to enter the network credentials in the form. So, the ESP must send the wifimanager.html file when it receives a request on the root / URL.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(LittleFS, "/wifimanager.html", "text/html");
});    

We must also handle what happens when the form is submitted via an HTTP POST request. The following lines save the submitted values in the ssid, pass, ip, and gateway variables and save those variables in the corresponding files.

server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
  int params = request->params();
  for(int i=0;i<params;i++){
    AsyncWebParameter* p = request->getParam(i);
    if(p->isPost()){
      // HTTP POST ssid value
      if (p->name() == PARAM_INPUT_1) {
        ssid = p->value().c_str();
        Serial.print("SSID set to: ");
        Serial.println(ssid);
        // Write file to save value
        writeFile(LittleFS, ssidPath, ssid.c_str());
      }
      // HTTP POST pass value
      if (p->name() == PARAM_INPUT_2) {
        pass = p->value().c_str();
        Serial.print("Password set to: ");
        Serial.println(pass);
        // Write file to save value
        writeFile(LittleFS, passPath, pass.c_str());
      }
      // HTTP POST ip value
      if (p->name() == PARAM_INPUT_3) {
        ip = p->value().c_str();
        Serial.print("IP Address set to: ");
        Serial.println(ip);
        // Write file to save value
        writeFile(LittleFS, ipPath, ip.c_str());
      }
      // HTTP POST gateway value
      if (p->name() == PARAM_INPUT_4) {
        gateway = p->value().c_str();
        Serial.print("Gateway set to: ");
        Serial.println(gateway);
        // Write file to save value
        writeFile(LittleFS, gatewayPath, gateway.c_str());
      }
      //Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
    }
  }

After submitting the form, send a response with some text so that we know that the ESP received the form details:

request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);

Set the restart variable to true.

restart = true;

In the loop(), we’ll check the value of the restart variable and proceed accordingly.

void loop() {
  if (restart){
    delay(5000);
    ESP.restart();
  }
}

That’s a quick summary of how the code works.

You can apply this idea to any of the other web server projects built with the ESPAsyncWebServer library.

Uploading Code and Files

Upload the files in the data folder to your ESP8266. Go to Tools > ESP8266 LittleFS Data Upload.

ESP8266 Tools LittleFS Data Upload Arduino IDE

If you’re using VS Code with the PlatformIO extension, follow one of the next tutorials to learn how to upload files to your boards:

After successfully uploading the files, upload the code to your board.

Arduino 2.0 Upload Button

Demonstration

After successfully uploading all files and sketch, you can open the Serial Monitor. If it is running the code for the first time, it will try to read the ssid.txt, pass.txt, and ip.txt files, and it won’t succeed because those files weren’t created yet. So, it will start an Access Point.

ESP-Wi-Fi Manager Sets Access Point Serial Monitor

On your computer or smartphone, go to your network settings and connect to the ESP-WIFI-MANAGER access point.

Connect to ESP32 ESP8266 Access Point Wi-Fi Manager

Then, open your browser and go to 192.168.4.1. The Wi-Fi Manager web page should open.

ESP32 ESP8266 Wi-Fi Manager Fields

Enter your network credentials: SSID and Password and an available IP address and gateway on your local network.

After that, you’ll be redirected to the following page:

ESP32 ESP8266 connected to station success Wi-Fi Manager

At the same time, the ESP should print something in the Serial Monitor indicating that the parameters you’ve inserted were successfully saved in the corresponding files.

After a few seconds, the ESP will restart. And if you’ve inserted the correct SSID and password, it will start in station mode:

ESP32 ESP8266 Connected Successfully to Wi-Fi Station

This time, open a browser on your local network and insert the ESP IP address. You should get access to the web page to control the outputs:

ESP32 ESP8266 Control Outputs Web Server

Wrapping Up

In this tutorial, you’ve learned how to set up a Wi-Fi Manager for your web server projects or for any other project that requires the ESP to be connected to the internet. With the Wi-Fi Manager, you can easily connect your ESP boards to different networks without the need to hard-code network credentials. You can apply the Wi-Fi Manager to any web server project built with the ESPAsyncWebServer library.

If you want to learn more about building web servers with the ESP32 and ESP8266 boards, make sure you take a look at our eBook:

Learn more about the ESP8266 with our resources:

We hope you’ve found this tutorial useful.

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

31 thoughts on “ESP8266 NodeMCU: Create a Wi-Fi Manager (AsyncWebServer library)”

  1. ESPAsyncWebServer.h and ESPAsyncTCP.h run concurrently. One benefit is that when coding in the arduino ide, my home’s wifi location doesn’t have to write the IP:name and password to control the device. It can also be controlled outdoors. As long as my phone is on mobile data (wifi), share to esp8266, it can be used anywhere in the world. Thanks for sharing good article 👍

    Reply
  2. Great code…sadly I ran into a problem: at the end it will not connect and restarts the WiFimanager.
    If I ‘cheat’ a bit and just hard code the ssid and pw then it will connect so I know they are correct.
    But if I print the (ssid.c_str()); and (pass.c_str()); values…that are read from memory…then they are also correct…at least, as far as I can see.
    So…I will have some further digging to do.
    take care

    Reply
  3. Simply Perfect ! Just two questions.
    – Will it be possible to fill a dropdown listbox with all available Wi-Fi SSIDs to make it easy for the user ?
    – Secondly, How to store the password encrypted in the file for security ?

    Reply
  4. Hi, simple question.
    If I want to load other SSID and PASWORD values, once I have obtained a correct connection, how do I delete (empty) the text files (that are not visible)?
    For example, to switch to another available network within the place of use.
    Thanks

    Reply
  5. Hello Sara, how can I do this if I want to enter another SSID and password while the device is running in this project. So how can I change the SSID information. Thank you.

    Reply
  6. Hi Sara,

    after endless tries to start the WebServer we are at the point of “give-up”.

    As mentioned above there is a restart-trap in the following condition:

    Als works fine an the server starts. On the first try we got the WiFi-Manager-Form and type in ssid, password an gateway and save those inputs. Server sends on serial the following:

    13:16:20.171 -> Dateisystem erfolgreich initialisiert!
    13:16:20.171 ->
    13:16:20.171 -> Lese Daten von: /ssid.txt
    13:16:21.174 -> Lese Daten von: /pass.txt
    13:16:22.164 -> Lese Daten von: /ip.txt
    13:16:23.171 -> Lese Daten von: /gateway.txt
    13:16:24.212 -> Lese Daten von: /subnet.txt
    13:16:25.200 -> SSID: (correct, but disabled for safety in this post!)
    13:16:25.200 -> Passwort: (disabled for safety in this post!)
    13:16:25.200 -> IP-Adresse: 192.168.178.50
    13:16:25.200 -> Gateway: 192.168.178.1
    13:16:25.200 -> Subnet: 255.255.255.0
    13:16:25.200 -> WiFi-Verbindung mit …
    13:16:45.223 -> Verbunden mit WLAN-Adresse: 192.168.178.50
    13:16:45.223 -> Nutze Access-Point-Modus!
    13:16:45.223 -> Access-Point IP-Adresse: 192.168.4.1

    As you can see it seems that all is right. But directly after connecting to Router he switched back to access-point-adress 192.168.4.1 and lost connection to router on 192.168.178.50. This is a never ending loop …..

    The only qestion left is: How can we delete the files in LittleFS to set all to zero? This should be done as the fastet solution to go ahead with programming and testing. As long as the wrong file-content (maybe wrong passwort e.g.) is in the file-system, there is no chance to start up again.

    Hope you can help to solve this endless-Loop-trap.

    Thanks and stay healthy,
    Manfred

    Reply
    • Hi.
      When you type the wrong password, or when the board cannot connect to the network, it will initialize in access point mode.
      Then, you should connect to that access point again and the form web page will load. Then, you can insert new details that will overwrite the previous.
      Regards,
      Sara

      Reply
    • Hi Manfred, I had the same issue you describe, please connect the esp8266 to the USB without cables and make sure has enough power supply, restarting loop means not enough power.

      Reply
  7. Hi Sara,

    as a beginner i use this code-example and it works fine. I have one question as follow:

    I like to extend the wifimanager.html with one more input-field named “router-name”. Coding is simple and writing from variable “routername” to file on “POST-Event” to a coresponding file “routername.txt” works fine.

    In setup-routine i read the stored value back in corresponding variable “routername” same way as other values – works also fine. But how can i write the value from the variable to the input-field in wifimanager.html at HTTP_GET-Routine ??? (processor-routine, using innerHTML() or ….)

    Thanks for suggestions or a short code-line/sample to do this.

    Greetings,
    Valentine

    Reply
  8. Hello,
    I want to add WiFi Manager to my project.
    I would like to use a time server with the following lines:
    configTime(0, 0, “za.pool.ntp.org”, “time.nist.gov”);
    setenv(“TZ”, “GMT-1”,1);
    Time_format = “M”;
    Unfortunately I don’t know where I have to insert the code shown in the WiFi manager code.
    I tried to insert the code at the beginning of setup() or at the beginning of loop(). Unfortunately or success (no current time). Could you give me a hint about this?

    Reply
      • Thanks. The question is, how do I launch telegram bot using a Wi-Fi manager (AsyncWebServer library)?
        The WiFi manager from this article works great for me, the telegram bot also works separately, but they don work together. What needs to be changed in the code to make bat work?

        Reply
        • Hi.
          Can you provide more details?
          Do you get any errors? What exactly happens?
          I never tried using both at the same time, but in theory, it should work. Maybe you need some adjustments in your code.
          Regards,
          Sara

          Reply
  9. Hi. Sarah
    This works with a Telegram bot.

    WiFi.mode(WIFI_STA);
    //localIP.fromString(ip.c_str());
    //localGateway.fromString(gateway.c_str());
    //if (!WiFi.config(localIP, localGateway, subnet)){
    //Serial.println(“STA не удалось настроить”);
    //return false;
    //}
    WiFi.begin(ssid.c_str(), pass.c_str());

    Serial.println(“подключение к WiFi…”);
    delay(20000);

    I send a dynamic ID to the telegram bot and go to the WEB page from the telegram.

    Reply
  10. It would be really nice if someone could let me know how to modify the code so I can revisit wifimanager.html and change the settings later on.
    wifimanager.html is available after setting everything up, but submitting the form throws error 500.
    What do I have to modify so I am able to resubmit the form with new settings?

    Reply
  11. olá tudo bem ele funciona do ESP8266 01 TENHO MUITO DELE AQUI
    Seria ótimos.
    Eu ate fiz um compilação normal nele ele funciona não da o cesso ap
    Obrigado

    Reply
  12. I’ve been successful to the point where it’s time to connect to 192.168.1.200 but at that point my browser times out. If anyone has any suggestions I’d be appreciative.

    I’ve tried connecting to both my iPhone hotspot & router in my shed and both connect just fine. LittleFS appears to have uploaded the the package from the tools folder created during this tutorial just fine.

    LittleFS has thrown a few access denied errors trying to write to COM port however these clear if you disconnect, reconnect the usb & try again.

    So I’m a bit lost how to diagnose. Is there a way of reading the content of the flash memory to see that the index.html file is there?

    I realise this is to 63 not 200 and I’d changed that in a futile attempt to see if I had a printer or something with a fixed address that clashed.

    Message on web browser attempting to access – This site can’t be reached192.168.1.63 took too long to respond.

    Cheers,

    Dave

    Reply
  13. There’s a key bit of information missing in this “How to..” page.

    You need to know the Default Gateway address of the network that you want to connect to if you use this message.

    In my case it was 192.168.20.1 so when it connected to the WiFi, the next step to enter the 192.168.1.200 didn’t work. It just hung and the browser timed out looking for it.

    Run IPCONFIG from a command prompt and look at what your default gateway is. Use that address in the default gateway field.

    Change the IP address you want the NodeMCU to appear at after you connect it to the SSID to match eg. 192.168.20.200 in my case.

    Make sure your subnet mask in the code matches the subnet mask in IPCONFIG.

    Then you might get somewhere when you connect. Mine is working now.

    All I have to do now is work out how to find the default gateway and subnet mask of an iPhone hotspot and whether they are the same for all iPhones. Otherwise when I’m out of the development environment here in my shed, and trying to use this on a hotspot it’s going to fail too.

    Wasted a lot of time finding this solution which really should have been covered in the article above.

    Hope this helps someone else.

    Dave

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.