ESP32 or Arduino Uno: What Breaks When You Move a Sketch From One Board to the Other
The same `setup()` and `loop()`, but ints of different sizes, a 1023 ADC against a 4095 one, 5 V against 3.3 V, and no printf. Twelve differences that make a sketch from the internet fail to compile or quietly misbehave — and how to fix them.
Why it matters
Most examples on the internet are written for an Arduino Uno, while projects are more and more often built on an ESP32 — and the other way round: someone takes a ready ESP32 sketch and flashes it onto a Nano. The language is the same and the functions look alike, but the boards are very different. Below are the differences that break sketches most often, from compile errors to silent bugs.
A quick comparison
| Arduino Uno (ATmega328P) | ESP32 | |
|---|---|---|
| Core | 8-bit, 16 MHz | 32-bit, 2 × 240 MHz |
| RAM | 2 KB | 320 KB (+ PSRAM) |
| Logic | 5 V | 3.3 V |
| ADC | 10-bit, 0–1023, linear | 12-bit, 0–4095, non-linear |
| PWM | analogWrite() only on ~ pins |
LEDC on any output |
| Interrupts | D2 and D3 only | any GPIO |
| Wi-Fi / Bluetooth | none | yes |
The language: int, double and printf
intis 16 bits on AVR.int x = 30000; x += 5000;gives −30536 on an Uno and 35000 on an ESP32. Uselong/unsigned longorint32_tfor millis, counters and products.60 * 1000is especially sneaky — on an Uno it already overflows: write60000UL.doubleisfloaton AVR. Only 6–7 significant digits. On an ESP32doubleis a real 64-bit type.- No
Serial.printf()on AVR. ESP32 sketches withprintfdo not compile on an Uno. Replace it with a chain ofSerial.print(), or usesnprintf()into a buffer. %fdoes not work in AVRsprintf— it prints?instead of the number. For decimals usedtostrf(value, width, digits, buf).
The hardware: voltages and pins
- 5 V on an ESP32 input does damage. A sensor powered from 5 V that outputs 5 V (HC-SR04 ECHO, some relay modules) needs a divider or a level shifter.
- 3.3 V from an ESP32 may not be enough. 5 V modules (relays, HC-SR501, an LCD 1602) are unreliable from 3.3 V, and a 3.3 V logic "one" is sometimes not recognised by 5 V logic.
- The ADC: 1023 vs 4095.
map(analogRead(A0), 0, 1023, …)gives four times too much on an ESP32, and the reverse on an Uno. The ESP32 ADC is also non-linear near its ends — for accuracy there isanalogReadMilliVolts(), which AVR does not have. - PWM. The ESP32 3.x core has
analogWrite()too, but for frequency and resolution useledcAttach(pin, freq, bits)andledcWrite(pin, duty). On an Uno PWM exists only on 3, 5, 6, 9, 10 and 11, at a fixed 490 Hz (980 Hz on 5 and 6).analogWrite()on a pin without~simply switches it fully on or off. - Interrupts. On an Uno
attachInterrupt()works only for D2 and D3, and its first argument is the interrupt number — so writedigitalPinToInterrupt(pin), not justpin. On an ESP32 the handler is markedIRAM_ATTR; AVR has no such keyword. - Pin numbers.
LED_BUILTINis 13 on an Uno and 2 on an ESP32 DevKit. I2C is A4/A5 on an Uno and 21/22 on an ESP32; SPI is 13/12/11/10 against 18/19/23/5.
Memory and libraries
- 2 KB of RAM. A 128×64 OLED buffer alone is 1 KB, half of an Uno's memory. Big
Strings, arrays and libraries that run "easily" on an ESP32 overflow the stack on an Uno without any message — the board just resets. Keep string constants in flash:Serial.println(F("text")). - Single-platform libraries.
WiFi.h,Preferences.h,ESP32Servo.handTFT_eSPIdo not exist on AVR. On an Uno useEEPROM.hinstead ofPreferencesandServo.hinstead ofESP32Servo. And the reverse: libraries that poke AVR registers (TimerOne,SoftwareSerialin old versions) do not compile on an ESP32; there you haveHardwareSerial Serial2and hardware timers.
Try it in the simulator
The simulator reproduces all of these differences. Pick a board in the list next to the examples, and the example opens already adapted to it. Compare, for example, potentiometer and PWM on an ESP32 with the same example on an Uno: ledcWrite became analogWrite, 4095 became 1023, and printf a chain of print. Try writing Serial.printf() for the Uno yourself — the compiler tells you what to use instead.