Adding picotool Information to RP2040 Programs

I am a very heavy user of the Raspberry Pi Pico 2040. It is my go-to chip for any designs, including my Creature Controller. There’s a feature in the RP2040 that allows you to leave markers in the binary for picotool to read, and I’ve found that a lot of folks aren’t taking advantage of it.

This blog post can hopefully show you how it works!

If you’d like to just jump straight to a working example, look at main.c in my Creature Controller firmware. Or, if you’d rather go to a first party source, this information is also in the Pi Pico SDK documentation.

If you’re not sure what picotool is, it’s a utility that Raspberry Pi provides for getting information about Pi Pico applications.

Setting up Your Source Code

There’s two things you need to do. First, you need to include the macros that the RP2040 SDK provides. They’re in the pico/binary_info.h file.

#include "pico/binary_info.h"

You shouldn’t have to add anything to your CMakeLists.txt to enable this library.

Annotating Your Source

Once you’ve got the macros defined, you can use them to define information about your binary that will be visible to picotool. Here’s an example out of my Creature Controller:

    bi_decl(bi_program_name("controller-firmware"))
    bi_decl(bi_program_description("April's Creature Workshop Controller"))
    bi_decl(bi_program_version_string("3"))
    bi_decl(bi_program_feature("FreeRTOS version " tskKERNEL_VERSION_NUMBER))
    bi_decl(bi_program_feature("Baud: 115200,N,8,1"))
    bi_decl(bi_program_url("https://creature.engineering/hardware/creature-controller/"))
    bi_decl(bi_1pin_with_name(POWER_PIN, "Power Relay"))
    bi_decl(bi_1pin_with_name(STATUS_LIGHTS_LOGIC_BOARD_PIN, "Status Lights for Logic Board"))
    bi_decl(bi_1pin_with_name(STATUS_LIGHTS_MOD_A_PIN, "Status Lights Module A"))
    bi_decl(bi_1pin_with_name(STATUS_LIGHTS_MOD_B_PIN, "Status Lights Module B"))
    bi_decl(bi_1pin_with_name(STATUS_LIGHTS_MOD_C_PIN, "Status Lights Module C"))
    bi_decl(bi_2pins_with_func(UART_TX_PIN, UART_RX_PIN, GPIO_FUNC_UART))
    bi_decl(bi_2pins_with_func(SENSORS_I2C_SDA_PIN, SENSORS_I2C_SCL_PIN, GPIO_FUNC_I2C))
    bi_decl(bi_1pin_with_name(SERVO_0_GPIO_PIN, "Servo 0"))
    bi_decl(bi_1pin_with_name(SERVO_1_GPIO_PIN, "Servo 1"))
    bi_decl(bi_1pin_with_name(SERVO_2_GPIO_PIN, "Servo 2"))
    bi_decl(bi_1pin_with_name(SERVO_3_GPIO_PIN, "Servo 3"))
    bi_decl(bi_1pin_with_name(SERVO_4_GPIO_PIN, "Servo 4"))
    bi_decl(bi_1pin_with_name(SERVO_5_GPIO_PIN, "Servo 5"))
    bi_decl(bi_1pin_with_name(SERVO_6_GPIO_PIN, "Servo 6"))
    bi_decl(bi_1pin_with_name(SERVO_7_GPIO_PIN, "Servo 7"))
    bi_decl(bi_1pin_with_name(CONTROLLER_RESET_PIN, "Controller Reset"))

What this is doing is setting up some metadata about the version of the firmware that’s running on a device. It also provides a helpful map of where to connect each pin.

Viewing the Output

Once you’ve got your binary annotated, you can use picotool to view it:

picotool info -a firmware.uf2

Here’s what the output of my Creature Controller firmware shows as of the time I’m writing this:

april@lop:~/code/creature-controller/firmware/build$ picotool info -a firmware.uf2
File firmware.uf2:

Program Information
 name:          firmware
 version:       3
 web site:      https://creature.engineering/hardware/creature-controller/
 description:   April's Creature Workshop Controller
 features:      Baud: 115200,N,8,1
                FreeRTOS version V11.1.0+
                UART stdin / stdout
 binary start:  0x10000000
 binary end:    0x10010434

Fixed Pin Information
 0:   UART0 TX, UART0 TX
 1:   UART0 RX
 2:   Controller Reset
 4:   UART1 TX
 5:   UART1 RX
 6:   Servo 0
 7:   Servo 1
 8:   Servo 2
 9:   Servo 3
 10:  Servo 4
 11:  Servo 5
 12:  Servo 6
 13:  Servo 7
 14:  Status Lights Module A
 15:  Status Lights Module B
 16:  Status Lights Module C
 17:  Status Lights for Logic Board
 20:  I2C0 SDA
 21:  I2C0 SCL
 25:  LED
 28:  Power Relay

Build Information
 sdk version:       1.5.1
 pico_board:        pico
 boot2_name:        boot2_w25q080
 build date:        Jun 13 2024
 build attributes:  Release

Pretty cool, uh?

I picked things to list in it that are useful for me, like the version of the protocol spoken (3, in this case), and the version of FreeRTOS it was compiled against.

I hope this information helps you make your binaries more discoverable! I couldn’t find much information on this in an Internet search, so I figured I would be the blog post writer I wanted to see! 😍