Investigating Betaflight Blackbox Data Loss on RP2350 PIO-UART

Investigating Betaflight Blackbox Data Loss on RP2350 PIO-UART

This article documents the complete investigation process, from discovering abnormal Betaflight Blackbox logs to finally identifying the UART throughput bottleneck.

5/15 — Abnormal Blackbox File Format

I connected TFC1’s PIOUART0 to the Pi Zero W2 UART0 (GPIO14, GPIO15), and enabled Blackbox Serial logging in Betaflight:

  • Ports tab: PIOUART0, Peripherals (Blackbox, 115200bps)
  • Blackbox tab
    • Blackbox logging device: Serial
    • Blackbox logging rate: 1/4 (2kHz)
    • Blackbox debugging mode: PIDLOOP

Then, on the Pi Zero W2, I used blackbox-logger to capture the Blackbox data:

$ node index.js -s /dev/ttyAMA0 -d /tmp --betaflight -c 2048 --cache-expire 10

Although Betaflight Blackbox Explorer (https://blackbox.betaflight.com/) could open the file successfully, the recording length was only about one second, and the number of data points was extremely small — far too little for flight analysis.

Further analysis using Blackbox tools (https://github.com/betaflight/blackbox-tools/) revealed that the file contained only a single I frame and no P frames at all, indicating that the Blackbox data stream was clearly corrupted.

$ ./obj/blackbox_decode --debug ~/Downloads/log_2026-05-15T10-08-10-240Z.bbl

Decoding log '/Users/yagamy/Downloads/log_2026-05-15T10-08-10-240Z.bbl' to '/Users/yagamy/Downloads/log_2026-05-15T10-08-10-240Z.01.csv'...
Data file contained no events

Log 1 of 1Statistics
I frames       1   61.0 bytes avg       61 bytes total
G frames       1    0.0 bytes avg        0 bytes total
S frames       1    4.0 bytes avg        4 bytes total
Frames         1   61.0 bytes avg       61 bytes total
Data rate: Unknown, no timing information available.

At the same time, I tested another flight controller board — a SpeedyBee F405 development board — using the exact same Blackbox settings (except using UART1 instead of PIOUART0). That setup worked perfectly and produced hundreds of thousands of valid data points.

$ ./obj/blackbox_decode --debug ~/Downloads/btfl_002.bbl

Decoding log '/Users/yagamy/Downloads/btfl_002.bbl' to '/Users/yagamy/Downloads/btfl_002.01.csv'...
Data file contained no events

Log 1 of 1, start 00:01.379, end 02:51.018, duration 02:49.638

Statistics
Looptime           1023 avg         1406.8 std dev (137.4%)
I frames    5015   47.7 bytes avg   239177 bytes total
P frames  155459   27.2 bytes avg  4221344 bytes total
S frames      20    3.0 bytes avg       60 bytes total
Frames    160474   27.8 bytes avg  4460521 bytes total
Data rate  945Hz  27272 bytes/s     272800 baud

481344 iterations are missing in total (127223ms, 75.00%)

I then analyzed the raw Blackbox file (log_2026-05-15T09-13-10-319Z.bbl) while referencing the Betaflight documentation:

https://betaflight.com/docs/development/Blackbox-Internals

Betaflight reduces Blackbox bandwidth by describing predictors and expected ranges in the BBL header, allowing P frames to be compressed significantly.

Key observations:

  • The F405 (STM32) log contains 38 fields. Each P frame is around 48–60 bytes, and roughly every 30 P frames an I frame appears.
  • The TFC1 (RP2350) log contains 50 fields. Aside from the initial I frame (~180 bytes), no additional I frames appear, and most P frames are only 4–6 bytes long — clearly abnormal.

At this point, it became necessary to investigate the Betaflight firmware itself to determine why the Blackbox output was failing.


5/18 — Betaflight Firmware Investigation

The first step was verifying whether Blackbox field definitions were being generated correctly.

After adding debug prints into the source code, the following output appeared:

blackbox.c:1465   sendFieldDefinition: headerIndex=0 fieldIndex=1 name=time
blackbox.c:1462   sendFieldDefinition: headerIndex=0 fieldIndex=2 name=axisP[0]
blackbox.c:1462   sendFieldDefinition: headerIndex=0 fieldIndex=3 name=axisP[1]
...

This confirmed that the Blackbox header fields themselves were correct.

Next, I instrumented the firmware to count how many fields were actually written into each P frame.

if (testBlackboxCondition(CONDITION(PID))) { strcat(m_tempString + strlen(m_tempString), "PID,"); m_tmpCounter++; }
if (testBlackboxCondition(CONDITION(RC_COMMANDS))) { strcat(m_tempString + strlen(m_tempString), "RC_COMMANDS,"); m_tmpCounter++; }
...
DBG("Interframe conditions: %d", m_tmpCounter);

Unexpectedly, after adding these debug statements, Blackbox Explorer suddenly started decoding the logs correctly, and the P frame field counts also became normal.

(The test consisted of a roughly three-minute recording using a bare TFC1 board, while manually shaking the board at around the one-minute and two-minute marks.)

Further investigation eventually pointed to the TX flow control logic in the PIO-UART implementation:

https://gitea.t2t.io/drone/betaflight-50f72d3-20260401/src/branch/master/src/main/blackbox/blackbox_io.c#L138-L152

On the RP2350 platform, serialTxBytesFree() frequently returned zero available bytes, causing portions of Blackbox frames to be dropped.

Statistics showed that each P frame was losing roughly 23–28 bytes:

blackbox.c:1010   Dropped 28 bytes when outputing P frame
blackbox.c:1010   Dropped 23 bytes when outputing P frame
blackbox.c:1010   Dropped 26 bytes when outputing P frame
...

Each P frame originally contained around 30–40 bytes, meaning nearly two-thirds of each frame was being discarded before transmission.

That explained why the decoded CSV files contained almost no usable fields.

From a bandwidth perspective:

  • Around 100 samples/sec
  • Roughly 30–40 bytes per sample
  • Total throughput around 3–4 KB/sec

In theory, 115200 bps should have been sufficient. Therefore, the initial suspicion was that the RP2350 PIO-UART TX implementation — particularly IRQ handling — was malfunctioning.

Searching through the Betaflight community revealed a recent pull request related to UART TX handling:

That PR even modified the PIO assembly code to improve TX behavior inside the IRQ handler.

The next step was therefore to merge that fix into the current Betaflight branch and retest the Blackbox output.


5/21 — Root Cause Identified

After observing that most P frames were incomplete, I added additional instrumentation to count how many frames were fully transmitted.

Modified source code:

Using the following CLI configuration:

# serial
serial PIOUART0 128 115200 57600 0 115200

# master
set blackbox_sample_rate = 1/16
set blackbox_device = SERIAL
set blackbox_mode = ALWAYS
set debug_mode = ACCELEROMETER

The results were shocking:

  • 3840 P frames existed between two S frames
  • Each P frame contained about 55 fields
  • Each frame required roughly 30–40 bytes

However:

  • Only 5 P frames were fully transmitted
  • The remaining 3835 frames were incomplete
  • Loss rate: 99.87%
  • Total dropped data: nearly 60 KB

Meanwhile:

  • Host computer throughput reached 11519 bytes/sec
  • Equivalent to roughly 92 kbps
  • Already very close to the practical limit of 115200 bps UART

The dropped data throughput was:

60721 bytes / 7.68 sec = 7910 bytes/sec

Equivalent to:

7910 * 8 = 63280 bps

Therefore, the actual required throughput was:

92152 + 63280 = 155432 bps

This immediately explained the problem:

115200 bps simply could not handle the total Blackbox bandwidth.

I then increased the PIO-UART baudrate to 921600 bps.

(Betaflight Configurator cannot configure 921600 directly, so CLI configuration was required. I also tried 230400 bps, but PIO-UART initialization failed. That issue was postponed for later investigation.)

# serial
serial PIOUART0 128 115200 57600 0 921600

# master
set blackbox_sample_rate = 1/16
set blackbox_device = SERIAL
set blackbox_mode = ALWAYS
set debug_mode = ACCELEROMETER

The results:

  • All 3840 P frames transmitted successfully
  • 0% frame loss
  • Host throughput: ~20616 bytes/sec
  • Only ~17.8% utilization of 921600 bps

At this point it became clear that the issue was not a broken PIO-UART implementation, but simply insufficient UART throughput.


Firmware and UART Combination Testing

The following table summarizes all firmware and UART combinations tested:

Firmware UART Port Baudrate P frame count Complete P frame count Loss rate Host data rate (bytes/sec)
OLD-FW PIO-UART0 115200 3840 5 99.87% 11519
OLD-FW PIO-UART0 921600 3840 3840 0% 20538
OLD-FW UART0 115200 FAILED - - -
OLD-FW UART0 921600 FAILED - - -
NEW-FW PIO-UART0 115200 3840 5 99.87% 11520
NEW-FW PIO-UART0 921600 3840 3840 0% 20538
NEW-FW UART0 115200 3840 5 99.87% 11520
NEW-FW UART0 921600 3840 3840 0% 20524

Conclusions:

  • The root cause was not the PIO-UART driver itself
  • 115200 bps was insufficient for the newer Betaflight Blackbox bandwidth
  • Increasing the baudrate to 921600 completely resolved the issue

Additionally, if using RP2350 Hardware UART (UART0) for Blackbox output, firmware newer than 2026-05-19 is required to include the SERIAL_CHECK_TX fix from PR #15117.


Sample Rate Testing

Using the same CLI configuration but varying the Blackbox sample rate:

Firmware UART Port Baudrate Blackbox Sample Rate Drop Rate Host data rate (bytes/sec)
NEW-FW PIO-UART0 921600 1/16 (500Hz) 0% 20538
NEW-FW PIO-UART0 921600 1/8 (1KHz) 0% 39506
NEW-FW PIO-UART0 921600 1/4 (2KHz) 0% 75951
NEW-FW PIO-UART0 921600 1/2 (4KHz) 99.8% 92162

This shows:

  • 921600 bps can reliably support 2KHz Blackbox logging
  • At 4KHz, UART throughput is exceeded again

Why Does F405 Still Work at 115200 bps?

The SpeedyBee F405 worked correctly at 115200 bps likely because:

  • It was running an older Betaflight version (v4.4.2)
  • Blackbox output only contained 38 fields
  • Each P frame was significantly smaller

Therefore, total throughput stayed below the UART bandwidth limit.

However, if newer firmware generated 55-field P frames on F405, it would likely encounter similar problems.

F405 version information:

2026-05-21 @09:14:49 -- Flight controller info, identifier: BTFL, version: 4.4.2
2026-05-21 @09:14:49 -- Board: SPBE/SPEEDYBEEF405MINI(STM32F405), version: 0

TFC1 (RP2350) version:

2026-05-23 @00:21:32 -- Flight controller info, identifier: BTFL, version: 2026.6.0-alpha
2026-05-23 @00:21:32 -- Board: TTIO/TFC1_RP2354B(RP2350B), version: 0

5/22 — Pi Zero W2 Integration

After confirming that TFC1’s PIO-UART0 could reliably output Blackbox data, the next step was integrating a Pi Zero W2.

System architecture:

  • Pi Zero W2 UART0 (GPIO14 / GPIO15)
  • Directly connected to TFC1 PIO-UART0
  • UART baudrate configured to 921600

This setup allowed stable reception of complete Blackbox logs.

Pi Zero W2 Configuration

/boot/firmware/config.txt

enable_uart=1
dtoverlay=disable-bt

/boot/firmware/cmdline.txt

(Remove console=ttyAMA0 and console=ttyS0 to prevent Linux from occupying UART0.)

console=tty1 root=PARTUUID=123afc93-02 rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=TW

blackbox-logger Installation

$ cd ~
$ mkdir projects
$ cd projects
$ git clone https://gitea.t2t.io/drone/blackbox-logger.git
$ cd blackbox-logger
$ npm install
$ pm2 start $(pwd)/index.js --name blackbox-logger -- -s '/dev/ttyAMA0?baudrate=921600' -d /home/pi/projects/blackbox-logger/data --betaflight -c 2048 --cache-expire 10
$ pm2 save

TFC1 Betaflight CLI Configuration

# serial
serial UART0 64 115200 57600 0 115200
serial PIOUART0 128 115200 57600 0 921600

# master
set blackbox_sample_rate = 1/16
set blackbox_device = SERIAL
set debug_mode = ACCELEROMETER

Test video:


5/25 — Notes and Remaining Issues

During the Pi Zero W2 integration process, several interesting issues appeared.

1. Pi Zero W2 Occasionally Fails to Connect to WiFi

Sometimes the Pi Zero W2 could not reconnect to an Apple AirPort Express access point, even after multiple reboots.

Current “magic workaround”:

  1. Remove the MicroSD card
  2. Boot it once on a Raspberry Pi 4
  3. Let it reconnect to WiFi
  4. Move the card back to Pi Zero W2

After that, WiFi works again.

The root cause is still unknown.

2. Betaflight Configurator WebSocket Issue

Pi Zero W2 can run websocketify successfully, allowing Betaflight Configurator to connect via:

ws://127.0.0.1:6761

However, connecting via:

ws://10.42.0.206:6761

produces:

Mixed Content: The page was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint

This happens because Betaflight Configurator is an Electron app, and HTTPS pages are blocked from connecting to insecure WebSocket (ws://) endpoints.

No ideal solution has been found yet.

3. blackbox_mode = NORMAL Problem

When blackbox_mode = NORMAL:

  • First ARM cycle works correctly
  • Second ARM cycle outputs no data at all

Currently, using:

set blackbox_mode = ALWAYS

works reliably.

This likely requires additional investigation into NORMAL mode state handling.


Conclusion

At first glance, this issue looked like a PIO-UART TX implementation bug.

However, deeper investigation revealed that the true root cause was much simpler:

  • Blackbox bandwidth exceeded what 115200 bps UART could sustain
  • Most P frames were truncated
  • Decoder could no longer reconstruct valid frame structures
  • Only a tiny number of complete frames remained usable

The RP2350 platform exposed this problem more clearly because newer Betaflight versions output significantly more Blackbox fields, dramatically increasing throughput requirements.

Once the UART baudrate was increased to 921600 bps, the issue disappeared entirely, confirming that UART throughput — not PIO-UART correctness — was the actual bottleneck.

Questions? Requests? Suggestions?

We are looking forward to hearing from you!

Are you looking for
Consulting Services,
Support Plans or
Training & Workshops?