Source: https://mokxi.com/learn/micropython-raspberry-pi-pico
Updated: 2026-10-26

Learn

# MicroPython on the Raspberry Pi Pico, with nothing to install

Beginner

Written by the Mokxi team, updated October 26, 2026

Start building free Open the editor

Or see every lesson.

- 455 parts on the bench
- 43 boards running now
- 1.00x real time, on every board

Click to open it in the editor

Press Run, then turn the knob. MicroPython reads GP26 with ADC and sets the PWM duty on GP15.

Mokxi runs MicroPython on its Raspberry Pi Pico. Put a Pico on the canvas, choose MicroPython (main.py) in its Firmware panel, write main.py and press Run. What runs is the official MicroPython v1.29.0 firmware for the Pico, the same file you would drag onto a real one, on Mokxi’s simulated RP2040, instruction by instruction.

When you press Run the board boots that firmware, and the computer on the end of its simulated USB cable copies your .py files onto the board’s filesystem the way Thonny does, then soft-reboots it. MicroPython prints MPY: soft reboot and runs main.py. Everything print() says shows in the Serial Monitor, and when main.py ends the REPL answers: type 6*7 and it says 42.

The circuit above is a potentiometer on GP26 and an LED on GP15. Turn the knob and the LED dims and brightens, and the Serial Monitor shows the knob’s position in percent.

## The program: ADC in, PWM out

ADC(26) is the RP2040’s analog input on GP26, and read_u16() gives 0 at 0 V and 65535 at the 3.3 V reference. The converter itself has 12 bits, so the value moves in steps of about 16. PWM(Pin(15)) is the PWM slice behind GP15, and duty_u16() takes the same 0 to 65535 range, so the knob’s reading can go straight in as the duty.

With the wiper at 1.1 V, a third of the way up, read_u16() printed 21845 every time here: 1365 counts of 4095, which MicroPython stretches to 16 bits by shifting it up four bits and repeating its top four bits underneath, as on a real Pico. At a duty of 32768 the LED pin measured high for 50.0% of each 1 ms period.

main.py: the knob dimmer

## Blink with machine.Pin, and how long a sleep really takes

The on-board LED of a Pico is GP25. The program below toggles it every 100 ms. Measured on the board’s pin, every period here was 200 ms to within a millisecond, because time.sleep_ms() waits on the RP2040’s own microsecond timer.

utime.ticks_us() reads that timer too. Four sleep_ms(250) calls in a row measured within half a millisecond of 1000000 microseconds every time here. machine.freq() says 125000000, the Pico’s 125 MHz.

main.py: blink the on-board LED

## A button with an interrupt

Pin.irq() runs a function when a pin changes, without the main loop watching it. Here a button from GP14 to ground uses the pin’s internal pull-up, so the pin reads 1 until the button is pressed, and IRQ_FALLING runs the handler once per press. Keep a handler short: add to a counter, set a flag, and let the main loop do the printing.

main.py: count presses with Pin.irq()

## Porting an Arduino sketch to MicroPython

Most of an Arduino sketch has a one-line MicroPython twin. There is no setup() and loop(): the top of main.py runs once, and a while True: loop at the end runs for ever. Pins are objects made from the machine module, and times come from the time module.

Two things change size. analogRead() on an Uno gives 0 to 1023, and read_u16() gives 0 to 65535 whatever the converter’s own resolution. And a time difference is taken with time.ticks_diff(), which stays right when the counter wraps, rather than by subtracting.

Below is the Arduino AnalogReadSerial example, ported, on the knob dimmer’s circuit. With the knob halfway it printed 0 ms 32743 1.65 V, then the same reading at 500 ms, 1000 ms and on: 32743 is 2046 of 4095 counts, stretched to 16 bits.

Arduino calls and their MicroPython equivalents on the Pico
Arduino | MicroPython

pinMode(15, OUTPUT); digitalWrite(15, HIGH); | led = Pin(15, Pin.OUT); led.value(1)

pinMode(14, INPUT_PULLUP); digitalRead(14) | button = Pin(14, Pin.IN, Pin.PULL_UP); button.value()

analogRead(A0), 0 to 1023 | ADC(26).read_u16(), 0 to 65535

analogWrite(15, 128), 0 to 255 | PWM(Pin(15)).duty_u16(32768), 0 to 65535

delay(500) | time.sleep_ms(500)

millis() | time.ticks_ms()

Serial.println(x) | print(x)

attachInterrupt(..., FALLING) | Pin.irq(trigger=Pin.IRQ_FALLING, handler=...)

main.py: AnalogReadSerial, ported

## I2C: an OLED and a BME280 with machine.I2C

machine.I2C(0) and machine.I2C(1) are the RP2040’s two I2C blocks, and the parts on a bus answer at their addresses: here the SSD1306 OLED is on I2C0 (GP4 and GP5) and scan() finds it at 0x3C (60), and the BME280 is on I2C1 (GP6 and GP7) at 0x76 (118). Both can share one bus too, as they have different addresses. Add the drivers as tabs, ssd1306.py from micropython-lib and a BME280 driver, and they are copied onto the board with main.py, as Thonny copies them.

The bus runs on the simulated clock at the speed the block really makes: 400 kHz asked is 376 kHz on the wire, and oled.show(), 1 KB of picture, takes 25.4 ms. Ask an address nobody answers and the call raises OSError: [Errno 5] EIO, as on a real Pico. machine.SPI works the same way, DMA and all, and SoftI2C and SoftSPI work on any pins.

main.py: a BME280 on an SSD1306

## When the program stops with an error

MicroPython stops a program that goes wrong and prints a traceback: the file, the line, and the error. Mokxi reads it off the serial port, marks the line in the editor and says what the error usually means. A NameError is a name used before it is given a value or imported (Python names are case-sensitive); a SyntaxError is a line that is not valid Python, often a missing colon at the end of an if, for, while or def line; an ImportError for RPi.GPIO or gpiozero means code written for a Raspberry Pi computer, not the Pico.

After the traceback the REPL answers, so you can try the line again by hand before you change main.py.

## What is simulated, and what is not

Simulated: the RP2040’s Cortex-M0+ core and its boot ROM, the second-stage bootloader, the flash chip and the littlefs filesystem on it, the clocks, the USB port, the GPIO pins with their pull-ups and interrupts, PWM, the ADC with the temperature sensor, the I2C and SPI blocks and the DMA, the timer, the RTC and the watchdog.

Not simulated yet: the second core, so _thread stops the board with a message saying so; the I2C block as a target (machine.I2CTarget); and the CPU clock, which stays at 125 MHz whatever machine.freq() is set to, though the timer and every time measurement stay right. The PIO blocks run, so rp2.PIO and rp2.StateMachine drive their pins. The Pico W runs its own MicroPython build, with its CYW43439 modeled from its pins: Pin("LED") lights the board’s LED and network.WLAN joins Mokxi’s simulated network.

## Questions

Is this real MicroPython or an imitation of it?

Real. The board runs the official MicroPython v1.29.0 firmware for the Raspberry Pi Pico, the same .uf2 file micropython.org publishes, on a simulated RP2040. Nothing in the web page interprets your Python: the firmware compiles and runs it on the simulated chip.

How does main.py get onto the board?

The way Thonny and mpremote do it. At Run, the simulated computer on the Pico’s USB port opens its serial port, enters the raw REPL, writes each .py tab into the board’s littlefs filesystem with a short script, and soft-reboots the board, which then runs main.py.

Can I add my own modules or a library?

Yes. Add a tab with the module’s .py file, such as ssd1306.py, and import it from main.py. Every .py tab is copied onto the board with main.py.

Does it download anything?

The first Run of a MicroPython project downloads the firmware, 229 KB, and the browser keeps it for every later Run. An Arduino project never downloads it.

Related

## Keep going

Raspberry Pi Pico Button: Wiring and Code ESP32 MicroPython Simulator, Free in Your Browser The MicroPython simulator Open the MicroPython knob dimmer in the editor A DHT11 on the Pico with MicroPython’s dht module Beginner An HC-SR04 on the Pico, timed with time_pulse_us() Beginner The project page, with the full sketch Beginner

Sources

## Where the facts on this page come from

- micropython.org: firmware for the Raspberry Pi Pico (v1.29.0, 2026-08-24)
- MicroPython documentation: quick reference for the RP2

## Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.

Start building free Open the editor
