voxl-rc-server
Bridge RC channels from a USB joystick or MAVLink stream to an ELRS transmitter or a MAVLink link.
Table of contents
Overview
voxl-rc-server takes RC channel input from a receive (RX) source and sends it to a transmit (TX) sink. It ships with the VOXL SDK. On FPV systems it runs on the VRX to bridge a transmitter or joystick to the vehicle.
| Direction | Module | Description |
|---|---|---|
| RX | joystick | A Linux evdev joystick, for example a USB radio in joystick mode |
| RX | mavlink | MAVLink over UDP: RC_CHANNELS, RC_CHANNELS_RAW, RC_CHANNELS_SCALED, or MANUAL_CONTROL |
| TX | crsf | An ELRS transmitter on a serial port, driven with CRSF RC frames |
| TX | mavlink | MAVLink over UDP: RC_CHANNELS, or MANUAL_CONTROL with arm and mode commands |
With no configuration file and no command-line options, the server builds a working setup from what it finds:
- It listens for MAVLink on UDP port 14550 over IPv6 multicast (
ff12::5352:5243) and sends what it receives to a locally connected ELRS transmitter, if one is present. - If a joystick is plugged in, it sends the joystick’s channels over IPv6 multicast.
- If a joystick and a transmitter are both connected locally, it translates the joystick input directly to the transmitter and does not multicast.
With no CRSF device given, the server opens the first serial device it finds under /dev/serial/by-id, then /dev/ttyUSB*, then /dev/ttyACM*, at 921600 baud, and sets the transmitter’s ELRS packet rate (50 Hz by default).
Enable the Service
systemctl enable voxl-rc-server
systemctl start voxl-rc-server
The service runs /usr/bin/voxl-rc-server with no arguments, so it uses /etc/modalai/voxl-rc-server.json when that file exists and the defaults above otherwise.
Command Line
Command-line options override the matching entries in the configuration file.
| Option | Description |
|---|---|
--rx <spec> | RX source: joystick (auto-detect), joystick:/dev/input/eventN, or mavlink:PORT |
--tx <spec> | TX sink: crsf:/dev/ttyUSB0 (optionally crsf:/dev/ttyUSB0@BAUD) or mavlink:IP:PORT |
--bind | Bind at start-up. A CRSF sink sends the ELRS bind frame; a MAVLink sink broadcasts MAV_CMD_START_RX_PAIR |
--list-joysticks | List the available /dev/input devices and exit |
--monitor | Show a live channel monitor (needs a terminal) |
--no-heartbeat | Do not send MAVLink heartbeats |
--rx-debug | Print verbose debug output from the RX module |
--debug-state | Print the joystick state on input events |
--debug-joystick-publish, --debug-mavlink-publish, --debug-crsf-publish | Print each publish call of that module |
-h, --help | Show the help text and exit |
For example, to drive an ELRS transmitter on /dev/ttyUSB0 from a local joystick and watch the channels:
voxl-rc-server --rx joystick --tx crsf:/dev/ttyUSB0 --monitor
The monitor prints one row of RX values in microseconds and one row of TX values (CRSF ticks for a CRSF output, microseconds for a MAVLink output), labeled with the channel labels from the configuration.
There is no unbind option on the command line. Unbind is available from a joystick button; see unbind_button below.
Configuration File
The configuration lives in /etc/modalai/voxl-rc-server.json. Every key is optional. Top-level keys:
| Key | Description |
|---|---|
orchestrator.rx, orchestrator.tx | Preference-ordered lists of RX and TX modules. Default ["joystick", "mavlink"] and ["crsf", "mavlink"] |
orchestrator.hysteresis | Source selection timing in ms: rx_min_hold_ms (default 300), rx_stale_ms (1000), rx_fallback_grace_ms (600), tx_retry_ms (500), status_poll_ms (100), reselect_backoff_ms (750) |
modules | Per-module settings, below |
channel_labels | Up to 16 channel labels for the monitor. Default ROL PIT THR YAW ARM FM LED SUP0 SUP1 REC CAM |
Module keys:
| Module | Keys |
|---|---|
rx_joystick | device, update_rate_hz (default 200), axes (with limit_throttle_range, center_zero_throttle), axes_channels, buttons (code, channel, press, release), bind_button, unbind_button, vrx_scan_button |
rx_manual_control | axes, buttons |
rx_mavlink | port (default 14550), mcast (default true), iface |
tx_crsf | device, baud (default 921600), packet_rate (default "50Hz") |
tx_mavlink | ip, port (default 14550), mcast (default true; used when no ip is set), iface, system_id, component_id, rate_hz, manual_control, heartbeat_hz (default 1), no_heartbeat, target_system, target_component (default 1), axis, buttons |
Joystick bind_button, unbind_button, and vrx_scan_button take a Linux input event code, the same value used for code in buttons. Pressing the button sends bind, unbind, or VRX scan to the active TX sink.
With tx_mavlink.manual_control set to true, the MAVLink sink sends MANUAL_CONTROL instead of RC_CHANNELS. Its buttons entries watch a channel (channel, pressed value, epsilon tolerance) and run an on_press or on_release action: arm, disarm, terminate, or set_mode with main_mode and sub_mode.
The package ships presets for the rx_joystick, rx_manual_control, and tx_mavlink modules. They include mappings for Zorro joysticks and a VOXL-FPV MAVLink preset that maps arm and flight-mode switches to commands.
voxl-configure-rc-server
voxl-configure-rc-server writes /etc/modalai/voxl-rc-server.json. With no arguments it opens an interactive menu, which includes a Custom flow that builds a configuration from the installed presets. The script needs jq in every mode; the package ships its own copy of gum for the menu prompts.
The scripted modes write the configuration and enable the service. They do not start it, so start it or reboot afterwards:
voxl-configure-rc-server factory_enable_fiber
voxl-configure-rc-server factory_enable_lte_joystick 192.168.1.10 14550
voxl-configure-rc-server disable
systemctl start voxl-rc-server
The script also has non-interactive factory modes for specific vehicle radio setups:
| Mode | Result |
|---|---|
factory_enable_fiber | Joystick with the Zorro preset, sent as MAVLink with the VOXL-FPV preset to 10.0.0.10:14550 (unicast) |
factory_enable_dtc | Joystick, sent as MAVLink with the DTC preset to 192.168.0.100:14550 (unicast) |
factory_enable_dgcs_tower | Receives MAVLink manual control on UDP port 14551 (voxl-joystick-server input preset) and sends it out as CRSF on /dev/ttyHS1 at 921600 baud and as MAVLink with the VOXL-FPV preset to 10.1.0.2:14550 (unicast) |
factory_enable_dgcs_pilot | Joystick with the Zorro preset, sent as MAVLink MANUAL_CONTROL to 10.0.0.10:14551 (unicast) |
factory_enable_lte_joystick IP PORT | Joystick, sent as MAVLink with the VOXL-FPV preset to the given IP and port (unicast) |
disable | Disable and stop the service |
help | Print the usage text |
On the vehicle, voxl-mavlink-server receives the MAVLink RC. For ELRS receivers and transmitters, see voxl-elrs.