A Linux desktop GUI for configuring RFDesign RFD900-series radio modems running the SiK firmware. Feature parity target: the official Windows-only RFD Modem Tools.
Supports RFD900, RFD900+, RFD900u (8051-based, Intel HEX firmware) and RFD900x, RFD900ux, RFD900x2 (STM32-based, .bin firmware).
Developer: Skoll — me@skoll.dev Part of: sUAS Tools
- Auto-detect connected radios across every USB serial port (FTDI / CP210x / CH340).
- Side-by-side local + remote S-register editor with friendly labels, validated ranges, and tooltips.
- Built-in profile presets ("MAVLink defaults", "Long range / low data rate", "Point-to-point max throughput") plus JSON profile import/export.
- Firmware update for both 8051 and STM32 radios — Intel-HEX path uses the SiK bootloader native protocol, STM32 path wraps
stm32flash. - Live RSSI / noise chart driven by either MAVLink
RADIO_STATUSpackets orATI7polling (radio-button toggle). - Raw AT terminal with command history, hex-byte sends, and timestamped log.
- Startup environment checks that warn on
ModemManagerinterference and missingdialoutgroup membership.
- Linux (tested on Ubuntu 22.04 / 24.04; should work on any modern distro)
- Python 3.10+
stm32flashfor RFD900x / RFD900ux firmware updates (optional; required only for STM32 flashing)
git clone <this repo>
cd rfdtool
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install pyserial # the `pyserial` import name; pulled in by requirements as well
sudo apt install stm32flash # optional, for RFD900x/ux flashing.venv/bin/python rfdtool.pyUseful flags:
.venv/bin/python rfdtool.py --port /dev/ttyUSB0 # auto-connect on launch
.venv/bin/python rfdtool.py --port /dev/ttyUSB0 --baud 115200
.venv/bin/python rfdtool.py --no-mavlink # skip RADIO_STATUS parsing
.venv/bin/python rfdtool.py --skip-checks # skip the startup environment checks.venv/bin/pip install pyinstaller
.venv/bin/pyinstaller rfdtool.spec
# output in dist/rfdtool/The protocol layer (AT command parsing, Intel HEX parsing, XMODEM-CRC, SiK bootloader protocol, MAVLink RADIO_STATUS extraction, STM32 flash subprocess wrapper) is covered by ~200 unit tests using a mock serial port — none of them touch real hardware.
.venv/bin/python -m pytest -vYour user is not in the dialout group:
sudo usermod -aG dialout $USER
# log out and back in (or reboot) for the group change to take effectrfdtool checks this at startup and warns if you're not a member.
If you see brief AT-command activity on the port after plugging in the radio, or commands time out the first ~10 seconds after connect, ModemManager is probing the device. Two fixes:
# Aggressive: remove ModemManager entirely (you don't need it on a desktop
# unless you have a USB cellular modem):
sudo apt remove modemmanager
# Targeted: tell ModemManager to ignore FTDI / CP210x adapters via udev:
sudo tee /etc/udev/rules.d/99-rfdtool.rules <<'EOF'
ATTRS{idVendor}=="0403", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="10c4", ENV{ID_MM_DEVICE_IGNORE}="1"
EOF
sudo udevadm control --reload-rules
sudo udevadm triggerSymptoms: connect succeeds (/dev/ttyUSB0 opens) but the parameter list never loads, and the log shows could not enter command mode. Causes and fixes:
- Wrong baud. The radio remembers its serial speed across reboots. Try 115200, 38400, 9600 in turn from the Connection panel.
- Active link saturating the UART. If a flight controller is streaming MAVLink at high rate into the same UART pair, the +++ escape can be defeated. Disconnect the autopilot (or hold its boot button to silence it) and reconnect.
- Wrong cable wiring. Verify TX/RX are crossed; the breakout silk-screens are TX from the breakout's perspective.
Hold the BOOT pin (also labelled CTS/IO0 on some breakouts) to ground while powering the radio on. This forces the bootloader to come up regardless of firmware state. With the BOOT pin still held:
- For 8051 radios (RFD900/+/u): re-run a known-good
.ihxflash from the Firmware tab. The bootloader is always there even if the firmware image is corrupt. - For STM32 radios (RFD900x/ux): re-run
stm32flashagainst a known-good.bin. The STM32 system memory bootloader is mask-ROM and cannot be bricked.
Release the BOOT pin after the flash completes.
sudo apt install stm32flashThe Firmware tab shows this hint automatically when you try to flash a .bin without stm32flash on PATH.
rfdtool lists every detected serial port and highlights likely radios (FTDI / CP210x / CH340 VID/PIDs in bold). Pick the one you want from the dropdown and connect. Future versions will support driving more than one radio simultaneously from a single window.
rfdtool.py entry point
rfd/ protocol + I/O (no Qt — fully unit-tested)
registers.py S-register definitions, ranges, tooltips
protocol.py AT command builders + ATI/ATI5/ATI7 parsers
radio.py RadioCore (sync) + Radio (Qt wrapper, threaded)
detector.py port enumeration + radio fingerprinting
ihx.py Intel HEX parser
xmodem.py XMODEM-CRC sender (sender-only)
mavlink_parser.py pymavlink RADIO_STATUS extractor
uploader_8051.py SiK 8051 bootloader driver
uploader_stm32.py stm32flash subprocess wrapper
presets.py built-in profiles + JSON I/O
ui/ Qt UI layer (depends on rfd/, no reverse dep)
main_window.py QMainWindow assembling all tabs
connection_panel.py port/baud/connect/LED row
settings_tab.py local + remote S-register editor
terminal_tab.py raw AT console
rssi_tab.py live RSSI/noise chart
firmware_tab.py firmware update workflow
system_checks.py ModemManager / dialout startup checks
theme.py forced light Fusion palette
tests/ pytest suite for the protocol layer
MIT — see LICENSE.
