When you want to tune a motor ESC — change timing, demag, or motor direction — you need to get a configurator talking directly to it. In stock Betaflight/Cleanflight setups, this is done via MSP passthrough: the flight controller CPU intercepts the USB serial stream and bit-bangs it out to the motor signal pin.
That works, but it is fragile. The CPU has to drop flight tasks, the timing is subject to scheduler jitter, and if the configurator times out mid-flash, you can brick an ESC. I set out to build something better (still under test).
What Is an ESC?
An Electronic Speed Controller (ESC) is the bridge between your flight controller and your motors. The flight controller (FC) doesn't drive the motors directly; it sends digital commands, and the ESC converts those into the three-phase power pulses that spin the brushless motors.
Every motor on a drone has its own dedicated ESC (often combined into a single 4-in-1 board). Each runs its own firmware—most commonly Bluejay or BLHeli_S. The "magic" happens over a single signal wire. During flight, this wire carries high-speed DShot pulses. But when you need to change settings, that same wire must transform into a half-duplex UART serial link to talk to the ESC bootloader.
The Solution: FPGA Hardware Offloading
rt-fc-offloader is an open-source FPGA project (Tang Nano 9K) that sits between a Raspberry Pi Zero 2W and your ESCs. By moving the serial logic into RTL (Hardwired Logic), we eliminate the CPU bottleneck entirely.
Evolution: From Pico PIO to FPGA
Before moving to the Tang Nano, we validated the protocol on a Raspberry Pi Pico using its PIO (Programmable I/O).
RPi Pico (PIO): Handles 1 motor at a time; deterministic but resource-limited.
- RPi Pico (PIO): Handles 1 motor at a time; deterministic but resource-limited.
Tang Nano 9K (FPGA): Handles 4+ motors simultaneously; nanosecond accurate RTL; Zero CPU load.
- Tang Nano 9K (FPGA): Handles 4+ motors simultaneously; nanosecond accurate RTL; Zero CPU load.
The heart of the offloader is a Serial/DShot Multiplexer. By writing to a single Wishbone register (0x40000400), the FPGA instantly reconfigures the motor pad's physical behavior.
- DShot Mode (0x01): Default flight mode using hardware-generated pulses.
- Serial Mode (0x00): Routes the pad directly to the FCSP serial tunnel.
- Bootloader Break (0x14): Forces the pad LOW (required for bootloader entry).
- Bit 0 (MODE_SEL): 1 = DShot pulses, 0 = Serial UART.
- Bit 2 (MOTOR_ID): Specifies which motor output to control.
- Bit 4 (FORCE_LOW): Overrides the signal to a physical Logic 0.
To "unlock" an ESC for configuration, you must pull the signal line LOW for at least 20ms. In our system, this is a simple hardware command:
Python
# Select Motor 1 (0x04) + Enable Serial (0x00) + Force LOW (0x10) = 0x14
wb_write(0x40000400, 0x14)
time.sleep(0.025) # Hold for 25ms
# Release the line - ESC is now listening at 19200 baud
wb_write(0x40000400, 0x00)The FCSP Transport LayerThe Pi communicates with the FPGA using the Flight Controller Serial Protocol (FCSP). In configuration mode, we use Channel 0x05 (ESC_SERIAL). This creates a raw, transparent byte tunnel between your Python script and the ESC.
The FPGA hardware UART engine handles the 1-wire half-duplex switching automatically—no direction-switching logic required in your software.
Flashing with ConfidenceOnce the tunnel is open, we speak the BLHeli 4-way protocol. Because the FPGA handles the UART framing in hardware, the bit-timing is perfect regardless of what the Raspberry Pi’s CPU is doing.
- Handshake: Send "BLHeli" to verify connection.
- Erase/Write: Send firmware chunks via FCSP Channel 0x05.
- Verify: Each command returns 0x30 for success.
- Safety Watchdog: Reverts the pins to DShot mode after 5 seconds of inactivity.
The python-imgui-esc-configurator companion app provides a clean GUI to manage this entire process.
- Firmware Repo:rt-fc-offloader
- GUI Configurator:python-imgui-esc-configurator
- Detailed Specs:BLHeli Passthrough Theory




Comments