Ra-11 / Ra-11H CMake Development Environment Setup Guide

This document explains how to set up a CMake cross-compilation environment for USP / RAC example projects for the Ai-Thinker Ra-11 / Ra-11H (RF chip: lr1121). It covers both Linux and Windows, and uses examples/main_examples/ping_pong_example as an example to illustrate the build and firmware outputs, and explains how to create a custom example project in this repository.


Obtain the Project Source Code (SDK)

Before development, please clone this project from GitHub (Ai-Thinker Ra-11 / Ra-11H / Ra-20 / Ra-20H USP / RAC SDK):

Method URL
HTTPS https://github.com/Ai-Thinker-AIoT/ra11-ra20.git
# Choose any directory, for example ~/project
mkdir -p ~/project && cd ~/project

# Or use HTTPS
git clone https://github.com/Ai-Thinker-AIoT/ra11-ra20.git

cd ra11-ra20

All subsequent cmake / ninja commands in this document are run from the repository root (the directory containing examples/ and smtc_rac_lib/).

Tip: If Git is not installed yet, install the dependencies described in Section 3 (Linux) or Section 4 (Windows) first, then return here to perform the clone operation.


1. Scope

1.1 Supported Microcontrollers (MCUs)

This project uses the CMake parameter -DBOARD= to select the MCU and the associated HAL / toolchain. Recommended for Ra-11 development:

-DBOARD= value MCU / Development Board HAL Directory Notes
STM32F103CB STM32F103CBT6 (commonly used by Ai-Thinker Ra-11 / Ra-11H) examples/smtc_hal_f1 Default recommendation for Ra-11
NUCLEO_L476 ST NUCLEO-L476RG examples/smtc_hal_l4 Frequently used for Semtech official validation (not the default for Ai-Thinker boards)
NUCLEO_L073 ST NUCLEO-L073RZ examples/smtc_hal_l0_LL Available, but less thoroughly validated

Note: You must reconfigure CMake after changing BOARD (clear build/ or use a new build directory). Otherwise, an old toolchain cache may be reused.

1.2 LoRa RF Chip (Driver)

The Ra-11 / Ra-11H use the LR11xx family. Select the following fixed radio option during compilation:

-DRAC_RADIO= Chip Family Typical Modules
lr1121 LR11xx Ai-Thinker Ra-11 / Ra-11H

The relevant drivers and abstraction layers are located in:

Some examples are enabled only for specific chips. (For example, cad_side_detector supports lr1121). After CMake configuration, the terminal prints Available examples; use that actual list as the source of truth.


2. Tool Overview (Common to Both Platforms)

Whether on Linux or Windows, the CMake cross-compilation flow is fundamentally the same:

Source code (C/ASM)
    → CMake configuration (select BOARD / RAC_RADIO / toolchain)
    → Ninja (or Make) compilation
    → arm-none-eabi-gcc links an ELF file
    → arm-none-eabi-objcopy generates .hex / .bin
    → (Optional) st-flash / STM32CubeProgrammer programs the target
Tool Purpose
ARM GNU Toolchain (arm-none-eabi-gcc, etc.) Cross-compiler, assembler, and linker for Cortex-M
arm-none-eabi-objcopy Converts ELF files to Intel HEX (.hex) or raw binary (.bin)
CMake (≥ 3.25 required by this project) Generates the build system (Ninja / Makefile) and manages dependencies, board options, and chip options
Ninja (recommended) or Make Runs the actual compilation commands. Ninja is faster and provides clearer logs
Git Retrieves and manages source code
stlink-tools (st-flash) or STM32CubeProgrammer Writes firmware to MCU Flash through ST-Link
Serial terminal (minicom / screen / PuTTY / serial assistant) Views logs and verifies examples such as ping-pong

Optional tools:

Tool Purpose
ccache Speeds up repeated builds
OpenOCD / gdb-multiarch On-target debugging (not required for compilation)
Python3 + pyserial Serial scripts and helper programming/logging tools

The official README broadly recommends gcc ≥ 13.3 (the distribution-provided 10.x version usually also works), CMake ≥ 3.28 (this project requires at least 3.25), and Ninja ≥ 1.11.


3. Linux Environment Setup

The following examples use Ubuntu / Debian / WSL2.

3.1 Install Dependencies

sudo apt update
sudo apt install -y \
  gcc-arm-none-eabi \
  binutils-arm-none-eabi \
  gdb-multiarch \
  cmake \
  ninja-build \
  git \
  stlink-tools \
  python3 python3-pip

# Optional
sudo apt install -y ccache
pip3 install --user pyserial

3.2 Package Descriptions

Package Purpose
gcc-arm-none-eabi Provides arm-none-eabi-gcc / g++ / as to compile STM32 firmware
binutils-arm-none-eabi Provides arm-none-eabi-objcopy, objdump, size, and similar tools to generate hex/bin files and inspect sections
cmake Reads examples/CMakeLists.txt, configures BOARD/RAC_RADIO, and generates build.ninja
ninja-build Builds targets such as ping_pong in parallel according to dependencies
stlink-tools Provides st-flash / st-info for programming .bin files through ST-Link
gdb-multiarch Multi-architecture GDB for debugging with OpenOCD/ST-Link (optional)
git Version control
python3 + pyserial Helper scripts such as serial monitoring (optional)

3.3 Verify the Installation

arm-none-eabi-gcc --version
arm-none-eabi-objcopy --version
cmake --version          # Recommended: ≥ 3.25
ninja --version
st-info --version        # When an ST-Link is connected

If the toolchain is not in the default PATH, set it as follows:

export PATH=/path/to/gcc-arm-none-eabi/bin:$PATH
# Assume the repository was cloned to ~/project/ra11-ra20 as described above:
cd ~/project/ra11-ra20

# Clear the old cache when configuring for the first time or changing BOARD / RAC_RADIO
rm -rf build

# Configure (Ra-11: STM32F103CB + lr1121)
cmake -S examples -B build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121

# Build the target
cmake --build build --target ping_pong

The output is placed in the build/ by default (see Section 5).


4. Windows Environment Setup

There are two common Windows paths: native Windows, or WSL2 (recommended; commands are identical to Linux).

  1. Install WSL2 and Ubuntu (from the Microsoft Store or using wsl --install).
  2. Within WSL, install the tools from Section 3: Linux.
  3. Store source code in the Linux file system (for example, ~/project/...) rather than /mnt/c/... to avoid slower builds.
  4. For ST-Link programming, either use st-flash in WSL (requires USB passthrough), or use STM32CubeProgrammer in Windows to program the .bin / .hex generated by WSL (accessible via the \\wsl$\... path).

The build commands are identical to Linux; use the CMake commands in Sections 3.4 and 5 directly.

4.2 Option 2: Native Windows

4.2.1 Required Software

Software Purpose Installation Notes
ARM GNU Toolchain (arm-none-eabi) Cross-compiles STM32 firmware Download the Windows version from ARM Developer; then add its bin directory to the system PATH
CMake Project configuration Select Add CMake to the system PATH during installation; version ≥ 3.25 is recommended
Ninja Build execution Download ninja.exe and place it on PATH, or use winget install Ninja-build.Ninja, Scoop, or Chocolatey
Git for Windows Retrieves source code Git Bash may be included
STM32CubeProgrammer or ST-Link Utility / stlink Programs firmware CubeProgrammer GUI / CLI is the most reliable option
Serial tool (PuTTY, MobaXterm, serial assistant, etc.) Views logs Choose the correct COM port and baud rate
(Optional) Python 3 Helper scripts Select Add to PATH, then run pip install pyserial

Optional IDEs:

4.2.2 Procedure (PowerShell or Git Bash)

  1. Confirm the tools are available on PATH
arm-none-eabi-gcc --version
cmake --version
ninja --version
  1. Enter the repository and configure it
# Assume the repository was cloned to D:\project\ra11-ra20 as described above:
cd D:\project\ra11-ra20

# Clear old cache on first configuration or after changing chip/board options
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue

cmake -S examples -B build `
  -G Ninja `
  -DCMAKE_BUILD_TYPE=MinSizeRel `
  -DBOARD=STM32F103CB `
  -DRAC_RADIO=lr1121
  1. Build the example
cmake --build build --target ping_pong
  1. Program the firmware (example: STM32CubeProgrammer CLI)
STM32_Programmer_CLI -c port=SWD -w build\ping_pong.bin 0x08000000 -v -rst

Alternatively, open build\ping_pong.hex / .bin in the CubeProgrammer GUI, select the ST-Link SWD interface, and use address 0x08000000.

Tip: If CMake cannot find the compiler, set the PATH first:
$env:Path = "C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\...\bin;" + $env:Path
This project generally configures arm-none-eabi-gcc automatically through its toolchain file.

4.2.3 Supplementary Notes for MSYS2 / MinGW

You can also install the build tools in MSYS2:

pacman -S mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja
# The official arm-none-eabi toolchain is still recommended; do not use the host GCC to build STM32 firmware

The cross-compiler must be arm-none-eabi-*. Do not use an x86_64 GCC directly to build firmware.


5. Build and Two-Device Transmit/Receive: ping_pong Example

Example source directory: examples/main_examples/ping_pong_example/
CMake target name: ping_pong

5.1 How the Two Devices Work Together

Ping-Pong requires two boards with different roles:

Device Role Behavior
Device A Manager (default) Sends PING after power-up and waits for PONG
Device B Subordinate Listens after power-up and replies with PONG after receiving PING

RF parameter settings must match on both sides (frequency, SF, BW, coding rate, sync word, etc.), see main_ping_pong.h.

Do not program both boards with the default Manager firmware. Otherwise, both devices send PING and wait for PONG, so they cannot communicate normally.

5.2 Build Commands (Pure CMake)

Step 1 — Build device A (Manager, default)

Ra-11 (lr1121) + STM32F103CB:

rm -rf build
cmake -S examples -B build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121

cmake --build build --target ping_pong

Outputs: build/ping_pong.bin and build/ping_pong.hex. Save or program these to device A first, for example:

cp build/ping_pong.bin build/ping_pong_manager.bin
cp build/ping_pong.hex build/ping_pong_manager.hex

Step 2 — Build Device B (Subordinate)

Enable -DPING_PONG_START_AS_SUBORDINATE=ON in the configuration and rebuild (CMake must be reconfigured):

rm -rf build
cmake -S examples -B build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121 \
  -DPING_PONG_START_AS_SUBORDINATE=ON

cmake --build build --target ping_pong

The outputs remain build/ping_pong.bin / .hex; program it to Device B.

Do not use -DBOARD=NUCLEO_L476 to test an Ai-Thinker Ra-11 board. NUCLEO_L476 targets the ST NUCLEO-L476RG. With the current pin configuration in this repository, building ping_pong for that board may fail because HW_MODEM_* macros are undefined.
For Ra-11, consistently use -DBOARD=STM32F103CB and -DRAC_RADIO=lr1121.

List targets available in the current configuration:

cmake --build build --target help

5.3 What Firmware Is Generated? What Is the Default Format?

After ping_pong builds successfully, build/ typically contains:

File Format Description
build/ping_pong (or ping_pong.elf, depending on the generator) ELF Direct linker output, containing debug symbols, used by GDB
build/ping_pong.bin Raw binary A contiguous image from the Flash base address; commonly used by st-flash
build/ping_pong.hex Intel HEX A text format with address records; commonly used by CubeProgrammer and some other programming tools

By default, this project generates both .hex and .bin during the POST_BUILD step in add_example(). It does not generate only one format.

The relevant logic (from examples/CMakeLists.txt):

# HEX
COMMAND ${CMAKE_OBJCOPY} -O ihex
        $<TARGET_FILE:${TARGET_NAME}>
        ${CMAKE_BINARY_DIR}/${TARGET_NAME}.hex

# BIN
COMMAND ${CMAKE_OBJCOPY} -O binary -S
        $<TARGET_FILE:${TARGET_NAME}>
        ${CMAKE_BINARY_DIR}/${TARGET_NAME}.bin

Therefore:

5.4 How to Ensure Both hex and bin Are Generated

  1. Use a standard project target (recommended)
    Targets registered using add_example (like ping_pong), already configure a POST_BUILD action that outputs both hex and bin by default.

  2. Run objcopy manually (for debugging or custom targets)

# Assume the ELF file is build/ping_pong
arm-none-eabi-objcopy -O ihex   build/ping_pong build/ping_pong.hex
arm-none-eabi-objcopy -O binary -S build/ping_pong build/ping_pong.bin
  1. If you see only an ELF and no hex/bin

    • Confirm that you built an add_example target such as ping_pong, rather than a temporary target without a POST_BUILD hook.
    • Confirm that arm-none-eabi-objcopy is on PATH (that is, available as CMake's CMAKE_OBJCOPY).
    • Check the build log for Creating ping_pong.hex... / Creating ping_pong.bin....

5.5 Programming and Running

# Linux: program the BIN at address 0x08000000
cmake --build build --target flash_ping_pong
# Or
st-flash --connect-under-reset write build/ping_pong.bin 0x8000000

Recommended procedure:

  1. Program the Manager firmware to Device A (without PING_PONG_START_AS_SUBORDINATE).
  2. Program the Subordinate firmware to Device B (with -DPING_PONG_START_AS_SUBORDINATE=ON).
  3. Power on Device B first, then Device A.
  4. Open a serial terminal and check the logs. You should see successful transmissions and receptions with incrementing counters.

Always clean and rebuild after changing BOARD, or reconfiguring parameters:

rm -rf build
cmake -S examples -B build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121
cmake --build build --target ping_pong

6. Create a New Project (Custom Example)

In this repository, a “project” is an example directory under examples/main_examples/, registered as an independently buildable target through add_example() in examples/CMakeLists.txt. The recommended approach is to copy and rename the closest existing example, then modify its application logic rather than building HAL and toolchain setup from scratch.

6.1 Directory and File Conventions

Assume the new example is named hello_lora (you can choose another name; this name is used below):

examples/
├── main.c                          # Common entry point; dispatches to examples using MAKEFILE_APP
├── main.h                          # Application IDs and main_xxx() declarations for examples
├── CMakeLists.txt                  # Registers targets using add_example()
└── main_examples/
    └── hello_lora_example/         # New directory
        ├── main_hello_lora.h
        ├── main_hello_lora.c       # Initializes HAL / RAC and contains the main loop
        ├── app_hello_lora.h
        └── app_hello_lora.c        # Radio application logic (TX/RX/CAD, etc.)

Reference templates:

Requirement Recommended Reference
LoRa TX/RX and state machine ping_pong_example
Continuous wave transmission / basic TX tx_cw_example

6.2 Procedure

Step 1 — Copy and rename

cd examples/main_examples
cp -r ping_pong_example hello_lora_example
cd hello_lora_example
# Rename files as needed, for example:
#   main_ping_pong.c  -> main_hello_lora.c
#   app_ping_pong.c   -> app_hello_lora.c
# Then globally replace function names, header names, and log prefixes.

Step 2 — Implement the entry function

Provide a public entry point in main_hello_lora.c. (Its name must match main.h and main.c.) A typical structure is:

void main_hello_lora( void )
{
    hal_mcu_init( );
    smtc_rac_init( );
    /* Initialize LEDs, buttons, and other peripherals (optional). */
    hello_lora_init( );   /* Implemented in app_hello_lora.c */

    while( true )
    {
        hal_watchdog_reload( );
        smtc_rac_run_engine( );   /* Must be called periodically to drive RAC / radio_planner. */
    }
}

On the application side, use APIs such as smtc_rac_open_radio(), smtc_rac_get_context(), and smtc_rac_submit_radio_transaction() to configure LoRa parameters and transmit or receive. (Follow the style in app_ping_pong.c.)

Step 3 — Register an application ID in examples/main.h

At the end of the existing #define list, add an unused numeric ID and declare the entry function:

#define HELLO_LORA 19   /* The value must be unique; do not conflict with existing applications */

void main_hello_lora( void );

Step 4 — Add a branch to examples/main.c

add_example() defines MAKEFILE_APP=HELLO_LORA for this target. The common main() uses it to call your entry point:

#elif MAKEFILE_APP == HELLO_LORA
    main_hello_lora( );

Place it in the existing #elif chain before the #else containing #error.

Step 5 — Register the target in examples/CMakeLists.txt

Add the following in the “Define all examples” section (adjust paths according to your directory structure):

add_example(hello_lora "HELLO_LORA"
    "${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example/main_hello_lora.c;${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example/app_hello_lora.c"
)
target_include_directories(hello_lora PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/main_examples/hello_lora_example
)

Notes:

Optional additions (for all_examples and log output):

# Add hello_lora to the ALL_EXAMPLES list
list(APPEND ALL_EXAMPLES hello_lora)

message(STATUS "  - hello_lora       : custom hello LoRa example")

If the example supports only Ra-11 (lr1121), wrap it in a conditional, for example:

if(${RAC_RADIO} MATCHES "^lr1121$")
  add_example(hello_lora "HELLO_LORA" "...")
  target_include_directories(hello_lora PRIVATE ...)
endif()

Step 6 — Reconfigure and build

After modifying CMakeLists.txt, main.h, or main.c, reconfigure CMake:

rm -rf build
cmake -S examples -B build \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121

cmake --build build --target hello_lora

Outputs:

Program:

cmake --build build --target flash_hello_lora
# Or
st-flash --connect-under-reset write build/hello_lora.bin 0x8000000

6.3 Checklist

Item Description
Unique APP ID in main.h Must not duplicate another #define
Declaration in main.h and branch in main.c Names must match HELLO_LORA / main_hello_lora
Second add_example argument Must correspond to the macro in main.h (like "HELLO_LORA")
target_include_directories Must make headers in this directory discoverable
smtc_rac_run_engine() in main loop Without it, transmit/receive callbacks do not run
Clean build/ after changing BOARD/RAC_RADIO Prevents cache-related errors

7. Common CMake Parameters

Parameter Meaning Example
-DBOARD= MCU board type STM32F103CB (recommended for Ra-11)
-DRAC_RADIO= LoRa chip lr1121
-DCMAKE_BUILD_TYPE= Optimization level MinSizeRel (recommended), Debug, Release
-G Ninja Generator Unix Makefiles can also be used
-DPING_PONG_START_AS_SUBORDINATE= ping_pong role OFF = Manager (default), ON = Subordinate

8. Frequently Asked Questions

Q: CMake reports “You have to define a -DBOARD=…”
A: Explicitly pass -DBOARD=STM32F103CB (or another supported board type).

Q: CMake reports “You must select the radio with -DRAC_RADIO=”
A: Ra-11 requires -DRAC_RADIO=lr1121.

Q: After changing configuration, the old driver is still linked / behavior is abnormal.
A: Delete build/ and reconfigure. CMakeCache retains old values for RAC_RADIO and BOARD.

Q: Why does configuration/building fail with -DBOARD=NUCLEO_L476?
A: Use -DBOARD=STM32F103CB for Ra-11. NUCLEO_L476 uses a different HAL. When HW_MODEM_* is commented out in the current modem_pinout.h, the L4 UART build may fail. This is a board-type and pin-configuration mismatch, not an error in the ping_pong command syntax.

Q: CMake reports that it cannot find axk_cmd_example/....
A: This optional example directory is not present in the source tree. CMakeLists.txt skips that target when its source is missing; run rm -rf build and configure again.

Q: Both boards continuously report unexpected payload in ping-pong.
A: Usually both boards were programmed with the default Manager firmware. Build one with the default configuration, then reconfigure and build the other with -DPING_PONG_START_AS_SUBORDINATE=ON. In logs, when prefix_received='80' is printed as decimal, it corresponds to ASCII 'P' (the first byte of PING/PONG).

Q: After creating a new project, why does linking fail with undefined reference to main_xxx?
A: Check that main.c contains a #elif MAKEFILE_APP == YOUR_APP branch calling main_xxx(), and that the APP string in add_example matches the macro in main.h.

Q: My new project builds successfully, but the radio does not respond.
A: Confirm that the main loop calls smtc_rac_run_engine(), and verify RAC_RADIO=lr1121, frequency/SF/BW settings, and antenna compatibility with the hardware.

Q: How do I build on Windows?
A: WSL2 is recommended because its commands are identical to Linux. Alternatively, install the native Windows toolchain as described in Section 4.2, then use cmake with ninja directly.


Document Contents
Root-level README_cn.md USP overview and official board/example list
Development Environment Setup Guide CMake development environment setup
examples/main_examples/ping_pong_example/readme.md Detailed Chinese Ping-Pong guide
examples/main_examples/ping_pong_example/FAQ.md Ping-Pong FAQ
doc/usp_porting_guide.md Porting guidance and PA / frequency-band notes

Project for this document: USP/RAC examples for Ai-Thinker Ra-11 / Ra-11H (-DBOARD=STM32F103CB -DRAC_RADIO=lr1121).