ESP32/Joining a network/41. Access point mode
Your chip
Your language
Joining a network · 41 of 81

Access point mode

The board becomes the network, so a phone can reach it before it has any Wi-Fi credentials at all. It is how every commercial device gets configured, and it fails in three ways station mode never does.

/esp32/access-point-mode · arduino · S3

Three things it does not do

softAP with max_connection = 4
3 joined
Devices trying to join3
max_connection4
Connected
3
Refused
0
Internet
none
Everything joins, and phones keep wandering off. Android and iOS both test for internet access after joining, find none, and quietly switch back to mobile data — taking your configuration page with them. A captive portal (a DNS server answering every name with the board's own IP) is what makes the page appear and stay.

Give it a name a person recognises

ESP_1A2B is the default and it is useless — a customer looking at a list of networks has no idea which one is yours. Use the product name and, if there might be several, the last four digits of the MAC: Greenhouse setup A3F2.

AP and station together

WIFI_AP_STA runs both. It is the right mode during provisioning: the phone stays on the setup page while the board tests the credentials, so you can tell it why the password failed instead of just disappearing.

It is the wrong mode permanently. One radio serves both, throughput halves, and the access point is dragged onto whatever channel your router picked.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

An access point plus a DNS server that answers every name with the board's own address. That second part is what makes the setup page appear by itself instead of waiting to be typed.

captive_ap.ino
#include <WiFi.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>

DNSServer dns;
AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_AP);
  WiFi.softAP("Greenhouse setup", "12345678", 1, false, 8);
  Serial.println(WiFi.softAPIP());          // 192.168.4.1 by default

  dns.start(53, "*", WiFi.softAPIP());      // every name -> us

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *r) {
    r->send(200, "text/html",
            "<form method=post action=/save>"
            "<input name=ssid><input name=pass type=password>"
            "<button>Save</button></form>");
  });
  server.onNotFound([](AsyncWebServerRequest *r) {
    r->redirect("/");                       // the captive portal nudge
  });
  server.begin();
}

void loop() { dns.processNextRequest(); }

Android and iOS both check for internet access after joining. Without the DNS trick they decide the network is useless and quietly switch back to mobile data.

When it does not work

The phone joins and then leaves after a few seconds

It tested for internet access, found none, and switched back to mobile data. A captive portal - a DNS server answering everything with your IP - is what keeps it there.

A fifth device cannot connect

max_connection defaults to four. Raise it in softAP. Ten is the hardware ceiling and each client costs RAM.

The AP disappears when the board also joins my router

In AP+STA mode there is one radio and one channel. When the station side associates, the access point follows the router's channel - anything already connected to it is dropped.

The password is refused as too short

WPA2 requires at least eight characters. Fewer than that and softAP silently creates an open network instead.

Where this goes next

Both halves at the same time, on one radio — which is the constraint that decides the order your code has to do it in.

Both at once: AP and station

Edit this page — content/esp32/access-point-mode.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum