diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index e96b32cbbe..b826725360 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -4,46 +4,88 @@ MicroPython remote control: mpremote ==================================== The ``mpremote`` command line tool provides an integrated set of utilities to -remotely interact with and automate a MicroPython device over a serial -connection. +remotely interact with, manage the filesystem on, and automate a MicroPython +device over a serial connection. -To use mpremote install it via ``pip``: +To use mpremote, first install it via ``pip``: .. code-block:: bash - $ pip install mpremote + $ pip install --user mpremote + +Or via `pipx `_: + +.. code-block:: bash + + $ pipx install mpremote The simplest way to use this tool is just by invoking it without any arguments: .. code-block:: bash - mpremote + $ mpremote This command automatically detects and connects to the first available USB -serial device and provides an interactive REPL. Serial ports are opened in -exclusive mode, so running a second (or third, etc) instance of ``mpremote`` -will connect to subsequent serial devices, if any are available. +serial device and provides an interactive terminal that you can use to access +the REPL and your program's output. Serial ports are opened in exclusive mode, +so running a second (or third, etc) instance of ``mpremote`` will connect to +subsequent serial devices, if any are available. +Additionally ``pipx`` also allows you to directly run ``mpremote`` without +installing first: + +.. code-block:: bash + + $ pipx run mpremote ...args Commands -------- -For REPL access, running ``mpremote`` without any arguments is usually all that -is needed. ``mpremote`` also supports a set of commands given at the command -line which will perform various actions on remote MicroPython devices. +``mpremote`` supports being given a series of commands given at the command line +which will perform various actions in sequence on a remote MicroPython device. +See the :ref:`examples section ` below to get an idea of how +this works and for some common combinations of commands. -For commands that support multiple arguments (e.g. a list of files), the -argument list can be terminated with ``+``. +Each command is of the form `` [--options] [args...]``. For commands +that support multiple arguments (e.g. a list of files), the argument list can +be terminated with ``+``. + +If no command is specified, the default command is ``repl``. Additionally, if +any command needs to access the device, and no earlier ``connect`` has been +specified, then an implicit ``connect auto`` is added. + +In order to get the device into a known state for any action command +(except ``repl``), once connected ``mpremote`` will stop any running program +and soft-reset the device before running the first command. You can control +this behavior using the ``resume`` and ``soft-reset`` commands. +See :ref:`auto-connection and auto-soft-reset ` for more details. + +Multiple commands can be specified and they will be run sequentially. The full list of supported commands are: -- connect to a specified device via a device-name shortcut: +- `connect ` +- `disconnect ` +- `resume ` +- `soft_reset ` +- `repl ` +- `eval ` +- `exec ` +- `run ` +- `fs ` +- `df ` +- `edit ` +- `mip ` +- `mount ` +- `unmount ` +- `rtc ` +- `sleep ` +- `reset ` +- `bootloader ` - .. code-block:: bash +.. _mpremote_command_connect: - $ mpremote - -- connect to specified device via name: +- **connect** -- connect to specified device via name: .. code-block:: bash @@ -54,41 +96,60 @@ The full list of supported commands are: - ``list``: list available devices - ``auto``: connect to the first available USB serial port - ``id:``: connect to the device with USB serial number - ```` (the second entry in the output from the ``connect list`` - command) - - ``port:``: connect to the device with the given path + ```` (the second column from the ``connect list`` + command output) + - ``port:``: connect to the device with the given path (the first column + from the ``connect list`` command output - any valid device name/path, to connect to that device -- disconnect current device: + **Note:** Instead of using the ``connect`` command, there are several + :ref:`pre-defined shortcuts ` for common device paths. For + example the ``a0`` shortcut command is equivalent to + ``connect /dev/ttyACM0`` (Linux), or ``c0`` for ``COM0`` (Windows). + + **Note:** The ``auto`` option will only detect USB serial ports, i.e. a serial + port that has an associated USB VID/PID (i.e. CDC/ACM or FTDI-style + devices). Other types of serial ports + +.. _mpremote_command_disconnect: + +- **disconnect** -- disconnect current device: .. code-block:: bash $ mpremote disconnect - After a disconnect, auto soft-reset is enabled. + After a disconnect, :ref:`auto-soft-reset ` is enabled. -- resume a previous ``mpremote`` session: +.. _mpremote_command_resume: + +- **resume** -- maintain existing interpreter state for subsequent commands: .. code-block:: bash $ mpremote resume - This disables auto soft-reset. + This disables :ref:`auto-soft-reset `. This is useful if you + want to run a subsequent command on a board without first soft-resetting it. -- perform a soft-reset of the device: +.. _mpremote_command_soft_reset: + +- **soft-reset** -- perform a soft-reset of the device: .. code-block:: bash $ mpremote soft-reset This will clear out the Python heap and restart the interpreter. It also - disables auto soft-reset. + prevents the subsequent command from triggering :ref:`auto-soft-reset `. -- enter the REPL on the connected device: +.. _mpremote_command_repl: + +- **repl** -- enter the REPL on the connected device: .. code-block:: bash - $ mpremote repl [options] + $ mpremote repl [--options] Options are: @@ -96,47 +157,121 @@ The full list of supported commands are: - ``--capture ``, to capture output of the REPL session to the given file - ``--inject-code ``, to specify characters to inject at the REPL when - Ctrl-J is pressed + ``Ctrl-J`` is pressed. This allows you to automate a common command. - ``--inject-file ``, to specify a file to inject at the REPL when - Ctrl-K is pressed + ``Ctrl-K`` is pressed. This allows you to run a file (e.g. containing some + useful setup code, or even the program you are currently working on). -- evaluate and print the result of a Python expression: + While the ``repl`` command running, you can use ``Ctrl-]`` or ``Ctrl-x`` to + exit. + + **Note:** The name "REPL" here reflects that the common usage of this command + to access the Read Eval Print Loop that is running on the MicroPython + device. Strictly, the ``repl`` command is just functioning as a terminal + (or "serial monitor") to access the device. Because this command does not + trigger the :ref:`auto-reset behavior `, this means that if + a program is currently running, you will first need to interrupt it with + ``Ctrl-C`` to get to the REPL, which will then allow you to access program + state. You can also use ``mpremote soft-reset repl`` to get a "clean" REPL + with all program state cleared. + +.. _mpremote_command_eval: + +- **eval** -- evaluate and print the result of a Python expression: .. code-block:: bash $ mpremote eval -- execute the given Python code: +.. _mpremote_command_exec: + +- **exec** -- execute the given Python code: .. code-block:: bash $ mpremote exec -- run a script from the local filesystem: + By default, ``mpremote exec`` will display any output from the expression until it + terminates. The ``--no-follow`` flag can be specified to return immediately and leave + the device running the expression in the background. + +.. _mpremote_command_run: + +- **run** -- run a script from the local filesystem: .. code-block:: bash - $ mpremote run + $ mpremote run -- execute filesystem commands on the device: + This will execute the file directly from RAM on the device without copying it + to the filesystem. This is a very useful way to iterate on the development of + a single piece of code without having to worry about deploying it to the + filesystem. + + By default, ``mpremote run`` will display any output from the script until it + terminates. The ``--no-follow`` flag can be specified to return immediately and leave + the device running the script in the background. + +.. _mpremote_command_fs: + +- **fs** -- execute filesystem commands on the device: .. code-block:: bash - $ mpremote fs + $ mpremote fs - ```` may be: + ```` may be: - ``cat `` to show the contents of a file or files on the device - ``ls`` to list the current directory - ``ls `` to list the given directories - - ``cp [-r] `` to copy files; use ":" as a prefix to specify - a file on the device + - ``cp [-r] `` to copy files - ``rm `` to remove files on the device - ``mkdir `` to create directories on the device - ``rmdir `` to remove directories on the device - ``touch `` to create the files (if they don't already exist) -- edit a file on the device: + The ``cp`` command uses a convention where a leading ``:`` represents a remote + path. Without a leading ``:`` means a local path. This is based on the + convention used by the `Secure Copy Protocol (scp) client + `_. All other commands + implicitly assume the path is a remote path, but the ``:`` can be optionally + used for clarity. + + So for example, ``mpremote fs cp main.py :main.py`` copies ``main.py`` from + the current local directory to the remote filesystem, whereas + ``mpremote fs cp :main.py main.py`` copies ``main.py`` from the device back + to the current directory. + + All of the filesystem sub-commands take multiple path arguments, so if there + is another command in the sequence, you must use ``+`` to terminate the + arguments, e.g. + + .. code-block:: bash + + $ mpremote fs cp main.py :main.py + repl + + This will copy the file to the device then enter the REPL. The ``+`` prevents + ``"repl"`` being interpreted as a path. + + **Note:** For convenience, all of the filesystem sub-commands are also + :ref:`aliased as regular commands `, i.e. you can write + ``mpremote cp ...`` instead of ``mpremote fs cp ...``. + +.. _mpremote_command_df: + +- **df** -- query device free/used space + + .. code-block:: bash + + $ mpremote df + + The ``df`` command will print size/used/free statistics for the device + filesystem, similar to the Unix ``df`` command. + +.. _mpremote_command_edit: + +- **edit** -- edit a file on the device: .. code-block:: bash @@ -147,7 +282,9 @@ The full list of supported commands are: variable ``$EDITOR``). If the editor exits successfully, the updated file will be copied back to the device. -- install packages from :term:`micropython-lib` (or GitHub) using the ``mip`` tool: +.. _mpremote_command_mip: + +- **mip** -- install packages from :term:`micropython-lib` (or GitHub) using the ``mip`` tool: .. code-block:: bash @@ -155,16 +292,34 @@ The full list of supported commands are: See :ref:`packages` for more information. -- mount the local directory on the remote device: +.. _mpremote_command_mount: + +- **mount** -- mount the local directory on the remote device: .. code-block:: bash $ mpremote mount [options] - During usage, Ctrl-D will soft-reboot and normally reconnect the mount automatically. - If the unit has a main.py running at startup however the remount cannot occur. - In this case a raw mode soft reboot can be used: Ctrl-A Ctrl-D to reboot, - then Ctrl-B to get back to normal repl at which point the mount will be ready. + This allows the remote device to see the local host directory as if it were + its own filesystem. This is useful for development, and avoids the need to + copy files to the device while you are working on them. + + The device installs a filesystem driver, which is then mounted in the + :ref:`device VFS ` as ``/remote``, which uses the serial + connection to ``mpremote`` as a side-channel to access files. The device + will have its current working directory (via ``os.chdir``) set to + ``/remote`` so that imports and file access will occur there instead of the + default filesystem path while the mount is active. + + **Note:** If the ``mount`` command is not followed by another action in the + sequence, a ``repl`` command will be implicitly added to the end of the + sequence. + + During usage, Ctrl-D will trigger a soft-reset as normal, but the mount will + automatically be re-connected. If the unit has a main.py running at startup + however the remount cannot occur. In this case a raw mode soft reboot can be + used: Ctrl-A Ctrl-D to reboot, then Ctrl-B to get back to normal repl at + which point the mount will be ready. Options are: @@ -173,22 +328,74 @@ The full list of supported commands are: local directory that is mounted. This option disables this check for symbolic links, allowing the device to follow symbolic links outside of the local directory. -- unmount the local directory from the remote device: +.. _mpremote_command_unmount: + +- **unmount** -- unmount the local directory from the remote device: .. code-block:: bash $ mpremote umount -Multiple commands can be specified and they will be run sequentially. + This happens automatically when ``mpremote`` terminates, but it can be used + in a sequence to unmount an earlier mount before subsequent command are run. +.. _mpremote_command_rtc: + +- **rtc** -- set/get the device clock (RTC): + + .. code-block:: bash + + $ mpremote rtc + + This will query the device RTC for the current time and print it as a datetime + tuple. + + .. code-block:: bash + + $ mpremote rtc --set + + This will set the device RTC to the host PC's current time. + +.. _mpremote_command_sleep: + +- **sleep** -- sleep (delay) before executing the next command + + .. code-block:: bash + + $ mpremote sleep 0.5 + + This will pause execution of the command sequence for the specified duration + in seconds, e.g. to wait for the device to do something. + +.. _mpremote_command_reset: + +- **reset** -- hard reset the device + + .. code-block:: bash + + $ mpremote reset + + **Note:** hard reset is equivalent to :func:`machine.reset`. + +.. _mpremote_command_bootloader: + +- **bootloader** enter the bootloader + + .. code-block:: bash + + $ mpremote bootloader + + This will make the device enter its bootloader. The bootloader is port- and + board-specific (e.g. DFU on stm32, UF2 on rp2040/Pico). + +.. _mpremote_reset: Auto connection and soft-reset ------------------------------ Connection and disconnection will be done automatically at the start and end of the execution of the tool, if such commands are not explicitly given. Automatic -connection will search for the first available USB serial device. If no action -is specified then the REPL will be entered. +connection will search for the first available USB serial device. Once connected to a device, ``mpremote`` will automatically soft-reset the device if needed. This clears the Python heap and restarts the interpreter, @@ -198,38 +405,60 @@ executed: ``mount``, ``eval``, ``exec``, ``run``, ``fs``. After doing a soft-reset for the first time, it will not be done again automatically, until a ``disconnect`` command is issued. -Auto soft-reset behaviour can be controlled by the ``resume`` command. And the -``soft-reset`` command can be used to perform an explicit soft reset. +Auto-soft-reset behaviour can be controlled by the ``resume`` command. This +might be useful to use the ``eval`` command to inspect the state of of the +device. The ``soft-reset`` command can be used to perform an explicit soft +reset in the middle of a sequence of commands. +.. _mpremote_shortcuts: Shortcuts --------- -Shortcuts can be defined using the macro system. Built-in shortcuts are:: +Shortcuts can be defined using the macro system. Built-in shortcuts are: -- ``devs``: list available devices (shortcut for ``connect list``) +- ``devs``: Alias for ``connect list`` -- ``a0``, ``a1``, ``a2``, ``a3``: connect to /dev/ttyACM? +- ``a0``, ``a1``, ``a2``, ``a3``: Aliases for ``connect /dev/ttyACMn`` -- ``u0``, ``u1``, ``u2``, ``u3``: connect to /dev/ttyUSB? +- ``u0``, ``u1``, ``u2``, ``u3``: Aliases for ``connect /dev/ttyUSBn`` -- ``c0``, ``c1``, ``c2``, ``c3``: connect to COM? +- ``c0``, ``c1``, ``c2``, ``c3``: Aliases for ``connect COMn`` -- ``cat``, ``ls``, ``cp``, ``rm``, ``mkdir``, ``rmdir``, ``touch``, ``df``: - filesystem commands +- ``cat``, ``edit``, ``ls``, ``cp``, ``rm``, ``mkdir``, ``rmdir``, ``touch``: Aliases for ``fs `` -- ``reset``: reset the device +Additional shortcuts can be defined by in user-configuration files, which is +located at ``.config/mpremote/config.py``. This file should define a +dictionary named ``commands``. The keys of this dictionary are the shortcuts +and the values are either a string or a list-of-strings: -- ``bootloader``: make the device enter its bootloader +.. code-block:: python3 -Any user configuration, including user-defined shortcuts, can be placed in the file -``.config/mpremote/config.py``. For example: + "c33": "connect id:334D335C3138", + +The command ``c33`` is replaced by ``connect id:334D335C3138``. + +.. code-block:: python3 + + "test": ["mount", ".", "exec", "import test"], + +The command ``test`` is replaced by ``mount . exec "import test"``. + +Shortcuts can also accept arguments. For example: + +.. code-block:: python3 + + "multiply x=4 y=7": "eval x*y", + +Running ``mpremote times 3 7`` will set ``x`` and ``y`` as variables on the device, then evaluate the expression ``x*y``. + +An example ``config.py`` might look like: .. code-block:: python3 commands = { - "c33": "connect id:334D335C3138", - "bl": "bootloader", + "c33": "connect id:334D335C3138", # Connect to a specific device by ID. + "bl": "bootloader", # Shorter alias for bootloader. "double x=4": "eval x*2", # x is an argument, with default 4 "wl_scan": ["exec", """ import network @@ -237,10 +466,16 @@ Any user configuration, including user-defined shortcuts, can be placed in the f wl.active(1) for ap in wl.scan(): print(ap) - """,], - "test": ["mount", ".", "exec", "import test"], + """,], # Print out nearby WiFi networks. + "wl_ifconfig": [ + "exec", + "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ifconfig())", + """,], # Print ip address of station interface. + "test": ["mount", ".", "exec", "import test"], # Mount current directory and run test.py. + "demo": ["run", "path/to/demo.py"], # Execute demo.py on the device. } +.. _mpremote_examples: Examples -------- @@ -249,38 +484,205 @@ Examples mpremote +Connect to the first available device and implicitly run the ``repl`` command. + +.. code-block:: bash + mpremote a1 - mpremote connect /dev/ttyUSB0 repl +Connect to the device at ``/dev/ttyACM1`` (Linux) and implicitly run the +``repl`` command. See :ref:`shortcuts ` above. - mpremote ls +.. code-block:: bash + + mpremote c1 + +Connect to the device at ``COM1`` (Windows) and implicitly run the ``repl`` +command. See :ref:`shortcuts ` above. + +.. code-block:: bash + + mpremote connect /dev/ttyUSB0 + +Explicitly specify which device to connect to, and as above, implicitly run the +``repl`` command. + +.. code-block:: bash mpremote a1 ls +Connect to the device at ``/dev/ttyACM0`` and then run the ``ls`` command. + +It is equivalent to ``mpremote connect /dev/ttyACM1 fs ls``. + +.. code-block:: bash + mpremote exec "import micropython; micropython.mem_info()" +Run the specified Python command and display any output. This is equivalent to +typing the command at the REPL prompt. + +.. code-block:: bash + mpremote eval 1/2 eval 3/4 +Evaluate each expression in turn and print the results. + +.. code-block:: bash + + mpremote a0 eval 1/2 a1 eval 3/4 + +Evaluate ``1/2`` on the device at ``/dev/ttyACM0``, then ``3/4`` on the +device at ``/dev/ttyACM1``, printing each result. + +.. code-block:: bash + + mpremote resume exec "print_state_info()" soft-reset + +Connect to the device without triggering a soft reset and execute the +``print_state_info()`` function (e.g. to find out information about the current +program state), then trigger a soft reset. + +.. code-block:: bash + + mpremote reset sleep 0.5 bootloader + +Hard-reset the device, wait 500ms for it to become available, then enter the +bootloader. + +.. code-block:: bash + + mpremote cp utils/driver.py :utils/driver.py + run test.py + +Update the copy of utils/driver.py on the device, then execute the local +``test.py`` script on the device. ``test.py`` is never copied to the device +filesystem, rather it is run from RAM. + +.. code-block:: bash + + mpremote cp utils/driver.py :utils/driver.py + exec "import app" + +Update the copy of utils/driver.py on the device, then execute app.py on the +device. + +This is a common development workflow to update a single file and then re-start +your program. In this scenario, your ``main.py`` on the device would also do +``import app``. + +.. code-block:: bash + + mpremote cp utils/driver.py :utils/driver.py + soft-reset repl + +Update the copy of utils/driver.py on the device, then trigger a soft-reset to +restart your program, and then monitor the output via the ``repl`` command. + +.. code-block:: bash + + mpremote cp -r utils/ :utils/ + soft-reset repl + +Same as above, but update the entire utils directory first. + +.. code-block:: bash + mpremote mount . - mpremote mount . exec "import local_script" +Mount the current local directory at ``/remote`` on the device and starts a +``repl`` session which will use ``/remote`` as the working directory. - mpremote ls +.. code-block:: bash + + mpremote mount . exec "import demo" + +After mounting the current local directory, executes ``demo.py`` from the +mounted directory. + +.. code-block:: bash + + mpremote mount app run test.py + +After mounting the local directory ``app`` as ``/remote`` on the device, +executes the local ``test.py`` from the host's current directory without +copying it to the filesystem. + +.. code-block:: bash + + mpremote mount . repl --inject-code "import demo" + +After mounting the current local directory, executes ``demo.py`` from the +mounted directory each time ``Ctrl-J`` is pressed. + +You will first need to press ``Ctrl-D`` to reset the interpreter state +(which will preserve the mount) before pressing ``Ctrl-J`` to re-import +``demo.py``. + +.. code-block:: bash + + mpremote mount app repl --inject-file demo.py + +Same as above, but executes the contents of the local file demo.py at the REPL +every time ``Ctrl-K`` is pressed. As above, use Ctrl-D to reset the interpreter +state first. + +.. code-block:: bash mpremote cat boot.py +Displays the contents of ``boot.py`` on the device. + +.. code-block:: bash + + mpremote edit utils/driver.py + +Edit ``utils/driver.py`` on the device using your local ``$EDITOR``. + +.. code-block:: bash + mpremote cp :main.py . +Copy ``main.py`` from the device to the local directory. + +.. code-block:: bash + mpremote cp main.py : +Copy ``main.py`` from the local directory to the device. + +.. code-block:: bash + mpremote cp :a.py :b.py +Copy ``a.py`` on the device to ``b.py`` on the device. + +.. code-block:: bash + mpremote cp -r dir/ : +Recursively copy the local directory ``dir`` to the remote device. + +.. code-block:: bash + mpremote cp a.py b.py : + repl +Copy ``a.py`` and ``b.py`` from the local directory to the device, then run the +``repl`` command. + +.. code-block:: bash + mpremote mip install aioble +Install the ``aioble`` package from :term:`micropython-lib` to the device. +See :ref:`packages`. + +.. code-block:: bash + mpremote mip install github:org/repo@branch +Install the package from the specified branch at org/repo on GitHub to the +device. See :ref:`packages`. + +.. code-block:: bash + mpremote mip install --target /flash/third-party functools + +Install the ``functools`` package from :term:`micropython-lib` to the +``/flash/third-party`` directory on the device. See :ref:`packages`. diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index dad14b78be..cb96369a53 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -306,7 +306,7 @@ _BUILTIN_COMMAND_EXPANSIONS = { "command": "connect list", "help": "list available serial ports", }, - # Filesystem shortcuts. + # Filesystem shortcuts (use `cp` instead of `fs cp`). "cat": "fs cat", "ls": "fs ls", "cp": "fs cp", @@ -314,6 +314,7 @@ _BUILTIN_COMMAND_EXPANSIONS = { "touch": "fs touch", "mkdir": "fs mkdir", "rmdir": "fs rmdir", + # Disk used/free. "df": [ "exec", "import uos\nprint('mount \\tsize \\tused \\tavail \\tuse%')\nfor _m in [''] + uos.listdir('/'):\n _s = uos.stat('/' + _m)\n if not _s[0] & 1 << 14: continue\n _s = uos.statvfs(_m)\n if _s[0]:\n _size = _s[0] * _s[2]; _free = _s[0] * _s[3]; print(_m, _size, _size - _free, _free, int(100 * (_size - _free) / _size), sep='\\t')",