Electronics - STM32 - Setting up PlatformIO in ROS 2 DevContainers

This guide provides a step-by-step workflow to bridge physical STM32 hardware into a virtualized ROS 2 DevContainer.

It leverages PlatformIO and usbipd to enable firmware flashing from a Docker environment.

First of all

Github project for this tutorial:

Shopping list:

  • An electronic board like the STM32F3Discovery
  • Windows 10 or 11
  • WSL 2 (Windows Subsytem for Linux)
    • Ubuntu 24.04
  • usbipd-win: https://github.com/dorssel/usbipd-win/releases
  • Dockerfile with ROS 2 image (osrf/ros:jazzy-desktop)
  • VSCode with extensions:
    • Dev Containers (ms-vscode-remote.remote-containers)
    • Robot Developer Extensions for ROS 2 (Ranch-Hand-Robotics.rde-ros-2)
    • PlatformIO IDE (platformio.platformio-ide)

WSL: Windows Subsystem for Linux

In order to get WSL, simply open a PowerShell terminal in Admin mode:

Right click the Windows Start menu > Windows PowerShell (Admin).

In the terminal type the following command:

wsl --install

It downloads the last Ubuntu (by default) Linux distribution.

In order to activate the WSL 2 version (useful for Docker and USB tools) we have to specify it:

wsl --set-default-version 2

Restart your computer to apply changes.

usbipd-win

For this tutorial we use the 5.3.0 version of usbipd (for Windows).

Once downloaded, install it by double clicking the usbipd-win_5.3.0_x64.msi file.

This is the moment to plug your STM32 board by USB to your PC.

From a PowerShell command, type this command:

usbipd list
You should see something like this:
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
3-1    0483:3748  STM32 STLink                                                  Not shared

We obtain a busID (3-1 in our case) that we have to share with WSL.

Type the following command:

usbipd attach --wsl --busid 3-1
It will result this output:
usbipd: info: Using WSL distribution 'Ubuntu-24.04' to attach; the device will be available in all WSL 2 distributions.
usbipd: info: Loading vhci_hcd module.
usbipd: info: Detected networking mode 'nat'.
usbipd: info: Using IP address 172.29.26.1 to reach the host.
If you display again the devices list of your USB ports:
usbipd list
You'll see:
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
3-1    0483:3748  STM32 STLink                                                  Attached

So your device is now shared from Windows to Ubuntu (Linux) and the STM32 board is available from our container inside Ubuntu.

If you encounter errors, then, uninstall usbipd, restart your computer and reinstall it.

And above all, each time you remove your board, you'll have to attach again the board with usbipd.

Native Ubuntu, Windows network and Docker container

For this tutorial we are going to create a bind mount between our local project folder and the container created by Docker.

It means our local project and the container's workspace are always in sync, any change made in one is immediately reflected in the other.

So every modification will be syncronized on the both directories.

For example, my local project is named ros2.

The specificity of using WSL 2 is that this directory can be reached through 3 different paths, depending on where you are looking from:

  • The Ubuntu native path (Ubuntu via WSL):
    • /home/mika/p/ros2
    • This path is the one created from Ubuntu, itself on Windows via WSL.
  • The Windows network path:
    • \\wsl.localhost\Ubuntu-24.04\home\mika\p\ros2
    • It's the bridge used by Windows to show the Linux files directly on Windows. 
  • The Docker container path:
    • /workspaces/ros2
    • This is the virtual address inside the ROS 2 container, mapped to the native path.

Despite these different names, they are exactly the same set of files, changing a line of code in /workspaces/ros2 will instantly update the file at /home/mika/p/ros2 and also from the Windows network.

From Ubuntu, create a .devcontainer folder inside your project directory (/home/mika/p/ros2) in order to have this directory tree:

/home/mika/p/ros2/.devcontainer/ros-dev-ok/
├── Dockerfile
└── devcontainer.json

VSCode and its extensions

We have now 2 files inside the .devcontainer directory.

Copy and paste the content of these 2 files from Github.

Dockerfile

The Dockerfile:

  • Uses the osrf/ros:jazzy-desktop Docker image;
  • Installs ROS 2, python, USB utils and STLink tools;
  • Sets the ENV for the Turtlebot3 model;
  • Sets the ubuntu user as sudo;
  • Sources the setup file for the jazzy ROS2 version;
  • Exports the PlatformIO bin directory in the PATH.

devcontainer.json

The devcontainer.json file explains how to build and configure the container:

  • --net=host: The container shares the same network as the WSL;
  • --DISPLAY & /tmp/.X11-unix: Graphic display (for RViz and Gazebo) for the Windows screen;
  • --privileged: The container has almost the same rights as the host machine;
  • --v /dev:/dev: The bridge, it mounts the /dev folder (STM device) from WSL to the container.

In order to activate all features from your Dockerfile and devcontainer.json files, your board has to be connected before building your container.

Otherwise you won't have the persmissions to read and use your board.

Furthermore, the postStartCommand line has to be changed if you have rebuild the project from another folder name (different from our example ros2) and modify the part "/worskspaces/YOUR_FOLDER_NAME/".

Then to open our project in the DevContainers extension, simply type CTRL + SHIFT + P in order to open the palette feature of VSCode.

Choose the option in the list: Dev Containers: Rebuild and Reopen in container.

If everything went OK you are now in the container.

You can check from your Docker container that your board is seen by your container, with the following commnand:

st-info --probe
It should display this kind of information:
Found 1 stlink programmers
  version:    V2J24
  serial:     56FE73964985155216130889
  flash:      262144 (pagesize: 2048)
  sram:       40960
  chipid:     0x422
  dev-type:   STM32F302_F303_358

PlatformIO

But even if every seems fine, the PlatformIO extension needs at least 2 minutes to load, so wait a bit for the extension to work and install automatically.

A new popup will appear to show that the PlatformIO has been successfully installed and inviting you to Reload Window.

Click Reload Window.

A new ant icon will then appear on the left menu bar.

If it's the case, then click it in order to open the PlaftformIO panel.

Inside the PIO Home directory, select the Project & Configuration line.

On the center of VSCode you should see the PIO Home.

Note that the following project code is already on Github, so either create a new one with a different name or use the one already available.

Click Create a New Project.

Then:

  • Name: stm32-project-1;
  • Board: STM32F3DISCOVERY;
  • Framework: Arduino;
  • Location: check the box.

Don't worry for the Arduino framework, it brings all the files necessary, even for the STM32 boards.

Then click Finish.

The PlatformIO is going to download  files for your project, so "Please wait..." about 5 minutes because it has to download a lot of files from Internet.

Once the downloads done, your project name has been generated with double backslashes ("\\") like it was on Windows.

This is a bug and we are going to move the project from the wrong path to the ros2 folder.

Let's start by setting your path to /workspaces/ros2 (if not already done):

cd /workspaces/ros2/

By default the projects are created in the following directory and should look to something like this:

  • ~/Documents/PlatformIO/Projects\\stm32-project-1/

Let's move it to the right directory:

mv ~/Documents/PlatformIO/Projects\\stm32-project-1/ /workspaces/ros2/

Even with this move, there is still one backslash:

/workspaces/ros2/Pro​jects\stm32-project-1

Let's finish it by removing it totally:

mv 'Projects\stm32-project-1'/ stm32-project-1

Your PlatformIO project is now ready to be used.

Let's enter in this new directory:

cd stm32-project-1

You can see all the following files:

/workspaces/ros2/stm32-project-1
|-- .gitignore
|-- .vscode
|-- include
|-- lib
|-- platformio.ini
|-- src
`-- test

Let's now open the following file:

/home/mika/p/ros2/stm32-project-1/src/main.cpp

And replace the content of this file by the following code:

#include <Arduino.h>

#define LED_PIN P8

void setup()
{
  //
  pinMode(LED_PIN, OUTPUT);
}

void loop()
{
  //
  digitalWrite(LED_PIN, HIGH);
  delay(500);

  //
  digitalWrite(LED_PIN, LOW);
  delay(500);
}

 

We are now ready to flash (upload) the firmware (code) to the board.

Let's set the path to:

cd /home/mika/p/ros2/stm32-project-1

Then type the command:

pio run --target upload

You should see something like this appears on your terminal:

Processing disco_f303vc (platform: ststm32; board: disco_f303vc; framework: arduino)

-----------------------------------------------------------------------------------------------------------------------------------

Tool Manager: Installing platformio/tool-stm32flash @ ~0.7.0

Downloading  [####################################]  100%

Unpacking  [####################################]  100%

Tool Manager: tool-stm32flash@0.7.0 has been installed!

Tool Manager: Installing platformio/tool-stm32duino @ ~1.0.1

Downloading  [####################################]  100%

Unpacking  [####################################]  100%

Tool Manager: tool-stm32duino@1.0.1 has been installed!

Tool Manager: Installing platformio/tool-openocd @ ~3.1200.0

Downloading  [####################################]  100%

Unpacking  [####################################]  100%          

Tool Manager: tool-openocd@3.1200.0 has been installed!

Tool Manager: Installing platformio/tool-dfuutil @ ~1.11.0

Downloading  [####################################]  100%

Unpacking  [####################################]  100%

Tool Manager: tool-dfuutil@1.11.0 has been installed!

Tool Manager: Installing platformio/tool-dfuutil-arduino @ ~1.11.0

Downloading  [####################################]  100%

Unpacking  [####################################]  100%

Tool Manager: tool-dfuutil-arduino@1.11.0 has been installed!

Verbose mode can be enabled via `-v, --verbose` option

CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/disco_f303vc.html

PLATFORM: ST STM32 (19.5.0) > ST STM32F3DISCOVERY

HARDWARE: STM32F303VCT6 72MHz, 40KB RAM, 256KB Flash

DEBUG: Current (stlink) On-board (stlink) External (blackmagic, cmsis-dap, jlink)

PACKAGES: 

 - framework-arduinoststm32 @ 4.21200.0 (2.12.0) 

 - framework-cmsis @ 2.60300.0 (6.3.0) 

 - framework-cmsis-dsp @ 1.16.2 

 - tool-dfuutil @ 1.11.0 

 - tool-dfuutil-arduino @ 1.11.0 

 - tool-openocd @ 3.1200.0 (12.0) 

 - tool-stm32duino @ 1.0.1 

 - tool-stm32flash @ 0.7.0 

 - toolchain-gccarmnoneeabi @ 1.120301.0 (12.3.1)

LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf

LDF Modes: Finder ~ chain, Compatibility ~ soft

Found 14 compatible libraries

Scanning dependencies...

No dependencies

Building in release mode

Compiling .pio/build/disco_f303vc/FrameworkArduinoVariant/PeripheralPins.c.o

...

Compiling .pio/build/disco_f303vc/SrcWrapper/src/HAL/stm32yyxx_hal.c.o

...

Compiling .pio/build/disco_f303vc/SrcWrapper/src/HardwareTimer.cpp.o

Compiling .pio/build/disco_f303vc/SrcWrapper/src/LL/stm32yyxx_ll_adc.c.o

...

Compiling .pio/build/disco_f303vc/SrcWrapper/src/new.cpp.o

...

Compiling .pio/build/disco_f303vc/SrcWrapper/src/syscalls.c.o

Compiling .pio/build/disco_f303vc/src/main.cpp.o

Compiling .pio/build/disco_f303vc/FrameworkArduino/HardwareSerial.cpp.o

...

Archiving .pio/build/disco_f303vc/libFrameworkArduino.a

Indexing .pio/build/disco_f303vc/libFrameworkArduino.a

Linking .pio/build/disco_f303vc/firmware.elf

Checking size .pio/build/disco_f303vc/firmware.elf

Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"

RAM:   [          ]   3.1% (used 1264 bytes from 40960 bytes)

Flash: [=         ]   5.5% (used 14308 bytes from 262144 bytes)

Configuring upload protocol...

AVAILABLE: blackmagic, cmsis-dap, jlink, mbed, stlink

CURRENT: upload_protocol = stlink

Uploading .pio/build/disco_f303vc/firmware.elf

xPack Open On-Chip Debugger 0.12.0-01004-g9ea7f3d64-dirty (2023-01-30-15:03)

Licensed under GNU GPL v2

For bug reports, read

        http://openocd.org/doc/doxygen/bugs.html

debug_level: 1


srst_only separate srst_nogate srst_open_drain connect_deassert_srst


[stm32f3x.cpu] halted due to debug-request, current mode: Thread 

xPSR: 0x01000000 pc: 0x08002678 msp: 0x2000a000

** Programming Started **

Warn : Adding extra erase range, 0x08003984 .. 0x08003fff

** Programming Finished **

** Verify Started **

** Verified OK **

** Resetting Target **

shutdown command invoked

================================================== [SUCCESS] Took 46.16 seconds ==================================================
 

If yes, then your board should blink the blue LED on your board.

You have now connected your device through a large process from Windows, WSL, Ubuntu, DevContainers and PlatformIO.

Well done!! cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.