Scans all 8 channels of the analog FPV Raceband (5658-5917 MHz) with a single RX5808/RTC6715-based receiver module and reports RSSI per channel over serial. Since one receiver can only listen to one frequency at a time, "monitoring 8 channels" means continuously re-tuning the module across all 8 Raceband channels and sampling RSSI after each retune (a frequency sweep), rather than listening to all 8 simultaneously.
Any RX5808 module works, including a modified RC832 with its RTC6715 tuner chip's SPI control lines exposed (the standard "RX5808 SPI mod": bridging or removing the onboard resistor/pad that ties the bus-select pin low, freeing up DATA/CLK/LE for direct microcontroller control instead of the module's onboard button-scan logic).
You'll also need one LED per Raceband channel (each with a current-limiting resistor, e.g. 220Ω) to show that channel's busy/free state.
Wire everything to an Arduino Uno/Nano (ATmega328P) as follows:
| Signal | Arduino pin |
|---|---|
| RX5808/RC832 DATA | D10 |
| RX5808/RC832 CLK | D11 |
| RX5808/RC832 LE / SEL (CS) | D12 |
| RX5808/RC832 RSSI | A0 |
| RX5808/RC832 GND | GND |
| RX5808/RC832 3.3V / 5V | per your module's onboard regulator |
| R1 busy/free LED | D2 |
| R2 busy/free LED | D3 |
| R3 busy/free LED | D4 |
| R4 busy/free LED | D5 |
| R5 busy/free LED | D6 |
| R6 busy/free LED | D7 |
| R7 busy/free LED | D8 |
| R8 busy/free LED | D9 |
LEDs wire from their pin, through the resistor, to GND. Each LED lights up whenever that specific channel is considered busy (see below).
RX5808 pin assignments are #defines at the top of include/rx5808.h
(overridable via build_flags, see the promicro16 env below); LED pin
assignments are CHANNEL_STATE_LED_PINS in src/main.cpp - change either
if you need different pins.
The Pro Micro (ATmega32U4) doesn't break out D11/D12 on its header, so the
promicro16 build environment remaps the RX5808 bus to D14-D16 instead:
| Signal | Arduino pin |
|---|---|
| RX5808/RC832 DATA | D14 |
| RX5808/RC832 CLK | D15 |
| RX5808/RC832 LE / SEL (CS) | D16 |
| RX5808/RC832 RSSI | A0 |
LED wiring (D2-D9) is unchanged.
The ESP32's WiFi is used to serve a live channel-status web page (see
below), so the esp32 build environment moves every pin off the AVR
defaults: GPIO6-11 are wired internally to the module's flash chip and
can't be used as GPIOs, and GPIO0/2/5/12/15 are boot-mode strapping pins
best left alone. RSSI is the only signal read with analogRead(), and
WiFi takes over ADC2 for as long as it's active, so RSSI specifically
needs an ADC1 pin - hence GPIO34. The LED pins are plain digitalWrite()
outputs, so a few of them (GPIO25/26/27) doubling as ADC2 channels doesn't
matter - that conflict only affects analog reads on a pin, not digital
I/O.
| Signal | ESP32 pin |
|---|---|
| RX5808/RC832 DATA | GPIO16 |
| RX5808/RC832 CLK | GPIO17 |
| RX5808/RC832 LE / SEL (CS) | GPIO18 |
| RX5808/RC832 RSSI | GPIO34 |
| R1 busy/free LED | GPIO19 |
| R2 busy/free LED | GPIO21 |
| R3 busy/free LED | GPIO22 |
| R4 busy/free LED | GPIO23 |
| R5 busy/free LED | GPIO25 |
| R6 busy/free LED | GPIO26 |
| R7 busy/free LED | GPIO27 |
| R8 busy/free LED | GPIO32 |
The ESP32 is 3.3V native, so unlike the 5V Nano/Uno/Pro Micro there's no need to worry about the RX5808's logic-level tolerance.
The esp8266 build environment also serves the live channel-status web
page, but has no per-channel busy/free LEDs - the ESP8266 doesn't have
eight spare GPIOs once WiFi and the RX5808 bus are accounted for (and
several of the ones it does have are boot-mode strapping pins best left
alone), so this board relies on the Web UI as its only busy/free indicator.
Only the RX5808 bus needs pins, remapped clear of the GPIO0/2/15
strapping pins:
| Signal | ESP8266 pin (NodeMCU) |
|---|---|
| RX5808/RC832 DATA | GPIO4 (D2) |
| RX5808/RC832 CLK | GPIO5 (D1) |
| RX5808/RC832 LE / SEL (CS) | GPIO14 (D5) |
| RX5808/RC832 RSSI | A0 |
Like the ESP32, the ESP8266 is 3.3V native, so there's no RX5808 logic-level tolerance to worry about. A NodeMCU/Wemos D1 mini's onboard resistor divider on A0 is also what makes it safe to feed the RX5808's 0-3.3V RSSI output directly into a pin whose bare ADC otherwise tops out around 1V.
pio run # build (defaults to nanoatmega328new)
pio run -t upload # build + flash
pio device monitor -b 115200 # view RSSI outputOther board environments are defined in platformio.ini:
nanoatmega328new- Nano with the newer bootloader (default)nanoatmega328- Nano with the old bootloader (uses 57600 baud upload)uno- Arduino Unopromicro16- Arduino/SparkFun Pro Micro, 5V/16MHz (see Wiring above for its remapped pins)esp32- Espressif ESP32 dev board, adds the web UI below (see Wiring above for its remapped pins)esp8266- Espressif ESP8266 dev board, adds the web UI below but has no busy/free LEDs (see Wiring above)nanoatmega328new_tvout- Nano with composite/analog video output added (see TV Output below)esp32_tvout- Same asesp32, with composite/analog video output added (see TV Output below)
Select one explicitly with pio run -e uno.
Each line is one full 8-channel sweep, printed as raw ADC RSSI (0-1023), a calibrated percentage, and each channel's resulting busy/free state:
1234 ms R1=142 (29%, FREE) R2=138 (25%, FREE) R3=205 (60%, BUSY) R4=95 (2%, FREE) R5=101 (5%, FREE) R6=110 (10%, FREE) R7=99 (4%, FREE) R8=120 (15%, FREE)
After every sweep, each channel's calibrated RSSI percentage is compared against the busy threshold (50% by default - see Calibration below). Channels at or above the threshold are considered busy - a video transmitter appears to be active on that frequency - and their LED is driven HIGH. Channels below it are free and their LED is driven LOW. This gives a quick visual per-channel "is this Raceband frequency clear" indicator without needing the serial monitor.
Skipped entirely on the esp8266 build (CHANNEL_STATE_LEDS_ENABLED in
src/main.cpp defaults to off there) - see the ESP8266 wiring notes above
for why. The busy/free state is still computed and reported over serial and
the Web UI either way, just without driving any LED pins.
On the esp32 and esp8266 builds, the scanner also hosts its own WiFi
access point and serves a live status page: one box per channel, green when
free and red when busy (matching the LEDs, on boards that have them). Each
box also carries a checkmark/warning-triangle icon (not just color) so the
state is readable regardless of color
vision. On boot it prints the network name and page URL to serial, e.g.:
Web UI: join WiFi "BandoBuddy" (password "raceband1") and browse to http://192.168.4.1
Join that network from a phone or laptop and open the printed address. The page polls the current busy/free state twice a second; there's no need to keep the serial monitor open. This is compiled out entirely on boards without WiFi (Nano/Uno/Pro Micro).
Pilots can tap a free or busy card, type their name, and reserve that channel - the card turns white with a padlock icon and their name so everyone at the field can see who's on which frequency. Tapping a reserved card again releases it. If the channel actually keys up while reserved, the card still turns red (the live RSSI reading always takes priority over the reservation display). Reservations are held in RAM on the ESP32/ESP8266 only and don't survive a reboot, and there's no login - anyone on the AP can reserve or release any channel, matching the rest of this trust-based, pit-side device.
The gear icon (⚙) next to the title links to /admin, a small settings
page for the RSSI calibration and busy threshold covered in Calibration
below - no reflash needed to retune them. It has three controls (RSSI min,
RSSI max, and the busy threshold as a slider) and a Save button, plus a
Calibrate... button that walks through measuring RSSI min/max instead
of guessing at raw ADC numbers:
- Step 1 (noise floor): with no video transmitter powered on nearby, the modal shows the live raw RSSI reading updating twice a second. Click Next once it's settled to capture it as the RSSI min candidate.
- Step 2 (signal strength): power on a video transmitter on Raceband channel R5 (5806MHz) and place it about 2m (6ft) from the receiver antenna. The modal tracks the live reading and the peak seen so far (so a moment of misalignment while positioning the VTX doesn't ruin the reading) - click Done once the peak has stopped climbing. A Reset button next to the peak clears it back to zero, for starting the measurement over without leaving the step (e.g. after a stray spike, or repositioning the VTX).
- Review: shows the RSSI min/max values it's about to set. Save writes them (keeping whatever busy threshold the slider is currently at); Cancel discards them and leaves the existing calibration untouched.
The wizard always measures channel R5 specifically (regardless of which channel you eventually fly on) so it has one consistent, repeatable reference point - the same min/max then applies to all 8 channels' RSSI readings, since the RX5808's absolute RSSI scale doesn't vary meaningfully channel-to-channel.
Like reservations, calibration values live in RAM only and reset to their compile-time defaults (see Calibration below) on reboot.
Two build environments add a composite (PAL/NTSC) video signal that mirrors
the Web UI's per-channel grid directly onto a small CRT, composite monitor,
or FPV goggles - no WiFi, browser, or phone needed. Both are opt-in
(disabled in the default nanoatmega328new/esp32 builds) since each needs
its own pin remap - see the wiring for each below. Reservations aren't shown
on either - those live only in the ESP32 Web UI's RAM, and pilots have no
way to type a name into a TV screen - so both mirror the same busy/free
bitmask that drives the LEDs.
The nanoatmega328new_tvout build environment uses the
TVout library:
pio run -e nanoatmega328new_tvout -t uploadEach of the 8 boxes shows the channel name and its "FREE"/"BUSY" state, same as the Web UI, laid out in the same 4-column grid. Composite video here only carries luma (brightness), no chroma, so unlike the Web UI's green/red coding, a busy channel is marked with a solid block in the corner of its box in addition to the text - a shape difference, not a color one, so the state still reads without relying on color, matching the accessibility approach already used for the LEDs and Web UI icons.
TVout hard-codes its sync and video pins per chip; on the ATmega328 (Uno/Nano) these are D9 (sync) and D7 (video), so this build environment moves R6's and R8's busy/free LEDs off those two pins onto A1/A2 (used as plain digital outputs) to free them up:
| Signal | Arduino pin |
|---|---|
| Composite sync | D9 |
| Composite video | D7 |
| R6 busy/free LED | A1 |
| R8 busy/free LED | A2 |
(R1-R5 and R7 stay on their default pins - see Wiring above.)
Mix the sync and video signals into a single composite line with two resistors, then feed that into an RCA jack (center pin = signal, shield = GND):
D9 (sync) ---[470ohm]---+
+--- RCA center pin --- to display
D7 (video) ---[1k ohm]----+
PAL is available via -D TV_OUT_PAL (added to that environment's
build_flags); NTSC is the default.
- The 128x64 monochrome frame buffer is allocated at runtime (~1KB) on the
ATmega328's 2KB of RAM, on top of the rest of the sketch's globals and
stack. This build has been compiled and its RAM/flash usage checked, but
not run on real hardware - if it locks up or corrupts the display, freeing
more RAM (e.g. dropping
RSSI_SAMPLES) or reducingTV_HRES/TV_VRESinsrc/tv_ui.cpp(must stay a multiple of 8) are the first things to try. - Generating a video signal digitally right next to a 5.8GHz RF front-end is a plausible source of noise on the RSSI reading. This hasn't been validated on hardware either - keep the composite video wiring/cable routed away from the RX5808 module and antenna, and re-check calibration (see below) once everything is wired up.
The esp32_tvout build environment uses the
ESP_8_BIT library,
which drives composite video off the ESP32's onboard DAC on GPIO25 (shield
to GND). See the known issue below before wiring this up for FPV
goggles - it's confirmed working electrically, but the picture isn't
watchable on at least one goggle model (Fatshark) as of this library
version:
pio run -e esp32_tvout -t uploadThis is on top of the regular esp32 build - the WiFi Web UI still runs, so
you get both a browser page and a goggles/monitor feed from the same board.
Unlike the AVR build, ESP_8_BIT's composite signal does carry color, so the
grid uses the same green (free) / red (busy) coding as the Web UI, plus the
"FREE"/"BUSY" text for the same colorblind-friendly redundancy as everywhere
else in this project.
ESP_8_BIT's DAC output is fixed to GPIO25, which is R5's busy/free LED in
the default esp32 pin mapping, so this build environment moves that LED to
GPIO33 instead:
| Signal | ESP32 pin |
|---|---|
| Composite video | GPIO25 |
| R5 busy/free LED | GPIO33 |
(Everything else - RX5808, R1-R4/R6-R8 LEDs - stays on the pins in the Wiring section above.)
PAL is available via -D TV_OUT_PAL (same flag as the AVR build, added to
esp32_tvout's build_flags); NTSC is the default.
Tested on real hardware (ESP32-D0WD-V3, well past the chip revisions this library's README warns don't work at all) into a pair of Fatshark goggles: the firmware runs fine - WiFi, RX5808 scanning, everything else is unaffected - but the picture on the goggles never holds still, rolling continuously regardless of:
- NTSC vs. PAL (
TV_OUT_PAL) - both roll the same way. - Signal level - tried GPIO25 direct, and in series with 1kOhm, 270Ohm, and 135Ohm resistors (targeting the ~1Vpp/75Ohm composite spec that real FPV video receiver modules output into the same input). Level changed brightness/visibility, not the roll.
- This isn't specific to this project's drawing code either - the
library's own unmodified example sketch (
GFX_HelloWorld) rolls the same way over the same wiring.
The likely cause: this library generates its signal via a low-level ESP32
hardware hack the author's own README describes as tested against
old-school tube TVs. CRTs have a free-running analog deflection oscillator
that's very forgiving of imprecise sync timing. FPV goggles instead decode
composite video with a digital chip driving an LCD/OLED panel, which
generally needs tighter, more consistent sync timing than a CRT to frame
the picture at all - so a signal loose enough for a CRT to display cleanly
can still roll on goggles. (PSRAM is a documented cause of near-identical
"jiggling" symptoms for this library, but doesn't apply here - the
esp32dev board build has no PSRAM support compiled in to begin with.)
Bottom line: this backend runs correctly (WiFi/RX5808/etc. all unaffected) and its author reports it working on CRT-class displays, but we don't have one to confirm that ourselves, and it isn't currently known to work cleanly with FPV goggles - that part is confirmed, on real hardware, not a compiled-only guess. If you get it stable on a Fatshark (or other) goggle - through different wiring, a different resistor value, or a library fix - an issue or PR updating this section would be welcome.
- ESP_8_BIT generates its signal off an interrupt tied to the I2S/DAC peripheral, independent of the main loop and the WiFi stack; the Web UI and RX5808 scan were confirmed unaffected during testing.
- As with the AVR build, generating a video signal next to a 5.8GHz RF front-end is a plausible source of RSSI noise; keep the composite wiring away from the RX5808 module and antenna and re-check calibration once wired up. (Not yet specifically confirmed either way.)
The printed/reported percentage is derived from an RSSI min (noise floor)
and RSSI max (strong signal) raw ADC value, and compared against a busy
threshold percentage - all three defined in include/calibration.h as
RSSI_RAW_MIN_DEFAULT (90), RSSI_RAW_MAX_DEFAULT (280), and
CHANNEL_BUSY_THRESHOLD_PCT_DEFAULT (50). These are just the starting
values a freshly flashed board boots with - overridable via build_flags for
a board-specific starting point (e.g. -D RSSI_RAW_MIN_DEFAULT=100), same
as the LED pin lists elsewhere in this README.
On esp32/esp8266: all three are also adjustable at runtime, no
reflash needed, from the Web UI's admin page (see "Admin page and
calibration wizard" above) - either by typing values directly or via the
guided "Calibrate..." wizard, which measures your specific RX5808 module's
noise floor and a real VTX's signal strength for you rather than needing a
serial-monitor readout. These runtime values live in RAM only and reset to
the compile-time defaults above on reboot; there's currently no persistence
across power cycles.
On Nano/Uno/Pro Micro (no Web UI to change them from), calibrate by editing the compile-time defaults directly:
- Flash and open the serial monitor with no video transmitter powered on
nearby - note the raw values (noise floor) → use as
RSSI_RAW_MIN_DEFAULT. - Power a video transmitter at close range on each Raceband channel in
turn and note the raw peak values → use the highest as
RSSI_RAW_MAX_DEFAULT. - Update the two defaults (in
include/calibration.h, or via build_flags) and reflash.
On ESP32/ESP8266, their ADCs are less linear than the AVR boards' -
calibrate on the esp32/esp8266 build itself (the runtime admin page
makes this easy - see above) rather than reusing values measured on a
Nano/Uno/Pro Micro.
RSSI_SETTLE_MS in src/main.cpp controls how long the code waits after
switching channels before trusting the RSSI reading (PLL relock + RSSI
output settling time). The default of 20ms gives a full 8-channel sweep in
roughly 170-200ms (~5 sweeps/second). Lowering it speeds up the scan but
increases reading noise/inaccuracy right after a channel switch.
