If you are trying to run MicroPython on a classic Arduino Uno or Nano (ATmega328P), stop right now: it will not work. Those 8-bit AVR boards only have 2KB of SRAM and 32KB of Flash, which is roughly 100 times too small for the MicroPython runtime. To run MicroPython on Arduino hardware, you must use their modern 32-bit boards based on ARM Cortex or ESP32 silicon.
This guide targets the Arduino Nano ESP32 (ABX00072), which houses an ESP32-S3 chip. It is currently the most capable official Arduino board for MicroPython, offering 512KB of SRAM, native Wi-Fi/Bluetooth, and a native UF2 bootloader that makes flashing firmware as easy as dragging and dropping a file. Below, we will wire up an I2C environmental sensor, write robust connection code, and troubleshoot the exact errors that brick your workflow.
Hardware Spec Sheet and Parts List
Before writing code, verify you have the correct board variant. The Arduino Nano RP2040 Connect also runs MicroPython, but the Nano ESP32 is preferred for IoT projects due to its larger memory footprint and native wireless radios.
| Component | Exact Variant / Model | Approx. Cost (2026) | Notes |
|---|---|---|---|
| Microcontroller | Arduino Nano ESP32 (ABX00072) | $24.00 | Ensure it says 'ESP32' on the silkscreen, not 'RP2040'. |
| Sensor | BME280 I2C Breakout (3.3V) | $4.50 | Do not buy the 5V-only BMP180; the ESP32-S3 GPIOs are not 5V tolerant. |
| Wiring | 22 AWG solid core jumper wires | $5.00 | Pre-cut kits are fine; keep I2C runs under 12 inches. |
| Cable | USB-C to USB-A Data Cable | $8.00 | Must be a data-sync cable, not a charge-only cable. |
Pin Mapping Table: Nano ESP32 to I2C Sensor
The Arduino Nano ESP32 uses an ESP32-S3 under the hood. The silkscreen on the board uses classic Arduino naming (A4, A5), but MicroPython addresses the underlying Espressif GPIO numbers. Always use the GPIO numbers in your machine.Pin definitions.
| Board Silkscreen | ESP32-S3 GPIO Number | Function | BME280 Pin |
|---|---|---|---|
| A4 | GPIO 11 | I2C SDA | SDI / SDA |
| A5 | GPIO 12 | I2C SCL | SCK / SCL |
| 3V3 | N/A (Power Rail) | 3.3V Power | VCC / VIN |
| GND | N/A (Ground) | Common Ground | GND |
Flashing the Firmware and Writing the Code
Unlike older ESP32 dev boards that require esptool.py and manual boot-button holding, the Nano ESP32 supports UF2 flashing. Download the latest .uf2 firmware from the official MicroPython download page. Double-tap the reset button on the board to enter bootloader mode (the RGB LED will pulse green), then drag and drop the .uf2 file onto the mounted NANOESP USB drive.
/dev/ttyACM0 (Linux) corresponding to your board.
Complete Compilable Code: I2C Scan and Wi-Fi Connect
This script verifies your I2C wiring by scanning the bus, then attempts a Wi-Fi connection with robust timeout and error handling. It uses the exact GPIO mappings defined above.
import machine
import time
import network
# --- PIN DEFINITIONS ---
# Arduino Nano ESP32 (ESP32-S3) I2C Pins
SDA_PIN = 11 # Silkscreen A4
SCL_PIN = 12 # Silkscreen A5
I2C_FREQ = 400000
# Wi-Fi Credentials
WIFI_SSID = 'YourNetworkName'
WIFI_PASS = 'YourNetworkPassword'
def scan_i2c_bus():
print('Initializing I2C bus...')
try:
i2c = machine.I2C(0, sda=machine.Pin(SDA_PIN), scl=machine.Pin(SCL_PIN), freq=I2C_FREQ)
devices = i2c.scan()
if not devices:
print('ERROR: No I2C devices found. Check wiring and pull-up resistors.')
else:
for dev in devices:
print(f'Found I2C device at address: {hex(dev)}')
except Exception as e:
print(f'I2C Initialization Failed: {e}')
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if wlan.isconnected():
print('Already connected to Wi-Fi.')
return wlan.ifconfig()
print(f'Connecting to {WIFI_SSID}...')
try:
wlan.connect(WIFI_SSID, WIFI_PASS)
# Timeout after 15 seconds
timeout = 15
while not wlan.isconnected() and timeout > 0:
time.sleep(1)
timeout -= 1
print('.', end='')
if wlan.isconnected():
print('\nConnection successful!')
print('Network config:', wlan.ifconfig())
return wlan.ifconfig()
else:
raise OSError('Wi-Fi connection timed out.')
except OSError as e:
print(f'\nWi-Fi Connection Error: {e}')
return None
# --- MAIN EXECUTION ---
if __name__ == '__main__':
# Blink onboard LED to confirm script start
led = machine.Pin(48, machine.Pin.OUT) # GPIO48 is the user LED on Nano ESP32
led.value(1)
time.sleep(0.5)
led.value(0)
scan_i2c_bus()
connect_wifi()
print('Setup complete. Entering main loop.')
while True:
time.sleep(5)
Debugging: Flashing Failures and REPL Timeouts
When working with the ESP32-S3 via MicroPython, you will inevitably hit communication errors between your PC and the board's USB-to-UART bridge. The most common and frustrating error occurs when Thonny or esptool fails to handshake with the bootloader.
A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet headerOr in Thonny:
ConnectionError: Failed to open REPL. Device is busy or port is locked.
The First Three Things to Check
- The USB-C Cable: 80% of 'timed out' errors are caused by charge-only cables. If your PC doesn't chime when you plug it in, or if it doesn't show up in Device Manager, swap the cable. You need all 4 internal data wires intact.
- Bootloader / DFU Mode: The ESP32-S3 sometimes gets stuck in a state where it ignores the auto-reset signal from the IDE. Double-tap the physical reset button on the Nano ESP32 quickly. The LED should pulse green, indicating it is in ROM bootloader mode and ready to accept firmware or REPL commands.
- Port Locks and Permissions: If Thonny crashed previously, it may still hold the serial port hostage. Close Thonny completely. On Linux, ensure your user is in the
dialoutgroup (sudo usermod -a -G dialout $USER). On Windows, check Device Manager to ensure no other software (like Cura or a 3D printer slicer) has claimed the COM port.
Ranked Causes for Persistent Timeouts
If the first three checks fail, investigate these edge cases in order of likelihood:
- Corrupted Filesystem: The MicroPython LittleFS partition may be corrupted. Fix this by re-flashing the
.uf2file while holding the B0 (Boot) button down while pressing reset. - Insufficient USB Power: The ESP32-S3 Wi-Fi radio draws up to 350mA during transmission. If you are plugged into an unpowered USB 2.0 hub, the board will brownout and reset during the handshake. Plug directly into a motherboard rear I/O port.
- Wrong Board Definition: Ensure you downloaded the firmware specifically named
ARDUINO_NANO_ESP32. Flashing generic ESP32-S3 DevKit firmware will fail because the Arduino board uses a different GPIO matrix for its USB bridge.
Extending and Simplifying the Build
Once you have the baseline I2C and Wi-Fi connection working, you will need to adapt the code for your specific project constraints.
How to Simplify (Battery / Solar Operation)
If you are running this on a 18650 Li-ion cell via a buck converter, Wi-Fi is your biggest enemy. It draws hundreds of milliamps. To simplify the build for low power:
- Remove the
networkmodule entirely. - Read the sensor data and log it to an SD card or internal flash.
- Use
machine.deepsleep(). The ESP32-S3 can drop to roughly 10µA in deep sleep. Set a timer to wake every 15 minutes, take a reading, and go back to sleep.
How to Extend (IoT Dashboard Integration)
To push data to the cloud, extend the script using MicroPython's built-in MQTT library. You will need to add umqtt.simple to your lib folder on the board. Replace the while True loop with an MQTT publish sequence, wrapping the client.publish() call in a try/except block to handle OSError: [Errno 110] ETIMEDOUT if your router drops the connection.
Frequently Asked Questions (FAQ)
Can I run MicroPython on an Arduino Uno or classic Nano?
No. The ATmega328P chip on classic Arduino boards has only 2KB of SRAM and 32KB of Flash. The MicroPython runtime requires a minimum of 16KB of RAM and 256KB of Flash just to idle. You must use 32-bit Arduino boards like the Nano ESP32, Nano RP2040 Connect, or the Portenta H7 to run MicroPython.
Why does Thonny say 'Device is busy' when I try to run my script?
This happens when the MicroPython REPL is locked in a blocking state, usually because a previous script is stuck in an infinite while loop without a time.sleep() yield, or it is waiting on a blocking network socket. Press the physical Reset button on the Nano ESP32, then immediately click the 'Stop' button (red square) in Thonny to send a KeyboardInterrupt signal before the main script auto-loads again.
How do I auto-run my MicroPython script on boot?
MicroPython executes specific files on startup. Save your I2C and Wi-Fi code as main.py on the root directory of the MicroPython virtual filesystem (visible in the Thonny file explorer). When the board powers on, it will run boot.py first (for system configs), and then automatically execute main.py. Do not put infinite blocking loops in boot.py, or you will lock yourself out of the REPL.
Is MicroPython on Arduino slower than standard C++ Arduino IDE sketches?
Yes, significantly. MicroPython is an interpreted language running on top of a virtual machine. Bit-banging protocols or running tight mathematical loops in MicroPython can be 10x to 50x slower than compiled C++ in the Arduino IDE. However, for I2C sensor polling, Wi-Fi MQTT publishing, and general IoT logic, the ESP32-S3's 240MHz dual-core processor is so fast that the Python overhead is entirely unnoticeable in practice. For high-speed signal processing (like audio FFTs), stick to C++ or use MicroPython's viper and native code emitters.






