Friday, 29 January 2021

Controlling a Raspberry Pi Pico remotely using PySerial

You can use a Raspberry Pi Pico as a powerful peripheral to a host - a Raspberry Pi, a Jetson Nano, a laptop or workstation. In this article you'll see how to interact with a Pico running MicroPython or CircuitPython by writing some Python code that runs on the host.

The software is easy to use. It enables you to send a Python statement to the Pico and read the results. The statement can be any valid MicroPython code.

Setting up the host and the Pico

For this article I've used a Raspberry Pi as the host, but any computer running Windows, Linux or Mac OS will do so long as it has Python 3.5 or later installed.

You can use this technique to connect a Raspberry Pi Pico to a Jetson Nano or any other member of the Jetson family.

You'll need to install MicroPython on the Pico. You'll find instructions for MicroPython installation in the official Getting Started Guide which I reviewed recently.

 The £10 Guide is worth buying, but if you can't wait for your copy to arrive you can download a free pdf.

The guide will also tell you how to use the Thonny editor to install the necessary code on your Pico.

Connect the host and the Pico

First, connect the host to the Pico using a USB data lead. Some USB leads only supply power. They will not work.

Install software on the Pico

Next, install the blinker script.

The script runs on the Pico, but you should rename it to Here's the code:

from machine import Pin

#use onboard LED which is controlled by Pin 25
led = Pin(25, Pin.OUT)

# Turn the LED on
def on():

# Turn the LED off
def off():
  1. Install it on the Pico using the Thonny editor.
    1. Open the gist on GitHub.
    2. Copy the code to your clipboard.
    3. Open Thonny and make sure it has connected to the Pico.
    4. Paste the code into the Thonny editor window.
    5. Save it on the Pico as
    6. Close the Thonny editor.

If you leave the Thonny editor open it will keep the serial port open on the host, and the serial program below will not work!

Since you saved the program as it will run on the Pico automatically.

The script runs on the host. It uses PySerial to send commands from the host to the Raspberry Pi Pico and read the result.

Here's the sender code:

import serial

class Sender:
    TERMINATOR = '\r'.encode('UTF8')

    def __init__(self, device='/dev/ttyACM0', baud=9600, timeout=1):
        self.serial = serial.Serial(device, baud, timeout=timeout)

    def receive(self) -> str:
        line = self.serial.read_until(self.TERMINATOR)
        return line.decode('UTF8').strip()

    def send(self, text: str) -> bool:
        line = '%s\r\f' % text
        # the line should be echoed.
        # If it isn't, something is wrong.
        return text == self.receive()

    def close(self):

On the host

  1. Run pip3 install pyserial.
  2. Copy from this github gist into an editor and save it in a directory of your choice.
  3. In that directory, run python3 to start an interactive session.
  4. Type from sended import Sender
  5. Type s = Sender(). If you are running on Windows, you will need to type s = Sender('COM6') replacing COM6 by whatever port the Pico is visible on.
  6. Type s.send('2 + 2')
  7. Type s.receive(). If all is well, this will print the result 4.
  8. Type s.send('on()'. The on-board LED on the Pico should turn on.
  9. Type s.send('off()'. The on-board LED on the Pico should turn off.
  10. When you have finished, type s.close().

Of course in a real application you'd normally create the sender, send and receive data using a Python script. In a future post I'll show how to do that to create a simple weather station with the Pico and plot the temperature and light level on the host. Before that I'll explore other ways of connecting and controlling the Pico from a Host.

To keep up to date with this series follow @rareblog on twitter.


  1. This isn't working for me. First, I had to modify the line that sends the text data:
    line = r'%s\r\f' % text
    to treat it as a raw string, otherwise I'd get '♀n()' being sent instead of 'on()'.
    However, I'm still not seeing the LED turn on or off and receiving 'False' after the timeout.

    Does the pico code ( need to be in a loop?

    1. You shouldn't use a raw string. What the program does is to send your string, followed by a carriage return '\r' and a tab '\t'.

      The pico code does not need to be put in a loop. You're sending Python commands from the Pi or Nano to the Pico; the Python REPL will execute them as they are received.

  2. What if I didn't wanted the blinker script to run constantly as, but rather, have the user decides when to toggle the LED by sending a command to the PICO over the serial connection?

    1. Then just use a terminal program like screen on the Pi, and type on() to turn the LED on and off() to turn it off. You don't need the sender program at all.