RP2040 MicroPython: mpremote

11 minute read

An updated look at the utility, 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. After using it extensively, I’ve found its biggest issue is the lack of documentation as compared to its capabilities. It is invaluable, if not mandatory for interacting with a MicroPython board.

Installation

# to install
pip install mpremote
# to upgrade
pip install --upgrade mpremote

Help Command

Once installed, use the help command to see the available commands. Make sure CoolTerm is not connected, otherwise you won’t be able to connect to the board via mpremote.

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)
  rtc         get (default) or set the device RTC
  run         run the given local script
  sleep       sleep before executing next command
  soft-reset  perform a soft-reset of the device
  umount      unmount the local directory
  version     print version and exit

Stages of use

I recommend learning to use mpremote in stages:

Stage 1: File management

  1. Use it to copy files to the board and understand what is on the board. At times, this might be the only tool which can do what you want. Examples:
# copy file from computer to board
mpremote fs cp filename :filename
mpremote fs cp filename :main.py
# list the files on the board
mpremote fs ls
# remove a file from the board
mpremote fs rm filename

Stage 2: Add New Commands

  1. Add more capability using a .config file. See Stage 2:. This can provide more and extremely helpful capabilities such as reformatting the filesystem or command simplification. When you add a .config file, the shortcuts you have added will be listed, however, I have yet to find a way to add a comment or documentation as to what the shortcut does.
# reformat the board, DELETES ALL FILES ON THE BOARD!
mpremote littlefs_rp2
# shortened command for listing files
mpremote fl
# provides detailed information as to filesystem
mpremote info

Stage 3: Customization

  1. Develop your own specific command shortcuts or actions, which you need to be more efficient. For example, in version 1.2.1, the ability for mpremote to set the real-time-clock (RTC) was added. This means a new copy command might be of value, which not only copies over the file, it also sets the value of the RTC. More than likely, using raw REPL might be required to build powerful commands, see Note 2.

Details on Stages

Stage 1: File Management

Copy a file

If you wish a program to always execute on boot, copy your file to main.py, . 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

Stage 2: Add New Commands Using a .config file

A compelling aspect of mpremote is its config file. The config file for mpremote may contain shortcuts and they can be quite powerful.

For example, based on this 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 simplify commands I quite often use 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 information 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.

mpremote  devs
/dev/cu.Bluetooth-Incoming-Port None 0000:0000 None None
/dev/cu.JabraEvolve65 None 0000:0000 None None
/dev/cu.usbmodem31401 e6614864d3323634 2e8a:0005 MicroPython Board in FS mode
/dev/cu.wlan-debug None 0000:0000 None None

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()"]
}

Stage 3: Custom Commands

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 light bulb 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” with the pyboard tool, just remember to use raw_repl.

Additional mpremote Commands

Mount folder

Using the mount command, “mpremote mount .”, you can mount the folder on the PC as local storage to the microcontroller. When the listdir() is executed it is showing the contents of the folder on the PC, not on the Pico. 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_key.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']

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  mount .
# now I'm in mpremote connected to a board, with the PC folder as local storage
# if I don't have the REPL, hit Ctrl-B
>>> from blink_key import Blink
>>> Blink()
# and now the board connected to the mpremote program is blinking!

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_key import Blink; Blink()"

However I have found that it requires a bit of work to stop it. The easiest thing to do is to press RESET on the Pico to stop.

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!

# make a simple change to the boot program
mpremote edit main.py
# edit the password in the secrets file
mpremote edit secrets.py

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_key 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_key.py

Notice that the blink_key.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.

Build an Application

As you begin to develop more complex programs, you will need to copy files to the Pico in a structured way. If you don’t, you might end up with a version control nightmare. As I developed the multiple versions of microserver, I realized I needed a way to easily delete all the files and reload the Pico with a new set.

The first command and possibly dangerous command is ’littlefs_rp2’. This command will reformat your storage on the Pico. It is the rm -rf / for the Pico. Only use it when you know you have backed up all of the files on your Pico! Thank you @jimmo

The second command uses a python application I wrote called mpbuild. It uses a text file to describe the files to copy to the board. There is a very simple syntax, which is described in the file:

# requires a text file containing the following:
# lines starting with '#' are comments and ignored
# lines starting with '/' are directories and are created
# lines starting with '!' are files to be copied and renamed,
#   two fields are required, separated by a ',', localname, piconame
# 1 line starting with '+' will be copied to main.py
# directory lines must appear prior to the files in the directories
# all other lines are considered valid files in the current directory
# PYBOARD_DEVICE environmental variable must be set to board serial port

A example file would be:

# light_leds_v4 files
/templates
/utemplate
computer.svg
microdot.py
microdot_utemplate.py
secrets.py
wlan.py
+light_leds_v4.py
templates/marx.css
utemplate/compiled.py
utemplate/recompile.py
utemplate/source.py
!templates/index_v4.html,templates/index.html
!templates/configured_v4.tpl,templates/configured.tpl

This file will create the following on the Pico W:

mpbuild.py files_v4.txt
/
        2837 computer.svg
        2645 main.py
       46368 microdot.py
        1331 microdot_utemplate.py
          36 secrets.py
           0 templates/
           0 utemplate/
        2346 wlan.py
/templates/
         652 configured.tpl
        1302 index.html
       18120 marx.css
/utemplate/
         380 compiled.py
         593 recompile.py
        6085 source.py
Connected 0.0 secs

I typically use it in this situation:

# delete all files on the Pico
mpr littlefs_rp2
# build a new application on Pico
mpbuild files_v4.txt

The program mpbuild does require CoolTerm_pip to be installed to automate disconnection and re-connection to CoolTerm, please see this entry to install or comment out the following lines in mpbuild.py:

17  from CoolTerm.CT_connect import conn
18  from CoolTerm.CT_disconnect import disc
66  disc()
132 conn()

Comments powered by Talkyard.