Ra-11 / Ra-11H (LR1121) + STM32F103C8T6 Ping-Pong Application Guide

This document explains how to configure the examples/main_examples/ping_pong_example project in this repository for:

It covers the project structure, TX/RX method, master/slave configuration, build and flash steps, serial logs, and troubleshooting.


1. Quick Reference

1.1 Correct Build Parameters

BOARD      = STM32F103CB
RAC_RADIO  = lr1121

Note: the MCU is an STM32F103C8T6, but BOARD must be STM32F103CB, not STM32F103C8.

See Section 3: the only F103 board option in this repository is STM32F103CB. Its linker script
STM32F103XB_FLASH.ld is configured for 64 KB Flash / 20 KB RAM, which matches the C8T6.
The C8T6 and CBT6 are both LQFP48 with identical pinouts; only the Flash size differs.

1.2 Master / Slave at a Glance

Role CMake Option Startup Behavior
master -DPING_PONG_START_AS_SLAVE=OFF (default) Sends 252 bytes of 0x55 immediately after power-up
slave -DPING_PONG_START_AS_SLAVE=ON Receives only; replies with 252 bytes of 0x88 after a valid 0x55

The role is fixed at build time and cannot be changed at runtime. The two boards must be built and flashed separately.

1.3 Measured Build Results

Measured with arm-none-eabi-gcc 10.3.1 and -DCMAKE_BUILD_TYPE=MinSizeRel:

Role FLASH RAM Result
master 43396 B / 64 KB (66.22%) 7832 B / 20 KB (38.24%) Build OK
slave 43320 B / 64 KB (66.10%) 7832 B / 20 KB (38.24%) Build OK

Both roles fit within the STM32F103C8T6's 64 KB Flash / 20 KB RAM.


2. Hardware Preparation

2.1 Module and MCU

Item Description
Module Ra-11 / Ra-11H, RF chip LR1121
MCU STM32F103C8T6, LQFP48, 64 KB Flash / 20 KB RAM
Debug UART USART1, PA9 (TX) / PA10 (RX), 115200 8N1
Power 3.3 V for both MCU and RF, common ground
Antenna Both boards must have an antenna for the matching band

2.2 Wiring Method

This section follows the order: power and ground → RF SPI/control lines → debug UART → SWD programming port → antenna.
The module pin silkscreen may vary slightly between batches/models. Always follow the module silkscreen and datasheet;
the mapping below is functional.

2.2.1 Wiring Overview

MCU to the Ra-11 / Ra-11H module:

STM32F103C8T6                     Ra-11 / Ra-11H (LR1121)
PA4  ───────────────────────────  NSS / CS
PA5  ───────────────────────────  SCK
PA6  ───────────────────────────  MISO
PA7  ───────────────────────────  MOSI
PB14 ───────────────────────────  NRST / RESET
PB1  ───────────────────────────  DIO1
PA0  ───────────────────────────  BUSY
GND  ───────────────────────────  GND
3.3V ───────────────────────────  3V3
                                  ANT ── antenna for the matching band

Power decoupling (place close to the module 3V3 pin):

3.3V ──┬────────────── Module 3V3
       │
      ─┴─ 100 nF
      ─┬─
       │
      ─┴─ 10 µF
      ─┬─
       │
GND  ──┴────────────── Module GND

Debug UART and SWD programming port:

STM32F103C8T6          USB-TTL (3.3V)        ST-Link
PA9  (USART1_TX) ────► RX
PA10 (USART1_RX) ◄──── TX
GND ────────────────── GND ───────────────── GND
PA13 (SWDIO) ─────────────────────────────── SWDIO
PA14 (SWCLK) ─────────────────────────────── SWCLK
3.3V ─────────────────────────────────────── 3.3V (optional; only if probe power is needed)
NRST ─────────────────────────────────────── NRST (optional but recommended for connect-under-reset)

2.2.2 RF-Side Wiring Table

STM32F103C8T6 Module Function Pin Required? Notes
PA4 NSS / CS Required Software chip select, idle high
PA5 SCK Required SPI mode 0
PA6 MISO Required SPI MISO
PA7 MOSI Required SPI MOSI
PB14 NRST / RESET Required Active-low reset, hold for at least 100 µs
PB1 DIO1 Required Rising-edge interrupt
PA0 BUSY Required Busy indicator; must be low before a command
GND GND Required Common ground with the MCU
3.3V 3V3 Required 3.3 V only; 5 V is forbidden
— ANT Required Connect an antenna for the matching band
PA8 — Optional TX status LED, series resistor to GND
PB15 — Optional RX status LED, series resistor to GND
— DIO2 / DIO3 Do not connect Not used in this example; do not connect to the MCU

LR1121's antenna switch is configured internally through the chip's RF switch
(RFSW0–RFSW3 are configured in ral_lr11xx_bsp.c), so this F103 example does not need an extra
MCU antenna-switch pin.

If the module exposes DIO2 / DIO3, leave them floating; do not connect them to the MCU.
Only follow the module schematic if the module explicitly requires an external TCXO or an
external RF switch.

2.2.3 Power and Ground

2.2.4 Debug UART Wiring

USB-TTL STM32F103C8T6 Notes
TX PA10 (USART1_RX) Cross-connected
RX PA9 (USART1_TX) Cross-connected
GND GND Common ground required
VCC Do not connect Do not connect when the board is powered separately

2.2.5 SWD Programming Wiring

ST-Link STM32F103C8T6 Notes
SWDIO PA13 Required
SWCLK PA14 Required
GND GND Required
3.3V 3.3V Optional; do not connect if the board is independently powered
NRST NRST Optional but recommended

2.2.6 Status LEDs and Antenna

  1. Power off; do not hot-plug.
  2. Connect GND first, then 3.3 V (to avoid ground-potential differences damaging the I/O).
  3. Connect SPI: PA4/PA5/PA6/PA7 → NSS/SCK/MISO/MOSI.
  4. Connect control lines: PB14 → NRST, PB1 → DIO1, PA0 → BUSY.
  5. Connect the debug UART: PA9/PA10 → USB-TTL RX/TX, common GND.
  6. Connect SWD: PA13/PA14 → ST-Link SWDIO/SWCLK, common GND.
  7. Connect the antenna.
  8. Before power-up, check with a multimeter:
    • 3.3 V and GND are not shorted;
    • NSS is high when idle;
    • NRST is high when idle;
    • DIO1 is low when idle.
  9. Power up; flash the slave first, then the master.

2.2.8 Two-Board Wiring

        ┌──────────────┐                  ┌──────────────┐
        │  Board A     │   LoRa air link  │  Board B     │
        │  master      │ ◄══════════════► │  slave       │
        │  Ra-11/11H   │                  │  Ra-11/11H   │
        │  C8T6        │                  │  C8T6        │
        └──────┬───────┘                  └──────┬───────┘
               │ USB-TTL                         │ USB-TTL
               ▼                                 ▼
              PC A                              PC B

2.2.9 Common Wiring Mistakes

Symptom Check First
No serial output at all PA9/PA10 swapped, no common ground, baud rate not 115200
RF init failure / chip not detected SPI wiring, NRST/BUSY connections, 3.3 V rail
Only TXDONE, no RXDONE DIO1 not connected to PB1, slave not powered, antenna not connected
Packet loss even at short range Antenna band mismatch, antenna not connected, noisy power supply
Cannot flash SWDIO/SWCLK swapped, NRST not connected, BOOT0 not grounded
Module overheating / abnormal current 5 V applied to 3V3, reversed power, short circuit
Garbled serial output Baud rate, crystal/clock configuration, USB-TTL level not 3.3 V

2.3 Frequency and Regulatory Notes

Defaults in main_ping_pong.h:

#define RF_FREQ_IN_HZ        470100000   /* 470.1 MHz */
#define TX_OUTPUT_POWER_DBM  14          /* 14 dBm */

To change them, edit examples/main_examples/ping_pong_example/main_ping_pong.h, then rebuild both boards.

2.4 Power and RF Notes


3. Why BOARD Is STM32F103CB

This is the most common pitfall in this document.

3.1 CMake Accepts Only Three Board Types

At the top of examples/CMakeLists.txt:

if(BOARD STREQUAL NUCLEO_L073)
    ...
elseif(BOARD STREQUAL NUCLEO_L476)
    ...
elseif(BOARD STREQUAL STM32F103CB)
    ...
else()
    message(FATAL_ERROR "You have to define a -DBOARD=NUCLEO_L476 or NUCLEO_L073 or STM32F103CB")
endif()

Therefore:

-DBOARD=STM32F103C8     # Wrong; CMake fails with FATAL_ERROR
-DBOARD=STM32F103CB     # Correct; F103 board option

3.2 The Linker Script Is the C8T6 64 KB / 20 KB One

STM32F103CB uses examples/smtc_hal_f1/cmake_stm32f1_toolchain.cmake, which contains:

set(BOARD_LDSCRIPT ${MCU_DRIVERS_DIR}/core/STM32F1xx/STM32F103XB_FLASH.ld)
...
-DSTM32F103xB -DUSE_HAL_DRIVER

The header of STM32F103XB_FLASH.ld states:

64KByte FLASH (STM32F103C8), 20KByte RAM

So the "CB" board option actually links against the C8T6's 64 KB / 20 KB resources, not the 128 KB CBT6.

3.3 C8T6 and CBT6 Have Identical Pinouts

Both are LQFP48 packages with identical SPI1, USART1, and GPIO pins; only the Flash size differs.
This project uses about 43 KB Flash and 7.8 KB RAM, so the C8T6 runs it comfortably.


4. Project Structure and Implementation

4.1 Directories and Files

examples/main_examples/ping_pong_example/
├── main_ping_pong.c       # Entry point: MCU/RAC init, main loop
├── main_ping_pong.h       # RF parameter macros (frequency, power, SF, BW, CR, etc.)
├── app_ping_pong.c        # Ping-Pong state machine, TX/RX scheduling, logs
├── app_ping_pong.h
├── app_periodic_uplink.c  # Periodic uplink (disabled by default for ping_pong)
├── app_periodic_uplink.h
└── readme.md              # Original example documentation

Related low-level modules:

Module Purpose
smtc_rac_lib/smtc_rac/ RAC (Radio Access Controller): transaction submission, LoRa parameters, callbacks
smtc_rac_lib/radio_planner/ Radio Planner: TX/RX timing scheduling, timeouts, failure protection
smtc_rac_lib/radio_drivers/lr11xx_driver/ LR1121 register-level driver
examples/radio_hal/ral_lr11xx_bsp.c LR11xx BSP: RF switch, power configuration, PA selection
examples/smtc_hal_f1/ STM32F103 HAL: SPI, UART, GPIO, RTC, watchdog

4.2 Entry Point main_ping_pong.c

void main_ping_pong( void )
{
    hal_mcu_init( );        // Clock, UART, SPI, GPIO, RTC, watchdog
    smtc_rac_init( );       // Initialize RAC and radio_planner

    hal_gpio_init_out( SMTC_LED_TX, 0 );
    hal_gpio_init_out( SMTC_LED_RX, 0 );

    ping_pong_init( );      // Open radio, fill transaction context, start master/slave flow
    periodic_uplink_init( );// ENABLE_PERIODIC_UPLINK is not defined for ping_pong, so it is not enabled

    while( true )
    {
        hal_watchdog_reload( );

        /* DIO1 missed-edge fallback: if the line is still high, invoke one more IRQ callback */
        if( ( smtc_rac_is_irq_flag_pending( ) == false ) &&
            ( rp_radio_is_free( smtc_rac_get_rp( ) ) == false ) &&
            ( hal_gpio_get_value( RADIO_DIOX ) != 0 ) )
        {
            rp_radio_irq_callback( smtc_rac_get_rp( ) );
        }

        smtc_rac_run_engine( );   // Must be called continuously to drive the RAC/planner state machine
    }
}

Key points:

  1. The main loop must not block for long periods. Call smtc_rac_run_engine() as often as possible; otherwise TX/RX scheduling is delayed and the watchdog may fire.
  2. DIO1 uses both an interrupt and a level fallback. Even if a rising edge is missed, the main loop invokes one more callback based on the RADIO_DIOX level.
  3. periodic_uplink_init() is called, but ENABLE_PERIODIC_UPLINK is not defined, so the periodic uplink is disabled and does not compete with the ping-pong transaction for the radio.

4.3 RAC Transaction Model

ping_pong_init() in app_ping_pong.c does the following:

ping_pong.radio_access_id = SMTC_SW_PLATFORM( smtc_rac_open_radio( RAC_HIGH_PRIORITY ) );
ping_pong.transaction     = smtc_rac_get_context( ping_pong.radio_access_id );

Then it fills the transaction context:

Each transaction is submitted through smtc_rac_submit_radio_transaction(). RAC and the radio planner are responsible for:

Scheduling modes:

Mode Meaning
SMTC_RAC_ASAP_TRANSACTION Starts at the scheduled time if possible; if missed, starts as soon as possible
SMTC_RAC_SCHEDULED_TRANSACTION Must start at the scheduled time; aborts if missed

4.4 Ping-Pong State Machine

Master

Power-up
  └─ ping_pong_start_as_master()
        └─ TX 252 × 0x55 (delay=0ms)
              └─ TXDONE
                    └─ RX (enters RX 10 ms later, 255-symbol preamble timeout)
                          ├─ RXDONE: validate 252 × 0x88
                          │     └─ counter++
                          │           ├─ counter < 25: TX 252 × 0x55 again after 250ms
                          │           └─ counter >= 25: restart, start from TX again
                          ├─ RX_TIMEOUT / invalid payload
                          │     └─ retry, up to 5 times; then restart (still master)
                          ├─ RX_CRC_ERROR / TASK_ABORTED
                          │     └─ resubmit the previous transaction (no 5-time limit in the code)
                          └─ other errors: panic (should not happen normally)

Slave

Power-up
  └─ restart_ping_pong()
        └─ RX (continuous RX + 255-symbol preamble timeout)
              ├─ RXDONE: validate 252 × 0x55
              │     └─ wait 100ms (SLAVE_PROCESSING_TIME_MS)
              │           └─ TX 252 × 0x88
              │                 └─ TXDONE
              │                       └─ continue RX
              ├─ RX_TIMEOUT
              │     └─ continue RX (slave does not count retries)
              └─ invalid payload
                    └─ continue RX, no reply

4.5 Key Parameters

RF Parameters (main_ping_pong.h)

Macro Default Description
RF_FREQ_IN_HZ 470100000 470.1 MHz
TX_OUTPUT_POWER_DBM 14 14 dBm
LORA_SPREADING_FACTOR RAL_LORA_SF9 SF9
LORA_BANDWIDTH RAL_LORA_BW_125_KHZ 125 kHz
LORA_CODING_RATE RAL_LORA_CR_4_5 4/5
LORA_PREAMBLE_LENGTH 12 12 symbols
LORA_PKT_LEN_MODE RAL_LORA_PKT_EXPLICIT Explicit header
LORA_IQ false Standard IQ
LORA_CRC true CRC enabled

The sync word is fixed in app_ping_pong.c:

static const smtc_rac_lora_syncword_t SYNC_WORD = LORA_PRIVATE_NETWORK_SYNCWORD;

That is the private-network sync word 0x12 (register value 0x1424).

Application Parameters (app_ping_pong.c)

Parameter Default Description
PAYLOAD_SIZE 252 Bytes per packet
MASTER_TX_PATTERN 0x55 Master payload
SLAVE_TX_PATTERN 0x88 Slave reply payload
DELAY 250 ms Interval between master rounds
RX_PROCESSING_TIME 10 ms Delay before the master enters RX after TXDONE
SLAVE_PROCESSING_TIME_MS 100 ms Delay before the slave replies after reception
RX_SYMBOL_TIMEOUT 255 symbols Preamble search timeout (about 1 s at SF9/125k)
MAX_RETRY_COUNT 5 Maximum consecutive failures for the master
MAX_EXCHANGE_COUNT 25 Number of rounds before the master restarts

SLAVE_PROCESSING_TIME_MS must be smaller than the preamble timeout (255 symbols, about 1 s at
SF9/125k); otherwise the master may time out before the slave replies. The default 100 ms is safe.

4.6 Timing Estimate

For 252 bytes, SF9, BW125, CR4/5, preamble 12, CRC enabled:

Therefore a TXDONE / RXDONE pair appears about every 2–3 seconds. This is normal, not a hang.


5. Build

5.1 Environment Requirements

arm-none-eabi-gcc --version    # 10.x or 12.x recommended
cmake --version                # requires >= 3.25
ninja --version
st-flash --version             # or st-info --version

Install (Ubuntu / Debian / WSL2):

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

Master:

cd /path/to/Ra-20_Ra-11_aithinker

BUILD_DIR=build-lr1121-master \
  ./build.sh lr1121 ping_pong

Slave:

BUILD_DIR=build-lr1121-slave \
PING_PONG_START_AS_SLAVE=ON \
  ./build.sh lr1121 ping_pong

Optional: adjust the slave reply delay (in ms):

BUILD_DIR=build-lr1121-slave \
PING_PONG_START_AS_SLAVE=ON \
SLAVE_PROCESSING_TIME_MS=100 \
  ./build.sh lr1121 ping_pong

Generated files:

build-lr1121-master/ping_pong.bin
build-lr1121-master/ping_pong.hex
build-lr1121-slave/ping_pong.bin
build-lr1121-slave/ping_pong.hex

build.sh defaults BOARD to STM32F103CB, so the commands above do not need an explicit
BOARD=STM32F103CB. If another BOARD has been exported in your environment, run unset BOARD
first or prefix the command with BOARD=STM32F103CB.

If you see the following during the build:

CMake Warning:
  Manually-specified variables were not used by the project:
    LEGACY_EVK_LR20XX

This is normal. LEGACY_EVK_LR20XX only applies to LR20xx (LR2012/LR2021/LR2022); LR1121 does not
use this option, so the warning can be ignored.

5.3 Manual CMake

Master:

rm -rf build-lr1121-master

cmake -S examples -B build-lr1121-master -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121 \
  -DPING_PONG_START_AS_SLAVE=OFF

cmake --build build-lr1121-master --target ping_pong

Slave:

rm -rf build-lr1121-slave

cmake -S examples -B build-lr1121-slave -G Ninja \
  -DCMAKE_BUILD_TYPE=MinSizeRel \
  -DBOARD=STM32F103CB \
  -DRAC_RADIO=lr1121 \
  -DPING_PONG_START_AS_SLAVE=ON \
  -DSLAVE_PROCESSING_TIME_MS=100

cmake --build build-lr1121-slave --target ping_pong

5.4 Build Options

CMake Variable Recommended Purpose
BOARD STM32F103CB F103/C8T6 board; do not use STM32F103C8
RAC_RADIO lr1121 Select the LR11xx driver family and define LR1121
CMAKE_BUILD_TYPE MinSizeRel Size optimization, suitable for 64 KB Flash
PING_PONG_START_AS_SLAVE OFF / ON Master / slave
SLAVE_PROCESSING_TIME_MS 100 Delay before the slave replies
STLINK_SERIAL Optional Probe serial number when multiple ST-Links are connected
RAC_LOG_PROFILE DEFAULT RAC internal log level (default is fine)

After changing BOARD, RAC_RADIO, the role, or SLAVE_PROCESSING_TIME_MS, you must reconfigure.
Do not reuse the previous build directory, or the CMake cache may still hold the old values.


6. Master / Slave Configuration Steps

6.1 Master

Build:

BUILD_DIR=build-lr1121-master ./build.sh lr1121 ping_pong

Flash:

st-flash --connect-under-reset write build-lr1121-master/ping_pong.bin 0x08000000

Or use the CMake target:

cmake --build build-lr1121-master --target flash_ping_pong

The first serial lines after power-up should be:

INFO: work mode: master
INFO: starting ping-pong as master

Behavior:

6.2 Slave

Build:

BUILD_DIR=build-lr1121-slave \
PING_PONG_START_AS_SLAVE=ON \
  ./build.sh lr1121 ping_pong

Flash:

st-flash --connect-under-reset write build-lr1121-slave/ping_pong.bin 0x08000000

Or:

cmake --build build-lr1121-slave --target flash_ping_pong

The first serial lines after power-up should be:

INFO: work mode: slave
INFO: this device is still slave
INFO: receiving...

Behavior:

6.3 Power-Up Order

Recommended:

  1. Power the slave first and confirm it has entered receiving...;
  2. Then power the master.

If the master is powered first, it sends a few packets, times out a few times, and then restarts. Once the
slave is powered, communication recovers, but the log will first show some retrying / timeout lines.
This is normal.

6.4 Items That Must Match on Both Boards

The following parameters must be identical on both boards; otherwise they cannot communicate:

Only the role (PING_PONG_START_AS_SLAVE) is allowed to differ.


7. Flashing and Serial Monitoring

7.1 Flashing

st-flash --connect-under-reset write build-lr1121-master/ping_pong.bin 0x08000000
st-flash --connect-under-reset write build-lr1121-slave/ping_pong.bin  0x08000000

When multiple ST-Links are connected, select the probe with --serial <SN>:

st-flash --serial <ST-LINK-SN> --connect-under-reset write build-lr1121-master/ping_pong.bin 0x08000000

7.2 Serial Monitoring

Use any serial terminal with 115200 8N1:

picocom -b 115200 /dev/ttyUSB0
# or
minicom -D /dev/ttyUSB0 -b 115200

On Windows, use any serial assistant, select the corresponding COM port, and set 115200 8N1.

Firmware logs include ANSI color control codes (ESC[0;32m, etc.) by default. Most terminals render
them as green INFO: text. If you see garbled prefixes, use a terminal that supports ANSI, or set
HAL_DBG_TRACE_COLOR to HAL_FEATURE_OFF in smtc_hal_dbg_trace.h and rebuild.


8. Successful Communication Logs

The RSSI/SNR values below are examples; actual values vary with distance, antenna, and environment.
As long as size=252 and the packet number keeps increasing, master/slave communication is working.

8.1 Master Log

INFO: compile time: Oct 29 2026 15:00:00
INFO: work mode: master
INFO: starting ping-pong as master
INFO: sending 252 bytes of 0x55 (delay=0ms)
INFO: TXDONE: packet #1 sent (252 bytes of 0x55)
INFO: receiving...
INFO: RXDONE: packet #1 received, size=252, RSSI=-42 dBm, SNR=9 dB
INFO: sending 252 bytes of 0x55 (delay=250ms)
INFO: TXDONE: packet #2 sent (252 bytes of 0x55)
INFO: receiving...
INFO: RXDONE: packet #2 received, size=252, RSSI=-41 dBm, SNR=9 dB
INFO: sending 252 bytes of 0x55 (delay=250ms)
INFO: TXDONE: packet #3 sent (252 bytes of 0x55)
INFO: receiving...
INFO: RXDONE: packet #3 received, size=252, RSSI=-43 dBm, SNR=8 dB
...

8.2 Slave Log

INFO: compile time: Oct 29 2026 15:00:00
INFO: work mode: slave
INFO: this device is still slave
INFO: receiving...
INFO: RXDONE: packet #1 received, size=252, RSSI=-40 dBm, SNR=8 dB
INFO: sending 252 bytes of 0x88 (delay=100ms)
INFO: TXDONE: packet #1 sent (252 bytes of 0x88)
INFO: receiving...
INFO: RXDONE: packet #2 received, size=252, RSSI=-40 dBm, SNR=8 dB
INFO: sending 252 bytes of 0x88 (delay=100ms)
INFO: TXDONE: packet #2 sent (252 bytes of 0x88)
INFO: receiving...
INFO: RXDONE: packet #3 received, size=252, RSSI=-41 dBm, SNR=8 dB
INFO: sending 252 bytes of 0x88 (delay=100ms)
INFO: TXDONE: packet #3 sent (252 bytes of 0x88)
...

8.3 Master/Slave Log Correlation

Time Master Log Slave Log Description
T0 sending ... 0x55 receiving... Master starts sending Ping
T0+1.3s TXDONE: packet #N sent RXDONE: packet #N received Slave receives 0x55
T0+1.4s receiving... sending ... 0x88 Slave replies after 100ms delay
T0+2.7s RXDONE: packet #N received TXDONE: packet #N sent Master receives 0x88
T0+2.95s sending ... (delay=250ms) receiving... Master sends the next packet after 250ms

8.4 Success Criteria

Master/slave communication is successful when all of the following are true:

  1. The first lines on the two boards are work mode: master and work mode: slave;
  2. The master continuously shows TXDONE followed by RXDONE, and the slave shows RXDONE followed by TXDONE;
  3. size=252, with contents 0x55 (master TX / slave RX) and 0x88 (slave TX / master RX);
  4. Packet numbers keep increasing;
  5. ERROR: unexpected payload does not appear repeatedly;
  6. retrying / max retry count reached do not appear repeatedly;
  7. No watchdog reset or HardFault;
  8. PA8 (TX LED) and PB15 (RX LED) alternate with TX/RX.

8.5 Non-Error Logs Seen During Normal Operation

Log Meaning Normal?
max exchange count reached, restarting Master completed 25 rounds and restarts automatically Normal
this device is still master Confirms the role is still master during restart Normal
starting ping-pong as master Starts sending again after restart Normal
usp/rac: new event: transaction (reception) has ended (timeout) No preamble received within the RX window Normal for slave / occasional for master
device is slave, continuing Slave continues listening after a timeout Normal
retrying (fails=1, max=5) Master missed a packet and is retrying Occasionally normal; investigate if continuous
max retry count reached, restarting Master restarts after 5 consecutive failures Occasionally normal; investigate if frequent

Note: restart only resets counter; it does not reset tx_packet_count / rx_packet_count.
Therefore packet # in the log keeps increasing and does not return to 1.


9. Troubleshooting

9.1 No Serial Output at All

9.2 Only compile time / work mode, No TX/RX Logs Afterwards

9.3 Master Shows Only TXDONE, No RXDONE

9.4 Both Boards Stay in receiving...

Both boards were built as slave. Check work mode: on both sides and rebuild at least one as master
(PING_PONG_START_AS_SLAVE=OFF).

9.5 Both Boards Transmit but Cannot Hear Each Other

Both boards were built as master. Two masters compete for the channel, producing many TXDONE and
timeout logs. Rebuild one board as slave.

9.6 ERROR: unexpected payload

A 252-byte packet was received but the contents are wrong. Common causes:

9.7 Repeated retrying / max retry count reached

If the log shows:

INFO: usp/rac: new event: ERROR: RX_CRC_ERROR

the radio received an over-the-air packet but CRC validation failed. Check whether SF/BW/CR/sync word
match on both sides, and investigate co-channel interference, antenna matching, and power-supply noise.
The code resubmits the previous RX transaction.

9.8 Watchdog Reset / HardFault

9.9 Build OK but Does Not Run After Flashing

9.10 Characters Such as [0;32m in the Terminal

These are log color control codes, not garbled text. Use a serial terminal that supports ANSI, or disable
HAL_DBG_TRACE_COLOR in smtc_hal_dbg_trace.h and rebuild.


10. Quick Checklist


11. References


Document version: 2026-10-29
Applies to: examples/main_examples/ping_pong_example
Target configuration: BOARD=STM32F103CB + RAC_RADIO=lr1121