ESP32 Complete Beginner's Guide: Pinout, WiFi, GPIO, and First Projects
Everything you need to go from unboxing an ESP32 to building WiFi-connected sensors, web servers, and MQTT IoT devices -- with complete, tested code for every example.
Contents
Why ESP32? ESP32 vs Arduino vs Raspberry Pi
| Feature | ESP32 | Arduino Uno | Raspberry Pi 4 |
|---|---|---|---|
| CPU | Dual-core 240 MHz | 16 MHz AVR | Quad-core 1.8 GHz ARM |
| RAM | 520 KB SRAM | 2 KB SRAM | 4-8 GB |
| WiFi | [YES] Built-in 2.4GHz | [NO] (shield needed) | [YES] Built-in |
| Bluetooth | [YES] BLE + Classic | [NO] | [YES] BLE |
| Power draw | ~240 mA active / 10 uA deep sleep | ~50 mA | ~3.4 W |
| OS | FreeRTOS (bare-metal) | None | Linux |
| Cost | ~$4-8 | ~$25 | ~$55+ |
| Best for | IoT, sensors, battery projects | Simple real-time control | Complex software, vision |
ESP32 Pinout Reference
ESP32 DevKit v1 (30-pin) -- Key Pins
??????????????????????????????????
GPIO 0 -- Boot mode select (pull HIGH for normal boot)
GPIO 2 -- Onboard LED (active HIGH on most boards)
GPIO 4 -- General purpose I/O
GPIO 5 -- SPI SS / General purpose
GPIO 12 -- JTAG TDI / ADC2_CH5 (avoid as OUTPUT on boot)
GPIO 13 -- ADC2_CH4 / General purpose
GPIO 14 -- ADC2_CH6 / General purpose
GPIO 15 -- JTAG TDO / ADC2_CH3
GPIO 16 -- UART2 RX
GPIO 17 -- UART2 TX
GPIO 18 -- SPI CLK
GPIO 19 -- SPI MISO
GPIO 21 -- I2C SDA
GPIO 22 -- I2C SCL
GPIO 23 -- SPI MOSI
GPIO 25 -- DAC1 / ADC2_CH8
GPIO 26 -- DAC2 / ADC2_CH9
GPIO 27 -- ADC2_CH7
GPIO 32 -- ADC1_CH4 (WiFi-safe ADC)
GPIO 33 -- ADC1_CH5 (WiFi-safe ADC)
GPIO 34 -- INPUT ONLY / ADC1_CH6
GPIO 35 -- INPUT ONLY / ADC1_CH7
GPIO 36 -- INPUT ONLY (VP)
GPIO 39 -- INPUT ONLY (VN)
??????????????????????????????????
[WARN] ADC2 pins don't work reliably when WiFi is active -- use ADC1 (GPIO 32-39)
[WARN] GPIO 6-11 are connected to internal flash -- never use them
Arduino IDE Setup
- Open Arduino IDE -> File -> Preferences
- Add to "Additional Boards Manager URLs":
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - Go to Tools -> Board -> Boards Manager, search "esp32", install esp32 by Espressif Systems
- Select board: Tools -> Board -> ESP32 Arduino -> ESP32 Dev Module
- Set upload speed: Tools -> Upload Speed -> 921600
[WARN] Driver issue on Windows? Install the CP210x or CH340 USB-UART driver depending on your board's chip. Check the USB chip marking on the board.
GPIO: Digital I/O, PWM, ADC
Digital Output -- Blink LED
const int LED_PIN = 2;
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED ON");
delay(1000);
digitalWrite(LED_PIN, LOW);
Serial.println("LED OFF");
delay(1000);
}
Digital Input -- Button with Debounce
const int BTN_PIN = 14;
const int LED_PIN = 2;
unsigned long lastDebounce = 0;
const int DEBOUNCE_MS = 50;
int lastState = HIGH;
bool ledState = false;
void setup() {
pinMode(BTN_PIN, INPUT_PULLUP); // internal pull-up, button connects to GND
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
}
void loop() {
int reading = digitalRead(BTN_PIN);
if (reading != lastState) {
lastDebounce = millis();
}
if ((millis() - lastDebounce) > DEBOUNCE_MS) {
if (reading == LOW) { // button pressed (active LOW)
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
Serial.println(ledState ? "ON" : "OFF");
}
}
lastState = reading;
}
PWM -- Fade LED / Control Servo
// ESP32 uses ledcWrite() -- NOT analogWrite()
const int LED_PIN = 2;
const int PWM_CHAN = 0;
const int PWM_FREQ = 5000; // Hz
const int PWM_RES = 8; // 8-bit = 0-255
void setup() {
ledcSetup(PWM_CHAN, PWM_FREQ, PWM_RES);
ledcAttachPin(LED_PIN, PWM_CHAN);
}
void loop() {
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(PWM_CHAN, duty);
delay(10);
}
for (int duty = 255; duty >= 0; duty--) {
ledcWrite(PWM_CHAN, duty);
delay(10);
}
}
ADC -- Read Analog Sensor (Use ADC1 with WiFi!)
const int SENSOR_PIN = 34; // ADC1 -- works with WiFi active
void setup() {
Serial.begin(115200);
analogReadResolution(12); // 12-bit = 0-4095 (default)
analogSetAttenuation(ADC_11db); // full 0-3.3V range
}
void loop() {
int raw = analogRead(SENSOR_PIN);
float voltage = raw * (3.3 / 4095.0);
Serial.printf("Raw: %d Voltage: %.3f V\n", raw, voltage);
delay(500);
}
WiFi: Station and Access Point Mode
Station Mode -- Connect to Router
#include <WiFi.h>
const char* SSID = "YourWiFiName";
const char* PASSWORD = "YourPassword";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASSWORD);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected! IP: " + WiFi.localIP().toString());
Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
}
Built-in Web Server -- Control GPIO from Browser
#include <WiFi.h>
#include <WebServer.h>
const char* SSID = "YourSSID";
const char* PASS = "YourPassword";
const int LED = 2;
WebServer server(80);
void handleRoot() {
String html = R"(
<!DOCTYPE html><html><head>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<style>body{font-family:sans-serif;text-align:center;padding:40px}
.btn{padding:20px 40px;font-size:20px;border:none;border-radius:8px;cursor:pointer}
.on{background:#27ae60;color:white} .off{background:#e74c3c;color:white}</style>
</head><body>
<h2>ESP32 GPIO Control</h2>
<a href='/on'><button class='btn on'>LED ON</button></a>
<a href='/off'><button class='btn off'>LED OFF</button></a>
</body></html>)";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("IP: " + WiFi.localIP().toString());
server.on("/", handleRoot);
server.on("/on", []() { digitalWrite(LED, HIGH); server.sendHeader("Location","/"); server.send(302); });
server.on("/off", []() { digitalWrite(LED, LOW); server.sendHeader("Location","/"); server.send(302); });
server.begin();
}
void loop() {
server.handleClient();
}
MQTT IoT Sensor Node
// Required libraries: PubSubClient, DHT sensor library
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#define DHTPIN 4
#define DHTTYPE DHT22
const char* SSID = "YourSSID";
const char* PASS = "YourPassword";
const char* MQTT_BROKER = "192.168.1.100";
const int MQTT_PORT = 1883;
const char* TOPIC_TEMP = "home/sensor/temperature";
const char* TOPIC_HUM = "home/sensor/humidity";
DHT dht(DHTPIN, DHTTYPE);
WiFiClient espClient;
PubSubClient mqtt(espClient);
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("Connecting MQTT...");
if (mqtt.connect("ESP32Sensor")) {
Serial.println("connected");
} else {
Serial.printf("failed (rc=%d), retry in 5s\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
dht.begin();
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
}
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (!isnan(temp) && !isnan(hum)) {
char buf[10];
snprintf(buf, sizeof(buf), "%.1f", temp);
mqtt.publish(TOPIC_TEMP, buf, true); // retained message
snprintf(buf, sizeof(buf), "%.1f", hum);
mqtt.publish(TOPIC_HUM, buf, true);
Serial.printf("Published -- Temp: %.1f degreesC Humidity: %.1f%%\n", temp, hum);
}
delay(30000); // publish every 30 seconds
}
Deep Sleep for Battery Projects
// Deep sleep draws only ~10 uA -- extends battery life from days to months
#include <esp_sleep.h>
#define uS_TO_S 1000000ULL
#define SLEEP_SECONDS 300 // wake every 5 minutes
RTC_DATA_ATTR int bootCount = 0; // persists across deep sleep
void setup() {
Serial.begin(115200);
bootCount++;
Serial.printf("Boot #%d\n", bootCount);
// --- Do your work here (read sensor, send MQTT, etc.) ---
readAndPublishSensor();
// --------------------------------------------------------
Serial.println("Going to deep sleep for 5 minutes...");
esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * uS_TO_S);
esp_deep_sleep_start(); // execution stops here; setup() runs again on wake
}
[YES] Power tip: For battery-powered sensors, budget your wake time. A DHT22 read + WiFi connect + MQTT publish + disconnect typically takes 3-5 seconds at ~180 mA. Then 5 minutes at 10 uA deep sleep. Average current: ~0.5 mA -- a 2000 mAh battery lasts ~167 days.
Key Takeaways: Never use ADC2 pins when WiFi is active -- they share the radio hardware. Use ledcWrite() for PWM, not analogWrite(). Design battery projects around deep sleep -- it's transformative. MQTT over WiFi is the standard protocol for ESP32 IoT sensor nodes.
ESP32IoTArduinoWiFiMQTTGPIOEmbedded
0 Comments