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

Callout Tip: The Nano RP2040 Connect routes its internal sensors (IMU, Crypto chip, and Microphone) to I2C Bus 0 (GPIO 12/13). External sensors wired to the exposed header pins should ideally use I2C Bus 1 (GPIO 26/27) to avoid address collisions and bus contention.

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

  1. Enter Bootloader Mode: Double-tap the reset button on the Nano RP2040. A USB mass storage drive named RPI-RP2 will appear on your computer.
  2. Download Firmware: Download the official MicroPython .uf2 file 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.
  3. Flash: Drag and drop the .uf2 file onto the RPI-RP2 drive. The drive will disconnect automatically when flashing is complete.
  4. Connect IDE: Open Thonny IDE, go to Tools > Options > Interpreter, and select 'MicroPython (RP2040)' and the correct COM/tty port.
  5. Install Libraries: In Thonny, go to Tools > Manage Packages and install lsm6dsox. This saves the .mpy driver to the board's /lib directory.

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:

  1. Library not installed to the correct path: You copied the Python file to the root directory instead of the /lib folder. Fix: Create a lib directory on the CIRCUITPY/MICROPYTHON drive and move the .mpy file inside it.
  2. Wrong architecture build: You downloaded the standard .py file instead of the compiled .mpy file, and the board ran out of RAM during compilation. Fix: Always use pre-compiled .mpy drivers for RP2040 sensor libraries.

2. The I2C Bus Collision

Exact Error String: OSError: [Errno 19] ENODEV (or ETIMEDOUT)

Ranked Causes:

  1. 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 to 0 in your initialization code.
  2. 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:

  1. Corrupted Filesystem: A sudden power loss while writing to the internal flash corrupted the LittleFS filesystem. Fix: Re-flash the .uf2 firmware, which reformats the flash memory.
  2. 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_esp32spi library, 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.