ESP32 Wi-Fi First Steps: STA/AP Modes, Connection Management and HTTP

Milestone: at this point you command GPIO, ADC, PWM and the buses for local peripherals. Bring Wi-Fi up and the ESP32 graduates from “microcontroller” to “IoT device” — data reaches the cloud, phones reach the device.


1. The three operating modes

ModeRoleUse case
STA (Station)Client that joins a routerDevices fetching/reporting data
AP (Access Point)Emits its own hotspotConfig portal without a router (phone connects to the device)
AP+STABoth at onceThe classic provisioning architecture: AP collects config → STA joins the router
WiFi.mode(WIFI_STA);              // client mode (most common)
WiFi.mode(WIFI_AP);               // hotspot mode
WiFi.mode(WIFI_AP_STA);           // dual mode

The classic ESP32 is 2.4GHz-only — not finding your 5GHz network is normal, not a fault.


2. Connecting: blocking vs event-driven

1. Blocking version (fine for prototypes)

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");

  while (WiFi.status() != WL_CONNECTED) {
    delay(300);
    Serial.print(".");
  }
  Serial.printf("\nConnected! IP: %s\n", WiFi.localIP().toString().c_str());
}

Problem: if it can’t connect, it waits forever (remember the watchdog from timers?).

2. Event-driven version (production posture)

WiFiEventHandler onGotIP, onDisconnect;

void setupWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);            // automatic reconnection
  WiFi.persistent(false);                 // don't wear the flash with credentials

  onGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &e) {
    Serial.printf("Connected, IP: %s\n", e.ip.toString().c_str());
  });
  onDisconnect = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected &e) {
    Serial.println("Disconnected, auto-reconnecting...");
  });

  WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
}

Key point: never call WiFi.begin() repeatedly in loop — every call restarts the radio and makes things worse. begin once + auto-reconnect + event callbacks is the standard architecture.


3. HTTP: GET and POST

#include <WiFi.h>
#include <HTTPClient.h>

void fetchWeather() {
  HTTPClient http;
  http.begin("http://api.example.com/weather?city=shenzhen");
  int code = http.GET();

  if (code == 200) {
    String payload = http.getString();
    Serial.println(payload);
  }
  http.end();                            // release the connection (mandatory!)
}

void postData() {
  HTTPClient http;
  http.begin("http://api.example.com/log");
  http.addHeader("Content-Type", "application/json");
  int code = http.POST("{\"temp\":25.5}");
  Serial.printf("POST status: %d\n", code);
  http.end();
}

HTTPS: just switch the URL to https:// (root certificates are built in; some self-signed services need config). Production code should be HTTPS-only.


4. ArduinoJson: parsing responses

#include <ArduinoJson.h>

void parseWeather(String json) {
  JsonDocument doc;                          // v7 auto-capacity
  DeserializationError err = deserializeJson(doc, json);
  if (err) { Serial.println("JSON parse failed"); return; }

  const char* city  = doc["city"];
  float temp        = doc["temperature"];
  int humidity      = doc["humidity"];

  Serial.printf("%s: %.1f°C, humidity %d%%\n", city, temp, humidity);
}

v6 and v7 differ (StaticJsonDocument/DynamicJsonDocument vs JsonDocument) — “not declared” errors mean you hit the version watershed, same story as LEDC.


5. Project: the full temperature reporting loop

Wiring together everything from this column (an L2 acceptance project):

// Architecture: timer(collect) → I2C(read AHT20) → Wi-Fi(HTTP report) → non-blocking loop
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

uint32_t tReport = 0;

void loop() {
  if (WiFi.status() == WL_CONNECTED && millis() - tReport > 60000) {
    tReport = millis();
    float temp = readAht20Temp();     // from the buses guide

    JsonDocument doc;
    doc["temp"] = temp;
    String body;
    serializeJson(doc, body);

    HTTPClient http;
    http.begin("http://api.example.com/report");
    http.addHeader("Content-Type", "application/json");
    http.POST(body);
    http.end();
  }
}

6. Pitfall cheat sheet (bookmark this)

SymptomCause and fix
Can’t see the 5GHz network2.4GHz-only — normal
15-second connect timeout5GHz SSID / non-ASCII SSID / wrong password; verify with a phone hotspot
Repeated begin makes it worsebegin once; leave reconnecting to auto-reconnect
HTTP returns -1Requested while offline; or DNS failure — check WiFi.status()
Forgot http.end()Connection leak — later requests fail
Random Brownout rebootsRadio transients ~450mA — power traps in the Chip Selection Guide
JSON “out of memory”v6 manual capacity too small; upgrade to v7 or enlarge
30s stall on bootStale NVS config + blocking wait; go event-driven

7. Further reading


License: CC BY-SA 4.0. Repost & remix with attribution: ESPDIY Maker Lab - espdiy.com