A template project for RP2040 development using the Kvasir SDK.
- Setup Options
- Option 1: VSCode Dev Container (Easiest)
- Option 2: Docker Development (Manual)
- Option 3: Native Development (Without Docker)
- Build Output
- Flashing/Debugging with Docker
- Alternative Flashing Method: Picotool & UF2
- CI/CD
- Make Targets Reference
- License
You can set up this project in three ways: using VSCode Dev Containers (easiest), Docker with manual setup, or native development.
This is the recommended approach for the fastest and easiest setup. VSCode will automatically configure everything for you.
- VSCode with the Dev Containers extension installed
- Docker installed on your system
Note: For flashing and debugging, see the Flashing/Debugging with Docker section below.
-
Open the project in VSCode:
-
Reopen in container:
- VSCode will detect the dev container configuration
- Click the "Reopen in Container" button when prompted
- Or press
F1→Dev Containers: Reopen in Container
-
Wait for setup (1-2 minutes on first run):
- VSCode pulls the Docker image
- Installs required extensions
- Configures the build environment
-
Start developing:
- All tools are ready (Clang, CMake, debugger)
- JLinkRemoteServer is automatically started on your host
- Build with
Ctrl+Shift+P→Tasks: Run Build Task - Flash and debug with
F5 - Or use make targets from the terminal (see Make Targets Reference)
That's it! Everything is pre-configured.
- Clang compiler toolchain
- CMake build system
- VSCode extensions (clangd, cortex-debug)
- Pre-configured build and debug tasks
VSCode does not automatically pull new image versions. To update:
- Pull the latest image:
docker pull docker.io/kvasirio/rp2040:latest - Press
F1→Dev Containers: Rebuild Container
This setup uses Docker to provide a pre-configured build environment with all dependencies included.
- Docker installed on your system
Note: For flashing and debugging, see the Flashing/Debugging with Docker section below.
-
Start the Docker container:
./scripts/container.sh start
-
Attach to the container:
./scripts/container.sh attach
-
Build your project from the command line:
cd /workspace/project ./scripts/build.shThis configures CMake and builds all configurations. After running
build.sh, you can use make targets for individual operations:cd /workspace/project/docker_build # Build debug configuration make debug # Flash debug firmware make flash_debug # View serial output make log_debug
See the Make Targets Reference section at the end for all available build configurations.
# Start container
./scripts/container.sh start
# Attach to running container (from terminal)
./scripts/container.sh attach
# Stop container
./scripts/container.sh stopWindows users (PowerShell): Use .\scripts\container.ps1 instead:
# Start container
.\scripts\container.ps1 start
# Attach to running container
.\scripts\container.ps1 attach
# Stop container
.\scripts\container.ps1 stopThis setup requires manual installation of dependencies but gives you full control.
- Clang 20+
- lld (llvm linker)
- CMake 3.28+
- Git
- python-intelhex
- SEGGER J-Link Software
-
Clone the required Kvasir repositories:
# Create a directory for Kvasir dependencies mkdir ~/kvasir_deps cd ~/kvasir_deps # Clone Kvasir SDK (recursively) git clone --recursive https://github.com/kvasir-io/Kvasir_SDK # Clone chip support (into a folder named "chip") git clone --recursive https://github.com/kvasir-io/chip_rp2040 chip # Clone device definitions git clone --recursive https://github.com/kvasir-io/kvasir_devices
-
Configure the project:
# Navigate back to your project directory cd ~/rp2040_template # Create build directory mkdir build cd build # Configure with CMake # Replace ~/kvasir_deps/Kvasir_SDK with your actual kvasir_deps location env CC=clang CXX=clang++ cmake .. \ -DKVASIR_ROOT=~/kvasir_deps/Kvasir_SDK \ -DUSE_FORCE_FETCH=ON
-
Build the project:
cmake --build . --parallel $(nproc)
-
Flash the firmware:
make flash_debug
-
View serial output (optional):
make log_debug
See the Make Targets Reference section for all available build configurations and commands.
After building, you'll find the firmware files in the build directory:
*.uf2- Drag-and-drop firmware for RP2040*.elf- ELF executable with debugging symbols for GDB/debugger*.bin- Raw binary firmware*.hex- Intel HEX format firmware*.map- Memory map showing symbol addresses and section layout*.lst- Assembly listing with source code*_string_constants.json- Contains all UC_LOG strings*.ssproj- Generated Serial Studio config with plots for all metrics
When using Docker (Options 1 or 2), the recommended approach is to use JLinkRemoteServer. This is required on Windows because Docker Desktop doesn't expose USB devices to containers.
- SEGGER J-Link Software installed
- JLinkRemoteServer (included with J-Link Software)
- J-Link probe connected to your RP2040 board
For VSCode Dev Container users (Option 1):
JLinkRemoteServer is automatically started when you open the dev container. No manual setup required!
For Docker manual setup users (Option 2):
-
Start JLinkRemoteServer on your host machine:
Windows:
JLinkRemoteServer.exeLinux/Mac:
JLinkRemoteServer
Leave it running in the background (listens on port 19020 by default).
-
That's it! The project is pre-configured to connect to your host machine.
If you need to use a different IP address (e.g., JLinkRemoteServer on another machine):
For VSCode users:
By default, JLink IP is automatically detected:
- On Linux: Uses
127.0.0.1(localhost) - On Windows: Auto-detects the Windows host IP
To override with a custom IP address, create .devcontainer/jlink.conf with:
JLINK_IP=192.168.1.100
Note: After creating or modifying jlink.conf, you need to rebuild the container:
- Press
F1→Dev Containers: Rebuild Container Without Cache
For command-line builds:
cmake .. -DJLINK_IP=192.168.1.100Replace 192.168.1.100 with your JLinkRemoteServer's IP address.
- VSCode Dev Container users: JLinkRemoteServer starts automatically
- Docker manual setup users: You must start JLinkRemoteServer manually whenever you want to flash or debug
If you don't have a J-Link probe, you can flash the firmware using the UF2 files generated during the build process. This method works with all setup options (Dev Container, Docker, or Native).
-
Enter BOOTSEL mode:
- Disconnect the RP2040 from USB
- Hold down the BOOTSEL button
- Connect USB while holding the button
- Release the button
- The device appears as a USB mass storage device
-
Flash using UF2 file:
# Simple drag-and-drop method (adjust path to your build directory) cp docker_build/debug_flash.uf2 /media/RPI-RP2/ # Or use picotool picotool load docker_build/debug_flash.uf2
Picotool can upload firmware without entering BOOTSEL mode if the device is already running compatible firmware:
# Flash debug firmware (adjust path to your build directory)
picotool load docker_build/debug_flash.uf2 -f
# Flash release firmware
picotool load docker_build/release_flash.uf2 -f
# Flash sanitize firmware
picotool load docker_build/sanitize_flash.uf2 -f
# Reboot the device after flashing
picotool rebootSee the Make Targets Reference section for details on available build configurations.
The project includes a GitHub Actions workflow that automatically builds firmware on every push to master.
Artifacts are available in the Actions tab after each build.
| Command | Description |
|---|---|
make debug |
Debug build with symbols and assertions |
make release_log |
Optimized build with logging enabled |
make release |
Fully optimized build without logging |
make sanitize |
Debug build with address/UB sanitizers |
| Command | Description |
|---|---|
make flash_debug |
Flash the debug build to device |
make flash_release_log |
Flash the release build (with logging) |
make flash_release |
Flash the release build (no logging) |
make flash_sanitize |
Flash the sanitizer build |
| Command | Description |
|---|---|
make log_debug |
View serial output from debug build |
make log_release_log |
View serial output from release build |
make log_sanitize |
View serial output from sanitizer build |
See LICENSE file for details.