Multilingual blink for Raspberry Pi Pico

A year ago today we launched our Raspberry Pi Pico board, the first product powered by the RP2040 microcontroller, a brand-new chip developed right here at Raspberry Pi. A year later we’ve sold nearly 1.5 million Picos, and thousands of you have used RP2040 in your own electronic projects and products.

A lot has happened over the last year, and everyone here at Pi Towers has their own list of favourite projects and products they’ve seen or played with. With the launch of Raspberry Pi Direct on Monday we’re sure to see a lot more really soon. So if you’re interested in getting started with RP2040 in hardware you should go read the “Hardware design with RP2040” and take a look at the associated minimal viable design.

While there is a growing hardware ecosystem around the RP2040, it’s been really interesting to see a software ecosystem grow alongside it. Although I wouldn’t call the development landscape for Raspberry Pi Pico and other RP2040-based boards mature, at least not quite yet, it’s starting to get there. Because a year on from launch, we’ve seen a proliferation of language ports.

When you’re writing software for hardware, the first program that is run in a new programming environment is typically turning an LED on, off, and then on again. Learning how to blink an LED gets you halfway to anywhere. We’re going to go ahead and blink the onboard LED on Pico, which is connected to pin 25 of the RP2040 chip.

So it’s time to figure out how to get from “zero to blink” in as many languages as we can manage in this post.

C

The Raspberry Pi Pico C/C++ SDK is the place to start for Pico and other RP2040-based boards. The C SDK is what the other language ports use to talk to the underlying hardware, if you’re writing in Python or one of the other language ports, typically what those languages are using to talk to the chip is the C SDK. But of course, you can go directly to the C SDK yourself.

I wrote up a full walkthrough of how to blink the onboard LED on and off again from C not long after we released the Raspberry Pi Pico, so if you’re interested in getting started, you should go read that and follow along.

#include "pico/stdlib.h"

const uint LED_PIN = 25;

int main() {
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    while (1) {
        gpio_put(LED_PIN, 0);
        sleep_ms(250);
        gpio_put(LED_PIN, 1);
        sleep_ms(1000);
    }
}

MicroPython

The official port of MicroPython shipped alongside Raspberry Pi Pico, and the C SDK, back in January last year. It’s probably your first port of call if you’re more comfortable with Python than C.

Our colleagues over at the Raspberry Pi Foundation have a walkthrough on how to get started with MicroPython and the Thonny editor on Raspberry Pi Pico, and inevitably the first thing they teach you to do is to blink the onboard LED on and off again.

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

def blink(timer):
    led.toggle()

timer.init(freq=2.5, mode=Timer.PERIODIC, callback=blink)

CircuitPython

The “other Python” that runs on Raspberry Pi Pico and other RP2040-based boards is CircuitPython. Also released alongside Pico at launch last year, while CircuitPython looks somewhat like MicroPython there are some differences.

While I’d generally point folks towards MicroPython if they need advanced features like interrupts and threading — or complete access to the RP2040’s Programmable I/O (PIO) in Python — for library support for sensors and other breakouts I would point you to CircuitPython. It’s well supported by our friends at Adafruit.

Adafruit also has a great walkthrough on how to get started with CircuitPython and the Mu editor on Raspberry Pi Pico, and again the first thing you do after you’ve got everything installed is to blink the onboard LED on and off again.

import time
import board
import digitalio

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)

Javascript

Since launch there have been other development options added to the ecosystem. Among them is a Javascript port called Kaluma. Kaluma is a tiny JavaScript runtime for microcontrollers powered by JerryScript. Released into the wild relatively recently, if you are a JavaScript developer familiar with Node.js you can now work with Raspberry Pi Pico without learning a new language.

There is some excellent documentation on how to get started with Kaluma and its web development environment, or with the command-line interface if you’d prefer to develop locally rather than in your browser. Inevitably, the first thing they have you do is to blink the onboard LED on and then back off again.

var led = 25; 
pinMode(led, OUTPUT);

setInterval(() => {
  digitalToggle(led);
}, 1000);

Arduino

Interestingly there are actually two entirely independent ports of the Arduino core targetting the RP2040 chip and the Raspberry Pi Pico.

The first to appear was a community port that sits on top of the “bare” C SDK along with a custom GCC 10.3 and Newlib 4.0 based toolchain. There is extensive documentation on how to get started with the community port, as well as how to use it with the work-in-progress PlatformIO integration.

There is also the official port of the Arduino environment. Released just a few weeks after the community port, like a lot of the more recent Arduino platform ports, the core sits on top of Arm Mbed rather than directly on top of the C SDK. If you already have the Arduino environment installed, installation of the official RP2040 port works directly through the Boards Manager menu.

Either way, the code to blink the onboard LED on and then off again is exactly the same.

#define LED 25

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  digitalWrite(LED, HIGH);   
  delay(1000); 
  digitalWrite(LED, LOW);
  delay(1000);
}

Rust

One of the earliest arrivals on Raspberry Pi Pico after launch, there is now fairly mature Rust support for RP2040 with a Hardware Abstraction Layer (or HAL) crate for the RP2040 chip, and Board Support Package crates for a number of RP2040-based boards, including of course the Raspberry Pi Pico. Alongside this, there is also a project template intended as a starting point for developing your own firmware based on the RP2040 HAL.

While there isn’t extensive getting started documentation, at least not for those who aren’t already Rust people, there are a few good tutorials kicking around which predictably start by turning the onboard LED on and then back off again.

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use defmt::*;
use defmt_rtt as _;
use embedded_hal::digital::v2::OutputPin;
use embedded_time::fixed_point::FixedPoint;
use panic_probe as _;
use rp2040_hal as hal;

use hal::{
    clocks::{init_clocks_and_plls, Clock},
    pac,
    io::Sio,
    watchdog::Watchdog,
};

#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;

#[entry]
fn main() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();
    let mut watchdog = Watchdog::new(pac.WATCHDOG);
    let sio = Sio::new(pac.SIO);

    let external_xtal_freq_hz = 12_000_000u32;
    let clocks = init_clocks_and_plls(
        external_xtal_freq_hz,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer()
);

    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let mut led_pin = pins.gpio25.into_push_pull_output();

    loop {
        led_pin.set_high().unwrap();
        delay.delay_ms(500);
        led_pin.set_low().unwrap();
        delay.delay_ms(500);
    }
}

Lua

Lua on embedded hardware rose to popularity as some of the early community-built firmware available for the ESP8266 chip from Espressif. With NodeMCU’s arrival, the ESP8266 became more than a way to give other micro-controllers a cheap WiFi connection; it became a micro-controller in its own right.

The Lua environment for RP2040 is still at the proof-of-concept stage, but building it from source and usage are well documented in the project’s Github repository. As well as the Lua run-time, the project includes a rudimentary shell that accepts Linux-like commands, a full-screen editor, and basic file management facilities. It supports general, polled, GPIO operation for digital I/O, analogue input, I2C read and write, and hardware PWM. It runs on a single core, leaving the other core idle. There is currently no support for DMA, interrupts, threading, or multi-core operation.

You can however turn the onboard LED on and off again in only a few lines of code.

gpio_pin = 25
pico.gpio_set_function (gpio_pin, GPIO_FUNC_SIO)
pico.gpio_set_dir (gpio_pin, GPIO_OUT)
while true do
  pico.gpio_put (gpio_pin, HIGH)
  pico.sleep_ms (300)
  pico.gpio_put (gpio_pin, LOW)
  pico.sleep_ms (300)
end

Go

TinyGo brings the Go programming language to embedded systems by creating a new compiler based on LLVM. Installation is well documented, and so is getting started. There’s also an excellent walkthrough on YouTube to help you get started blinking the onboard LED on and back off again.

package main

import (
    "machine"
    "time"
)

func main() {
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    for {
        led.Low()
        time.Sleep(time.Millisecond * 500)

        led.High()
        time.Sleep(time.Millisecond * 500)
    }
}

FreeRTOS

FreeRTOS isn’t a language port. Instead, it’s an entire real-time operating system (RTOS) for microcontrollers that supports SMP across both of the RP2040’s cores. If you’ve not worked with an RTOS before there is some excellent getting started documentation, along with a number of good tutorials that start you off by blinking the onboard LED on and back off again and with FreeRTOS.

#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include "pico/stdlib.h"


void led_task() {   
    const uint LED_PIN = PICO_DEFAULT_LED_PIN;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    while (true) {
        gpio_put(LED_PIN, 1);
        vTaskDelay(100);
        gpio_put(LED_PIN, 0);
        vTaskDelay(100);
    }
}

int main() {
    stdio_init_all();

    xTaskCreate(led_task, "LED_Task", 256, NULL, 1, NULL);
    vTaskStartScheduler();

    while(1){};
}

Where now?

Hopefully, we’ve hit your favourite language, or RTOS, during this post. But if not, there’s still hope. There are a number of other language ports underway at various levels from .NET to Forth and Ada, but if nobody is working on a port of your favourite language…maybe you should give it a go yourself?

Support for developing for Pico can be found on the Raspberry Pi forums. There is also an (unofficial) Discord server where a lot of people active in the new community seem to be hanging out. You can find the details on how to get started, along with links to more detailed PDF documentation, on our documentation site. Feedback on the documentation should be posted as an Issue to the pico-feedback repository on Github, or directly in the relevant repository.

You can find the documentation from your Pico very easily. To access the documentation, just press and hold the BOOTSEL button on your Pico, plug it into your laptop or Raspberry Pi, then release the button. Go ahead and open the RPI-RP2 volume, and then click on the INDEX.HTM file. That will always take you to the documentation.

14 comments

Avatar

It’s a great chip and at a great price, with great documentation. I’m not surprised it’s done so well!

Avatar

Hi,
Another one to add to the growing list is a BASIC Interpreter for the Raspberry Pi Pico written by Geoff Graham. Well worth a look if you have fond memory’s of things like C64, Amiga, Spectrum, ZX81 etc. Full details and uf2 file downloads here…
https://geoffg.net/picomite.html

Liz Upton

Oh that’s cool – I don’t think we’d been aware that it existed! Thanks for the link.

Avatar
Avatar

Hi Liz, It’s VERY good, I’ve done a conversion of the Pico Solar System Clock, which was Python, that you featured earlier in the year, to it, complete with touch screen & SD card reader. :-) https://forums.raspberrypi.com/viewtopic.php?t=325534

Avatar

Totally! Such a nice and well done modern Basic.

Avatar

Congrats on RP2040’s first anniversary 😀

Avatar

As a learning exercise I wrote link in Arm assembler. A dozen or so equates, then nine instructions for set up. The main loop is six instructions, five of which do the delay.

Avatar

Hello, you forgot Object Pascal. See Lazarus and free Pascal web sites

Avatar

Free Pascal too, been a bit more progress since I last played with it. Hard enough learning all the Pi languages, now there are all these for Pico.
https://wiki.freepascal.org/ARM_Embedded_Tutorial_-_FPC_and_the_Raspberry_Pi_Pico

Avatar

Glad to hear you guys found my tutorial on TinyGo for the Pico. Really made my day to see it on here!

Liz Upton

Group hug!

Avatar

But wait there’s more, Ruby on Pico.
Handy for Pico keeboars?
https://engineering.monstar-lab.com/en/post/2021/04/13/PicoRuby/

Avatar

Is there Lisp?
Yep, there is.
http://www.ulisp.com/show?3KN3
Comparing a Pico to old Unix computers, will all the old languages run on Pico’s?

Leave a Comment

Comments are closed