Objective
While building a complex Raspberry Pi Pico robotics project, I wanted to be able to choose whether to build my model as normal, or to build a set of unit tests. This was particularly important for me because there were multiple moving parts, many of which had to work in co-ordination with each other and/or had safe working range limits. I needed to be able to check all the edge case conditions to make sure that parts wouldn’t crash in to each other or behave unexpectedly.
My Solution
I am using VS Code with the Raspberry Pi Pico extension to write and manage the coding aspect of my project. I put my source code under two separate directories (called src and test) and then have the build system create two executable targets. The actual robot model is based purely on the code within the src directory structure, and a set of unit tests defined under the test directory structure which would refer to the code under the src directory tree. The unit tests would report back over the USB connection to my PC with the results of the tests. I did consider running the tests directly on my PC (it’s not too hard using mocks) but I am a big believer in keeping the test environment as close as possible to the live one.
Step 1: Create the project
I am assuming that VS Code is already installed and working, along with the Raspberry Pi Pico extension. I also am going to assume that you already have the “CMake Tools” extension installed. If not then do all of that first there are plenty of good tutorials on how to do that elsewhere, and this “How To” is long enough already.
Use the Pico extension to create a new C++ project in the normal way by:
- Clicking the “Pico” icon in the left-hand toolbar to reveal the sidebar menu of actions.
- Choose the “New C/C++ Project” option to reveal the project creation dialog view.
- Name your project. For the sake of this tutorial I have called mine “Robot”, but the name is unimportant.
- Choose your board type from the drop-down list (such as “Pico” or “Pico 2” etc.
- Ensure that the location shown for where the project directory will be created is where you want it to be,
- Optionally select which SDK you wish to use. You can probably let that default.
- Select any features that you need for your project such as “SPI” or “I2C”, etc. For the purpose of this tutorial I didn’t choose any.
- Choose which stdio support you need UART/USB/None. I chose USB for my test harness to report back over.
- Select any code generation options that you want. I chose “Generate C++ code”.
- Select the debugger option that you prefer. I use “DebugProbe”.
- Tick the “Enable CMake-Tools extension integration”
- Click the “Create” button.
For convenience here is a screen shot of my filled in dialog:

At this point you should have a normal Pico base project setup for you. You would be wise to build and run it to make sure that everything is working… It will be!
Step 2: Modify the file structure
My reason for doing this is to be able to unit test my code. So I want two source code trees: One for actual robot model; The other for the unit tests that I will write to check my logic. So I did this:
- Create a directory called “src” under the root of the project (where you currently have your “main.cpp” file.
- Move the existing “main.cpp” file into the newly created “src” directory.
- Open up the “CMakeList.txt” file under the project root so that you can modify it.
- Search for the line that says “add_executable(Robot main.cpp )” and change it to say “add_executable(Robot src/main.cpp )”.
- Search for the line that says “arget_include_directories” and change the part that says “${CMAKE_CURRENT_LIST_DIR}” to say “${CMAKE_CURRENT_LIST_DIR}/src” instead.
- In the same definition change it from being marked as “PRIVATE” to being “PUBLIC“.
- Save the file, rebuild the project and run it to make sure that you haven’t broken anything.
At this point your project behaves exactly as it did by default, with the exception that you have your source code under the “src” folder instead of in the project root.
Step 3: Add a second target to build
Create something trivial to be your second build target
- Create a directory called “test” (or whatever is appropriate to your needs) under the root of the project (where you created the “src” directory in the last step).
- Copy the existing “main.cpp” file from the “src” directory into the new “test” (or whatever you named it) directory.
- Rename the newly created file under “test” (or whatever you called the new directory) to be something appropriate for your new build target. For me I will call it “AllTests.cpp” as it will eventually be my test runner.
- Change the new file so that when it runs you can tell it apart from the original “main.cpp”. I just changed the wording in the “printf” statement that is sent back over the USB console from “Hello world!” to “Hello from the Test Harness!”.
Add the new target to your build system
So far your new target is invisible to the build system. What we need to do is to add the new target to the build system’s CMakeLists.txt file, in the root of the project, that we edited earlier. Open it up for editing and add the following to the very end of the existing file:
#=========================================================
# Add another executable target (in this case TestTarget)
# ========================================================
# 1st declare the executable and add the source .cpp files to build
# Add all your new test cpp files here too.
add_executable(TestTarget
test/AllTests.cpp
)
# Next name the executable and give it a version number
pico_set_program_name(TestTarget "TestTarget")
pico_set_program_version(TestTarget "0.1")
# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(TestTarget 1)
pico_enable_stdio_usb(TestTarget 0)
# Add all the required libraries to the build
target_link_libraries(TestTarget
pico_stdlib)
# Add the standard include files to the build
# Note that CMAKE_SOURCE_DIR is the root of the project
# and NOT the source code directory.
target_include_directories(TestTarget PRIVATE
${CMAKE_SOURCE_DIR}/test
${CMAKE_SOURCE_DIR}/src
)
# By default only the .elf file is generated.
# This line adds the .uf2, .bin .hex .map and .dis files as well.
pico_add_extra_outputs(TestTarget)
Save and close the “CMakeList.txt” file that you just edited.
To simplify selecting which target to build/deploy edit the “settings.json” file which can be found under the “.vscode” directory.
Search for the section “cmake.options.statusBarVisibility” and change it to match the following:
"cmake.options.statusBarVisibility": "visible",
"cmake.options.advanced": {
"build": {
"statusBarVisibility": "visible"
},
"launch": {
"statusBarVisibility": "visible"
},
"debug": {
"statusBarVisibility": "visible"
},
"variant": {
"statusBarVisibility": "compact",
},
"buildTarget": {
"statusBarVisibility": "visible",
}
},
- Save and close the “settings.json” file. Along the bottom status bar of VS Code you should now have an option to select and change the build target. Clicking the current target brings a drop-down list of possible targets in the command pallet at the top of the VS Code window. The options that you are interested in are labeled as “Executable”. In the case of this example they are the two named “Robot” and “AllTests”. By default “All” targets will be built.
- Select each of your builds, rebuilding/deploying/testing each one in turn.
Still ToDo:
- Document how to incorporate the “Cpputest” framework into one of the builds without affecting the other builds
- Document how to be able to switch toolchains (compilers) for different targets so that some targets can be built with the Pico SDK toolchain and others with the host native compiler etc.