Getting started
This article goes over the steps to get set up to write embedded rust for the Raspberry Pi Pico.
1. Dependencies #
Before you install any dependencies, make sure everything is up to date with
rustup self update
rustup update stable
If you’re using Nix or NixOS you can use this flake instead.
To compile your code for the pico, you’ll need the thumbv6m-none-eabi
toolchain and stack overflow protection, so for that run
rustup target add thumbv6m-none-eabi
cargo install flip-link
Depending on what you use to flash your pico, you’ll need different tools installed:
Flashing via usb (only one pico needed) #
You’ll need the tool that converts the elf binary to uf2 for the pico to be able to run it:
cargo install elf2uf2-rs --locked
Flashing via probe (two picos or a pico and a picoprobe needed) #
If you have a second pico you want to use as a probe, you can flash the picoprobe firmware. If you have a picoprobe, the firmware comes pre-installed. To flash and communicate with the probe, you’ll need to install probe-rs
.
Look at how to install it here: https://probe.rs/docs/getting-started/installation/
Creating a udev rule #
For your PC to be able to identify the probe, you’ll need to create a udev rule. You’ll need to know the probe’s identifier. for that run the following with the probe connected.
lsusb
This will output a list of USB devices, and you have to find the line that’s similar to the following:
Bus 001 Device 005: ID 2e8a:000c Raspberry Pi Picoprobe CMSIS-DAP
You’ll notice it says ID AAAA:BBBB
. For me, this code is 2e8a:000c
, but it could be different for you.
Write down the code you got, you’ll need for the udev rule.
You’ll also need to know what group the probe is owned by for the udev rule, so for that run
grep 'uucp\|dialout' /etc/group
It will output a line cotaining either uucp
or dialout
. Write down which one of the two it is. If both come up, pick one. And if nothing copes up, you’ll have to create the group uucp
with
sudo groupadd uucp
Before adding yourself to the group, you can check if you’re already in it, so you can skip the step. Check with
groups
If you’re not in the group, you can add yourself with
sudo usermod -a -G GROUP_NAME YOUR_USERNAME
and apply the changes with
newgrp GROUP_NAME
(or rebooting)
And now, to finally create the udev rule. You can either dowload the official multi-purpose probe-rs rule file or create your own.
If you dowloaded the rule file, move it to the following directory. If you wish to make your own, you’ll need to create a file on the following directory:
/etc/udev/rules.d/
A udev rules file name should be formatted the following way:
[priority]-[name].rules
- [priority] has to be a two digit number, from 01 to 99. Make sure it’s lower than
73
though, otherwise it can cause issues. - [name] is the name you want to give the file so it is easily identifiable.
- With this in mind, I will name the file
50-pico.rules
, but if for any reason a file with that exact name already exists, you can change it up a little
The file has to contain the following:
ATTRS{idVendor}=="AAAA", ATTRS{idProduct}=="BBBB", MODE="664", GROUP="YOUR_GROUP"
Remember to open it as root, otherwise you won’t be able to save. You replace AAAA
with the first part of the code you got when running lsusb
and BBBB
with the second. You’ll also have to change YOUR_GROUP
to uucp
or dialout
depending on what you got earlier.
You can apply the rule changes with the following command:
sudo udevadm control --reload-rules; sudo udevadm trigger
Connecting the probe #
Connect the probe to the target according to the following diagram: The USB on the target is not necessary if you’re not using a pico W and aren’t pulling a lot of power from the picos. The purple and blue connestions are optional.
2. Creating a project #
To create a project you’ll need a template. Starting from a regular cargo project and adding the extra stuff yourself is possible, but it’s tedious, error prone and time consuming.
There are several options, here I list two:
- The official one: https://github.com/rp-rs/rp2040-project-template
- The one I made: https://gitlab.com/slusheea/rp2040-template
Both of them use cargo-generate
, which means it walks you through the creation of a project specifically tailored to you.
This means you have to install cargo-generate
first though. Here’s how to generate a project:
cargo generate rp-rs/rp2040-project-template # If you want to use the official template
cargo generate gl:slusheea/rp2040-template # If you want to use mine
3. Looking at the template #
The first thing you’ll see when you open main.rs
is #[no_std]
and #[no_main]
. That’s because we’re on a microcontroller, so we won’t be using the standard library and we’ll use a never ending loop in main.
Next up are use rp_pico as bsp
, use bsp::entry
and some other imports from the BSP
. BSP
stans for Board Support Package, it is what contains all the abstractions for the different features on the pico. You’ll see that from the BSP
we import the system clocks, the PAC
(the pico’s peripherals), the Watchdog and the SIO
.
From the generic embedded HAL, we take OutputPin
, which lets us blink the LED. I explain this more in depth in the next article, since it’s about GPIO.
panic_probe
lets the pico panic, and if you’re using a probe, you’ll see defmt
and defmt-rtt
, which handle communications with the probe on the background.
Just before the main function, there’s the entry
macro that tells the compiler where the program starts, since we used #[no_main]
.
Now finally inside main, there’s a block that we’ll need 99% of the time, and that basically lets us access the pico’s features.
After that the system clock gets initialized. In this case we need it for the delay, but even if we were to not use it, it’s recommended to still initalize it. That will give a compiler warning though, so you can add an underscore in front of the name to get rid of it.
Next is the delay object that lets us call .delay_ms()
and the initialization of the LED’s pin. Since we’re making the built in LED, we take led
from the pins
struct.
And that’s all the ‘boilerplate’ done! We call .set_high()
on the LED to turn it on and .set_low()
to turn it off.
4. Flashing #
USB flashing #
cargo run --release
Or the following if you have just
installed and are using my template
just flash
Probe flashing #
cargo build --target thumbv6m-none-eabi
probe-rs run target/thumbv6m-none-eabi/debug/PROJECT_NAME --chip RP2040
Where PROJECT_NAME is replaced by the project name in the Cargo.toml, or if you’re using my template and have just installed:
just build flash
5. Troubleshooting #
Errors flashing with a probe #
If you get a similar error when flashing with a probe
Error failed to demangle defmt symbol `{"package":"firmware","tag":"defmt_debug","data":"Booted","disambiguator":"18423169058307175441"}`: missing field `crate_name` at line 1 column 97
or
ERROR probe_rs::run: defmt wire format version mismatch: firmware is using 3, `probe-run` supports 4
suggestion: use a newer version of `defmt` or `cargo install` a different version of `probe-run` that supports defmt 3 Continuing without RTT...
Try first force-reinstalling probe-rs
, and if that doesn’t work, try adding this to the bottom of the Cargo.toml
:
[patch.crates-io]
defmt = { git = "https://github.com/knurling-rs/defmt" }
LED on the Pico W doesn’t blink #
The LED on the Raspberry Pi Pico W is connected to the WiFi module, and the pin 25 (the one that’s connected to the LED on the non-W) is connected to the WiFi module. For this reason, it won’t blink. You can try to change the pin on the code to a physical one and use an LED connected to that pin and ground. You can also:
- Try the code on a non-W pico (Where the LED should blink)
- Select “Raspberry Pi Pico W” when being prompted for the developement board if you’re using my template.