gwordal

Lesson 1 of 5 · 16 min

The ESP32 at a glance

The Arduino Uno taught you the shape of every embedded program: configure, act, wait. The ESP32 keeps that shape but changes the scale. It is a 32-bit dual-core system with a built-in radio, priced like an Uno clone. The price of that power is a handful of rules you must respect, because a board that connects to Wi-Fi also has far more ways to surprise you. This lesson maps the chip, and the traps, before we use the radio.

What is inside the chip

The classic ESP32 has two Xtensa LX6 cores that run at up to 240 MHz, 520 KB of on-chip SRAM, a Wi-Fi radio (802.11 b/g/n, 2.4 GHz only) and Bluetooth with both classic and Low Energy (BLE). Program code lives in external SPI flash, typically 4 MB, which the chip reads through a cache.

FeatureArduino Uno (ATmega328P)ESP32 (classic)
CPU8-bit AVR, 1 core, 16 MHz32-bit Xtensa LX6, 2 cores, up to 240 MHz
SRAM2 KB520 KB
Program storage32 KB on chip4 MB typical, external flash
Logic level5 V3.3 V
ADC10-bit, 6 channels12-bit, up to 18 channels, two units
RadiononeWi-Fi + Bluetooth classic + BLE

Do the ratios: 240 MHz / 16 MHz = 15 times the clock, and 520 KB / 2 KB = 260 times the RAM. A single TLS connection needs tens of kilobytes for its buffers, which an Uno could never hold. That is why networking only became practical on hobby boards once chips like this arrived.

Drag to orbit

Blue pins: analog inputs. Orange pins: digital I/O.

A development board. Real ESP32 boards label some pins as input only, and a few as boot-sensitive. Check your board's pinout sheet.

Two cores and an RTOS

Underneath the Arduino API the ESP32 runs FreeRTOS, a small real-time operating system. The Arduino setup() and loop() run inside one task, pinned to core 1. The Wi-Fi and TCP/IP stack run in system tasks, mostly on core 0. So while your loop() is busy, the radio keeps working in the background and the connection stays alive.

That has a consequence worth knowing: a long delay() in loop() no longer freezes the whole chip, but a task that blocks for seconds can still starve your own network code. The habit from the Uno course, using millis() instead of long delays, matters even more here.

3.3 V logic

Every ESP32 pin works at 3.3 V, and pins are not 5 V tolerant. A pin can only drive a modest current (design for 12 mA or less, the datasheet absolute maximum is 40 mA), and feeding it 5 V can damage the input stage over time or at once.

To read a 5 V signal, use a resistor divider:

Vout = Vin x R2 / (R1 + R2) = 5 V x 2 kΩ / (1 kΩ + 2 kΩ) = 3.33 V

For a fast or bidirectional signal, such as I2C to a 5 V device, use a proper level-shifter module instead.

The GPIO matrix and pin traps

On an Uno, pin 9 means Timer 1 and nothing else. On the ESP32, most peripheral signals (UART, SPI, I2C, PWM) go through a GPIO matrix, a programmable crossbar that can connect a peripheral to almost any pin. That freedom is why Wire.begin(21, 22) can be moved to other pins in one line.

Freedom has exceptions:

  • GPIO 34 to 39 are input only and have no internal pull-up or pull-down.
  • GPIO 6 to 11 are wired to the flash chip. Never use them.
  • Strapping pins are sampled at reset to choose the boot mode. If your circuit pulls them the wrong way, the board will not start.
PinSampled at boot forSafe rule
GPIO 0Low means serial download modeLeave floating or high at power-up
GPIO 2Must be low or floating to flashFine for the on-board LED
GPIO 12Flash voltage (high selects 1.8 V)Do not hold high at boot on 3.3 V flash
GPIO 5, 15Boot log and timingAvoid heavy loads at boot

GPIO 12 is the classic trap: a sensor that pulls it high at power-up makes the chip configure the flash for the wrong voltage, and the board crash-loops with no useful message.

The ADC2 and Wi-Fi conflict

The ESP32 has two ADC units. ADC1 is on GPIO 32 to 39. ADC2 is on GPIO 0, 2, 4, 12 to 15 and 25 to 27, but the Wi-Fi driver needs ADC2 for itself. While Wi-Fi is running, analogRead() on an ADC2 pin fails and returns garbage or zero.

So for any project that connects to a network, put analog sensors on ADC1. A 12-bit reading gives 2^12 = 4096 steps, so with a 3.3 V range one step is 3.3 V / 4095 = 0.8 mV. The converter is somewhat nonlinear near the ends of its range, so for readings in millivolts use analogReadMilliVolts(), which applies factory calibration.

Your first ESP32 sketch

This sketch reports what the chip really has, then blinks. Open the Serial Monitor at 115200 baud.

const uint8_t LED_PIN = 2;   // on-board LED on many DevKit boards (also a strapping pin)

void setup() {
  Serial.begin(115200);      // the ESP32 console default is much faster than 9600
  delay(500);                // let the monitor attach

  Serial.printf("Chip: %s rev %d, %d cores\n",
                ESP.getChipModel(), ESP.getChipRevision(), ESP.getChipCores());
  Serial.printf("CPU: %u MHz\n", ESP.getCpuFreqMHz());
  Serial.printf("Heap: %u of %u bytes free\n", ESP.getFreeHeap(), ESP.getHeapSize());
  Serial.printf("Flash: %u KB\n", ESP.getFlashChipSize() / 1024);
  Serial.printf("setup() runs on core %d\n", xPortGetCoreID());

  pinMode(LED_PIN, OUTPUT);  // configure the LED pin as an output
}

void loop() {
  digitalWrite(LED_PIN, HIGH);   // 3.3 V, LED on
  delay(500);
  digitalWrite(LED_PIN, LOW);    // 0 V, LED off
  delay(500);
}

Expect to see a free heap well under 520 KB. Part of the SRAM is reserved for the system, and Wi-Fi will take a further share when you turn it on in the next lesson.

Check yourself

Which pin group is safe for analogRead while Wi-Fi is running?

Check yourself

A sensor outputs 5 V and you build a divider with 1 kΩ on top and 2 kΩ to ground. What voltage reaches the ESP32 pin?