RAK3172 + ChirpStack FUOTA: Class-C session confirmed active, but zero radio IRQ activity (no RxDone / RxTimeout / RxError) multicast fragments
RAK3172 + ChirpStack FUOTA: Class-C session confirmed active, but zero radio IRQ activity (no RxDone / RxTimeout / RxError) — multicast fragments never received on one of two identical devices
Summary
I have two identical devices (same firmware, same hardware BOM) running FUOTA over LoRaWAN using a RAK3172 module (STM32WLE5-based, AT-command slave) driven by an STM32H7 host MCU. One device completes FUOTA perfectly every time. The second device — same code, same ChirpStack FUOTA deployment, same gateway — gets all the way to a confirmed Class-C switch and then receives nothing. No fragments, no radio errors, nothing.
I’ve isolated this down to the radio IRQ level and would like input from anyone who has hit something similar with RAK3172 / STM32WL Class-C behavior.
Setup
- End device MCU: STM32H7 (host), communicating with RAK3172 over UART using AT commands
- LoRa modem: RAK3172 (STM32WLE5 based), running STM32CubeWL / LoRaMac-node stack
- LNS: ChirpStack v4.17.0 (self-hosted)
- FUOTA: ChirpStack native FUOTA deployment, Class-C multicast group
- Multicast DR: DR5 (SF7BW125)
- Multicast frequency: 868.1 MHz
- Fragmentation redundancy: 0% (also tested at higher values, no change)
- Fragment size: 128 bytes, ~3501 total fragments (~448 KB firmware image)
- Region: EU868
What works
- Both devices join fine (OTAA), send confirmed uplinks normally, RSSI/SNR on unicast is excellent on the failing device too (RSSI -43 dBm, SNR +9, CRC_OK — this is not a weak-link problem)
- FUOTA deployment on ChirpStack completes all server-side steps successfully: multicast group creation, add devices, add gateways, multicast group setup, fragmentation session setup, multicast session setup, enqueue fragments — all “ok”
- Device receives and ACKs the setup commands over unicast:
FragSessionSetupAns(port 201) received and answeredMcClassCSessionAns(port 200) received and answered
- Gateway “LoRaWAN frames” log confirms downlinks are actually being transmitted, on the exact configured frequency/DR (868100000 Hz, SF7, BW125000, CR_4_5), repeating every ~5s, scheduled
immediately— so the server and gateway side is confirmed correct - On the failing device,
LmHandlerRequestClass(CLASS_C)returns success, and theOnClassChangecallback fires, confirming the LoRaMac MIB (MIB_DEVICE_CLASS) is actually set toCLASS_C
What doesn’t work
After the Class-C switch is confirmed (MIB set to CLASS_C, callback fired), the device never receives a single multicast fragment. On the working device, +EVT:RX_C_MC events stream in continuously. On the failing device: nothing.
Debugging done so far
- Ruled out application-layer race conditions — added return-value checking around
LmHandlerRequestClass(), confirmed noBUSY_ERROR/NO_NETWORK_JOINEDon this run. - Ruled out debugger interference — initially saw
HDR KO(LoRa header CRC failure) printed from the radio driver, but this only appeared while a breakpoint was active during debugging. On a clean run (no breakpoints),HDR KOdoes not appear at all — and neither does anything else. - Ruled out RF link quality — unicast RSSI/SNR on this exact device is excellent (see above).
- Ruled out gateway/server misconfiguration — confirmed via gateway’s live LoRaWAN frames log that downlinks are transmitted on the correct frequency/DR/timing.
- Added instrumentation directly in the LoRaMac-node IRQ dispatcher:
static void LoRaMacHandleIrqEvents( void ) { LoRaMacRadioEvents_t events; CRITICAL_SECTION_BEGIN( ); events = LoRaMacRadioEvents; LoRaMacRadioEvents.Value = 0; CRITICAL_SECTION_END( ); if( events.Value != 0 ) { printf("IRQ: Tx=%d Rx=%d TxTO=%d RxErr=%d RxTO=%d, class=%d\r\n", events.Events.TxDone, events.Events.RxDone, events.Events.TxTimeout, events.Events.RxError, events.Events.RxTimeout, Nvm.MacGroup2.DeviceClass); ... } }
Result on the failing device, after the confirmed Class-C switch: this print never fires at all — not RxDone, not RxTimeout, not RxError. Nothing. This is during the exact window where the gateway log confirms it is transmitting fragments every ~5 seconds on 868.1 MHz.
** Non-working device log (for comparison — same firmware, same gateway, same FUOTA deployment)**
Test run 1:
[18:06:11:030] OK␍␊
[18:06:11:457] AT+NJS=1␍␊
[18:06:11:475] OK␍␊
[18:06:11:940] OK␍␊
[18:06:13:105] +EVT:SEND_CONFIRMED␍␊
[18:07:55:680] AT+NJS=1␍␊
[18:07:55:691] OK␍␊
[18:07:56:137] OK␍␊
[18:08:01:278] +EVT:SEND_CONFIRMED␍␊
[18:08:27:492] +Total Fragments:3501␍␊
[18:09:55:670] AT+NJS=1␍␊
[18:09:55:685] OK␍␊
[18:09:56:128] OK␍␊
[18:10:02:014] +EVT:SEND_CONFIRMED␍␊
[18:10:28:900] +Total Fragments:3501␍␊
[18:12:29:824] 1785847368s066:HDR KO␍␊
[18:12:34:552] 1785847372s795:HDR KO␍␊
[18:15:30:711] 1785847548s965:IRQ_CRC_ERROR␍␊
Test run 2 (fresh join):
[13:21:46:446] F06C53D75FB6850EEF0E10B9BD1C4822␍␊
[13:21:46:489] OK␍␊
[13:21:46:672] F06C53D75FB6850EEF0E10B9BD1C4822␍␊
[13:21:46:711] OK␍␊
[13:21:46:963] OK␍␊
[13:21:47:118] F06C53D75FB6850EEF0E10B9BD1C4822␍␊
[13:21:47:160] OK␍␊
[13:21:47:338] OK␍␊
[13:21:47:501] OK␍␊
[13:21:47:849] OK␍␊
[13:21:53:056] +EVT:JOINED␍␊
[13:21:53:243] OK␍␊
[13:21:53:424] AT+NJS=1␍␊
[13:21:53:437] OK␍␊
[13:21:53:950] 324C950B45F76F59350EDD1DD3BBCF66␍␊
[13:21:53:988] OK␍␊
[13:21:54:054] C2F6311FDF45AAB0419818608B0272E9␍␊
[13:21:54:093] OK␍␊
[13:21:54:114] AT+NJS=1␍␊
[13:21:54:127] OK␍␊
[13:21:54:337] OK␍␊
[13:21:55:501] +EVT:SEND_CONFIRMED␍␊
[13:21:55:526] +EVT:SEND_CONFIRMED␍␊
[13:23:38:774] AT+NJS=1␍␊
[13:23:38:788] OK␍␊
[13:23:39:231] OK␍␊
[13:23:40:379] +EVT:SEND_CONFIRMED␍␊
[13:25:38:776] AT+NJS=1␍␊
[13:25:38:793] OK␍␊
[13:25:39:234] OK␍␊
[13:25:40:376] +EVT:SEND_CONFIRMED␍␊
[13:25:47:769] +Total Fragments:3501␍␊
[13:27:38:779] AT+NJS=1␍␊
[13:27:38:798] OK␍␊
[13:27:39:236] OK␍␊
[13:27:40:379] +EVT:SEND_CONFIRMED␍␊
[13:27:47:146] +Total Fragments:3501␍␊
This tells me the radio is never being armed into RX for the Class-C continuous-receive window on this specific unit — even though the MAC-layer device class state itself is correctly set to CLASS_C.
Working device log (for comparison — same firmware, same gateway, same FUOTA deployment)
This is the host-side (STM32H7) log on the device where FUOTA completes successfully. Note that fragments are actually being received, indexed, and their payload dumped — this is the behavior we never see on the failing unit:
[12:37:02:509] --------------------------------------
[12:37:02:554] Received Data from IPC:
[12:37:02:583] +Total Fragments:3501
[12:37:02:669] Total Fragments Count Received is: 3501
[12:37:49:909] ----------- DEVICE_SEND STATE-------
[12:37:50:095] AT+NJS=?
[12:37:50:208] AT+NJS=1
[12:37:50:219] OK
[12:37:50:271] Join Status : 1
[12:37:50:392] AT+SEND=99:1:24080200679bc9a6
[12:37:50:535] OK
[12:37:50:588] Data binary send on port = 99 → OK
[12:37:51:138] ----------- LORA_DEVICE_SEND_CONFIRM STATE-------
[12:37:51:655] +EVT:SEND_CONFIRMED
[12:37:51:725] Msg confirm successfully
[12:38:03:751] Received Data from IPC:
[12:38:03:775] +Total Fragments:3501
[12:38:03:858] Total Fragments Count Received is: 3501
[12:40:33:841] Current Index: 0
[12:40:33:855] Current Frame: 0
[12:40:33:884] Payload:
08 01 00 00 00 08 24 85 06 04 08 7D 01 04 08 85 01 04 08 B5 02 04 08 BD 02 04 08 C5 02 04 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B1 91 05 08 CD 02 04 08 00 00 00 00 01 94 05 08 69 94 05 08 D5 06 04 08 …
[12:40:41:860] Current Index: 1
[12:40:41:872] Current Frame: 1
[12:40:41:904] Payload:
08 02 00 D5 06 04 08 D5 06 04 08 D5 06 04 08 …
[12:40:42:862] Current Index: 2
[12:40:42:878] Current Frame: 2
[12:40:42:897] Payload:
08 03 00 D5 06 04 08 D5 06 04 08 D5 06 04 08 …
[12:40:47:883] Current Index: 3
[12:40:47:894] Current Frame: 3
[12:40:47:925] Payload:
08 04 00 D5 06 04 08 D5 06 04 08 41 03 04 08 …
[12:40:52:898] Current Index: 4
[12:40:52:911] Current Frame: 4
[12:40:52:942] Payload:
08 05 00 D5 06 04 08 D5 06 04 08 D5 06 04 08 …
[12:40:57:914] Current Index: 5
[12:40:57:928] Current Frame: 5
[12:40:57:962] Payload:
08 06 00 D5 06 04 08 D5 06 04 08 D5 06 04 08 …
[12:41:02:928] Current Index: 6
…(fragments continue incrementing steadily, roughly every 5s, until all 3501 are received)
Questions
- Has anyone seen a case where
Nvm.MacGroup2.DeviceClass/ MIB reportsCLASS_Ccorrectly, but the radio is never actually re-armed for continuous RX (noRadio.Rx()call happening at the SX126x driver level)? - Is there a known hardware/silicon revision difference between RAK3172 units that could cause this — e.g., a batch with different internal RAK AT-firmware that mishandles Class-B/C continuous RX re-arming?
- Any known issue where the STM32WL internal SUBGHZ SPI/IRQ line can silently stop generating interrupts after a class switch (something that would explain zero IRQ activity, not even timeouts)?
- Is there a recommended way to directly verify (from AT commands, without custom firmware) whether the RAK3172 radio is actually in continuous RX vs just reporting Class C at the LoRaMac state level?
Additional notes
- Two devices are physically/electrically identical (same PCB revision, same firmware binary flashed via the same build)
- Planning to physically swap the RAK3172 module between the two boards next to isolate whether the fault follows the module or stays with the board — will update this thread with results
- Happy to share full AT firmware version / build config if useful
Photos of RAK3172 modules

