Setting up an access point with ESP32

Hello -
I'm working on building an ESP32 project. Still working on the project, but I know I need to build in some initial functionality. That being:

  1. Connect to WiFi network
  2. If connection to WiFi network fails initiate access point with captive portal for user to select SSID and enter password
  3. Save SSID and password details in ESP32 preferences (non-volatile memory) and restart ESP32.
  4. Back to step 1.

I've written some code to accomplish these goals, but am now stuck. When I run the program I don't see the ESP32 in my list of SSID's. When I run an example sketch to initiate an access point it works as I would expect, so I assume hardware is OK.

// Step 1 - Begin by telling the compiler which libraries will be used in this program
#include <Arduino.h>  // This is the core Arduino library
#include <Wifi.h>  // These are used for wifi capability and OTA updates
#include <ArduinoOTA.h>
#include <WiFiClientSecure.h>
#include <ESPAsyncWebServer.h>  // These are used for the captive web portal
#include <DNSServer.h>
#include <AsyncTCP.h>
#include <Preferences.h>  // This allows access to non-volatile memory to store WiFi provisioning credentials in between restarts.
#include <SPIFFS.h>  // This library allows access to files outside of the main.cpp file. Specifically, the HTML page for access point creation.

// Step 2 - Create a section to declare variables that will be used in the program.
Preferences preferences;  // Preferences object to save SSID and password
DNSServer dnsServer; //DNSServer object
AsyncWebServer server(80); //AsyncWebServer object for use when connectd to wifi.
String ssidList;  // Create SSIDList variable

const char *SSID = "BLANK";
const char *password = "BLANK";
unsigned long previousMillis = 0;  // Stores the last time the LED was updated
const long interval = 10000;  // Interval at which to blink (milliseconds)
bool ledState = LOW;  // Variable to store LED state
int timeout = 0;
bool connected = LOW;

// Step 3 - Define the class `CaptiveRequestHandler` above setup to avoid incomplete type error
class CaptiveRequestHandler : public AsyncWebHandler {
public:
    CaptiveRequestHandler() {}
    virtual ~CaptiveRequestHandler() {}

    bool canHandle(AsyncWebServerRequest *request) {
        return true;
    }

    void handleRequest(AsyncWebServerRequest *request) {
        File file = SPIFFS.open("/index.html", "r");
        if (!file) {
            request->send(404, "text/plain", "File not found");
            return;
        }
        // Serve the HTML file directly from SPIFFS
        request->send(SPIFFS, "/index.html", "text/html");
    }
};

// Step 4 - Create a section to declare functions that will be used in the program
void blinkNonBlocking();  // Better version of blink, non-blocking
void saveCredentials(const String &ssid, const String &password);

// The setup function runs once when you press reset or power the board.
void setup() {
    // Initialize digital pin LED_BUILTIN as an output.
    Serial.begin(115200);
    pinMode(13, OUTPUT);

    // Initialize SPIFFS for document linking (HTML file)
    if (!SPIFFS.begin(true)) {
        Serial.println("SPIFFS Mount Failed");
        return;
    }

    WiFi.begin(SSID, password);

    Serial.println("Now attempting to connect to Wifi");

    while (WiFi.status() != WL_CONNECTED && timeout < 30) {
        Serial.print("Still not connected to WiFi. ");
        delay(500);
        timeout++;
        Serial.print("The timeout counter is: ");
        Serial.print(timeout);
        Serial.println(" of 30");
    }

    Serial.println("Now connected to WiFi, or timed out");
    Serial.println("The WiFi.status() value is:");
    Serial.println(WiFi.status());
    Serial.println("The WL_CONNECTED value is:");
    Serial.println(WL_CONNECTED);

    if (WiFi.status() == WL_CONNECTED) {
        Serial.print("If you're seeing this message, the ESP32 thinks it connected to a network. The IP address is: ");
        Serial.println(WiFi.localIP());
        connected = true;
        ArduinoOTA.begin();
    } else {
        connected = false;
    Serial.println("Connection unsuccessful, attempting to start access point");

    // Start access point for captive portal
    if (!WiFi.softAP("Klik Me No PassWOrd")) {
    log_e("Soft AP creation failed.");
    while (1);
    }
    IPAddress myIP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(myIP);
    Serial.println("Server started");
}

    // Start DNS server
    if (dnsServer.start(53, "*", WiFi.softAPIP())) {
        Serial.println("DNS server started successfully");
    } else {
        Serial.println("Failed to start DNS server");
        return;  // Exit if DNS server initialization fails
    }

    // Start the server
    server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);
    Serial.println("The access point should have started now");

        
        // Dynamic HTML serving handler for SSID list
        server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
            request->send(SPIFFS, "/index.html", "text/html");
        });

        // Handler for saving credentials from the form
        server.on("/save", HTTP_POST, [](AsyncWebServerRequest *request) {
            if (request->hasParam("ssid", true) && request->hasParam("password", true)) {
                String ssid = request->getParam("ssid", true)->value();
                String password = request->getParam("password", true)->value();
                saveCredentials(ssid, password);  // Call function to save credentials
            } else {
                request->send(400, "text/plain", "Invalid SSID or password");
            }
        });

        // API to get the list of available SSIDs
        server.on("/ssid-list", HTTP_GET, [](AsyncWebServerRequest *request) {
            // Scan for available networks
            int n = WiFi.scanNetworks();
            String json = "[";

            for (int i = 0; i < n; ++i) {
                if (i > 0) json += ",";
                json += "\"" + WiFi.SSID(i) + "\"";
            }

            json += "]";
            request->send(200, "application/json", json);
        });
        
    }

// The loop function runs over and over again forever.
void loop() {
    // These are actions dependent on the wifi connectivity status.
    if (connected) {
        ArduinoOTA.handle();  // Handle the OTA updates
    } else {
    // If a wifi connection was not successful, start the DNS server for use with the access point to redirect users.
        dnsServer.processNextRequest();
    }

    // These are actions independent of the wifi connectivity status.
    blinkNonBlocking();
}

// This function causes the output on pin 13 to blink every 5 seconds without locking up the system.
void blinkNonBlocking() {
    unsigned long currentMillis = millis();  // Get the current time

    // Check if it's time to toggle the LED
    if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;  // Save the last time you blinked the LED

        // Toggle the LED state
        ledState = !ledState;
        digitalWrite(13, ledState);  // Turn the LED on/off

        // Output the LED state to the serial monitor
        if (ledState) {
            Serial.println("Light on");
        } else {
            Serial.println("Light off");
        }
    }
}

// Function to save SSID and password
void saveCredentials(const String &ssid, const String &password) {
    preferences.begin("wifiCreds", false);  // Open preferences
    preferences.putString("ssid", ssid);  // Save SSID
    preferences.putString("password", password);  // Save password
    preferences.end();

    Serial.println("Saved SSID and password, restarting...");
    ESP.restart();  // Restart ESP32 to apply the new Wi-Fi settings
}

Any thoughts? I've intentionally mis-spelled the WiFi SSID and password to force the program into launching an access point. These are the serial messages that are printed when I run the program.

Now attempting to connect to Wifi
Still not connected to WiFi. The timeout counter is: 1 of 30
Still not connected to WiFi. The timeout counter is: 2 of 30
**** Messages 3-29 skipped for clarity ****
Still not connected to WiFi. The timeout counter is: 30 of 30
Now connected to WiFi, or timed out
The WiFi.status() value is:
1
The WL_CONNECTED value is:
3
Connection unsuccessful, attempting to start access point
AP IP address: 192.168.4.1
Server started
DNS server started successfully
The access point should have started now
Light on
Light off
Light on
... etc.

  • Everything you are asking for is covered here.

Thanks, I'll take a look. In the meantime an update... I re-uploaded the code a few times without making changes. Approximately 1 in 10 times I'm seeing the ESP32 SSID with my laptop and phone.

Update 2. That's an amazing video. It's where I'd like to be at the end of this project. I think the issue I'm having is before that point though, focused on WiFi provisioning using an access point on the ESP32.

Exactly what a WiFiManager does.
I use the one from tzapu. Comes with the IDE's library manager.
This is basically all you need.

#include <WiFi.h>
#include <WiFiManager.h>  // https://github.com/tzapu/WiFiManager

void setup() {
  WiFi.mode(WIFI_STA);
  WiFiManager wm;
  wm.setConnectTimeout(120);      // optional timeout before portal starts (power cut)
  wm.setConfigPortalTimeout(60);  // optional portal active period (safety)
  wm.autoConnect("Portal");       // open, 192.168.4.1
  //wm.autoConnect("Portal", "PortalPW"); // or with password
}

void loop() {
}

Leo..

1 Like

Wow. That's exactly it. Thanks, I'm up and running without reinventing the wheel.

Zach

Follow-up question. Is it possible to modify the library files and include the modified library in the program?

Specifically I'm interested in changing the look and layout of the web pages served up by the access point to be a little more personalized.

Thanks,
Zach

Yes, that's frequently done. Library page with examples is in the link in the sketch.
In a project I added extra fields for custom data, which is stored with "preferences".
Leo..

Thank you both for the guidance. I now have the ESP32 set up with a modified Wifi Manager to handle initial connection, and a webserver showing the inputs and outputs demonstrated in the video LarryD linked (https://www.youtube.com/watch?v=pL3dhGtmcMY).

I notice when I interface with the webserver via my PC the webpage sometimes freezes for 3-6 seconds before resuming function. When accessing with my phone browser the connection is very smooth. Do you have any suggestions towards how to diagnose what's causing the PC based browser to hang?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.