NOTE: The project is found at Github
Session 1¶
Step 1: Virtual machine¶
For this laboratory exercise, you can use your own computer, or university workstation. We strongly recommend using the university workstations - this way we can ensure that everything works as intended. In any case, at this stage only one member of the team needs to do this setup.
If you use your own computer¶
Install VirtualBox along with VirtualBox Extension Pack. You can install the Extension Pack by following these instructions. After installing these, you can follow the same instructions as the students using university workstations (below).
If you use university workstation¶
Download the virtual appliance (.ova-file). Files and history can be found in Lovelace
In VirtualBox, choose
After some time, you will see a screen like the one below. Choose the freshly imported virtual machine and press "Start" at the top right.
File -> Import appliance and find the previously downloaded .ova-file. In the next screen, choose the directory you want to place the file in, and press "Import".After some time, you will see a screen like the one below. Choose the freshly imported virtual machine and press "Start" at the top right.
Wait (patiently) for the virtual machine to start. In the meanwhile answer the attendance sheet.
Step 2: Introducing Visual Studio Code¶
Starting up¶
1. Open Visual Studio Code. This will be our IDE (Integrated Development Environment), which we use for writing, compiling, debugging, and flashing our device (Raspberry Pi Pico W).
The welcome screen shows the options available. Initially, you can choose to Open Folder, Clone Repository, or Create Pico Project from scratch.
We are not going to create one from scratch, because configuring a new project requires some additional steps that we will skip for now. Instead, we will download the project from the GitHub repository.
Before doing that, let’s explore the different tools available in the menu on the left.
The tools that we are going to use most are Explorer, where we edit our code, and Source Control, which is used if you are working with Git.
2. Let’s download our default project. You have two options. The first one (recommended by the course staff) is using
In any case, knowing Git is not required for this course, but it is a tool that you will definitely need later in your studies, since it is widely used in industry. Moreover, we will use a graphical interface, so you do not need to memorize command-line commands.
git, if you have a GitHub account. The second one, if you do not have a GitHub account or do not want to use Git, is to download the files directly. In any case, knowing Git is not required for this course, but it is a tool that you will definitely need later in your studies, since it is widely used in industry. Moreover, we will use a graphical interface, so you do not need to memorize command-line commands.
Option 1: Using GitHub version control system¶
For this option you should have a Github account.
2.1 Open the repository and fork it by pressing the Fork button at the top-right corner. Only one team member should do this; the others can then join the project.
After you have forked the repository, you no longer need to access the original one — you will use your personal or group repository that was just created.
After you have forked the repository, you no longer need to access the original one — you will use your personal or group repository that was just created.
2.2 The next step is to clone the repository. Open your forked project and copy the repository URL by clicking the Code button. Select the HTTPS URL and copy it.
2.3 Now press the Clone Repository button. In the Command Palette, choose
Clone with GitHub, and then paste the URL of your personal repository.
2.4 You might be asked to add your Github account in a new browser window.
2.5 Select the destination folder for the repository. It should be inside
If you are asked which "kit" to use, select "
/home/student/jtkj-projects. Click Set as repository folder. When asked, choose Yes, I trust the authors. The system will start downloading the necessary libraries. If you are asked which "kit" to use, select "
Pico".2.6 The system is now ready. Check that the terminal at the bottom shows the following text:
Option 2: Download the folder to your computer¶
NOTE: Use this option only if you did not choose Option 1.
2.1 Open the repository in Firefox (use the browser in the virtual machine).
2.2 Press the green
Code button, then select Download ZIP in the new window. This will download a ZIP file to /home/student/Downloads.
2.3 Extract the folder into your
/home/student directory. We recommend using the folder /home/student/jtkj-projects.2.4 In Visual Studio Code, press the Open Folder button. Select the folder where you extracted the repository and click Open.
When asked, press Yes, I trust the authors. The system will start downloading the necessary libraries.
If you are asked which "kit" to use, select "
When asked, press Yes, I trust the authors. The system will start downloading the necessary libraries.
If you are asked which "kit" to use, select "
Pico".
2.5 The system is now ready. Check that the terminal at the bottom shows the following text:
Exploring VSCode¶
If you have followed carefully previous instructions the Explorer present the following folders:
- vscode: Configuration files for Visual Studio Code and the Pico Extension. In general we are not going to modify any file in that folder.
- build: This folder is generated by the "Visual Studio Code extension". It produce all configuration and output files necessary to build executable
- config: This folder configuration files for different extensions and library. In our case only contains FreeRTOS Configuration
- examples: Some source and configuration files of different applications that we are going to execute in this exercise.
- libs: External libraries / SDK used in the course. It contains the Hat SDK that we will use in Exercise 2 as well as the usb-serial-debug library that will use in the future to send debug commands to the terminal.
- src: The source file of your main program. In this case it contains the
main.cwhich you can open. It is just a hello world that we will execute very soon. - additional files: The
FreeRTOS_kernel_import.cmakeconfigure our development environment to use FreeRTOS whilepico_sdk_import.cmakeis used to configure our Pico SDK. We are never touching any of those files. TheCMakeLists.txtis a CMake configuration file which defines the libraries to be used as well as the location of our sources. We will learn a bit more about this file later.
At the bottom we can see an important bar that we are going to use a lot:
The button that reads
[all] indicates which is the target that we are compiling. The target is the application which contains the main() function you are compiling. We will explain later how do you know which are the possible targets (small advance, you would need to check the CMakeList.txt) The two buttons on the right, are used to compile and flash the compiled code into the device. Let's first quickly compile and upload the first code to the Raspberry Pi Pico.
Open the file
Open the file
main.c from the directory src. Have a quick look the file and try to understand the structure. Remember that we run always the main function first. This code will just toggle the Raspberry Pi Pico LED every second. The code is a bit strange, because the Raspberry Pi Pico W controls the LED using the networking chip (strange, right?), but if you read the comments it should be easy to follow. Compiling and uploading code¶
First, we need to select which target we are going to compile. On the bottom bar, you can see the text
Build and next to it all. If we keep it as all, we are compiling the entire project (including libraries and example applications). Let’s do that now, as it will save time later. Since we are compiling many files, the process may take a while.
To start the compilation, press the
Compile button on the right side of the bottom bar. If you are asked for a launch target for project jtkj_project, select hello_blink. The launch target indicates which application will be uploaded to the device when we press run. For now, let’s focus on compilation. Select
hello_blink as the launch target and let the system compile all your source files. You can see in the terminal how the .o files are being generated.At the end of the process, you should see a Build completed message, and it should return with status code 0 (meaning everything is OK). If that is not the case, contact the course staff.
Now your files have been compiled, and the executable/binary file (in this case a .bin, .elf, or .uf2 file, depending on the flashing method you are using) is stored in the
build folder. You do not need to worry about the exact location because the VS Code extension handles that for you.Now that we have the file, we still need to upload it to the device. The process is a bit tricky, so please follow the next steps carefully.
1. Connect the USB cable to your Pico, press the
By doing this, we put the Pico in USB Mass Storage (bootloader) mode. In this mode, the Pico appears as a removable USB drive named RPI-RP2, which allows uploading the binary file. Whenever you want to upload new software, you must disconnect the USB and press the BOOTSEL button. Later we will see that this step is sometimes necessary only the first time you connect the Pico to the computer.
BOOTSEL button (image below), and while holding it, connect the other end of the USB cable to the PC. By doing this, we put the Pico in USB Mass Storage (bootloader) mode. In this mode, the Pico appears as a removable USB drive named RPI-RP2, which allows uploading the binary file. Whenever you want to upload new software, you must disconnect the USB and press the BOOTSEL button. Later we will see that this step is sometimes necessary only the first time you connect the Pico to the computer.
2. After the Pico is connected to the computer via the USB cable, it will appear in the
Devices -> USB menu of VirtualBox as Raspberry Pi RP2 Boot.You need to explicitly select this device so the virtual machine can interact with the Pico.
NOTE: You can make the VM automatically recognize your device when it is connected by selecting
Devices > USB > USB Settings in your VM menu and adding the device to the USB Device Filters (see image below).
3. Once your device is recognized by the Virtual Machine (NOT by the host computer), press the
Run button on the bottom bar.When the binary file is uploaded to the device, you should see a success message (see below). In addition, you should see the LED on your Pico blinking every second.
4. Now the device appears as a different USB device to the VM. You should repeat step 2, but this time select Raspberry Pi Pico [0100] as the device.
You should notice that the bottom bar now shows a new element:
The target on the left is the Compilation target, and the one on the right is the Launch target. This means that from now on, we do not need to compile the entire project, only the specific targets we modify. We can also choose which target to upload to the device. This is useful later when you have, for example, different versions of the software to test.
If you want, you can select
[hello_blink] as the compilation target, modify part of the code (for example, change the value of LED_DELAY_MS), compile, and run again. Try first running the code without disconnecting the USB cable. If you have followed the previous steps, it should not be necessary to remove the Pico from the device.Using the terminal¶
Ok, ok. But the
main.c is using printf, and you mentioned in the lectures that we are going to use printf and puts to debug our code. So, we should be able to see the printed messages. Actually, you can use any serial terminal. The virtual machine has screen and minicom already installed. But this time, we are using the VSCode integrated serial monitor. Open the "Serial Monitor" tab located at the bottom of the screen.
When you open the tab, all settings should already be correct (Monitor mode: serial, View mode: text, Baud rate: 115200, Line ending: None). You just need to select the correct serial port from the "Port" menu. In the Linux operating system used by our virtual machine, this is usually
/dev/ttyACM0 or /dev/ttyACM1. If the Pico does not appear in the menu, check that it is connected to the virtual machine from VirtualBox’s
Devices -> USB menu, and then press the refresh button next to the "Port" menu. Once the correct port is selected, you only need to press the blue "Start monitoring" button, and the serial connection will open. If everything works as expected, you should see a Tick message every second, just as it was programmed in the code.
We recommend activating Toggle Autoscroll so that the terminal always displays the latest line sent by the Pico, and also Toggle Automatic Reconnection.
If that is activated, try disconnecting and reconnecting the Raspberry Pi Pico.
If you have followed the previous steps and the USB device is already added to the VM’s filter list, the Serial Monitor should automatically reconnect.
If that is activated, try disconnecting and reconnecting the Raspberry Pi Pico.
If you have followed the previous steps and the USB device is already added to the VM’s filter list, the Serial Monitor should automatically reconnect.
A walk through the CMakeLists. Executing examples¶
We promised you that we were going to explain in a bit more detail the
CMakeLists.txt. This file is a configuration file used by CMake, the build automation tool we are using for the Pico. CMake is a build automation tool that helps developers compile programs automatically on different systems. Instead of writing long and platform-specific build commands by hand, CMake lets you describe your project once (in the CMakeLists.txt), and then it generates the correct build system for your platform. It helps detect compilers and system paths, manage dependencies, and it works across different operating systems.The
CMakeLists.txt performs the following functions:- Define the project name and its targets (executables or libraries).
- Specify which source files belong to each target.
- Tell CMake which SDKs, libraries, and include paths are required.
- Configure how to build and link those files (for example, whether to enable USB or Wi-Fi).
- Generate the build system that actually compiles your code.
We will see that we can have several nested
CMakeLists.txt files: one that defines the general settings, and others that define settings for specific folders (for example, a folder containing additional examples).Let's have a quick look at the
CMakeLists.txt file located in the root folder. Open the CMakeLists.txt. The file has the following sections:- The first part initializes and sets up the pico_sdk, as well as creates a new project.
- The second part includes the necessary library and configuration files required to run FreeRTOS.
- The third part specifies where additional libraries and example applications are located.
- Finally, the last part defines the settings required to create the main target (the one under the root
srcfolder). This is the section that you might need to edit later. Please note that the variablesMAIN_TARGETandAPP_NAMEare defined at the top of the project:set(APP_NAME JTKJ_app)andset(MAIN_TARGET hello_blink). You can change them if you wish. Let’s check the most important commands: add_executable: Lists all the.cfiles that must be included in the executable.target_link_libraries: Lists all the libraries that will be used. The C SDK provides names for different hardware libraries, such ashardware_adcorhardware_gpio. The library pico_stdlib includes most of the commonly used functions, so usually having this one is enough. Note that you need to add the correct library here if you want to use its header with#include library_namein your C code.pico_enable_stdio_usb: Indicates that you want to sendstdiooutput through USB. In general, we use this unless we are using theusb-serial-debuglibrary.pico_add_extra_outputs: Generates the different output files required for flashing the device, as well as debugging and profiling the code.
Right now, we are not going to modify it, but in the next step, you might need to make a small change in the file to run the code. Stay tuned!
At some point, we mentioned that you could use the
CMakeLists.txt to generate additional targets. In the root CMakeLists.txt, there are several add_subdirectory() commands. When one of those subdirectories is opened, we expect to find another CMakeLists.txt file inside that provides additional build instructions.The
examples folder includes some additional examples (more will come in Lecture 2). Please, in the Explorer view, open the hello_blink_freertos folder. You can see a new CMakeLists.txt file and a src folder containing main.c. If you open the main.c file, you will notice that it is exactly the same code currently running on your Pico, but this time it uses FreeRTOS (we create only one task). If you open the CMakeLists.txt file, you will see that it contains the four instructions we summarized before. In this case, the target name is hello_blink_freertos, and the target_link_libraries section includes the libraries FreeRTOS-Kernel and FreeRTOS-Kernel-Heap4, which are required to call FreeRTOS functions (see the #include "FreeRTOS.h" and #include "task.h" lines in main.c).Since you know the target name, please, compile and run the
hello_blink_freertos target. The LED on the board should start blinking. Open the Serial Monitor. You should now see the text Tack FreeRTOS printed every second.If you have time, you can also explore and run the following examples:
- hello_pico
- hello_freertos
The others are a little bit more complex, and we will explore them in the next session.
Solving compilation problems¶
Imagine the following scenario: a first-year student was helping with the programming, but wrote the code with several errors. Your task is to find and fix those errors so the project compiles, runs on the device, and prints meaningful output.
The broken code is in the folder
/examples/compilation_errors. You can either copy the code into your main target under src/, or use the CMakeLists.txt provided inside /examples/compilation_errors to build that folder as its own target.Please, be sure that you execute following tasks.
1. Uncomment the line
2. Open the files in
3. Identify and fix the main problem in the code, by checking and understanding compilation errors and warning.
4. Rebuild the project until the build completes with status code 0 and no warnings.
5. Flash and run the corrected program on the device.
6. Open the Serial Monitor and verify that the output appears once the program is running.
1. Uncomment the line
add_subdirectory(examples/compilation_errors) in the root CMakeLists.txt2. Open the files in
/examples/compilation_errors and read the error messages shown in the terminal when you try to build.3. Identify and fix the main problem in the code, by checking and understanding compilation errors and warning.
4. Rebuild the project until the build completes with status code 0 and no warnings.
5. Flash and run the corrected program on the device.
6. Open the Serial Monitor and verify that the output appears once the program is running.
If everything is fixed and working, you should see lines similar to the following in the serial monitor:
time:2494951,temp:19,lux:1281 time:2496451,temp:18,lux:791 time:2497951,temp:34,lux:575
When you change the
CMakeLists.txt you might need to reload the cache. To do that, press F1 (or Ctrl+Shift+P) and select: CMake: Delete Cache and Reconfigure. This will reload the build folder. Sometimes, you need to even remove the whole build folder, but let's hope we do not need to do that in this exercise.When you finish upload the file
main.c already with changes done to the following return box. Visualizing data using Arduino plotter¶
Sometimes, we would like to visualize a graph with the sensor data. One option would be to print the values in the terminal, copy them to a file, and then process them using any Time Series tool. However, Arduino environment offer a very simple tool for doing that: The Serial Plotter.
In order to make it work we would need to send a string of the following format:
variable1:xxx,variable2:xxx\n.First step is modifying your code so the output from the
printTask is the following: temp:xxx,lux:yyy\n where xxx and yyy are the values coming from the sensors (in this case stored in global variables). Check that is working using the Serial Monitor, and
Stop MonitoringAfter that, open Arduino IDE environment and follow next steps:
- In
Tools > Portchoose the/dev/ttyACM0(or whatever port your code is running) - Go to
Tools > Serial Plotterand open the Serial Ploter. Voila! you can see your sensor data.
This does not allow operate with the data, nor for instance, define the time scale. But might be interesting to detect thresholds or some basic behaviours.
Please, take a screenshot of the figure you have obtained and include it in the following return box.
Using external libraries. The serial-usb-debug¶
Great progress. But know, let's explain how to include libraries that are defined in the same project. We are going to provide you with libraries for WiFi connectivity, to interact with the Pico HAT (much more of this in session 2) and to create two different serial communication ports: one for writing debug messages and the other to send raw data (e.g sensor data).
We have defined libraries in the same way as we have defined examples. In this way the libraries are in their respective folder inside
libs. If you open usb-serial-debug you observe similar structure to a project, but the CMakeLists.txt is slightly different (Come on!, open the CMakeLists.txt inside the usb-serial-debug, if you have not done it yet). Instead of using add_executable we use add_library. Libraries do not have a main so they cannot be a "Launch target", but they can be a compilation target. The name of the library is given also in the
add_library. Observe how the target_link_libraries have PUBLIC libraries (are included also to the program using current library) and PRIVATE libraries (they are internal from this library and are not shared with others).In other to show how the library is working, we have included an example:
hello_dual_cdc. If you open its CMakeLists.txt you can see that the target_link_libraries includes the usb_serial_debug. If you want to include this library in your project, you should do something similar. If you open the src/main.c in the hello_dual_cdc you can see that it includes #include "usbSerialDebug/helper.h". Where does this path comes from, well if you open the library hello_dual_cdc and its include folder that is the one that is included in the library. This folder contains another folder named usbSerialDebug that is the one we are referring. It looks a bit complicated, but if you understood how libraries work in C should be easy to grasp.You can have a look to the code. It is explained in the Serial Communication Chapter. At some point you should be able to understand what is happening there for your final project. But today we are just interested in running the code and visualizing the data. JUST ONE IMPORTANT REMINDER, IF YOU ARE USING THIS LIBRARY, YOU CANNOT USE ANY OF THE STDIO FUNCTIONS, INSTEAD YOU SHOULD USE THE
usb_serial_print FUNCTIONAnother important note, as it can be seen in the
CMakeLists.txt, we are not redirecting the stdio to the USB, that is why the pico_enable_stdio_usb is set to 0. Compile and run the
hello_dual_cdc target. This time you should open two different serial monitors: one for /dev/ttyACM0 (debug) and the other /dev/ttyACM1 to send raw data.IMPORTANT NOTE: Since the library modifies the USB profile of the device, it is is recognized with a different name:
University of Oulu TKJHAT. You need to mark this device in the VM -> Devices USBTake a screenshot of your screen showing the two serial monitors running in parallel.
Storing the files¶
If you want to save your files you have two options:
- Save the files that you have modified (generally only the files under your root
srcfolder). It is a good idea to .zip them. IMPORTANT: You cannot drag&drop to the host, do not even triy, likely the SYSTEM WILL CRASH. Hence, you would need either a USB stick to save your files in some online repository (GoogleDrive, OneDrive... that can run FROM THE VIRTUALMACHINE). For your your project you can use the Temporary Code Sharing Space we created in the Module 2 Project. - Use the proper method: GIT (AGAIN, THERE ARE OTHER ALTERNATIVES, THIS IS NOT A MANDATORY METHOD, BUT RECOMMENDED).
During this lecture we do not need to save anything else, but when working on your project you might need it. So, we include here additional instructions. Just follow these instructions, if you want to push the project to the repo you likely created at the beginning of the session.
The mandatory part of the exercise is finished. You can read further if you want to learn GIT in more depth.
APPENDIX: Storing files with GIT¶
- First open the Terminal and write the following commands
git config --local user.email "myemail@gmail.com" git config --local user.name "Myname" git config --local --list
- Open a remote repository (if it is not yet opened)-
- Open the Git view by pressing the Git-icon located at the left-hand side of the window:
- You can the files modified since the last commit. To create a new commit, you need to enter a message that describes, what kind of changes you made to the code. In this case, the message could just state that this is the first commit of the repository. The easier option is to choose all files, but you can choose individual files by pressing the + button after each file. Once everything is ready, click the big, blue "Commit"-button, and your changes will be saved locally, in the virtual machine you are currently using.
- You can select also
"Commit+Push"that will upload the code to the remote repository and sharing with others. You can also press thePushbutton in the Graph view. - To continue working on code created previously, you need to either clone the project repository, or synchronize your local repository. The choice between these two options depends on if you are continuing your work in the same virtual machine/device as before or starting in a new one.
Cloning
If you are continuing your work in a new virtual machine or device, you need to clone the project repository, so that you get a copy of the latest version of your code and its change history. Cloning happens the easiest by using the GitHub-integration of VSCode (if your repository is saved in GitHub).
Upon opening VSCode in the new virtual machine, you can clone a repository located in GitHub by clicking "Clone Git Repository" at the start screen of VSCode.
Following view will open at the top of the window:
Here we choose the option "Clone from GitHub", after which the browser opens and you can login to GitHub. Follow the instructions given, and give VSCode the access to your GitHub-repositories. After completing the login, the browser will guide you back to VSCode, where you can select a repository to clone from the list containing all of your GitHub repositories. Choose the home folder of the virtual machine as the saving location for your repository.
Synchronizing
If you are continuing your work in the same virtual machine or device as before, you need to open the previous project folder where you created the Git-repository. It is a good practice to download the latest changes from the version control system server, so that you can avoid conflicts. This process, also called synchronizing, can be done in VSCode by clicking the synchronize-button located at the bottom left corner of the window:
When the synchronization is ready, you have the latest version of the project's code in your local Git-repository. Now you can start making changes, and be sure that changes made by for example other group members are included.