Programming the Raspberry Pi Pico Microcontroller with MicroPython

February 06, 2021
Written by
Reviewed by

Programming the Raspberry Pi Pico Microcontroller with MicroPython

With the release of the Raspberry Pi Pico, the Raspberry Pi Foundation has expanded its product offering beyond their highly successful line of mini-computers and went even smaller by entering the microcontroller market.

Raspberry Pi Pico

Unlike the Raspberry Pi, which functions as a general purpose computer and runs the popular Linux operating system, the Pico is a much more primitive device that lacks a central operating system and can only be programmed to perform specific tasks or control connected peripherals, usually as part of an embedded system or Internet of Things device.

While most microcontrollers can only be programmed in C or C++, the Pico also offers support for MicroPython, a slimmed down version of Python that is designed specifically for small devices. This makes it a great choice for beginners who want to design their own devices but don’t have the patience or interest to learn low-level programming.

In this article you are going to learn how to set up and program your Pico with MicroPython. Don’t have one yet? They sell for $4 USD so buy one (or two!) and follow along.

Requirements

To follow this tutorial you will need the following items:

  • A Raspberry Pi Pico microcontroller. You can find out where to buy one in your region here.
  • A computer with one USB or USB-C available port. Windows, Mac and Linux are all good!
  • A USB to micro-USB cable. These are used as charger cables for many cell phones, so you may already have one. Here is a link to one if you need to buy it or see a picture. If your computer has USB-C ports, then you will also need a USB to USB-C adapter. 
  • Python 3.6+ installed on your computer. If you don’t have it, you can download an installer.

Installing MicroPython on your Pico

Before you can program the Pico with MicroPython, this language needs to be installed on the microcontroller board. You can visit the MicroPython for Pico Downloads page to find the current version. You will find a list of stable and unstable builds. From the list, find the most recent stable build and download it to your computer. This is going to be a file with a .uf2 extension.

MicroPython builds

Now comes the fun part. Take the USB to Micro-USB cable and plug the USB end to your computer.

Next, grab your Pico and locate the BOOTSEL button.

Pico BOOTSEL button

Press the button, and while you keep it pressed, plug the micro-USB end of your cable into your Pico (the micro-USB connector is seen on the right side in the above picture). In a second or two a new disk drive will appear on your computer. You can release the button when you see it.

Pico disk drive

To install MicroPython, drag and drop the .uf2 file that you downloaded earlier inside this disk drive. As soon as the file transfers, the drive will disappear and the device will reboot itself. On some computers you may get a warning about the drive being disconnected without properly ejecting it. You can ignore this warning.

Congratulations, you have successfully installed MicroPython on your Pico!

Accessing the MicroPython REPL

Like the standard Python language, MicroPython comes with a REPL, where you can enter statements and have them evaluated interactively. The MicroPython REPL can be accessed from your computer while connected with the USB cable.

The Pico should appear as a serial port device on your computer, so the next task is to figure out which of possibly several serial ports installed on your computer is the one that is connected to your microcontroller, and this varies depending on which operating system you are using:

  • If you are using a Mac computer, then the name of the Pico serial device is /dev/tty.usbmodem0000000000001.
  • On a Linux computer the device name may vary, but it usually has the format /dev/ttyACM<n>, where <n> is a number, likely 0. You can use the lsusb command to list all the USB attached devices. If you can’t identify which device maps to the Pico, you can unplug it from the computer and run lsusb again to see which device is now missing.
  • On a Windows computer the device name will have the format COM<n> where <n> is a number. You can open the Device Manager of the Control Panel and look under “Ports (COM & LPT)” for the list of serial devices. If you cannot identify which of the devices listed there is your Pico, unplug it from USB and check the list again to find which one is now missing.

Great, now you know what serial device your computer is using to connect to the Pico microcontroller.

The Pico behaves like a standard serial device, so it can be accessed with any serial terminal program available for your operating system. In this article you are going to use rshell, a tool written in Python that is designed to connect to and manage MicroPython microcontroller boards.

Open a terminal window in your computer and create a new directory where you will store your Pico experiments:

mkdir pico-tests
cd pico-tests

Create and activate a Python virtual environment. If you are using a Mac or Linux computer, do it as follows:

python3 -m venv venv
source venv/bin/activate

On a Windows computer, use the following commands:

python -m venv venv
venv\Scripts\activate

Make sure that the Python virtual environment is activated by checking that your command prompt was modified to include a (venv) prefix.

Next install the rshell tool in your virtual environment:

pip install rshell

Make sure the Pico is still connected to your computer with the USB cable (you will not see a disk drive now), and then execute the following command to connect to it from your computer:

rshell -p <your-pico-serial-device> --buffer-size 512

Make sure to replace <your-pico-serial-device> with the serial device name assigned to your Pico in the command above. For example, on a Mac computer the actual command is:

rshell -p /dev/tty.usbmodem0000000000001 --buffer-size 512

On Windows, assuming the Pico is connected to COM3, the command would be:

rshell -p COM3 --buffer-size 512

The output of rshell should look as follows:

Using buffer-size of 512
Connecting to /dev/tty.usbmodem0000000000001 (buffer-size 512)...
Trying to connect to REPL  connected
Testing if ubinascii.unhexlify exists ... Y
Retrieving root directories ...
Setting time ... Feb 04, 2021 14:33:07
Evaluating board_name ... pyboard
Retrieving time epoch ... Jan 01, 1970
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.
/Users/miguel/pico-tests>

The command should end with a new prompt. Type help in this prompt to see all the commands rshell supports:

/Users/miguel/pico-tests> help

Documented commands (type help <topic>):
========================================
args    cat  connect  date  edit  filesize  help  mkdir  rm     shell
boards  cd   cp       echo  exit  filetype  ls    repl   rsync

Use Control-D (or the exit command) to exit rshell.
/Users/miguel/pico-tests>

For now, let’s concentrate on the repl command, which starts a MicroPython interactive shell on your Pico device:

/Users/miguel/pico-tests> repl
Entering REPL. Use Control-X to exit.
>
MicroPython v1.14 on 2021-02-02; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>>

Does this look familiar? If you’ve ever used a Python REPL you will surely recognize the >>> prompt. You can now enter Python statements, exactly like you would in a Python shell running on your computer. But keep in mind that MicroPython runs on the Pico board and your computer is acting as a dumb terminal.

To exit the MicroPython shell, press Ctrl-X and that will bring you back to the rshell prompt. Then press Ctrl-D at the rshell prompt to exit back to your terminal.

If all you want to do is access the MicroPython REPL, then you can skip the intermediate step of the rshell prompt by adding repl at the end of the rshell command:

rshell -p <your-pico-serial-device> --buffer-size 512 repl

Programming the Pico

To work with a microcontroller such as the Pico, you basically have to issue read or write requests to any of its I/O pins. Some of the pins in the Pico are available for you to connect external devices, while some others are internally connected. Below you can see a diagram that shows the functions of all the I/O pins in the board:

Pico pinout diagram

You can find a higher resolution version of this diagram in PDF format here. As you can see, most pins have several functions.

In this section you will learn how to work with two components that are included in the Pico board: the LED, which you can see labeled as GP25 at the top of the above diagram, and a temperature sensor, which for some strange reason has not been included in this diagram.

Accessing the onboard LED

The LED that comes with the Pico board is connected to a pin labeled GP25. This is one of several “GPIO” or General-Purpose Input/Output pins available in the Pico board. You can recognize these pins because they are labeled with GP followed by a number.

MicroPython includes the machine module, and more specifically the machine.Pin class to access and work with GPIO pins.

Enter the MicroPython REPL and then type the following Python commands to turn the LED on:

from machine import Pin
led = Pin(25, Pin.OUT)
led.on()

Here you are creating a Pin object that is attached to GPIO pin 25, and specifying that this pin will be used as an output pin. The on() method sets a “high” value on the pin, which triggers the onboard LED to light up.

Pico LED

You can probably guess what happens if you call led.off(). Another interesting option you can try is to call led.toggle().

Obtaining a temperature reading

The temperature sensor that comes with the Pico is connected to one of a few special pins called ADCs or Analog-to-Digital Converters. The difference between a standard GPIO pin and an ADC pin is that a GPIO pin supports only two states, high and low, while an ADC pin supports a range of values, which is determined by the input voltage applied to the pin.

In the Pico, the ADC pins support 12-bits, which means that their value can go from 0 to 4095. MicroPython, however, scales the ADC values to a 16-bit range, so effectively the range is from 0 to 65535. The microcontroller runs at 3.3 V, which means that an ADC pin will return a value of 65535 when 3.3 V are applied to it, or 0 when there is no voltage. All the values in between are obtained when the voltage applied to the pin is between 0 and 3.3 V.

The ADC pins in the Pico board use their own numbering scheme instead of going by their GPIO pin number. In the pin diagram above you can see the pins labeled ADC0, ADC1, ADC2 and ADC_VREF (technically ADC3), which are the four externally accessible ADC pins. The temperature sensor does not have a physical pin in the board, but is accessed as ADC4.

The machine module provides the ADC() class to work with ADC pins. The following code can be used to read the temperature sensor from the MicroPython shell:

from machine import ADC
temp_sensor = ADC(4)
temperature = temp_sensor.read_u16()

If you print the value of the temperature value you are going to get an integer number between 0 and 65535, as indicated above. This is not very useful as a temperature measurement, so you will probably want to convert this value either to the Celsius or Fahrenheit degree scales.

The temperature sensor works by delivering a voltage to the ADC4 pin that is proportional to the temperature. According to the specifications of this sensor, a temperature of 27 degrees Celsius delivers a voltage of 0.706 V, with each additional degree reducing the voltage by 1.721 mV, or 0.001721 V. Conversely, each degree below 27 degrees Celsius increases the voltage by the same amount. The first step in converting the 16-bit temperature is to convert it back to volts, which is done based on the 3.3 V maximum voltage used by the Pico board:

to_volts = 3.3 / 65535
temperature = temperature * to_volts

With this conversion the temperature value holds a value between 0 and 3.3. A second conversion is now necessary to bring the temperature to the Celsius scale:

celsius_degrees = 27 - (temperature - 0.706) / 0.001721

And finally, if you want to use the Fahrenheit scale, there is one more conversion to be applied on the Celsius temperature:

fahrenheit_degrees = celsius_degrees * 9 / 5 + 32

Writing a complete application

In addition to executing little tests, the Pico can be programmed to run a complete MicroPython application when it boots.

As an example, let’s build a short application that blinks the onboard LED five times. Here is the code, which you can copy into a new file on your computer named led_blink.py:

from machine import Pin
from utime import sleep

led = Pin(25, Pin.OUT)
for i in range(5):
    led.on()
    sleep(1)
    led.off()
    sleep(1)

One of the many functions the rshell command can perform to help you manage your MicroPython board is to copy files between your computer and the board’s file system. To install the above application in your Pico board so that it automatically executes when the board is powered on, you have to copy it with the name main.py, which is automatically imported at the end of the boot sequence.

To do this, start by entering the rshell prompt:

rshell -p <your-pico-serial-device> --buffer-size 512

Then in the rshell prompt use the cp command to copy the file to the Pico board:

cp led_blink.py /pyboard/main.py

The cp command, as well as other file system based commands, use the convention that any paths that begin with /pyboard/ refer to the virtual file system set up by MicroPython inside the microcontroller board. Any paths or filenames that do not have this prefix are assumed to reference your computer’s file system.

The cp command above copies the local led_blink.py file from your computer to the root directory of the Pico file system with the name main.py.

After you copy the file, exit rshell and then power cycle your Pico by unplugging it from USB and then plugging it again. You should now see the LED blink five times! The main.py file is now part of the boot sequence of the board, so it will also run if you trigger a soft reboot by pressing Ctrl-D in a REPL session.

Feel free to experiment by changing the above application to blink the LED in different patterns. After making changes to the code in your computer remember to use the cp command in rshell to update the copy stored in the Pico.

When you are done playing with the application you can remove the file from the board’s file system with the rm command:

rm /pyboard/main.py

Are you wondering why I named the local file led_blink.py instead of using the main.py name that has to be used in the Pico’s file system? The reason is that in rshell it is too easy to make a mistake and run rm main.py when you really wanted to run rm /pyboard/main.py. Using different names ensures that you don’t accidentally delete your local file.

Conclusion

I hope this was a fun and easy introduction to programming the Pico microcontroller with MicroPython!

Are you interested in more articles about the Raspberry Pi Pico? I’ve also written a fun tutorial for a mute/unmute physical button that you can use during your video conference calls!

This article only explored a tiny fraction of the wide range of applications that can be built with this microcontroller. There are a number of compatible devices such as displays, LED lights, buttons and sensors that can be connected to the Pico through its GPIO pins to help you get the most out of it. A good place to start when looking for accessories for your Pico is the Pimoroni Pico store.

I can’t wait to see what you build!

Miguel Grinberg is a Python Developer for Technical Content at Twilio. Reach out to him at mgrinberg [at] twilio [dot] com if you have a cool Python project you’d like to share on this blog!