The “Arduino core for ESP32” is why half the world uses this chip. It lets you
write pinMode() and digitalWrite() like on an Arduino UNO, but underneath
there is an ESP32 running FreeRTOS. Understanding that layer is the difference
between copying examples and knowing what you are doing.
What it actually is
It is not an emulated Arduino. Since version 3.x, the core is a wrapper over
ESP-IDF: your setup() and loop() run inside a real FreeRTOS task (called
loopTask), on core 1, with 8 KB of stack by default.
That explains two very typical things:
- If your
loop()hangs, the watchdog fires and the board reboots. - If the project grows, you can create tasks with
xTaskCreatePinnedToCore().
Installation (two ways)
Arduino IDE — add the board URL in Preferences:
https://espressif.github.io/arduino-esp32/package_esp32_index.json
Then install “esp32 by Espressif” from the Boards Manager and pick your board.
PlatformIO (my recommendation) — platformio.ini:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200setup() and loop() under the hood
void setup() {
Serial.begin(115200);
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, !digitalRead(2));
delay(1000);
}
It works, but keep in mind:
delay()blocks the task. With Wi-Fi or several sensors, usemillis().setup()runs once; if you want parallel work, create tasks there.- The watchdog watches the loop: take too long and it reboots.
Anti-blocking version:
unsigned long last = 0;
constexpr unsigned long INTERVAL = 1000;
void loop() {
if (millis() - last >= INTERVAL) {
last = millis();
digitalWrite(2, !digitalRead(2));
}
}Libraries: not all of them work
- Generic Arduino libraries usually work if they do not touch AVR registers.
- Those using timers or specific interrupts may fail.
- For sensors, look for the “ESP32” version or use the vendor library.
Wire(I2C) andSPIexist, but you can choose the pins.
Differences from an Arduino UNO
| Thing | Arduino UNO | ESP32 |
|---|---|---|
| Voltage | 5 V | 3.3 V |
| Bits | 8 | 32 |
| EEPROM | 1 KB | none (use Preferences) |
| ADC | 10-bit | 12-bit (and ADC2 with Wi-Fi, careful) |
| Wi-Fi | ❌ | ✅ |
| Cores | 1 | 2 |
Saving something in the ESP32 “EEPROM” is done like this:
#include <Preferences.h>
Preferences prefs;
void setup() {
prefs.begin("my-app", false);
int boots = prefs.getInt("n", 0) + 1;
prefs.putInt("n", boots);
prefs.end();
}Good practices
- Do not block the loop: use
millis(), tasks orvTaskDelay. - Serial at 115200, and wait a moment after
begin()if needed. - Do not abuse
String: it fragments memory. Usechar[]andsnprintf. - Split your code into files; a 2000-line
.inois painful. - Debug with logs, not
Serial.printlneverywhere.
When to move to ESP-IDF
When you need fine control over tasks and memory, precise timing, serious OTA partitions, or to squeeze power. The Arduino core hides a lot, and sometimes that is fine… until it is not.
Summary
- It is a wrapper over ESP-IDF; your
setup/loopare a FreeRTOS task. delay()blocks; learnmillis()from day one.- 3.3 V, no EEPROM (use NVS/Preferences), 12-bit ADC.
- Not every Arduino library compiles: check compatibility.
- Current core version: 3.3.11 (on ESP-IDF 5.5.5).
Next step: ESP-IDF in depth for when you need total control. Or go back to the series hub.