To run Python directly in an Arduino board, you must use a microcontroller with enough SRAM and a supported runtime, specifically the Arduino Nano RP2040 Connect (ABX00052) running MicroPython. Standard ATmega328P boards like the Uno R3 lack the memory and architecture to execute Python natively; they require a host PC running PySerial. By flashing MicroPython onto the RP2040 chip inside the Nano Connect, you bypass C++ compilation entirely, gaining access to a live REPL (Read-Eval-Print Loop), dynamic memory allocation, and rapid prototyping directly on the hardware.
Hardware Spec Sheet and Board Variants
Not all Arduino-branded boards support native Python. When planning a 'Python in Arduino' project, selecting the correct silicon is the first critical step. The RP2040 and STM32H7 architectures are your primary targets. Below is a data-dense comparison of current Arduino boards and their Python runtime compatibility as of 2026.
| Board Variant (Part #) | MCU Core | Flash / SRAM | Python Runtime Support | Native WiFi/BLE |
|---|---|---|---|---|
| Arduino Uno R3 (A000066) | ATmega328P (AVR) | 32KB / 2KB | None (Requires Host PC / Firmata) | No |
| Nano RP2040 Connect (ABX00052) | Dual Cortex-M0+ (RP2040) | 16MB / 264KB | MicroPython & CircuitPython | Yes (Nina-W10) |
| Portenta H7 (ABX00042) | Dual Cortex-M7/M4 (STM32H7) | 2MB / 1MB | MicroPython (M7 Core only) | Yes (Murata 1DX) |
| Nano 33 IoT (ABX00027) | Cortex-M0+ (SAMD21) | 256KB / 32KB | CircuitPython Only (No official uPy) | Yes (Nina-W10) |
Source: Arduino Official Hardware Documentation
Pin Mapping: Arduino Silkscreen vs MicroPython GPIO
The most common failure point when transitioning from Arduino C++ to MicroPython is pin addressing. The Arduino IDE uses abstracted digital pin numbers (D2, D3, A0), while MicroPython requires the raw RP2040 GPIO numbers. If you try to initialize machine.Pin(2) expecting Arduino Pin D2, you will actually be toggling GPIO2, which is not broken out to the Nano's header pins.
| Arduino Silkscreen | RP2040 GPIO Number | MicroPython Object | Primary Function / Notes |
|---|---|---|---|
| D2 | GPIO 25 | machine.Pin(25) |
Digital I/O, PWM capable |
| D3 | GPIO 15 | machine.Pin(15) |
Digital I/O, PWM capable |
| A4 (SDA) | GPIO 12 | machine.I2C(0, sda=12) |
Internal I2C Bus 0 (Routes to onboard LSM6DOX IMU and ATECC608A) |
| A5 (SCL) | GPIO 13 | machine.I2C(0, scl=13) |
Internal I2C Bus 0 (Pull-ups enabled by default on this bus) |
| D12 (LED Red) | GPIO 6 | machine.Pin(6) |
Onboard RGB LED (Active High via transistor) |
Source: MicroPython RP2 Quick Reference
Flashing MicroPython and the IMU Reader Code
This project targets the Arduino Nano RP2040 Connect (ABX00052). We will read the onboard LSM6DOX 6-axis IMU and output the telemetry to the serial console, blinking the red channel of the RGB LED on every successful read.
Step-by-Step Flashing Procedure
- Enter Bootloader Mode: Double-tap the reset button on the Nano RP2040. A USB mass storage drive named
RPI-RP2will appear on your computer. - Download Firmware: Download the official MicroPython
.uf2file specifically compiled for the Arduino Nano RP2040 Connect from the MicroPython downloads page. Do not use the generic Raspberry Pi Pico firmware; it lacks the specific I2C bus configurations for the Nano's onboard sensors. - Flash: Drag and drop the
.uf2file onto theRPI-RP2drive. The drive will disconnect automatically when flashing is complete. - Connect IDE: Open Thonny IDE, go to Tools > Options > Interpreter, and select 'MicroPython (RP2040)' and the correct COM/tty port.
- Install Libraries: In Thonny, go to Tools > Manage Packages and install
lsm6dsox. This saves the.mpydriver to the board's/libdirectory.
Complete MicroPython Implementation
import machine
import time
import sys
# Pin Definitions (Nano RP2040 Connect specific)
LED_RED = machine.Pin(6, machine.Pin.OUT)
I2C_SDA = 12
I2C_SCL = 13
# Initialize I2C Bus 0 for onboard sensors
i2c = machine.I2C(0, sda=machine.Pin(I2C_SDA), scl=machine.Pin(I2C_SCL), freq=400000)
def setup_imu():
try:
from lsm6dsox import LSM6DSOX
imu = LSM6DSOX(i2c)
print('LSM6DOX IMU initialized successfully.')
return imu
except ImportError:
print('FATAL: lsm6dsox library missing. Install via Thonny Package Manager.')
sys.exit()
except OSError as e:
print(f'FATAL: I2C Communication Error: {e}. Check bus ID and pull-ups.')
sys.exit()
def main():
imu = setup_imu()
print('Starting telemetry loop... Press Ctrl+C to stop.')
while True:
try:
# Read accelerometer and gyroscope data
accel = imu.read_accel()
gyro = imu.read_gyro()
# Toggle Red LED to indicate successful loop iteration
LED_RED.toggle()
# Format and print telemetry
print(f'Accel: X:{accel[0]:.2f} Y:{accel[1]:.2f} Z:{accel[2]:.2f} | '
f'Gyro: X:{gyro[0]:.2f} Y:{gyro[1]:.2f} Z:{gyro[2]:.2f}')
time.sleep(0.5)
except KeyboardInterrupt:
print('\nLoop interrupted by user. Turning off LED.')
LED_RED.value(0)
break
except Exception as e:
print(f'Runtime Error during read: {e}')
time.sleep(1)
if __name__ == '__main__':
main()
Debugging: Exact Error Strings and Ranked Causes
When working with Python in Arduino environments, the REPL will throw specific exceptions when hardware abstraction layers fail. Here are the first three things to check when your script crashes, mapped to their exact error strings.
1. The Missing Driver Exception
Exact Error String: ImportError: no module named 'lsm6dsox'
Ranked Causes:
- Library not installed to the correct path: You copied the Python file to the root directory instead of the
/libfolder. Fix: Create alibdirectory on the CIRCUITPY/MICROPYTHON drive and move the.mpyfile inside it. - Wrong architecture build: You downloaded the standard
.pyfile instead of the compiled.mpyfile, and the board ran out of RAM during compilation. Fix: Always use pre-compiled.mpydrivers for RP2040 sensor libraries.
2. The I2C Bus Collision
Exact Error String: OSError: [Errno 19] ENODEV (or ETIMEDOUT)
Ranked Causes:
- Wrong I2C Bus ID: You initialized
machine.I2C(1)but the onboard IMU is hardwired to Bus 0 (GPIO 12/13). Fix: Change the bus ID to0in your initialization code. - Address Collision: Another script or the default Arduino bootloader is holding the I2C bus lock. Fix: Hard reset the board (single press of the reset button) and immediately run
i2c.scan()in the REPL. You should see[106](0x6A) for the LSM6DOX.
3. The Bootloop / UF2 Fallback
Symptom: The board does not run main.py on power-up; instead, it mounts as a USB flash drive named RPI-RP2.
Ranked Causes:
- Corrupted Filesystem: A sudden power loss while writing to the internal flash corrupted the LittleFS filesystem. Fix: Re-flash the
.uf2firmware, which reformats the flash memory. - BOOTSEL Pin Pulled Low: GPIO 28 (which is tied to the BOOTSEL button logic) is being pulled low by an external circuit you wired to pin D10. Fix: Remove external wiring from D10 and reset.
Extending and Simplifying the Build
Once you have the basic MicroPython environment running on the Nano RP2040 Connect, you can scale the project up or down based on your application requirements.
How to Simplify (The Bare Minimum Blink)
If you are just validating your toolchain and want to strip away the I2C complexity, delete the IMU logic and focus purely on GPIO manipulation. Use GPIO 6 (Red LED) and GPIO 25 (Arduino Pin D2) to create a simple alternating blink circuit. This confirms your Thonny IDE connection, firmware integrity, and basic machine.Pin imports are functioning without relying on external C-compiled sensor drivers.
How to Extend (Adding WiFi via the Nina-W10)
The Nano RP2040 Connect includes a u-blox NINA-W102 WiFi/BLE module. However, extending this build to include network connectivity reveals a major architectural quirk of 'Python in Arduino': MicroPython on the RP2040 does not natively support the NINA-W10 coprocessor out of the box.
To add WiFi, you have two distinct paths:
- Path A (Switch to CircuitPython): Adafruit's CircuitPython firmware for the Nano RP2040 Connect includes the
adafruit_esp32spilibrary, which handles the SPI bridge to the NINA-W10 module seamlessly. This is the recommended path for IoT projects requiring HTTP requests or MQTT. - Path B (Custom MicroPython Build): If you must stay in MicroPython, you have to compile a custom firmware build from the MicroPython source tree, enabling the SPI driver and writing a custom Python wrapper to send AT commands or SPI packets to the NINA module's ESP32 core. This is an advanced endeavor and generally not recommended for rapid prototyping.
By understanding the exact silicon limitations and pin mappings of the Nano RP2040 Connect, you can successfully deploy Python directly on Arduino hardware, bypassing the C++ compilation loop and leveraging the speed of interactive REPL debugging.






