Plug the ST-Link into a USB port and verify the device is detected:

$ lsusb | grep STLINK
Bus 001 Device 070: ID 0483:3754 STMicroelectronics STLINK-V3

Install stlink-tools from source, in a directory next to your bandolibre checkout:

sudo apt remove stlink-tools
sudo apt install build-essential cmake libusb-1.0-0-dev
git clone --depth 1 --branch testing https://github.com/stlink-org/stlink
cd stlink
git apply ../bandolibre/docs/guide/0001-fix-st-trace-fix-SWO-trace-on-STLINK-V3-HS-bulk-endp.patch
make release && sudo make install && sudo ldconfig

Once the ST-Link is plugged into the board:

$ st-info --probe
Found 1 stlink programmers
  version:    V3J15
  serial:     002F00413235510637333439
  flash:      131072 (pagesize: 2048)
  sram:       131072
  chipid:     0x469
  dev-type:   STM32G47x_G48x

Install build prerequisites

The ARM toolchain and the build backend:

sudo apt install gcc-arm-none-eabi ninja-build

I use just to run commands from a configuration file such as code/main-g474/justfile:

curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to DEST

or

cargo install just

Checkout the source code

git clone --recurse-submodules https://github.com/bandolibre/bandolibre.git

Build the firmware

Generate the build system once, then build:

cd code/main-g474
just init_build_release build_release

Flash the firmware

Flashing wing firmware onto a main board — or the reverse — drives pins against the connected hardware and can damage the boards, so a chip must be registered before it can be flashed. code/tool/boards.csv maps each STM32 unique device ID to its board type, and is checked before every flash.

Register a board once, before its first flash:

cd code/main-g474
just registry_add

Then flash with:

cd code/main-g474
just flash_release

just flash_release programs the board on the connected ST-Link, and refuses if its chip is not registered as that board type. Connect one ST-Link at a time.

The same recipe apply in code/wing-g474.

Main board: bootloader and application

The main board carries a bootloader at 0x08000000 so it can be updated from the browser or by copying a file onto a USB drive — see firmware_update.md and code/boot-g474/README.md. Both builds of the application (Debug and Release, both -Os) link above it, at 0x08008000, in the 92 KB below the saved settings:

Recipe Builds Writes at Bootloader after
just flash Debug, -Os -g2 0x08008000 untouched
just flash_release bootloader + Release, -Os -flto 0x08000000, 0x08008000 installed
just dfu Release, packaged as .uf2 0x08008000 (no flashing; update over USB)
cd ../boot-g474 && just flash the bootloader only 0x08000000 installed

Install the bootloader once per board (just flash_release, or the bootloader-only recipe); just flash then never touches it, and a Debug board can still be updated from the browser.

just dfu is the one to use for a release: it builds the Release configuration and writes build/Release/main-v1_2.uf2 (named after the release’s major and minor version), bootloader and firmware together.

None of this affects code/wing-g474, which has a single memory map.

Read UART debug console

Characters written with printf (via _write retargeted to HAL_UART_Transmit) are sent over USART to the STLink VCP bridge, which forwards them to a host /dev/ttyACMx device.

Use tio to read it — it auto-reconnects across resets, unlike screen or cat:

tio /dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_*

The glob resolves to the correct /dev/ttyACMx regardless of what other USB serial devices are present. Or via the justfile recipe:

just console

Press ctrl-t q to quit.