Note: This tutorial uses the ESP32 development board to create a WiFi hotspot (AP mode), which is an important step in learning advanced ESP32 wireless communication. Please make sure your ESP32 has the correct drivers and board core support installed.
AP Mode (Access Point) means the ESP32 itself becomes a WiFi hotspot, similar to your home wireless router. Other devices (phones, computers, other ESP32 boards) can connect to the ESP32 just like connecting to a regular WiFi network, enabling data exchange with it.
Simple analogy: Just like turning on your phone's "Personal Hotspot", when the ESP32 enables AP mode, it becomes a miniature "wireless router" that other devices can search for and connect to via its WiFi signal.
ESP32's WiFi has two main working modes. Beginners need to clearly understand the differences:
| Mode | Full Name | Analogy | Description |
|---|---|---|---|
| STA | Station | Phone connecting to router | ESP32 acts as a "client" connecting to an existing WiFi hotspot |
| AP | Access Point | The router itself | ESP32 creates its own WiFi hotspot for other devices to connect to |
| AP+STA | Both modes simultaneously | Both a router and a client | Can connect to others and be connected to by others |
This tutorial uses AP mode, where the ESP32 creates its own WiFi hotspot for other devices to connect to.
ESP32 is an IoT development board developed by Espressif:
ESP32_AP192.168.4.124:6F:28:A4:B2:C1), determined by hardware255.255.255.0WiFi channels are the frequency bands used for wireless signal transmission:
softAP()In AP mode, the ESP32 can manage devices connected to it (called Stations):
softAP(), maximum 10)192.168.4.2| Function | Description | Role in This Project |
|---|---|---|
WiFi.softAPConfig(ip, gateway, subnet) |
Configure the static IP for AP | Set the ESP32's AP IP to 192.168.4.1 |
WiFi.softAP(ssid, password, channel, hidden, max) |
Start AP hotspot | Create a hotspot named ESP32_AP |
WiFi.softAPIP() |
Get the AP's IP address | Get the current AP's IP |
WiFi.softAPmacAddress() |
Get the AP's MAC address | Print the ESP32's hardware address |
WiFi.softAPgetStationNum() |
Get the number of connected clients | Periodically print the current connection count |
WiFi.channel() |
Get the current WiFi channel | Print the channel number in use |
WiFi.softAP() Parameter DetailsWiFi.softAP(ssid, password, channel, hidden, max_connection);
| Parameter | Description | Value Range |
|---|---|---|
ssid |
Hotspot name | Any string (recommended: English + numbers) |
password |
Hotspot password | Empty ("") for open network; otherwise at least 8 characters |
channel |
WiFi channel | 1~13, default 1 |
hidden |
Whether to hide the SSID | 0 = visible, 1 = hidden |
max_connection |
Maximum connections | Default 4, maximum 10 |
This project uses:
WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 0, 4), which means channel 6, SSID visible, maximum 4 clients
| Function | Description | Role in This Project |
|---|---|---|
millis() |
Returns milliseconds since boot | Implement 3-second periodic client count check |
delay(ms) |
Blocking delay | Serial stabilization, loop throttling, etc. |
#include <WiFi.h>
// ==================== AP Configuration ====================
const char* AP_SSID = "ESP32_AP"; // Hotspot name
const char* AP_PASSWORD = "12345678"; // Min 8 characters, or "" for open network
#define LED_BUILTIN 2
// Set static IP for the AP (clients will get IPs in this range)
IPAddress local_IP(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
// ==================== Timing ====================
unsigned long lastClientCheck = 0;
const unsigned long CLIENT_CHECK_INTERVAL = 3000; // Check every 3s
Code Explanation:
#include <WiFi.h>: ESP32 WiFi core library, providing AP/STA related functionsAP_SSID: Hotspot name, this is what you see when searching for WiFi on your phoneAP_PASSWORD: Hotspot password, must be at least 8 characters; leave empty for an open network (no password)LED_BUILTIN 2: The on-board LED pin number of the ESP32 dev board (usually GPIO2), used as a connection status indicatorlocal_IP: The ESP32's IP address in AP mode, default subnet is 192.168.4.xgateway: Gateway address, usually the same as the device's own IP in AP modesubnet: Subnet mask, 255.255.255.0 means the same subnet can accommodate 254 deviceslastClientCheck / CLIENT_CHECK_INTERVAL: Non-blocking timing variables, check the client count every 3 secondsModification Tip: You can change AP_SSID and AP_PASSWORD to your preferred name and password.
setup() Initialization Functionsetup() runs only once after the ESP32 powers on, and is responsible for completing all AP initialization.
Serial.begin(115200);
delay(1000);
Serial.println("\n========================================");
Serial.println(" ESP32 AP Mode");
Serial.println("========================================");
Step Analysis:
Serial.begin(115200): Initialize the serial port at a baud rate of 115200, used to output debug information to the computerdelay(1000): Wait 1 second for the serial port to stabilize, avoiding loss of early outputSerial.println(): Print a welcome banner to confirm the program is running normally// ---------- Step 1: Configure static IP ----------
Serial.println("[1/3] Configuring static IP...");
if (!WiFi.softAPConfig(local_IP, gateway, subnet)) {
Serial.println(" Failed to configure AP IP!");
} else {
Serial.print(" AP IP: ");
Serial.println(local_IP);
}
Code Analysis:
WiFi.softAPConfig(local_IP, gateway, subnet): Set a static IP for the APsoftAP(), otherwise the IP will not take effect192.168.4.1, and clients will be assigned starting from 192.168.4.2Why configure a static IP? By default, the ESP32's AP IP is also 192.168.4.1, but explicit configuration ensures the IP does not change with firmware version changes, making it easier for clients to connect.
// ---------- Step 2: Start AP mode ----------
Serial.println("[2/3] Starting AP mode...");
bool result = WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 0, 4);
if (!result) {
Serial.println(" Failed to start AP!");
return;
}
Serial.print(" SSID : ");
Serial.println(AP_SSID);
Serial.print(" Password : ");
Serial.println(AP_PASSWORD);
Serial.print(" Channel : ");
Serial.println(WiFi.channel());
Serial.print(" AP MAC : ");
Serial.println(WiFi.softAPmacAddress());
Code Analysis:
WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 0, 4): Start AP, parameter meanings:
ESP32_AP123456786 (1~13 available, commonly 1, 6, 11 to avoid interference)0 (SSID visible, can be searched by phone)4result: true means startup successful, false means failedreturn is used to directly end setup() to avoid subsequent invalid operationsChannel Selection Tip: 2.4GHz WiFi has channels 1~13, among which 1, 6, and 11 do not overlap with each other and are commonly used "non-interfering channels".
// ---------- Step 3: Ready ----------
Serial.println("[3/3] Setup complete. Waiting for clients...");
Serial.println("----------------------------------------");
Serial.println("Connect to this AP with WiFi:");
Serial.print(" SSID: ");
Serial.println(AP_SSID);
Serial.print(" IP : ");
Serial.println(local_IP);
Serial.println("----------------------------------------\n");
pinMode(LED_BUILTIN, OUTPUT);
Code Analysis:
ESP32_AP with a phone/computerpinMode(LED_BUILTIN, OUTPUT): Set the on-board LED pin to output mode for subsequent connection status indicationloop() Main Loop Functionloop() is called repeatedly after setup() finishes. It is the "heartbeat" of the program. The main loop of this project is responsible for periodically checking the client count and indicating the status via LED.
void loop() {
// ---------- Periodic WiFi client report ----------
unsigned long now = millis();
if (now - lastClientCheck >= CLIENT_CHECK_INTERVAL) {
lastClientCheck = now;
int numClients = WiFi.softAPgetStationNum();
Serial.print("[WiFi] Connected stations: ");
Serial.println(numClients);
// LED indicator: on if any client connected
if (numClients > 0) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
}
delay(10);
}
Code Analysis:
Step 1: Non-Blocking Timing
unsigned long now = millis();
if (now - lastClientCheck >= CLIENT_CHECK_INTERVAL) {
lastClientCheck = now;
millis(): Returns the number of milliseconds since the ESP32 booted (overflows after approximately 50 days)now - lastClientCheck: Calculates how long it has been since the last checkCLIENT_CHECK_INTERVAL (3000ms): Only execute a check if more than 3 seconds have passed, avoiding frequent queriesWhy use
millis()instead ofdelay()? Becausedelay()blocks the entire program, whilemillis()only records a timestamp and does not affect other logic execution. This is an important "non-blocking" programming paradigm.
Step 2: Query Client Count
int numClients = WiFi.softAPgetStationNum();
Serial.print("[WiFi] Connected stations: ");
Serial.println(numClients);
WiFi.softAPgetStationNum(): Get the current number of clients connected to the APStep 3: LED Status Indication
if (numClients > 0) {
digitalWrite(LED_BUILTIN, HIGH); // Clients connected, LED on
} else {
digitalWrite(LED_BUILTIN, LOW); // No clients, LED off
}
Step 4: Loop Throttling
delay(10);
Main Loop Flowchart:
loop() starts
│
├─ Less than 3s since last check? ── YES ──→ delay(10), enter next round
│
└─ 3s or more since last check
│
├─ Query current client count and print
│
├─ Has clients? ── YES ──→ LED on
│
└─ No clients ──────→ LED off
│
└─ delay(10), enter next round
Open ap_mode.ino and modify the following content as needed:
const char* AP_SSID = "MyESP32"; // Change to your desired hotspot name
const char* AP_PASSWORD = "mypassword"; // At least 8 characters, or "" for no password
Note: SSID is recommended to use only English letters and numbers to avoid encoding issues with Chinese or special characters.
========================================
ESP32 AP Mode
========================================
[1/3] Configuring static IP...
AP IP: 192.168.4.1
[2/3] Starting AP mode...
SSID : ESP32_AP
Password : 12345678
Channel : 6
AP MAC : 24:6F:28:A4:B2:C1
[3/3] Setup complete. Waiting for clients...
----------------------------------------
Connect to this AP with WiFi:
SSID: ESP32_AP
IP : 192.168.4.1
----------------------------------------
[WiFi] Connected stations: 0
ESP32_AP12345678 to connectConnected stations: 1, and the on-board LED will also light up========================================
ESP32 AP Mode
========================================
[1/3] Configuring static IP...
AP IP: 192.168.4.1
[2/3] Starting AP mode...
SSID : ESP32_AP
Password : 12345678
Channel : 6
AP MAC : 24:6F:28:A4:B2:C1
[3/3] Setup complete. Waiting for clients...
----------------------------------------
Connect to this AP with WiFi:
SSID: ESP32_AP
IP : 192.168.4.1
----------------------------------------
[WiFi] Connected stations: 0
[WiFi] Connected stations: 1
[WiFi] Connected stations: 1
[WiFi] Connected stations: 2
At this point, the on-board LED will light up.
[WiFi] Connected stations: 1
[WiFi] Connected stations: 0
At this point, the on-board LED turns off.
[2/3] Starting AP mode...
Failed to start AP!
This situation usually occurs when the password is less than 8 characters or the SSID contains illegal characters.
Q1: The Serial Monitor shows garbled characters?
A: Please make sure the baud rate of the Serial Monitor is set to 115200 (bottom right dropdown).
Q2: My phone cannot find the ESP32_AP hotspot?
A: Please check in order:
Failed to start AP!? (If so, the password needs to be ≥ 8 characters)hidden=0, visible by default)Q3: The phone prompts "No internet access" after connecting?
A: This is normal. The ESP32's AP mode only establishes a local network and has no routing function, so it cannot access the internet. If you want to access the internet, you need to use STA mode or AP+STA hybrid mode.
Q4: The LED never lights up?
A: Please check:
#define LED_BUILTIN 2.Q5: I get kicked offline after connecting multiple devices?
A: The ESP32 AP mode supports a maximum of 4 clients by default. If you need more, you can modify the last parameter of softAP() (maximum 10):
WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 0, 10); // Up to 10 clients
Q6: Can the AP IP address be changed to something else?
A: Yes, just modify IPAddress local_IP(...), for example 192.168.10.1. Clients will automatically obtain IPs from the same subnet.
Q7: Can I make the hotspot passwordless?
A: Yes, just leave the password parameter empty:
WiFi.softAP(AP_SSID, "", 6, 0, 4); // Open network
| Problem | Possible Cause | Solution |
|---|---|---|
| Garbled serial output | Wrong baud rate | Change to 115200 |
| Cannot upload program | Port/driver issue | Check COM port and CH340/CP2102 driver |
| AP startup fails | Password < 8 characters | Password must be at least 8 characters, or leave empty "" |
| AP startup fails | SSID contains special characters | Use English + numbers |
| Phone cannot find hotspot | SSID is hidden | Check if hidden parameter is 0 |
| Phone cannot connect | Wrong password | Verify password case sensitivity |
| LED not lit | No client connected | First connect the hotspot with a phone |
| LED not lit | Wrong pin definition | Modify LED_BUILTIN to match your board's pin |
| Frequent disconnections | Insufficient power | Use an independent 5V power supply (VIN pin) |
| Connection limit reached | Exceeded max_connection | Increase the 5th parameter of softAP |
const char* AP_SSID = "MyHomeIOT"; // Change to a distinctive name
const char* AP_PASSWORD = "88888888"; // Change to your own password (≥8 characters)
WiFi.softAP(AP_SSID, "", 6, 0, 4); // Empty password means open network
WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 1, 4); // hidden=1
// Clients need to manually enter the SSID to connect
WiFi.softAP(AP_SSID, AP_PASSWORD, 6, 0, 10); // Up to 10 clients
WiFi.softAP(AP_SSID, AP_PASSWORD, 1, 0, 4); // Change to channel 1
WiFi.softAP(AP_SSID, AP_PASSWORD, 11, 0, 4); // Change to channel 11
1, 6, and 11 are three commonly used non-interfering channels. You can choose according to environmental interference.
const unsigned long CLIENT_CHECK_INTERVAL = 1000; // Change to check once per second
const unsigned long CLIENT_CHECK_INTERVAL = 5000; // Change to check once every 5 seconds
IPAddress local_IP(192, 168, 10, 1); // Change to 192.168.10.1
IPAddress gateway(192, 168, 10, 1);
IPAddress subnet(255, 255, 255, 0);
WiFi.mode(WIFI_AP_STA); // Enable both AP and STA simultaneously
WiFi.softAP(AP_SSID, AP_PASSWORD); // Start AP
WiFi.begin("YourHomeWiFi", "password"); // Connect to router simultaneously
The AP mode password must be at least 8 characters, otherwise softAP() will return false. If you want an open network (no password), please leave the password parameter as "".
ESP32 only supports 2.4GHz WiFi, not 5GHz. However, since AP mode is the ESP32's own signal transmission, you do not need to worry about the router's frequency band. All devices supporting 2.4GHz can connect.
The ESP32's WiFi function has high power consumption (peak can reach over 300mA). If restarts occur when connecting multiple clients, it is recommended to use an independent 5V power supply through the VIN pin rather than relying solely on USB.
ESP32's AP mode supports up to 4 clients by default, and can be set to a maximum of 10 through the max_connection parameter. New clients cannot connect after exceeding the limit.
Devices connected to the ESP32 AP hotspot cannot access the internet (because the ESP32 has no routing function). The phone will prompt "Connected but no internet access", which is normal. This project only establishes a local network between the ESP32 and clients.
The on-board LED pin may vary across different ESP32 development boards. This project uses GPIO2 (the most common model). If your board's LED is on another pin (such as GPIO0, GPIO4, GPIO33), please modify #define LED_BUILTIN 2.
WiFi.softAPConfig() must be called before WiFi.softAP(), otherwise the static IP configuration will not take effect and the ESP32 will use the default IP 192.168.4.1.