ESP32 follow-on reference

ESP32 Reference

Lookup notes for the ESP32 follow-on lessons: 3.3 V I/O, strapping pins, ADC, Wi-Fi, HTTP and JSON, MQTT, BLE, deep sleep, FreeRTOS and OTA. The Uno language and I/O notes are on the Theory Reference.

ESP32 guide (Units G-I)

Lookup notes for the ESP32 track. Practical wiring and full sketches stay in the ESP32 follow-on lessons. Classic ESP32 DevKit / ESP32 Dev Module is the default lab board unless a lesson says otherwise.

note

ESP32 3.3 V I/O safety

ESP32-family GPIO is 3.3 V class. Do not drive an ESP32 input from a 5 V Uno output or a 5 V sensor signal without level shifting. Many modules accept 5 V on USB or VIN while the chip I/O remains 3.3 V.

Design LED pin current around 12 mA or less. NeoPixel / WS2812 strips need an external 5 V supply and common GND for more than a few LEDs - do not power a long string from the ESP32 3.3 V pin.

3.3 V ESP32 I/O versus 5 V Uno I/O
Same IDE idea, different voltage class.
table

Strapping pins and boot

Some GPIOs are sampled at reset to select boot mode. Avoid using them for buttons that might be held during reset, or learn the safe idle level for your board. Exact sets vary by chip; classic ESP32 commonly cares about GPIO 0, 2, 12 and 15.

Strapping pins to treat carefully at boot
Check your module pinout before committing a wire.
TopicCourse habit
GPIO 0Often BOOT - leave free or know pull state
GPIO 2Often onboard LED - OK for blink; watch boot
GPIO 12 / 15Strapping on classic ESP32 - prefer other pins for critical inputs
ADC1 pinsPrefer ADC1 (e.g. GPIO 34-39 on classic) when Wi-Fi is on
note

ESP32 ADC notes

Arduino-ESP32 often exposes 12-bit analogRead (0-4095). That is not the same scale as Uno 0-1023. The ADC is useful for pots and relative sensing; it is not a precision lab meter. Attenuation settings change the usable voltage range. Nonlinearity is worse near the rails.

Classic ESP32 DAC exists on GPIO 25 and 26. Many C-series chips have no DAC - check the datasheet.

10-bit Uno versus 12-bit ESP32 ADC counts
More counts, still not a calibrated instrument.
note

WiFi Station and Access Point

Station (STA): the ESP32 joins your router. After WL_CONNECTED, WiFi.localIP() is the LAN address (usually from DHCP); gatewayIP() is typically the router; RSSI() is signal strength in dBm (more negative is weaker).

Access Point (AP / SoftAP): the ESP32 creates a hotspot for phones to join (handy for first-time setup). softAPIP() is often 192.168.4.1 - a different address from the STA LAN IP.

Classic ESP32 is 2.4 GHz only. Use Serial.begin(115200). Keep WiFi credentials as placeholders - never commit real passwords to shared repos.

Station mode versus Access Point mode
STA joins a router. AP is its own hotspot.
table

HTTP request and response

HTTP is the text protocol behind browsers and many APIs. A request has a method and path; a response has a status code and often a body. ESP32 Lesson 09 is HTTP server; ESP32 Lesson 10 is HTTP client.

PieceCourse habit
GETRead a page or API document
POSTSend a body (form fields or JSON) to create/update
200OK - body is usable
303 + LocationRedirect after an action so refresh is safe
-1 (HTTPClient)Transport fail - DNS, WiFi, timeout, blocked HTTP
Content-Typetext/html for pages; application/json for APIs
note

JSON for IoT

{
  "room": "Lab room",
  "level": 2048,
  "alert": true,
  "sensor": {
    "temp_c": 21.5,
    "humid": 48
  },
  "tags": ["lab", "bench"]
}

JSON (JavaScript Object Notation) is structured text used by almost every web API and many MQTT payloads. If you can read the braces and quotes, you can invent your own documents for a room node, weather reply or heater command.

ArduinoJson by Benoit Blanchon (JsonDocument in v7) turns that text into values your sketch can test. Workflow: HTTP status 200 (or a known MQTT string) -> deserializeJson -> check DeserializationError -> read fields with optional | defaults -> then act. Print the raw body when a path looks wrong.

The six JSON value types

"text"string

Always double quotes. Keys are strings too. Single quotes are invalid JSON.

21.5number

Integer or decimal. No units in the value - put units in the key name (temp_c).

true / falseboolean

Lowercase only. Not True or TRUE.

nullnull

Means no value. Different from 0 or "".

{ ... }object

Unordered set of "key": value pairs, comma-separated. Nested objects are normal.

[ ... ]array

Ordered list of values (any type). Index from 0.

Rules that break parsers

,Trailing comma

No comma after the last item in an object or array.

'Quotes

Keys and strings must use ". Comments (//) are not allowed in strict JSON.

NaNSpecial numbers

Avoid NaN / Infinity - many parsers reject them. Send null or omit the field.

UTF-8Encoding

Stick to plain UTF-8 text. Escape newlines inside strings as \n.

Path in the exampleArduinoJson readResult type
roomdoc["room"]string
leveldoc["level"] | 0number (with default)
alertdoc["alert"] | falseboolean
sensor.temp_cdoc["sensor"]["temp_c"]number (nested)
tags[0]doc["tags"][0]first array string
Tip: Course labs stay on plain HTTP JSON where possible so certificates do not hide the structure lesson.

Nesting and arrays

// Nested object
float t = doc["sensor"]["temp_c"] | -999.0;

// Array of objects
// { "slides": [ { "title": "Wake up" }, { "title": "..." } ] }
const char* title = doc["slides"][0]["title"] | "?";

Objects hold named fields. Arrays hold ordered items. Mix them freely: an object can contain an array of objects (weather forecasts, slide decks, RFID events).

Zero-based indexes: the first element is [0]. Missing keys or out-of-range indexes yield null / empty - use | defaults so your sketch does not act on garbage.

Write your own JSON

// Hand-built (small)
String body = "{\"temp\":";
body += String(t, 1);
body += ",\"heater\":";
body += heaterOn ? "true" : "false";
body += "}";
// -> {"temp":21.5,"heater":false}

// ArduinoJson build (safer for growth)
JsonDocument out;
out["temp"] = t;
out["heater"] = heaterOn;
serializeJson(out, Serial);

Design the document before the sketch. Pick short stable key names, one clear meaning each, and nest only when a group of fields belongs together (sensor, wifi, alarm).

Building text by hand is fine for small payloads: start with {, add "key": value pairs with commas, close with }. For larger documents, prefer ArduinoJson serializeJson so commas and quotes stay valid.

Tip: Match names exactly on publisher and subscriber - temp and Temp are different keys.

Parse checklist

1) Confirm the body is JSON (not an HTML error page). 2) deserializeJson into a JsonDocument. 3) If error, print the error and the raw body. 4) Read only the fields you need with | defaults. 5) Then drive GPIO / Serial / MQTT.

MistakeFix
Parse before HTTP 200Check status first
Wrong nesting pathPrint body; walk keys one level at a time
Expecting a number, got a stringAPI may send "21.5" - read as string or use as<float>() carefully
Heap blow-upKeep documents small; avoid copying huge String bodies forever
note

Local web server habit

WebServer on port 80 maps URL paths to handler functions (server.on). loop must call server.handleClient() often - a long delay blocks the page.

A 303 redirect after an action (for example /led/on) sends the browser back to / so the page refreshes. Raw string literals R"=====( ... )=====" make HTML easier to embed without escaping every quote.

Browser GET request and ESP32 HTML response
Request path selects the handler; response can be HTML or a redirect.
table

MQTT, BLE and deep sleep

Quick map of Units H-I topics. Prefer the dedicated entries below for BLE, deep sleep, FreeRTOS and OTA. MQTT topics are exact string matches; payloads are often plain text or JSON.

TopicOne-line idea
MQTTPublish/subscribe through a broker - live updates without HTTP polling
QoS / retainDelivery effort and last-message memory on the broker (overview in ESP32 Lesson 11)
BLEPhone talks to a GATT service/characteristic - prefer BLE over Classic SPP
Deep sleepTimer or GPIO wake; RTC memory can keep a small counter
OTAArduinoOTA updates firmware over Wi-Fi after first USB upload
note

BLE GATT essentials

BLE peripheral advertises; phone (central) connects and uses GATT. A service groups characteristics; each characteristic is a readable/writable value identified by a UUID. Properties include READ, WRITE and NOTIFY. Labs often use custom 128-bit UUIDs.

Prefer BLE for short-range phone control without a router. Prefer Wi-Fi for LAN browsers, MQTT and cloud HTTP. Classic Bluetooth is legacy for new ESP32 labs. ESP32-P4 needs a companion radio for BLE.

Phone writing a BLE characteristic on an ESP32 GATT server
Advertise, connect, write a characteristic, drive GPIO.
TermRole
Peripheral / serverESP32 advertising GATT
Central / clientPhone app
ServiceGroup of characteristics
CharacteristicThe value you read or write
NOTIFYServer pushes updates to the phone
note

Deep sleep and wake

Deep sleep powers down most of the chip; normal SRAM is lost and wake typically restarts into setup(). Arm a timer with esp_sleep_enable_timer_wakeup(us) then call esp_deep_sleep_start(). Other wake sources include EXT0/EXT1 GPIOs and touch on supported pins.

RTC_DATA_ATTR keeps a variable across deep sleep but not across power loss - use Preferences/NVS for durable settings. DevKit USB-UART chips and LEDs can hide true sleep current on a USB meter.

Wake, work, deep sleep, timer wake cycle
Duty-cycle: wake, work, sleep.
note

FreeRTOS tasks on ESP32

Arduino-ESP32 runs on FreeRTOS. setup()/loop() usually run on core 1; WiFi/BT work often uses core 0. Create workers with xTaskCreatePinnedToCore; wait with vTaskDelay(pdMS_TO_TICKS(ms)). Start stack sizes around 2048 and raise if the board reboots under load.

Shared globals without protection cause race conditions - use queues or mutexes when tasks share data. Do not add a task for every blink; millis in one loop is often enough.

Dual-core FreeRTOS tasks on ESP32
Pin workers thoughtfully; keep networking responsive.
note

OTA updates

ArduinoOTA: after STA connect, call ArduinoOTA.begin() and ArduinoOTA.handle() often in loop. First flash is USB; later uploads can use Tools → Port → network hostname. Set a unique hostname and an OTA password outside trusted lab LANs. Keep USB recovery available.

HTTP OTA (device pulls an image URL) is the usual field-fleet pattern - different from IDE ArduinoOTA.

IDE OTA upload over WiFi to ESP32
USB once, then network uploads with handle() kept alive.
note

NeoPixel / WS2812 power

Addressable LEDs chain data (DIN to DOUT). Full white can draw about 60 mA per LED - budget the 5 V supply. Share GND with the ESP32. Put a series resistor on the data line (~330-470 ohm) and a bulk capacitor on 5 V near the strip. Call show() after you change pixel colours. Library for this course: Adafruit NeoPixel.

ESP32 data line and external 5 V NeoPixel supply with common GND
External 5 V for the strip; common GND; data from a GPIO.