Archive for the ‘Pico’ Category

Raspberry Pi Pico and Pico W Project Templates

Tuesday, September 5th, 2023

Pico Template Build Complete

There are somethings in software development that we do not do very often and because we perform the task infrequently we often forget the nuances (well I do). One of these tasks is creating a new project.

In this post we will look at one option for automating this process, GitHub Templates. This work is partially my own work but also a case of bringing together elements of other blog posts and GitHub repositories.

All of the code discussed below can be found in the PicoTemplate repository and this should be used as a reference throughout this post.

Starting a New Project

Several days ago, I was in the process of starting a new project for the Raspberry Pi Pico W using the picotool. For those who are not familiar with this tool is is meant to automate the project creation process for you. You simply tell the tool which features you are going to be using and a few other parameters like board type and it will generate a directory containing all of the necessary code and project files.

Sounds too good to be true. For me it was as no matter which feature list I requested I could not get an application to deploy to the board and talk to the desktop computer over UART or USB.

At this point I decided to take this back to basics and get Blinky up and running. The problem definition became:

  1. C++ application
  2. Blink the onboard LED
  3. Support both Pico and Pico W boards
  4. Standard IO output over UART or USB

It would also be desirable to automate as much of the process as possible.


One complication with the Pico boards is that they both use a different mechanism to access the on board LED. The Pico has the LED wired directly to GPIO 25 whilst the Pico W uses a GPIO from the onboard WiFi / Bluetooth chip (hereafter referred to as just wireless). This means we have different versions of Blinky depending upon which board is selected. This also means that the Pico W board also needs an additional library to support the wireless chip even if we are not using any wireless features.

The simplest way of doing this is to use a template directory to contains the main application code and the project CMakeFileList.txt file for each board. We can then copy the template files for the appropriate board type into the sources directory.

Keeping with the theme of forgetting the nuances, we will add some scripts to perform some common tasks:

  1. Configure the system (copying the files to the correct locations)
  2. Build the application
  3. Flash a board using openocd

Our first task will be to set the system up with the correct main.cpp and CMakeFileList.txt files and put the files in the correct place. Checking the default CMakeFileList.txt file we see that the project is named projectname which is not very informative so the configuration script will also rename the project as well.

The purpose of the build script should be obvious, it will build the application code and also offer the ability to perform a full rebuild if necessary.

The final script will flash the board using openocd using another Raspberry Pi Pico configured as a debug probe. This was discussed a few weeks ago in the post PicoDebugger – Bringing Picoprobe and the PicoW Together. This set up also has the advantage of exposing the default UART to the host computer.

Using the Scripts

Using the scripts is a three step process, one of which is only really performed once.

The first step is to use the script to copy the files and rename the project:

./ -b=picow -n=TestApplication

After running this command the src/main.cpp file will contain the code to blink the LED on a Pico W board and the project will have been renamed TestApplication.

Secondly, we can build the application using the script:


Finally, the application can be deployed to the board using the script.


The UF2 file (in the case above, TestApplication.uf2) can also be copied to the board if a debug probe is not available.

Scope Creep

The story was supposed to end here but this is not going to be the case.

There are some improvements that we can look at to make the system a little more comprehensive. These include:

  1. Add a testing framework
  2. Create a Docker file for build and testing

Both of these items are currently work in progress.


This project has been inspired by several others on github:

  • [Raspberry Pi Pico Examples](
  • [Raspberry Pi Pico Template](


The initial requirement of creating a template for Pico and Pico W development could have been achieved simply by creating two different templates, one for each board type. This would arguably have been quicker and less complex.

The addition of the testing framework and docker container into the requirement would have resulted in some duplication of work in each template. This made bringing the two templates together in one repository more logical as errors or additions in one project type are automatically part of the other board template.

20×4 LCD Display and NuttX

Sunday, July 23rd, 2023

20x4 LCD Display

Time for some more experimentation with NuttX, today serial LCDs.

Serial LCDs

Small LCD displays can be found in many scientific instruments as they provide a simple way to display a small amount of information to the user. Typically these displays are 16×2 (2 lines of 16 characters) or 20×4 (4 lines of 20 characters) displays. The header to this article shows part of the output from a 20×4 display.

Communication with these displays is normally through a 4 or 8 bit interface when talking directly to the controller chip for the LCD. Using 4 or 8 data lines for communication with the LCD is a large burden on a small microcontroller. To overcome this, several vendors have produced a backpack that can be attached to the display. The backpack uses a smaller number of microcontroller lines for communication while still being able to talk to the LCD controller chip.

This post looks at using such and LCD and backpack with NuttX running on the Raspberry Pi Pico W.

NuttX Channel (Youtube)

A great place to start with NuttX is to have a look at the NuttX Channel on Youtube as there are a number of quick getting started tutorials covering a number of subject. In fact there is one covering a 16×2 LCD display which is similar to what will be used in this tutorial, with a small difference.

The video linked above covers a lot of what is needed to get 16×2 LCD up and running. There are some small changes that are needed as NuttX has moved on since the video was released.


The major changes compared to the video above are:

  • Microcontroller used will be the Raspberry Pi Pico W
  • LCD display will be 20×4 display

The LCD display used here will be a larger physical display (20×4 instead of 16×2) but it will still use the same interface on the backpack, namely the PCF8574 GPIO expander. This uses I2C as a communication protocol so reduces the number of GPIO lines required from 8 to 2.

There are two I2C busses on the Pico W, I2C0 and I2C1 and in this case I2C1 will be the chosen interface. Something that caused some issues but more on that later.

For now we start with a base configuration with the LCd connected to GPIO6 and GPIO7 on the Pico W.

Configuring NuttX

Configuration followed much of the video linked above enabling the following:

  • Enable I2C master and I2c1 in the System Type menu
  • I2C Driver Support in the Device Drivers menu
  • PCF8574 support through Device Drivers, Alphanumeric Drivers menus
  • Segment LCD CODEC in the Library Routines menu
  • Segment LCD Test in the Application Configuration, Examples menu

Time to build, deploy and run.

  • make clean followed by make -j built the system
  • The application was then deployed to the Pico W and board was reset
  • The application can be run by connecting a terminal/serial application to the board an running the command slcd

Nothing appears on the display. Time to check the connections and break out the debugger.


Checking the connections showed that everything looked to be in order. The display was showing a faint pixel pattern which is typical of these displayed when they have been powered correctly but there is no communication. Double checking the I2C connections showed everything in theory was good.

Over to the software. Running through the configuration again and all looks good here. So lets try I2C0 instead of I2C1, quick change of the configuration in the software and moving some cables around and it works!

So lets go back to the I2C1 configuration, recompile and deploy to the board and it works. What!

It turns out that I had not moved the connections from I2C0 back to I2C1.

The default application was also only displaying 1 line of text. So let’s expand this to display 4 lines of text, namely:

  • Hello
  • Line1
  • Line2
  • Line3

Running the application gives only two line of text:

  • Hello
  • Line3

How odd.

Lets Read the Sources

After a few hours of tracing through the sources we find ourselves looking in the file rp2040_common_bringup.c where there is this block of code:

    /* slcd:0, i2c:0, rows=2, cols=16 */

    ret = board_lcd_backpack_init(0, 0, 2, 16);
    if (ret < 0)
        syslog(LOG_ERR, "Failed to initialize PCF8574 LCD, error %d\n", ret);
        return ret;
        return ret;

This suggests that the serial LCD test example is always configured to use a 16×2 LCD display on I2C0. This explains why we saw only two lines of output on the display and also why the code did not work on I2C1.

Changing ret = board_lcd_backpack_init(0, 0, 2, 16); to ret = board_lcd_backpack_init(0, 1, 4, 20); and recompiling generated the output we see at the top of this post.

Navigating to the System Type menu also allowed the I2C1 pins to be changed to 26 and 27 and the system continued to generate the expected results.


This piece of work took a little more time than expected and it would have been nice to have had the options within the configuration system to change the display and I2C parameters. As it stands at the moment using a 20×4 display requires changes to the OS source code. This is a trivial change but it does make merging / rebasing with later versions of the system a little more problematic.

SSEM Program Execution Complete

Wednesday, July 19th, 2023

SSEM Program Execution Complete

A while ago I put together an emulator for the Small Scale Experimental Machine (SSEM), also known as the Manchester Baby. This was a basic console application allowing a program written for the Manchester Baby to be run in a console application on a modern computer. As things turned out, I now spend most of my time working in either C or C++. This has left me with a piece of code that is difficult to maintain as I have to relearn Python every time I want to make any improvements.

Time to rewrite the application in C++.

SSEM Simulator

The simulator provides a number of features:

  • Assembler/compiler to take source files and generate the binary to be execution
  • Console interface to control the execution of the application
  • Simulated display of the registers and memory

More information about the Python version of the simulator can be found in this blog and on the Small Scale Experimental Machine web site with full source code available on GitHub.

Porting the Simulator

The aim of the initial port is to provide the same functionality of the original application with any changes necessary to provide additional robustness as we are undoubtedly going to be seeing pointers in the C++ port.

Where possible, the structure of the original Python code has been maintained to keep a 1:1 mapping with the original code and test suite. This will provide an easy way to validate the unit tests in the port against the original Python code. The original Python code was validated against David Sharps Java Simulator.

The long term aim of this port is to provide a way of running the application on a Raspberry Pi Pico connected to hardware which will emulate the original SSEM. The application on the Raspberry Pi Pico will target the NuttX RTOS. As we will see later, compiling and running on NuttX will present some interesting issues.

Initial Port

The first stage of the port is to reproduce the core functionality of the SSEM showing the application output in a console interface targeting C++ 17. The only real complication here is ensure the user interface and platform specific code are abstracted to keep as much functionality in common with a desktop and NuttX implementation as possible.

The original Python code and the C++ port can be found in the Manchester Baby GitHub repository. A quick check of the source code shows that the 1:1 mapping has been kept where possible. The only real significant difference between the two code bases is the separation of the unit tests from the class implementation. The Python code keeps the unit test code in the class definitions themselves, the C++ code implements the unit tests in their own files.

Memory Checks

The switch from Python to C++ brings a new danger, memory access issues and memory leaks.

One memory issue that we can address relatively easily is memory leaks. If we can abstract the core functionality into a self contained group of files then we can use valgrind to check for memory leaks. A small glitch with using valgrind is that the application is not available for Mac from the key repositories. There is an informal project on GitHub.

The issue of valgrind not running on the mach was resolved by putting together a Dockerfile containing common development tools. The memory check could then be run on the desktop using Docker.

Running the Emulator

The emulator can be run on both a desktop computer as well as a board running NuttX.

Run from the Desktop

Running the application on the desktop is the simplest way to test the emulator:

  • Open a command console and change to the Desktop directory in the repository
  • Build the emulator with the command make
  • Run the emulator with the command ./ssem_main

Run on NuttX

Running on NuttX is a little more complex as we need to build the application and the operating system and then deploy the binary to a board. The processes of adding the SSEM application to a Raspberry Pi PicoW board has already been documented in the article Adding a User Application to NuttX. The first step is to follow the steps in the article to add the SSEM basic applicatiom.

The next stage is to copy the contents of the NuttX directory over the application directory created in the above article. The code should then be rebuilt with the command make clean && make -j. The application can now be deployed to the board.

Now we have the OS and the application deployed to the Raspberry Pi (or your board of choice) we can connect a serial adapter to the board and press the enter key twice. This will bring up the NuttX shell. Typing help should show the ssem application deployed to the board. Simply execute this by entering the command ssem.

Application Output

In both cases the emulator should run the hfr989.ssem application (the source for this can be found in the SSEMApps folder in the repository). Both the desktop and the NuttX versions of the emulator will run the SSEM application and will show the start and end state of the SSEM on the console / serial port. The first output will show the SSEM application loaded into the store lines:

NuttShell (NSH) NuttX-10.4.0
nsh> ssem
   0: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
   1: 0x48020000 - 01001000000000100000000000000000 LDN 18           ; 16402
   2: 0xc8020000 - 11001000000000100000000000000000 LDN 19           ; 16403
   3: 0x28010000 - 00101000000000010000000000000000 SUB 20           ; 32788
   4: 0x00030000 - 00000000000000110000000000000000 CMP              ; 49152
   5: 0xa8040000 - 10101000000001000000000000000000 JPR 21           ; 8213
   6: 0x68010000 - 01101000000000010000000000000000 SUB 22           ; 32790
   7: 0x18060000 - 00011000000001100000000000000000 STO 24           ; 24600
   8: 0x68020000 - 01101000000000100000000000000000 LDN 22           ; 16406
   9: 0xe8010000 - 11101000000000010000000000000000 SUB 23           ; 32791
  10: 0x28060000 - 00101000000001100000000000000000 STO 20           ; 24596
  11: 0x28020000 - 00101000000000100000000000000000 LDN 20           ; 16404
  12: 0x68060000 - 01101000000001100000000000000000 STO 22           ; 24598
  13: 0x18020000 - 00011000000000100000000000000000 LDN 24           ; 16408
  14: 0x00030000 - 00000000000000110000000000000000 CMP              ; 49152
  15: 0x98000000 - 10011000000000000000000000000000 JMP 25           ; 25
  16: 0x48000000 - 01001000000000000000000000000000 JMP 18           ; 18
  17: 0x00070000 - 00000000000001110000000000000000 HALT             ; 57344
  18: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  19: 0xc43fffff - 11000100001111111111111111111111 HALT             ; -989
  20: 0x3bc00000 - 00111011110000000000000000000000 JMP 28           ; 988
  21: 0xbfffffff - 10111111111111111111111111111111 HALT             ; -3
  22: 0x243fffff - 00100100001111111111111111111111 HALT             ; -988
  23: 0x80000000 - 10000000000000000000000000000000 JMP 1            ; 1
  24: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  25: 0x08000000 - 00001000000000000000000000000000 JMP 16           ; 16
  26: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  27: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  28: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  29: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  30: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  31: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0

Reading from left to right, the above output shows the following:

  • Store line number (i.e. the memory address) 0:, 1: etc.
  • The hexadecimal representation of the store line contents.
  • Binary representation of the store line contents
  • Disassembled representation of the store line contents JMP 0 etc.
  • Decimal representation of the store line contents

It must be remembered when reading the above that the least significant bit is at the left of the word and the most significant bit is to the right. This is honoured with the hexadecimal and binary components of the above output. The decimal value to the right should be read in the usual way for a base 10 number.

After a short time the contents of the store lines at the end of the run will be displayed:

Program execution complete.
   0: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
   1: 0x48020000 - 01001000000000100000000000000000 LDN 18           ; 16402
   2: 0xc8020000 - 11001000000000100000000000000000 LDN 19           ; 16403
   3: 0x28010000 - 00101000000000010000000000000000 SUB 20           ; 32788
   4: 0x00030000 - 00000000000000110000000000000000 CMP              ; 49152
   5: 0xa8040000 - 10101000000001000000000000000000 JPR 21           ; 8213
   6: 0x68010000 - 01101000000000010000000000000000 SUB 22           ; 32790
   7: 0x18060000 - 00011000000001100000000000000000 STO 24           ; 24600
   8: 0x68020000 - 01101000000000100000000000000000 LDN 22           ; 16406
   9: 0xe8010000 - 11101000000000010000000000000000 SUB 23           ; 32791
  10: 0x28060000 - 00101000000001100000000000000000 STO 20           ; 24596
  11: 0x28020000 - 00101000000000100000000000000000 LDN 20           ; 16404
  12: 0x68060000 - 01101000000001100000000000000000 STO 22           ; 24598
  13: 0x18020000 - 00011000000000100000000000000000 LDN 24           ; 16408
  14: 0x00030000 - 00000000000000110000000000000000 CMP              ; 49152
  15: 0x98000000 - 10011000000000000000000000000000 JMP 25           ; 25
  16: 0x48000000 - 01001000000000000000000000000000 JMP 18           ; 18
  17: 0x00070000 - 00000000000001110000000000000000 HALT             ; 57344
  18: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  19: 0xc43fffff - 11000100001111111111111111111111 HALT             ; -989
  20: 0x54000000 - 01010100000000000000000000000000 JMP 10           ; 42
  21: 0xbfffffff - 10111111111111111111111111111111 HALT             ; -3
  22: 0x6bffffff - 01101011111111111111111111111111 HALT             ; -42
  23: 0x80000000 - 10000000000000000000000000000000 JMP 1            ; 1
  24: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  25: 0x08000000 - 00001000000000000000000000000000 JMP 16           ; 16
  26: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  27: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  28: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  29: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  30: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
  31: 0x00000000 - 00000000000000000000000000000000 JMP 0            ; 0
Executed 21387 instructions in 30000000 nanoseconds

The original SSEM ran at about 700 instructions per second, modern PCs and even RP2040 processors are running the application much faster.


Even small boards (such as the Raspberry Pi Pico) running relatively low power processors can now emulate the Manchester Baby running application intended for the SSEM many times faster that the original hardware. The hfr989.ssem application would have run in about 30 seconds in 1948, today we can run this in an emulator in less that 30ms.

PicoW with SmartFS

Sunday, July 2nd, 2023

Mounting SmartFS

One feature that I want to add to my current project is to add a small file system with files that have been built into the system at compile time. These files would then be available to the application at run time. Let’s look at how we can do can do this with NuttX.

This tutorial assumes that you have NuttX cloned and ready to build, if not then you can find out how to do this in the first article in this series.

Adding SmartFS to the Build

NuttX has a built in configuration for the PicoW with SmartFS already configured. The first thing we need to do is to start with a clean system and then configure the build to include NSH and the flash file system. Start by changing to the NuttX source directory and then executing the following commands:

make distclean
./tools/ -l raspberrypi-pico-w:nsh-flash

Now we have the system configured we can build the OS and applications by executing the following command:

make -j

This should take a minute or so on a modern machine. Now we can deploy the system to the PicoW either by using openocd or by dragging the uf2 file onto the PicoW drive. Now connect to the PicoW using a serial application and type help to show the menu of commands. You should see something like the following:

SmartFS Builtin Apps

SmartFS Builtin Apps

We can check to see if the SmartFS is available checking the contents of the /dev directory with the command ls /dev. This should result in something like the following if SmartFS has been enabled correctly:

Device Directory Listing

Device Directory Listing

We can mount the file system using the command mount -t smartfs /dev/smart0 /data and then check the contents of the /data directory and we should find one file in the directory, test. Checking the contents of the file with the command cat /data/test should find that it contains a single ine of text which should be Hello, world!.

So far, so good, we have built the system and proven that it contains the default file and correct contents.

Adding a New File to the System Image

The next piece of the puzzle is to work out how to add new files to the file system. This took a few hours to figure out, but here goes…

The first attempt lead me searching for RP2040_FLASH_FILE_SYSTEM in the source tree (ripgrep is a great tool for doing this). This lead to a number of possible files. Maybe we can narrow the search down a little.

Second attempt, let’s have a look for Hello, world!. This resulted in a smaller number of files leading to the file arch/arm/src/rp2040/rp2040_flash_initialize.S. This file is well documented and shows how to set up the SmartFS file system and at the end of the file it shows how to create an entry for the file we see when we list the mounted directory. Scrolling down to the end of the fie we find the following:

    sector      3, dir
    file_entry  0777, 4, 0, "test"

    sector      4, file, used=14
    .ascii      "Hello, world!\n"

    .balign     4096, 0xff
    .global     rp2040_smart_flash_end

This looks remarkably familiar. So what happens if we change the above to look like this:

    sector      3, dir
    file_entry  0777, 4, 0, "test"
    file_entry  0777, 5, 0, "test2"

    sector      4, file, used=14
    .ascii      "Hello, world!\n"

    sector      5, file, used=14
    .ascii      "Testing 1 2 3\n"

    .balign     4096, 0xff
    .global     rp2040_smart_flash_end

Building the system, deploying the code and executing the following commands:

mount -t smartfs /dev/smart0 /data
cd /data

results in the following:

New file added to SmartFS

New file added to SmartFS

If we execute the command cat test2 we are rewarded with the output Testing 1 2 3.

Further testing shows that the file system survives through a reset. We can do the following:

  • echo “My test” > test3
  • rm test
  • reboot

These commands should remove the file test, create a new file test3 and then reboot the system. Checking the file system contents shows that the system persists the changes through a reset.


This experiment was a partial success. A simple file system has been made available to an application and the file system survives a reset. One issue remains, adding new files is a little complex. It also requires changes to the NuttX source tree outside of the applications folder. This could result in changes being lost when a new version of NuttX is released.

There could be a solution, ROMFS, stay tuned for the next episode.

VSCode Debugging with NuttX and Raspberry Pi PicoW

Wednesday, June 14th, 2023

VS Code Halted in NuttX __start

In the previous post, we managed to get GDB working with NuttX running on the Raspberry Pi PicoW. In this post we will look at using VSCode to debug NuttX.

For a large part of this post it was case of following Shawn Hymels guide Raspberry Pi Pico and RP2040 – C/C++ Part 2 Debugging with VS Code. This is an excellent guide and I recommend using it as a companion to this post.

We will start with the assumption that you have followed the previous post in this series and have a working NuttX build for the Raspberry Pi PicoW. We will also assume that you have a working VSCode installation with the Cortex-Debug extension installed.

Configuration Files

We will need to create (or modify) three configuration files to allow VSCode to debug NuttX.

  • launch.json
  • tasks.json
  • settings.json

In the first article of this series we created the directory NuttX-PicoW to hold the apps and nuttx folders holding out NuttX code. This directory is the project directory or in VS Code parlance, the workspaceFolder. The workspaceFolder should also contain the Raspberry Pi PicoW SDK and the Raspberry Pi specific version of openocd.

We now add a .vscode directory to the workspaceFolder. This directory will hold the three configuration files listed above.

Start by opening VS Code and opening the workspaceFolder. This should show the four folders already in the workspaceFolder. Now create a .vscode directory in the workspaceFolder if it does not already exist.


Only one entry is required in the settings.json file and this is the location of the openocd executable. If you have followed these posts so far this will be in the openocd/src folder. Create a setting.json file in the .vscode folder and add the following to the file.

    "cortex-debug.openocdPath": "${workspaceFolder}/openocd/src/openocd"

Note that the name of the executable may vary depending upon your operating system this post is being written from a MacOS perspective.


The tasks.json file holds the entry that will be used to build NuttX prior to deployment. In Shawns document the projects being worked on use the cmake system. We need to modify this to build NuttX using make. We want VS Code to use the command make -C ${workspaceFolder}/nuttx -j to build NuttX. The build task below will create a task Build NuttX to do just this:

    "version": "2.0.0",
    "tasks": [
          "label": "Build NuttX",
          "type": "cppbuild",
          "command": "make",
          "args": [


Of the three files we are creating, the launch.json file is the most complex. Much of the file remains the same as that presented by Shawn but there are some differences. The file used here looks like this:

    "version": "0.2.0",
    "configurations": [
            "name": "Pico Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}/nuttx/nuttx.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "gdbPath" : "arm-none-eabi-gdb",
            "device": "RP2040",
            "configFiles": [
            "svdFile": "${workspaceFolder}/pico-sdk/src/rp2040/hardware_regs/rp2040.svd",
            "runToEntryPoint": "__start",
            "searchDir": ["${workspaceFolder}/openocd/tcl"],
            "openOCDLaunchCommands": ["adapter speed 5000"],
            "preLaunchTask": "Build NuttX"

The following items are the ones that need to be changed:

“executable”: “${workspaceFolder}/nuttx/nuttx.elf”

This is the path to the executable that will be created by the build system. In this cse it is the NuttX ELF file. This may need to be changed to “executable”: “${workspaceFolder}/nuttx/nuttx” depending upon the output of the build system, this may simply be nuttx.


The recent versions of the picoprobe software use a different interface to talk to the picoprobe. For versions 1.01 and above of the picoprobe software this interface changed from picoprobe.cfg to cmsis-dap.cfg.

“svdFile”: “${workspaceFolder}/pico-sdk/src/rp2040/hardware_regs/rp2040.svd”

We have a version of the Pico SDK specifically for our build requirements and so for this reason the location of the file is changed to reference the workspaceFolder rather than the global PICO_SDK_PATH environment variable.

“runToEntryPoint”: “__start”

This entry replaces the “runToMain”: true entry. Unlike convention C/C++ applications, NuttX does not have a main method. Having the runToMain entry generates an error stating that main cannot be found and stops at the first executable statement in our code, namely in __start. Replacing runToMain with runToEntryPoint achieves the same thing but does not generate the error.

Doing this also removes the need to have the postRestartCommands specified.

“searchDir”: [“${workspaceFolder}/openocd/tcl”]

This entry is used by openocd to look for the interface and target configuration files specified in the configFiles entry.

“openOCDLaunchCommands”: [“adapter speed 5000”]

These commands are executed by openocd when it starts. The change to the adapter speed is required and is documented in Getting Started with Raspberry Pi Pico documentation from the Raspberry Pi Foundation.

“preLaunchTask”: “Build NuttX”

The final entry tells Cortex-Debug how to build NuttX. It references the task we previously defined in the tasks.json file.


Testing this should simply be a case of saving all of the files above and pressing F5 in VS Code. If the changes have been successful then VS Code will first try to build NuttX in a Terminal window and it will then deploy NuttX to the Pico board and start the debugger. VS Code should look something like this (click on image for full view):

Debugging PicoW with VSCode

Debugging PicoW with VSCode

The inclusion of the SVD file allows us to examine the PicoW peripheral registers as well as the core registers:

PicoW Peripheral Registers in VSCode

PicoW Peripheral Registers in VSCode

Pico Cortex Registers in VSCode

Pico Cortex Registers in VSCode


GDB is a great debugger but it is often more convenient to use an IDE to debug your code. VS Code with the Cortex-Debug extension allow visual debugging of NuttX with a few nice additional features thrown in:

  • Easily viewed call stacks for both cores.
  • The SVD file allows the peripheral registers to be viewed through VS Code

We should also note that the use of VS Code resolved the issues noted at the end of the previous post as Cortex-Debug is able to deploy a binary using the picoprobe without resorting to the UF2 method of deployment. This results in a seamless build, deploy and debug process.

We have two debug options and it is now down to personal preferences as to which one to use.

Debugging NuttX on the Raspberry Pi Pico

Sunday, June 11th, 2023

GDB showing thread information

At some point in the development lifecycle we will hit some unexpected application behaviour. At this point we reach for the debugger to allow us to connect to the application an interrogate the state and hopefully we will be able to determine the cause of the problem. Working with NuttX is no different from any other application. The only complication we have is connecting to application running on the PicoW requires a debug adapter. Luckily, the Raspberry Pi Foundation has an affordable solution to this problem.


The getting started guide for the Pico has instructions on how to set up the environment for Linux, Mac and Windows. This post will be following the guide for MacOS adding some hardware to allow for a stable connection between the debug probe and the PicoW.

There are two options for the debug probe:

  • The Raspberry Pi debug probe
  • Use a Pico programmed as a debug probe

I have a number of Pico and PicoW boards and so the later option, using a Pico programmed as a debug probe, is going to be the most cost effective and this is the route we will look at here.


For the purpose of discussion we will use the following terms to describe the two boards:

  • Picoprobe – Pico that is programmed to be a debug probe
  • Target – PicoW that is running the application we wish to debug

The getting stared guide shows how two Pico boards should be connected to provide both debugging and serial console output through a UART on the target board. This shows the two boards on a breadboard setup (image is taken from the Raspberry Pi Pico getting started guide under CC-BY-ND license).

Picoprobe and Pico Fritzing

Picoprobe and Pico Fritzing

The same setup can be reproduced on protoboard. This will make the setup a little more robust and permanent. Breaking out the soldering iron and the parts bin yielded the following:

Pico on Protoboard

Pico on Protoboard

The flying leads are needed to connect to the SWD debug headers on the PicoW as it was not possible to connect these through to the protoboard. The black (GND) and red (5V) wires provide power to the target board. This means that we only have one connector to the system, namely to the Picoprobe but we must take care not to exceed the power capabilities of the USB connection.

The white (SWCLK) and blue (SWDIO) flying leads provide the debug connection between the two boards. As with the permanent connections above, the black lead is a GND connection.

The blue and yellow leads connected permanently to the protoboard connect the UART from the target board through to the picoprobe. The picoprobe will use the USB connection to present the host computer with a connection through to the UART on the target board.


The first piece of software needed is the Picoprobe software itself. There are instruction on how to build this but I found the simplest thing to do was to download the latest release from the Picoprobe GitHub repository. At the time of writing this is version 1.0.2. The Picoprobe software is deployed to the Pico being used as a Picoprobe in the usual way, reset the Pico whilst hold the BootSel button and then drag and drop the firmware file onto the drive present to the host computer.

The next thing we need is a version of openocd that can talk to the Picoprobe. The machine I am using is used to develop software for multiple boards and so the preferable solution is to build the Raspberry Pi Pico version of openocd for this setup and then reference this locally rather than install the software globally. This is the same approach taken for the various development environment I use to prevent them interfering with each other.

Following the guide (for Mac) we need to execute the following commands:

cd ~/pico
git clone --branch picoprobe --depth=1 $ cd openocd
export PATH="/usr/local/opt/texinfo/bin:$PATH" 1
./configure --enable-picoprobe --disable-werror 2
make -j4

Check the latest version of the guide for instructions for your OS.

Now to try openocd… This is where I hit a problem caused by having out of date documentation. Following the latest documentation we need to execute the following command:

src/openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -s tcl

I was following I hit two errors due to the out of date documentation:

  • Error: Can’t find a picoprobe device! Please check device connections and permissions.
  • Error: CMSIS-DAP command CMD_DAP_SWJ_CLOCK failed.

Both of these errors were corrected by using the command above to connect to the debug probe. Moral of the story, make sure you are using the latest information. If everything goes well then we should be rewarded with something similar to the following output:

Open On-Chip Debugger 0.11.0-g4f2ae61 (2023-06-10-18:20)
Licensed under GNU GPL v2
For bug reports, read
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 5000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E66058388356A232
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections

If we have got here then we have the the debug probe programmed and we have a copy of openocd that can communicate with the picoprobe.

Reconfiguring NuttX

In previous posts we used UART over USB to communicate with the host computer. The picoprobe gives us another option, using the UART on the target board which is routed through the picoprobe. We must reconfigure NuttX in order to take advantage of the UART redirection. The configuration we want is raspberrypi-pico-w:nsh, so following the first post in this series and making the change to the configuration we need to execute the following:

make distclean
./tools/ -l raspberrypi-PicoW:nsh
make -j

At the end of this process we should have a new nuttx.uf2 file configured to use the UART port on the target board to communicate with the NuttX shell. This UF2 file can be deployed using the usual deployment process (Bootsel and reset followed by drag and dropping the file) to the target board.

At this point we should have two Pico boards programmed, one to be the picoprobe and one to be the target board (with NuttX). Now we can connect the boards (in my case using the protoboard shown above) and verify that NuttX is active and communicating through the UART.

Plugging the Pico (picoprobe) and the PicoW (target board) into the protoboard and then connecting the host computer to the picoprobe through USB presents the computer with a new serial port, usbmodem14302. Connecting to this port and hitting enter shows the nsh prompt. Asking for help shows the expected output.

nsh> help
help usage:  help [-v] [<cmd>]

    .         cat       df        free      mount     rmdir     truncate  
    [         cd        dmesg     help      mv        set       uname     
    ?         cp        echo      hexdump   printf    sleep     umount    
    alias     cmp       env       kill      ps        source    unset     
    unalias   dirname   exec      ls        pwd       test      uptime    
    basename  date      exit      mkdir     reboot    time      usleep    
    break     dd        false     mkrd      rm        true      xd        

Builtin Apps:
    nsh  sh   

So we have a good UART connection and openocd can connect to the picoprobe.

Now we need to connect the debugger to openocd.


To debug the board we will start with GDB, specifically arm-none-eabi-gdb-py as this has Python scripting enabled. We can use the scripting engine to automate some tasks later.

First thing to do is add a .gdbinit file to the directory we will be executing GDB from, in this case we will use the nuttx directory. The following commands should setup GDB and openocd ready for debugging. The nuttx ELF file will be loaded for us so by the time the GDB prompt is show we will have the symbol file loaded. Our .gdbinit file should look something like this:

set history save on
set history size unlimited
set history remove-duplicates unlimited
set history filename ~/.gdb_history

set output-radix 16
set mem inaccessible-by-default off

set remote hardware-breakpoint-limit 4
set remote hardware-watchpoint-limit 2

set confirm off

file nuttx
add-symbol-file -readnow nuttx

target extended-remote :3333
mon gdb_breakpoint_override hard

To debug we will need two terminal sessions open. In the first session run openocd as described above. In the second session start GDB (simply enter the command arm-none-eabi-gdb-py in the terminal session) making sure that you are in the directory containing the nuttx file and the .gdbinit file. If you are successful then you will see something like this:

GNU gdb (GDB) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin22.3.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word".
add symbol table from file "../nuttx/nuttx"

We can now start to debug the application on the board. A few simple tests, firstly, reset the board with the GDB command mon reset halt. We should see something like this:

target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ea msp: 0x20041f00
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ea msp: 0x20041f00

We can now set a breakpoint, say in nsh_main (b nsh_main) and run the application to the breakpoint (c). If we then ask for information about the threads (info thre) we should see something like this:

  Id   Target Id                                           Frame
* 1    Thread 1 (Name: rp2040.core0, state: breakpoint)    nsh_main (argc=0x1, argv=0x20003170) at nsh_main.c:58
  2    Thread 2 (Name: rp2040.core1, state: debug-request) 0x000000ea in ?? ()


Getting debugging up and running took a little more effort than I expected but was not expensive due to the ability to use a Pico to debug a Pico. There is some excellent debugger documentation on the NuttX web site. This suggests that openocd should support NuttX however I was not able to get this working with the Raspberry Pi release of openocd. It was a question of having Pico support or NuttX support. I’m going with the Pico support for the minute.

I was hoping to use the picoprobe board to deploy the application meaning that we only have one connection between the host computer and the development hardware. A little more research is going to be necessary to see if this can be done.

Adding SMP to NuttX on the Raspberry Pi PicoW

Tuesday, June 6th, 2023

ps Command Results without SMP Enabled

So far in this series we have configured NuttX to build C++ applications on the PicoW and we have created our own custom application and successfully deployed this to the board.

In this post we will look at configuring the system to use both cores on the RP2040 processor using the configuration we have developed over the previous posts. NuttX includes a SMP configuration already, raspberrypi-pico-w:smp, this uses UART0 for communication rather than USB hence we will look at add this configuration on top of the USB configuration.

Default Configuration

It appears that the default configuration for NuttX running on the PicoW is to run the processor as a single core processor. Running the ps in the NuttX shell yields the following output:

    0     0   0 FIFO     Kthread N-- Ready              0000000000000000 001000 Idle Task
    1     1 100 RR       Task    --- Running            0000000000000000 002000 nsh_main   

From the output we see that there are only two tasks running on the system. From this we can deduce that only one of the cores is enabled. If both cores were running then we should see two Idle Tasks running, one on each core plus the nsh_main task. This is confirmed in the SMP Documentation where we find the statement:

Each CPU will have its own IDLE task.

Now we have this confirmed, let’s move on to reconfiguring the system.

Enabling SMP

According to the documentation we need set three options through the configuration system in order to turn on SMP:


Starting the configuration system with the make menuconfig we are presented with the top level options screen:

NuttX configuration system

NuttX configuration system

This poses the question “How do I find CONFIG_SMP and the rest of the options?”. Luckily, kconfig has a search option, pressing the / key presents the user with a search dialog box:

Search dialog here.

Typing SMP and pressing return shows a number of results, some are relevant to use, some are not. The longer the search term the more accurate the results will be. In our case, searching for SMP yields a number of results that are irrelevant. However, the top result looks like it is the one for us:

SMP Search results image Here.

The results give us a fair amount of information about the configuration options. If we look at the top entry for SMP (this is actually the option that we need to turn on) the system is giving us the following information:

  • The option name (SMP) which will translate to CONFIG_SMP in the various include files
  • The location of the option, in this case RTOS Features -> Tasks and Scheduling
  • In which file (and the line number) the option is defined, sched/Kconfig, line 271
  • Any other options that this option depends upon.
  • Any options that will be turned on as a result of selecting this option

You will also note a number in brackets at the side of Tasks and Scheduling. This is the short cut key, in this case 1 and pressing 1 will take us to the appropriate place in the configuration system.

RTOS Features options.

Selecting Tasks and Scheduling by pressing enter we are presented with the following:

Tasks and scheduling, no SMP.

Notice that there is no option to turn on SMP. Confused, so was I the first time I saw this. Pressing the ESC key a few times should take us back to the search results. Reading these more carefully we note the following:


So we have to ensure that three conditions are met in order to enable SMP:

  • HAVE_MULTICPU is set to turned on, this looks to be the case (ARCH_HAVE_MULTICPU [=y])
  • HAVE_TESTSET is turned on, again this looks to be the case (ARCH_HAVE_TESTSET [=y])
  • INTERRUPTSTACK has a non-zero value, this appears to be false (ARCH_INTERRUPTSTACK [=0]!=0)

It appears that we have not met all of the conditions to activate SMP. We need to look at defining a size for the interrupt stack. So using the search system again but this time for ARCH_INTERRUPTSTACK we get the following results:

Interrupt stack search results

Pressing the 1 key takes us directory to the entry we need to change. Setting the value to 2048 should be a reasonable starting point. We can always adjust this later if we find it is causing problems.

Exiting this menu and the search for ARCH_INTERRUPTSTACK we can search for SMP again. Pressing the 1 key in the search results presents us with a different view, this time we are taken to the SMP option and we can now turn this on. If we do this the system will add some new entries to the menu, one of which defines the number of CPUs available and this is set to 4. We will need to change this value to 2.

SMP Configured image.

We are now ready to build the system.

Build and Deploy NuttX

We should now be ready to build and deploy the system to the PicoW. Executing the following command will build NuttX and create the UF2 file ready for deployment:

make clean
make -j

At the end of the build process we can deploy the UF2 file to the board by putting the board in bootloader mode and dropping the file to the drive that is shown on the host computer. Se the first article in this series for more information on how to do this.

Time to connect our serial terminal to the PicoW and run the ps command once again:

SMP Enabled ps output.

As you ca see from the screen shot above, we now have two ide tasks as expected and the ps command has some additional information, notably the CPU that each task is allocated to.


Adding SMP to the system was not as simple as it first sounded. The main lesson from this was to check the dependencies in the search results. The requirement for an interrupt stack was not discussed in the SMP documentation and this caused some confusion initially. The help in the configuration system came to our rescue and the situation was easily recoverable.

If you have been following the series we are now at the point where we have think about some serious development. We now have space for our own application code, C++ is enabled and we are able to use both cores on the RP2040 microcontroller.

At this point the series will slow down a little as some of the features now need some in depth investigation most notable:

  • Using SMP correctly in NuttX
  • Interprocess communication between tasks running on different cores
  • RP2040 PIO and NuttX

Adding a User Application to NuttX

Tuesday, June 6th, 2023

Enable SSEM User Application

So far we have seen how to deploy NuttX to a Raspberry Pi PicoW and enable C++ in NuttX. Next up we will create our own application and deploy it to the PicoW.

Creating a New Application

The build system for NuttX applications uses a a makefile that is written in such a way that all we have to do to add a new application is simply create a directory containing some template files. The easiest way to do this is to use an existing application as a template. So let us start by making a copy of the helloxx application. This can be found in the apps repository in the examples directory. So we will start by making a recursive copy of this directory and renaming it ssem.

Looking inside the ssem directory we find a number of files:


Time to start editing the files.


We will start by renaming the file to represent the application name, so we will rename this to ssem_main.cxx. Now editing the file we change all occurrences of helloxx_main to ssem_main. This will allow us to verify that we have indeed deployed the application.


Make a similar change to the Makefile by changing all occurrences of helloxx to ssem. Make changes to the comments to reflect that this application is a new application. Our Makefile should now look something like this:

include $(APPDIR)/Make.defs

# SSEM Application

MAINSRC = ssem_main.cxx

# ssem built-in application info


include $(APPDIR)/


The Make.defs file conditionally adds the new application to the list of configured applications and ensures that the file will be built. This file should be edited to look something like this:

CONFIGURED_APPS += $(APPDIR)/examples/ssem


The Kconfig file allows us to define and change application parameters, something we may look at later. For the moment it simply defines the application name and title and tells the configuration system how to turn compilation on or off. Making changes similar to the above we will have a file that contains something like this:

tristate "SSEM example"
default n
depends on HAVE_CXX
    Enable the SSEM application

Build the SSEM Application

Now we have set up the application we can start to reconfigure the build system and deploy the application. First thing to do is to clean the current build environment in order to make NuttX aware of the presence of the application. Issuing the command make clean will remove the previous build components.

Now we can configure the environment by issuing the make menuconfig command. This will present the configuration menuing system we saw in the previous post. So now head over to the following menu items and disable the Hello, world example and enable the SSEM example.

  • Main menu -> Application Configuration -> “Hello, World!” C++ example
  • Main menu -> Application Configuration -> SSEM example

We should now be ready to build the system, so execute the command make -j to build the system. The build system should show output similar to the following if the changes to the system have been made correctly:

Create version.h
LN: platform/board to /Projects/NuttX/apps/platform/dummy
Register: ssem
Register: nsh
Register: sh
CPP:  /Projects/NuttX/nuttx/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld-> /Projects/NuttX/nuttx/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.lLD: nuttx
Generating: nuttx.uf2
tools/rp2040/elf2uf2 nuttx nuttx.uf2;

Time to deploy NuttX to the board and check to see if the application has built correctly. So copy the NuttX.uf2 file to the board and connect a serial console to the serial port. Press enter a few times until the nsh prompt appears and now type help. The new application should appear in the list of built in applications:

Builtin Apps:
    nsh   sh    ssem  

Executing the ssem application should now present something similar to the following (depending upon how you modified the helloxx_main application code):

nsh> ssem
ssem_main: Saying hello from the dynamically constructed instance
CHelloWorld::HelloWorld: Hello, World!!
ssem_main: Saying hello from the instance constructed on the stack
CHelloWorld::HelloWorld: Hello, World!!


The process of creating a new application and having this included in the NuttX build system is not a difficult task, it simply means editing a few files.

One key step is the make clean step as omitting this from a system which already contains build artefacts may not include the new application in the configuration process and hence exclude the files from the build.

In the next post we will add SMP to the configuration.

Enabling NuttX C++ Support on PicoW

Monday, June 5th, 2023

Enable C++ compiler support

In the last article we looked at getting NuttX running on the Raspberry Pi Pico. The system deployed the default applications which are written in C. In this post we will look at enabling C++ support.

The first thing to do is to follow the instructions in the previous post to get the development system downloaded and deployed. Next up we can clean any object files that may be remaining from any previous builds:

make distclean
./tools/ -l raspberrypi-pico-w:usbnsh

Now we have a clean system wew can enabled C++ compiler support. This is done through the configuration system which is accessed by executing the command:

make menuconfig

This will show the main menu for the configuration system:

NuttX configuration system

NuttX configuration system

Using the arrow and enter keys we need to navigate the menus and change the following options:

  • Main menu -> Library Routines -> Have C++ compiler
  • Main menu -> Application Configuration -> “Hello, World!” C++ example

Turning on the C++ compiler will show the following additional menu items:

Enable C++ compiler support

Enable C++ compiler support

The 12.1 release of the PicoW implementation deploys some additional applications which are not needed so we will turn these off in order to save memory. The two application to be disabled are:

  • ostest
  • getprime

These applications can be turned off using the configuration system, the menu items can be found here:

  • Application Configuration -> Testing -> OS test example
  • Application Configuration -> Testing -> getprime example

The system would be ready to be built using the make -j command. Once built, deploy the application to the board by copying the UF2 file to the board as discussed in the previous post. The board should reset automatically so connect a serial console to the board and hit enter a few times to get to the nsh prompt. Typing help at the command problem should show the built in commands plus the additional commands that have been deployed:

Builtin Apps:
    helloxx  nsh      sh

Finally we should verify that the C++ application has been built and deployed correctly by running the helloxx application. If all is well then the serial console should show the following output:

helloxx_main: Saying hello from the dynamically constructed instance
CHelloWorld::HelloWorld: Hello, World!!
helloxx_main: Saying hello from the instance constructed on the stack
CHelloWorld::HelloWorld: Hello, World!!


This is a small step forward getting C++ enabled ready for developing a new C++ application which we will look at in the next post.

In the next post we will add a new C++ user application to the system.

Deploying NuttX to the Raspberry Pi PicoW

Sunday, June 4th, 2023

Close up of Raspberry Pi PicoW

Currently taking a few weeks break and after a few days away from the computer I decided to do a little bit of research into the PicoW and NuttX. So time for a quick attempt at getting the system up and running.

I have for the last few years been working on the Meadow F7 board for Wilderness Labs. This board aims to provide a full .NET development environment bringing embedded development to a wider audience. There are three main technologies underpinning this board:

I spend much of my time working with NuttX and FreeRTOS so as you can gather, I’m not worried about working with C or C++. This gives a few options for working with the board:

  • Native development using the Pico SDK
  • NuttX
  • FreeRTOS

I decided to start with a look at NuttX as I am currently spending a lot of time working with this RTOS.

Let’s Follow the Guide

First thing to do is head over the the documentation for NuttX on the RP2040. The getting started guide is really good and I thought I would be able to use the copy of the Pico SDK I had already on my machine.

This is where I hit my first problem. NuttX requires a specific version of the Pico SDk and using this version would impact any other Pico development that I would undertake in the future. To overcome this I decided to take a second copy of the Pico SDK specifically for use when developing with NuttX.

Having said we would follow the guide, we are deviating already.

So first up we create a development environment for the PicoW (all instructions are for working on a Mac but should also work on Linux).

mkdir NuttX-PicoW
cd NuttX-PicoW
git clone -b 1.1.2
export PICO_SDK_PATH=$PWD/pico-sdk

Now we need to clone the NuttX OS and applications repositories:

git clone nuttx
git clone apps
cd nuttx

Now we have the NuttX components cloned we need to make sure we are on the current releases. This is done using git to checkout the correct branches. At the time of writing the current release is 12.1 so the following commands will checkout the correct branches:

cd apps
git checkout releases/12.1
cd ../nuttx
git checkout releases/12.1

Now we have the correct branches we need to configure the system to work with the PicoW. There are several different boards and configurations available using the RP2040, not just the pico. The following command will list all of the available board configurations available for the Pico series of boards (not just the PicoW but all of the Pico boards):

./tools/ -L | grep pico

For NuttX 12.1 this returns the following list:


The configuration that is interesting here (to get started) is the raspberrypi-PicoW:usbnsh configuration. This will redirect the console output from NuttX over USB to the host machine using serial communication. This will allow a simple terminal application to connect to the board without the need for any additional hardware. So let’s configure NuttX and build the system.

First up, clean the system and set the configuration:

make distclean
./tools/ -l raspberrypi-PicoW:usbnsh

This should clear any previous configurations (distclean) and then set the board type and specific configuration to be built.

Now we should be able to build the system.


You can also tell make to use as many cores on your system as possible with the command make -j. After a short time the system will compile the NuttX OS and applications and create a UF2 file that can be uploaded to the PicoW.

The board can be programmed by starting the board in bootloader mode (press and hold the BOOTSEL button while plugging in the USB cable). The Pico should now present itself as a USB drive on the host computer. Drag and drop the nuttx.uf2 file onto the Raspberry Pi Pico drive on the host computer. The file will be copied to the board and the board will restart.

Connecting to NuttX

Time to check that the board has been programmed correctly. Open your favourite terminal application and set your connection settings to 115200,n,8,1 and connect to the serial port presented by the PicoW. The default is something like usbmodem01. Connect to the PicoW and hit enter a couple of times. On doing this you should be presented with something like this:

NuttShell (NSH) NuttX-12.1.0

If you have got this far then you have successfully built NuttX and deployed the OS to the PicoW. Typying help in the serial console should reveal the list of deployed applications.


At this point we have NuttX running on the PicoW. This opens up some new channels for investigation:

  • Using both cores on NuttX
  • C++ application development

In the next post we will look at enabling C++ development in NuttX.