MicroPython for Microcontrollers Module 1 – Getting started: boards, firmware, the REPL and mpremote
Install firmware and use the REPL
Pick the right MicroPython firmware, install it on a Pico by copying a file or on an ESP32 with esptool, then use the REPL keys and the raw REPL.
What you will learn
- Choose the firmware file for a board from its name, variant and file type
- Install MicroPython on a Pico by copying a UF2 file, and on an ESP32 with esptool
- Verify and record which firmware a board runs
- Use the REPL keys Ctrl-C, Ctrl-D, Ctrl-E, Tab and the _ variable
- Explain how the raw REPL frames a reply, and why tools such as mpremote rely on it
Before you start
What you’ll learn · Before you start
What you will learn
- Choose the firmware file for a board from its name, variant and file type
- Install MicroPython on a Pico by copying a UF2 file, and on an ESP32 with esptool
- Verify and record which firmware a board runs
- Use the REPL keys Ctrl-C, Ctrl-D, Ctrl-E, Tab and the _ variable
- Explain how the raw REPL frames a reply, and why tools such as mpremote rely on it
Before you start
On this page
MicroPython’s download pages list separate firmware files for the Pico and the Pico W, five builds for one ESP32 board, and preview builds of the next release beside every release. Pick the wrong file and the board may still start, then fail the first time your code touches Wi-Fi, which is a confusing place to discover a download mistake.
This lesson installs the right file on a Pico with a file copy and on an ESP32 with esptool, then teaches the prompt you get afterwards, the REPL: Read-eval-print loop: the interactive >>> prompt that MicroPython runs on a board's USB serial port or UART. Each line you type is compiled, run, and its result printed.: the keys that stop, restart and paste, and the raw mode that tools such as mpremote use to run your code, byte by byte.
The firmware file must match your board, not only its chip
The Pico and the Pico W share the RP2040 chip, and the Pico firmware runs on both. Only the Pico W file, though,
includes the network module and the driver for the board’s Wi-Fi chip, so on a Pico W with the Pico file,
import network raises ImportError. An ESP32 module needs the same care: the ESP32_GENERIC download page offers a
plain build and four variants, and tells you which board each one is for.
| On the download page | Choose it when | Watch out for |
|---|---|---|
| RPI_PICO, RPI_PICO_W, RPI_PICO2_W | It is your board’s exact name | The Pico file on a Pico W runs, without the network module |
| ESP32_GENERIC, plain | Most ESP32 boards with 4 MiB of flash or more | It is the default when no variant below fits |
| ESP32_GENERIC-SPIRAM | A WROVER module, or any board with SPIRAM (PSRAM) | The page recommends it for every board with SPIRAM |
| ESP32_GENERIC-D2WD | An ESP32-D2WD chip, with only 2 MiB of flash | The plain build is made for 4 MiB of flash or more |
| ESP32_GENERIC-UNICORE | A single-core ESP32, such as a SOLO module | |
| ESP32_GENERIC-OTA | You will update MicroPython itself over the air | It leaves less room in flash for your files |
| A release, not a preview | Always, for a first install | Previews are automatic builds of the next release, still in development |
Every file name has the same parts: the board, an optional variant, an eight-digit build stamp, the version and the
file type, .uf2 for the Pico boards and .bin for an ESP32. The program below splits six names into those parts
and checks each against the board it is meant for:
"""Split MicroPython firmware file names into their parts, and check each one against the board it is meant for.
The names follow the pattern of MicroPython's download pages: BOARD, an optional -VARIANT, an eight-digit build
stamp, -v and the version, an optional preview suffix, then the file type.
"""
import re
NAME = re.compile(
r"^(?P<board>[A-Z0-9_]+?)(?:-(?P<variant>[A-Z0-9]+))?-(?P<stamp>\d{8})"
r"-v(?P<version>\d+\.\d+\.\d+)(?P<preview>-preview\.[0-9a-z.]+)?\.(?P<kind>uf2|bin|app-bin)$"
)
# For each board: its firmware name and the file type its download page installs.
BOARDS = {
"Pico": ("RPI_PICO", "uf2"),
"Pico W": ("RPI_PICO_W", "uf2"),
"Pico 2 W": ("RPI_PICO2_W", "uf2"),
"ESP32 WROOM": ("ESP32_GENERIC", "bin"),
"ESP32 WROVER": ("ESP32_GENERIC", "bin"),
}
# Builds whose firmware includes the network module (the Wi-Fi boards).
NETWORK = {"RPI_PICO_W", "RPI_PICO2_W", "ESP32_GENERIC"}
def check(file_name, board):
"""The problems of installing file_name on board, as a list of sentences (empty: the file fits)."""
parts = NAME.match(file_name)
if parts is None:
return ["not named like a MicroPython firmware file"]
want, kind = BOARDS[board]
problems = []
if parts["board"] != want:
problems.append(f"built for {parts['board']}, not {want}")
if want in NETWORK and parts["board"] not in NETWORK:
problems.append("that build has no network module, so Wi-Fi code fails with ImportError")
if parts["kind"] == "app-bin":
problems.append("not the .bin file that the download page says to install")
elif parts["kind"] != kind:
problems.append(f"a .{parts['kind']} file, but this board installs a .{kind} file")
if parts["preview"]:
problems.append("a preview build of the next release, not a release")
return problems
CASES = [
("RPI_PICO-20260824-v1.29.0.uf2", "Pico"),
("RPI_PICO-20260824-v1.29.0.uf2", "Pico W"),
("RPI_PICO2_W-RISCV-20260824-v1.29.0.uf2", "Pico 2 W"),
("ESP32_GENERIC-SPIRAM-20260824-v1.29.0.bin", "ESP32 WROVER"),
("ESP32_GENERIC-20260824-v1.29.0.app-bin", "ESP32 WROOM"),
("RPI_PICO_W-20261016-v1.30.0-preview.147.g9e348d293c.uf2", "Pico W"),
]
for file_name, board in CASES:
parts = NAME.match(file_name)
print(file_name)
print(f" board {parts['board']}, variant {parts['variant'] or '-'}, version {parts['version']}")
problems = check(file_name, board)
print(f" on the {board}: {'fits' if not problems else '; '.join(problems)}") Output
RPI_PICO-20260824-v1.29.0.uf2 board RPI_PICO, variant -, version 1.29.0 on the Pico: fits RPI_PICO-20260824-v1.29.0.uf2 board RPI_PICO, variant -, version 1.29.0 on the Pico W: built for RPI_PICO, not RPI_PICO_W; that build has no network module, so Wi-Fi code fails with ImportError RPI_PICO2_W-RISCV-20260824-v1.29.0.uf2 board RPI_PICO2_W, variant RISCV, version 1.29.0 on the Pico 2 W: fits ESP32_GENERIC-SPIRAM-20260824-v1.29.0.bin board ESP32_GENERIC, variant SPIRAM, version 1.29.0 on the ESP32 WROVER: fits ESP32_GENERIC-20260824-v1.29.0.app-bin board ESP32_GENERIC, variant -, version 1.29.0 on the ESP32 WROOM: not the .bin file that the download page says to install RPI_PICO_W-20261016-v1.30.0-preview.147.g9e348d293c.uf2 board RPI_PICO_W, variant -, version 1.30.0 on the Pico W: a preview build of the next release, not a release
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 firmware_name.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The second case is the mistake of the opening: the RPI_PICO file on a Pico W runs, and fails at the first
import network. The fifth is a trap of the ESP32 page, which lists an .app-bin file beside each .bin. The page
says to download the .bin file, which is the combined image that the esp32 port’s README describes: a bootloader,
a partition table and MicroPython itself.
Keep a record of what you installed: the file name and its SHA-256: A hash function that turns any file into a 256-bit fingerprint, written as 64 hexadecimal digits. Two files with the same SHA-256 have the same content, so comparing hashes shows which files changed. More in “mpremote: run, copy, mount and sync”, which you can compute without uploading the file anywhere. With them you can install the same firmware again on a replacement board, or tell two boards apart when one behaves differently.
Hash Generator Compute the SHA-256 of a downloaded firmware file in your browser, and keep it with the file name.Checkpoint
Check: Choosing the firmware file
A Pico installs by copying a file; an ESP32 through esptool
Pico boards install firmware as a UF2 file: A firmware file made of 512-byte blocks that a board's bootloader writes to flash when the file is copied onto the USB drive it shows. Pico boards install MicroPython this way.. Their chip has a built-in
bootloader: A small program that runs at reset, before the firmware, and can receive new firmware: the RP2040's USB drive mode and the ESP32's serial ROM bootloader are two. that shows the board to your computer as a USB drive, and copying a .uf2
file onto that drive installs it. Step through a first install:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Bootloader mode. Hold the BOOTSEL button while you plug the USB cable in, then let go. A board that already runs MicroPython can do the same from its prompt with machine.bootloader().
- The drive. The chip's built-in bootloader shows the board to your computer as a USB drive, called RPI-RP2 on a Pico or Pico W and RP2350 on a Pico 2 or Pico 2 W. It is only there to receive a file.
- Copy the file. Copy the .uf2 file for your exact board onto the drive, by dragging it in a file manager or with a copy command. For a Pico W on 1.29.0, it is the file whose name starts with RPI_PICO_W and ends in v1.29.0.uf2.
- Flash written. The bootloader writes the firmware into the board's flash and resets the board by itself when it is done, so the drive disappears again.
- First start. MicroPython starts. Its frozen _boot.py looks for a filesystem in the flash, finds none on a new board, formats one and mounts it at /, without printing a word.
- The prompt. The board now appears as a USB serial port, /dev/ttyACM0 on Linux, /dev/cu.usbmodem followed by a number on a Mac and a COM port on Windows, and the REPL waits there for you.
The last step is the one to check. The prompt appears on a USB serial port, and once it does, two lines tell you exactly what you installed:
import sysand thensys.implementationshow the version,(1, 29, 0, ''), and the build,_build='RPI_PICO_W'on a Pico W, as the previous lesson decoded.- If
_buildnames another board than yours, install the right file again: the next copy simply replaces it.
An original ESP32 chip has no USB of its own, so it cannot appear as a drive: boards reach it through a USB-serial converter. Espressif’s esptool talks to the chip’s ROM bootloader over that serial connection, and the download page gives two commands, an erase and a write. Step through them, and through what happens when the erase is skipped:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Bootloader mode. esptool puts the chip into its ROM bootloader over the USB-serial connection. If the board does not answer, hold BOOT, press and release RESET, then release BOOT.
- Erase everything. esptool.py erase_flash empties the whole flash, the old program and any old filesystem with it. The download page asks for this before a first install.
- Write at 0x1000. esptool.py --baud 460800 write_flash 0x1000 with the .bin file writes the image from address 0x1000 on, the address the download page gives for the original ESP32.
- First start. After a reset, MicroPython's _boot.py finds no filesystem, checks that its first block is empty, prints Performing initial setup, formats the flash and writes a boot.py.
- The prompt. The REPL now runs on the board's serial connection at 115200 baud, the speed the ESP32 tutorial gives. mpremote or any serial terminal can open it.
- If the erase was skipped. Without the erase, old data can sit where the filesystem goes. Then the first start prints that the filesystem appears to be corrupted, again every 3 seconds, until you erase the flash and write the image again.
The erase matters for a first install only. The esp32 port’s first-boot code checks that the block where the filesystem goes is empty before it formats it; on a board that held other firmware it may not be, and MicroPython then refuses to touch it. Updating a board that already runs MicroPython is different: the reset and boot documentation notes that installing new firmware usually keeps the filesystem, so your files survive an update.
Updating to fix a board that will not start
When a board that ran MicroPython stops starting after a change to your files, a new firmware file usually does not help, because the update keeps the filesystem and with it the file that breaks the start. The lesson on boot files shows how to get the board back; installing again only works after the flash is erased completely.
Checkpoint
Check: Installing on a Pico and an ESP32
Three keys stop, restart and paste at the REPL
mpremote with no arguments, or any serial terminal, opens the prompt: it connects to the first USB serial device it
finds, and Ctrl-] or Ctrl-X leaves it again. At the >>> prompt a few control keys do more than edit text. MicroPython
reads them as commands only when the line is still empty, which the firmware’s line editor checks for each key:
| Key | At an empty prompt | With text on the line |
|---|---|---|
| Ctrl-C | A new prompt | Cancels the line being typed |
| Ctrl-D | A soft reset: A restart of the MicroPython interpreter without resetting the chip: Python objects, imports and most peripheral set-up are cleared, then the boot files run again. Ctrl-D at an empty prompt does one. More in “boot.py, main.py and a layout that survives resets”: variables and imports are cleared, then the boot files run again | Ignored, or deletes a character on builds with Emacs-style keys |
| Ctrl-E | paste mode: A REPL mode, opened with Ctrl-E at an empty prompt, that takes pasted text as it is, with no auto-indent, and compiles it as one block when you press Ctrl-D.: the prompt becomes ===, Ctrl-D runs the text, Ctrl-C cancels it |
Moves the cursor to the end of the line |
| Ctrl-A | The raw REPL, for programs (next section) | Moves the cursor to the start of the line |
| Ctrl-B | Prints the banner again; in the raw REPL, returns to the friendly one | Moves the cursor back one character on builds with Emacs-style keys |
| Tab | Lists the names you have defined or imported so far | Completes a name as far as it is unique; after machine. it lists what the module holds |
While a program runs, the key that counts is Ctrl-C: it raises KeyboardInterrupt inside the program, which stops
it and brings back the prompt unless the program catches that exception. Two habits follow from the table. Before
you paste a block of code, press Ctrl-E on an empty line: in normal mode the REPL indents
each new line after a colon by itself, so pasted code that is already indented gets indented twice and fails with
IndentationError. And after an expression, the special variable _ holds its value, so x = _ keeps a result you
forgot to name.
Pressing Ctrl-E with text on the line
Ctrl-E opens paste mode only at an empty prompt. With text on the line it moves the cursor to the end of it, and the paste that follows lands in the normal REPL with its auto-indent. Clear the line with Ctrl-C first.
Checkpoint
Check: The REPL's control keys
The raw REPL is the same prompt, framed for programs
The friendly REPL is made for people: it echoes keys, indents, and prints a prompt after each line. A program that
sends code needs something stricter, and that is the raw REPL: The REPL's mode for programs, entered with Ctrl-A: it echoes nothing, runs the code it received when Ctrl-D arrives, and frames every reply with OK and two 0x04 bytes.. Ctrl-A switches to it, the
board stops echoing, and each block of code ends with Ctrl-D. Every reply has the same frame: OK, the output, a
0x04 byte, the error output, a second 0x04 and the > prompt, so a tool can tell where output ends and a
traceback starts. Step through one run of print(6 * 7):
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- Stop what runs. The tool sends Enter and Ctrl-C, which stops a running program, and throws away whatever the board prints back. The board should now be waiting at a prompt.
- Switch to raw mode. Enter and Ctrl-A switch the board to the raw REPL. It answers with a fixed banner and a single > as its prompt, which the tool waits for before it goes on.
- The banner. The banner is the same on every board, raw REPL; CTRL-B to exit, so a tool can recognise it without knowing the board.
- The code, no echo. The tool sends the program as plain bytes, print(6 * 7). The raw REPL echoes nothing, so nothing comes back yet, and there is no auto-indent to spoil pasted code.
- Ctrl-D runs it. Ctrl-D marks the end of the code. The board answers OK at once, to say it has the code and is compiling and running it.
- OK. OK is the board's receipt. If the tool gets anything else, the board did not take the code, and mpremote stops with an error instead of waiting forever.
- The output, then Ctrl-D. The program prints 42, which arrives as 42 followed by a carriage return and a newline. A Ctrl-D byte, 0x04, ends the program's normal output.
- The error part. Next comes the error output, empty here because nothing failed, and a second 0x04. After a crash the traceback sits between the two 0x04 bytes instead.
- The next prompt. A > ends the reply, and the board waits for the next program in raw mode. Ctrl-B would take it back to the friendly REPL, where it prints its banner again.
The next program does what mpremote’s serial transport does: it switches the board into raw mode, sends two
programs and splits each reply. Its board is fake_board.py, a stand-in written from the firmware’s own raw REPL
loop that runs the code with your computer’s Python, so it shows the bytes of the protocol, not MicroPython’s own
results:
"""A stand-in for a MicroPython board's REPL, written from the firmware's own loop (shared/runtime/pyexec.c).
It answers the raw REPL the way the firmware does, and the few keys of the friendly REPL that a tool sends to reach
it. It runs the code it receives with the Python of this computer, so it shows the protocol, not MicroPython's own
results.
"""
import contextlib
import io
BANNER = b"raw REPL; CTRL-B to exit\r\n"
class FakeBoard:
def __init__(self):
self.raw = False # False: the friendly REPL, with its >>> prompt
self.line = bytearray() # what the host has typed since the last Ctrl-D
self.sent = bytearray() # bytes the board has sent and the host has not read yet
self.globals = {"__name__": "__main__"}
def write(self, data):
"""Bytes from the host, one at a time, as they arrive over USB serial."""
for byte in data:
if self.raw:
self._raw_key(byte)
else:
self._friendly_key(byte)
def read(self):
"""Everything the board has sent since the last read."""
data = bytes(self.sent)
self.sent.clear()
return data
def _friendly_key(self, byte):
if byte == 0x01: # Ctrl-A at an empty prompt: switch to the raw REPL
self.sent += b"\r\n" + BANNER + b">"
self.raw = True
elif byte in (0x0D, 0x03): # Enter, or Ctrl-C, at an empty prompt: a new prompt
self.sent += b"\r\n>>> "
def _raw_key(self, byte):
if byte == 0x01: # Ctrl-A: start the raw REPL again
self.line.clear()
self.sent += BANNER + b">"
elif byte == 0x02: # Ctrl-B: back to the friendly REPL (a board then prints its banner)
self.raw = False
self.sent += b"\r\n"
elif byte == 0x03: # Ctrl-C: throw away the code typed so far
self.line.clear()
elif byte == 0x04: # Ctrl-D: run what was typed
self.sent += b"OK"
self._run(bytes(self.line).decode())
self.line.clear()
self.sent += b">"
else: # anything else is code, and nothing is echoed
self.line.append(byte)
def _run(self, source):
out = io.StringIO()
error = ""
try:
with contextlib.redirect_stdout(out):
exec(compile(source, "<stdin>", "exec"), self.globals)
except Exception as e:
tb = e.__traceback__
while tb.tb_next is not None:
tb = tb.tb_next
# The traceback in the form MicroPython prints it.
error = (
"Traceback (most recent call last):\n"
f' File "<stdin>", line {tb.tb_lineno}, in <module>\n'
f"{type(e).__name__}: {e}\n"
)
self.sent += out.getvalue().replace("\n", "\r\n").encode()
self.sent += b"\x04"
self.sent += error.replace("\n", "\r\n").encode()
self.sent += b"\x04" """Drive a board's raw REPL the way mpremote's serial transport does, and print every byte that crosses the cable.
The board here is fake_board.py, a stand-in that answers as the firmware does. mpremote sends the same bytes over a
real serial port, and reads until it sees each marker it expects: the raw REPL banner, OK, and the two Ctrl-D bytes.
"""
from fake_board import FakeBoard
def enter_raw_repl(board):
board.write(b"\r\x03") # Enter and Ctrl-C: stop whatever is running
board.read() # throw away the prompts that come back
board.write(b"\r\x01") # Enter and Ctrl-A: switch to the raw REPL
reply = board.read()
if not reply.endswith(b"raw REPL; CTRL-B to exit\r\n>"):
raise RuntimeError(f"could not enter the raw REPL: {reply!r}")
return reply
def exec_raw(board, code):
"""Send code, then Ctrl-D; return what it printed and its error output, as the board framed them."""
board.write(code.encode() + b"\x04")
reply = board.read()
if not reply.startswith(b"OK"):
raise RuntimeError(f"the board did not accept the code: {reply!r}")
out, err, prompt = reply[2:].split(b"\x04")
return reply, out, err
board = FakeBoard()
print("Ctrl-A ->", enter_raw_repl(board))
for code in ["print(6 * 7)", "reading = None\nif reading is None:\n raise ValueError('sensor not found')"]:
reply, out, err = exec_raw(board, code)
print("code ->", code.encode() + b"\x04")
print("reply <-", reply)
print(" output: ", out)
print(" error: ", err) Output
Ctrl-A -> b'\r\n>>> \r\nraw REPL; CTRL-B to exit\r\n>'
code -> b'print(6 * 7)\x04'
reply <- b'OK42\r\n\x04\x04>'
output: b'42\r\n'
error: b''
code -> b"reading = None\nif reading is None:\n raise ValueError('sensor not found')\x04"
reply <- b'OK\x04Traceback (most recent call last):\r\n File "<stdin>", line 3, in <module>\r\nValueError: sensor not found\r\n\x04>'
output: b''
error: b'Traceback (most recent call last):\r\n File "<stdin>", line 3, in <module>\r\nValueError: sensor not found\r\n'
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 raw_session.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The first reply is OK42\r\n\x04\x04>: the output, an empty error part between the two 0x04 bytes, and the
prompt. The second program fails, so its output part is empty and the traceback, in the form MicroPython prints it,
sits in the error part. That split is how mpremote exec can print your output and then exit with an error status
when your code raised an exception.
Key idea
The raw REPL answers every block of code with the same frame, OK, the output, 0x04, the error output, 0x04 and the prompt, so a tool always knows where your output ends and an error begins.
mpremote and the project’s own pyboard.py tool drive boards this way, and since version 1.27 the unix port answers
the raw REPL too, because its REPL now uses the same loop as the boards. mpremote first asks for raw-paste mode, a
variant of the raw REPL with flow control: the board says how many bytes it can take at once, and the tool waits for
room before it sends more, which makes long programs arrive safely.
Change the program
Press Edit on the second program and put a third piece of code in the list, such as print(sum(range(5))). Run it
and find the 10 between OK and the first 0x04.
Checkpoint
Check: The raw REPL and its replies
Practise: split a raw REPL reply
A tool that drives boards needs one small, careful function: the one that cuts a reply into output and error, and
refuses a reply that is not complete. The sample tests include a traceback and output that contains > characters:
Exercise · Easy · Python
Split a raw REPL reply into output and error
After a tool sends code and Ctrl-D to a board's raw REPL, the board answers with one reply: OK, the program's output, a 0x04 byte, its error output, a second 0x04 byte, and the > prompt. Write split_raw_reply(data) in reply.py. data is that reply as bytes; return the pair (output, error), both bytes.
split_raw_reply(b"OK42\r\n\x04\x04>")returns(b"42\r\n", b"").- A reply whose program failed has the traceback as its error part, and an empty error part means the program ended normally.
- Output may itself contain
>characters: only the bytes after the second0x04are the prompt.
Raise ValueError when the reply is not a complete frame: it does not start with OK, it has fewer than two 0x04 bytes, or the second 0x04 is not followed by exactly >.
Starter code · reply.py
def split_raw_reply(data):
"""Return (output, error) of a raw REPL reply: b"OK" + output + b"\x04" + error + b"\x04" + b">"."""
return (b"", b"") The sample tests · test_reply.py
from reply import split_raw_reply
def raises_value_error(data):
try:
split_raw_reply(data)
except ValueError:
return True
return False
def test_output():
"""splits a reply with output and no error"""
assert split_raw_reply(b"OK42\r\n\x04\x04>") == (b"42\r\n", b"")
def test_traceback():
"""returns the traceback as the error part"""
tb = b'Traceback (most recent call last):\r\n File "<stdin>", line 1, in <module>\r\nValueError: no sensor\r\n'
assert split_raw_reply(b"OKstart\r\n\x04" + tb + b"\x04>") == (b"start\r\n", tb)
def test_empty_program():
"""handles a program that printed nothing"""
assert split_raw_reply(b"OK\x04\x04>") == (b"", b"")
def test_prompt_signs_in_output():
"""keeps > characters that the program printed"""
assert split_raw_reply(b"OK>>> 3 > 2\r\n\x04\x04>") == (b">>> 3 > 2\r\n", b"")
def test_bad_frames():
"""refuses replies that are not complete frames"""
assert raises_value_error(b"raw REPL; CTRL-B to exit\r\n>")
assert raises_value_error(b"OK42\r\n\x04")
assert raises_value_error(b"OK42\r\n\x04\x04")
assert raises_value_error(b"OK42\r\n\x04\x04>>") A hint
Check the start first: data.startswith(b"OK"). Then cut the rest at the 0x04 bytes with data[2:].split(b"\x04"): a complete frame gives exactly three pieces, the output, the error part and what follows the second 0x04, which must be b">".
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then review the four ideas of this lesson, from the firmware file to the raw REPL. The self-check starts where your answers so far are weakest, and it explains every choice the moment you make it:
Practice that adapts to you
Practise this lesson
12 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): At the MicroPython REPL, what do Ctrl-C and Ctrl-D do, and when does Ctrl-D not reset?
Ctrl-C interrupts: it raises KeyboardInterrupt in the running program, which stops it and returns to the prompt unless the program catches the exception, and at the prompt it cancels the line being typed. Ctrl-D at an empty prompt does a soft reset: the interpreter restarts, every variable and imported module is cleared, and the boot files run again, while the USB serial connection stays open. Ctrl-D does not reset when the line holds text, because the control keys of the REPL work as commands only on an empty line, and in paste mode, where it ends the paste and runs the code. In the raw REPL, Ctrl-D after code runs that code, and Ctrl-D with no code is the soft reset that mpremote uses before its first command.
Two longer interview questions of this module start from this lesson:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
After a repair, one Pico W of a fleet of 50 starts normally but fails at import network with ImportError. How do you establish which firmware it runs, fix it, and make sure no board of the fleet ends up with the wrong file again?
-
Mid to senior
Your own Python script drives boards through the raw REPL over a serial port. On some runs it hangs forever after sending a program. Walk through the protocol, say where a host can wait forever, and explain how you would make the script robust.
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What happens when a Pico W runs the plain RPI_PICO firmware?
It starts and runs, but it has no network module and no Wi-Fi driver, so the first import network raises ImportError. Install the RPI_PICO_W file instead.
-
Which ESP32_GENERIC download suits a WROVER module or any board with PSRAM?
The SPIRAM variant, ESP32_GENERIC-SPIRAM, which the download page recommends for every board with SPIRAM. The plain build is for most boards with 4 MiB of flash or more.
-
How is MicroPython installed on a Pico?
Hold BOOTSEL while you plug the cable in: the chip's built-in bootloader appears as a USB drive. Copy the board's .uf2 file onto it, and the bootloader writes it to flash and resets the board by itself.
-
Why erase an ESP32's flash before its first MicroPython install?
At its first start MicroPython formats the filesystem's block only if that block is empty, and data left by other firmware makes it refuse. An update of a board that already runs MicroPython keeps the filesystem and your files.
-
What does Ctrl-D do at an empty REPL prompt?
A soft reset: variables and imports are cleared and the boot files run again, while the USB connection stays open. With text on the line it does not reset, because the control keys act only on an empty line.
-
Why press Ctrl-E before pasting code into the REPL?
Paste mode takes the text as it is. The normal REPL indents after every colon by itself, so code that is already indented gets indented twice and fails with IndentationError. Ctrl-D then runs the paste.
-
How does the raw REPL frame every reply?
OK, the output, a 0x04 byte, the error output, another 0x04, then the > prompt. Tools such as mpremote rely on it to tell where your output ends and a traceback starts.
-
What does _ hold at the MicroPython REPL?
The value of the last expression, so x = _ keeps a result you forgot to name.
Key takeaways
- The firmware file must match the board: RPI_PICO runs on a Pico W but has no network module, and an ESP32 needs the variant for its RAM, flash and cores.
- A Pico installs by copying a .uf2 file to the drive its bootloader shows; an original ESP32 installs with esptool, an erase and a write at 0x1000.
sys.implementationconfirms what was installed, and a record of the file name and its SHA-256 lets you install the same firmware again.- At an empty prompt Ctrl-C gives a new prompt, Ctrl-D soft-resets and Ctrl-E opens paste mode; while a program runs, Ctrl-C stops it with KeyboardInterrupt.
- The raw REPL frames every reply as OK, the output, 0x04, the error output, 0x04 and >, which lets mpremote and editors run code and tell output from errors.
Terms in this lesson
- bootloader
- A small program that runs at reset, before the firmware, and can receive new firmware: the RP2040's USB drive mode and the ESP32's serial ROM bootloader are two.
- paste mode
- A REPL mode, opened with Ctrl-E at an empty prompt, that takes pasted text as it is, with no auto-indent, and compiles it as one block when you press Ctrl-D.
- raw REPL
- The REPL's mode for programs, entered with Ctrl-A: it echoes nothing, runs the code it received when Ctrl-D arrives, and frames every reply with OK and two 0x04 bytes.
- REPL
- Read-eval-print loop: the interactive >>> prompt that MicroPython runs on a board's USB serial port or UART. Each line you type is compiled, run, and its result printed.
- UF2 file
- A firmware file made of 512-byte blocks that a board's bootloader writes to flash when the file is copied onto the USB drive it shows. Pico boards install MicroPython this way.
References
- MicroPython downloads: Pico (RPI_PICO) (MicroPython project)
- MicroPython downloads: Pico W (RPI_PICO_W) (MicroPython project)
- MicroPython downloads: Pico 2 W (RPI_PICO2_W) (MicroPython project)
- MicroPython downloads: ESP32 / WROOM (ESP32_GENERIC) (MicroPython project)
- Raspberry Pi documentation: MicroPython (drag-and-drop installation) (Raspberry Pi Ltd)
- Getting started with MicroPython on the ESP32 (MicroPython project)
- The esp32 port's first-boot setup (ports/esp32/modules/inisetup.py, v1.29.0) (MicroPython project)
- The rp2 port's frozen _boot.py (ports/rp2/modules/_boot.py, v1.29.0) (MicroPython project)
- Reset and Boot Sequence (MicroPython project)
- The MicroPython Interactive Interpreter Mode (aka REPL) (MicroPython project)
- The REPL loops (shared/runtime/pyexec.c, v1.29.0) (MicroPython project)
- Line editing of the REPL (shared/readline/readline.c, v1.29.0) (MicroPython project)
- mpremote's serial transport (tools/mpremote/mpremote/transport_serial.py, v1.29.0) (MicroPython project)
- MicroPython remote control: mpremote (MicroPython project)
- The esp32 port's README (the combined firmware image, v1.29.0) (MicroPython project)
- MicroPython v1.27.0 release notes (raw REPL on the unix port) (MicroPython project)
- Quick reference for the RP2 (MicroPython project)
- sys – system specific functions (MicroPython project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress