RP2040 MicroPython: mpremote

7 minute read

An updated look at the utility called mpremote used to communicate with a board running MicroPython.

Review

A tool which has improved a great deal with the 1.20 release is mpremote - micropython remote control. From using it extensively, I’ve found its biggest issue is more the lack of documentation as compared to its capability. (See below)

Hint 1: Use a .config file

For example, after reading about mpremote on the GitHub Discussions and Discord, a powerful aspect of mpremote is its config file. See below for more details.

Hint 2: Understand raw REPL vs. REPL

The single biggest “aha!” I have found from developing code in MicroPython was raw REPL. It happened when I was attempting to write mpbuild, a program which can automatically load your Pico with the desired files. I tried multiple methods of automating the copy process, using a python exec call on mpremote, creating a subprocess call on mpremote…etc. I knew I was going down the wrong path when I saw this commentI do not recommend using mpremote via subprocess! Everything you need should be available from pyboard.py.

Ok, fine. I began to explore using pyboard.py, same issues remained. I wasn’t able to get my version of the example to work. In my version, I wanted to copy a file and not print(1 + 1).

I gave up for a few hours and did a little bit more searching. And bless Dave Hylands…in 2014, he had this to say: “Primarily - yeah. You can think of raw REPL as a programmatic interface, and regular REPL as a human interface. The raw REPL also makes it much easier (from the script’s perspective) to determine exactly what the real output of a command (like print) is.

With that the lightbulb went off, and I changed my code. In the example, there was a call to raw_repl, which I wasn’t doing as I wasn’t “attempting to execute code” such as the example. Wrong! If you want to execute the pyboard code, you need to be in raw_repl mode, then everything works!

So my example became (this is the key code in the program mpbuild):

from mpremote.pyboard import Pyboard
pyb = Pyboard(os.environ['PYBOARD_DEVICE'], 115200)
pyb.enter_raw_repl()
pyb.fs_put(s, d)
pyb.exit_raw_repl()
pyb.close()

It works great! And mpbuild works exactly as I wish. If you wish to create your own interface Python programs to communicate between your MicroPython microcontroller board and your PC, I highly recommend “playing” the pyboard tool, just remember to use raw_repl.

Help Command

For documentation (from help command):

mpremote help
mpremote -- MicroPython remote control
See https://docs.micropython.org/en/latest/reference/mpremote.html

List of commands:
  connect       connect to given device
  disconnect    disconnect current device
  edit          edit files on the device
  eval          evaluate and print the string
  exec          execute the string
  fs            execute filesystem commands on the device
  help          print help and exit
  mip           install packages from micropython-lib or third-party sources
  mount         mount local directory on device
  repl          connect to given device
  resume        resume a previous mpremote session (will not auto soft-reset)
  run           run the given local script
  soft-reset    perform a soft-reset of the device
  umount        unmount the local directory
  version       print version and exit

Installation

# to install
pip3 install mpremote
# to upgrade
pip3 install --upgrade mpremote

Commands Usage

Confirm Board

Its helpful to confirm you are connected to a board, particularly, if you have mulitple boards connected to your computer. I use a version of the blink program to do this. The program is here and I use it in this manner:

mpremote repl
# now I'm in mpremote connected to a board
# if I don't have the REPL, hit Ctrl-B
>>> import blink
>>> Blink()
# and now the board connected to the mpremote program is blinking!

Copy a file

Copy your file to main.py, if you wish the program to always execute on boot. Remember the destination requires a preceding “:”. It is helpful to execute a ls following, to ensure the program was copied.

# to copy a file
mpremote fs cp pintest.py :main.py
cp pintest.py :main.py
# to show contents of Pico filesystem
mpremote fs ls
ls :
        2332 main.py

Mount folder

Using the mount command, “mpremote mount .”, you can mount the folder on the PC as local storage to the microcontroller. Here is an example:

mpremote mount .
Local directory . is mounted at /remote
Connected to MicroPython at /dev/cu.usbmodem141201
Use Ctrl-] or Ctrl-x to exit this shell
>
MicroPython v1.20.0 on 2023-04-26; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
>>> import os
>>> os.listdir()
['hello.py', 'serial_test.py', 'index.html', '.DS_Store', 'config.py', 'delay.txt', 'blink.py', 'wlan.py', 'favicon.png', 'microdot.py', '.gitignore', 'secrets.py', 'light_leds.py', 'blink_wo_delay.py', 'bulma.min.css', 'pin_test.py', 'computer.svg', '.git']
>>> from blink import Blink
>>> Blink()

This allows me to immediately determine if my “empty” Pico can execute a command. I don’t need to load it with the blink program, I am able to import it and execute it locally!

A single line program is this:

mpremote mount . exec "from blink import Blink; Blink()"

However I have found that it requires a bit of work to stop it. I’ve had to use a Ctrl-C to kill the mpremote program.

Edit file

If you set your editor in the system environmental variable $EDITOR, mpremote will use it to enabling editing a file on the microcontroller. You use the EXPORT command in bash or the set command in the fish shell to do so. Once I did, I was able to enter mpremote edit main.py and the Pico version of main.py opened in my editor. Cool!

I wouldn’t recommend you use this as a code development practice, however, it works great for testing a quick and dirty fix to a program. For example, let’s say you are attempting to connect to a new wireless LAN and you use the secrets file for the SSID and password. You could use mpremote edit secrets.py to edit the file on the Pico and quickly change the SSID/password to new values.

Specific Connection

The mpremote connect command is of value when you have multiple microcontrollers attached to you PC. Here is an example, where I have two Picos attached:

ls /dev/cu.*
/dev/cu.BLTH                     /dev/cu.JabraEvolve65            /dev/cu.usbmodem141301
/dev/cu.Bluetooth-Incoming-Port  /dev/cu.usbmodem141201
mpremote connect /dev/cu.usbmodem141201 fs ls
ls :
      206838 bulma.min.css
        2837 computer.svg
          18 config.py
        1245 draw.html
        1264 lost.html
        4165 main.py
       46276 microdot.py
        1331 microdot_utemplate.py
          36 secrets.py
           0 templates/
           0 utemplate/
        1464 wlan.py
        1241 won.html

Or combine the hint using blink along with the connect command to blink a specific Pico led:

mpremote connect /dev/cu.usbmodem141201 mount . exec "from blink import Blink; Blink()"

Execute local files

A local file is one which resides on your PC, not on the microcontroller. The run command provides the capability of running the local file on your microcontroller without downloading it first. For example, another way to blink the led on a connected board would be:

# you may also use the run command to execute the local file on the Pico
mpremote connect /dev/cu.usbmodem141201 run blink.py

Notice that the blink.py file is not on the Pico (see the fs ls example above), and the run command is executing the local file (one which resides on your PC) on the microcontroller. Again, I needed to use Ctrl-C to disconnect from the board.

config file

The config file for mpremote may contain shortcuts and they can be quite powerful! For example, based on an entry by @jimmo, I used his shortcut on the Pico W to clear file memory. The command is a simple, yet powerful way to remove all files from the Pico, in order to fix a problem or start anew on a project. Be sure you want to erase your Pico program files, if you use this command!

I also simplified commands I use quite often such as mpremote fs ls to mpr fl, for which, I had to do two things, first, setup an alias of mpremote to mpr (I’ll use mpr going forward instead of mpremote, their use is identical.) then create the shortcut fl in the mpremote config file.

The other two examples are extremely handy for using a file on your computer on the microcontroller. The first mpr test will execute the program test.py in my local directory on my computer on the Pico. The second mpr info will execute a program which prints the informaton on local file storage on the Pico. This command is valuable to help you understand the storage impact of program components.

Local directory . is mounted at /remote
Attempting to determine filesystem
Littlefs v2 filesystem
0008  magic                            littlefs
0014  version                          0x20000
0018  block_size                       4096
001c  block_count                      212
0020  name_max                         255
0024  file_max                         2147483647
0028  attr_max                         1022
0000  program memory                   868352
0000  used memory                      151552
0000  free memory                      716800

When you run mpr devs, it will list the serial ports on your computer along with an ID of each MicroPython board. You can use the ID to connect with a specific board using the shortcut shown in the config file, as in mpr A, will connect to the board with the specific ID e6614864d3323634.

Finally, I am also able to combine some commands shown above into a specific command which will blink the connected board.

~/.config/mpremote/config.py

commands = {
    "A": "connect id:e6614864d3323634",
    "fl": "fs ls",
    "littlefs_rp2": [
        "exec",
        "--no-follow",
        "import os, machine, rp2; os.umount('/'); bdev = rp2.Flash();\
                os.VfsLfs2.mkfs(bdev, progsize=256); \
                vfs = os.VfsLfs2(bdev, progsize=256); \
                os.mount(vfs, '/'); machine.reset()",
    ],
    "test": ["mount", ".", "exec", "import test"],
    "info": ["mount", ".", "run", "fs_info.py"],
    "blink": ["mount", ".", "exec",
              "from blink import Blink; Blink()"]
}

Comments powered by Talkyard.