The remote-control stack brings together three C utilities that ship with this repository:
- autod web UI / API backend – a CivetWeb-powered HTTP control plane written in C. It exposes discovery, execution, UDP relay, and helper-management endpoints while optionally serving the bundled HTML UI. Operators use it to supervise joystick2crsf and ip2uart, persist configuration, and surface telemetry through Server-Sent Events.
- joystick2crsf – an SDL2-based utility that samples a USB HID controller at up to 250 Hz, maps inputs to 16 CRSF channels, and outputs frames over UDP. Runtime behaviour is defined in
/etc/joystick2crsf.conf. - ip2uart – a UART↔IP bridge with TCP server/client and UDP peer modes. It can coalesce UDP packets, maintain reconnect loops, and reload settings from
/etc/ip2uart.confonSIGHUP.
Together they keep CRSF as the control language while IP networking provides transport flexibility.
| Component | Key Technologies | Advantages | Considerations |
|---|---|---|---|
| autod backend | C daemon embedding CivetWeb; INI configs under configs/ |
Minimal dependencies, suitable for SBCs, ships with HTML UI assets | Lacks built-in authentication; deployments should place it behind a trusted network |
| joystick2crsf | C + SDL2 joystick stack; configurable CRSF packer | Precise timing (250 Hz loop), flexible mapping/inversion, UDP streaming and SSE telemetry | Requires SDL2 runtime and accurate controller database; needs calibration for each device |
| ip2uart | C bridge with epoll loop, TCP/UDP/stdio back-ends | Handles blocking UARTs via ring buffers, supports cadence health stats, reloadable config | Only one TCP client at a time in server mode; UART performance depends on adapter quality |
| Controllers (e.g., Xbox Series pads) | USB HID over Linux input stack | Affordable, widely available, good ergonomics | Device-specific quirks demand per-axis tuning; drift necessitates deadbands |
| Transport | Wi-Fi or Ethernet between ground and vehicle | Uses standard networking gear; compatible with VPN tunnels | Wi-Fi interference and latency spikes must be mitigated with channel planning |
Baseline assumption: a Radxa Zero 3 runs the ground station with autod and joystick2crsf, while the RC vehicle carries another Linux SBC with autod and ip2uart. The same binaries can be cross-compiled via the provided Makefile for other ARM targets, including Android (Termux) with OTG accessories.
-
Ground Station (Radxa Zero 3)
- Build and install the tools with
make install, or copyautod,joystick2crsf, and the UI bundle to the device. - Connect the USB controller and confirm it registers under
/dev/input/js*or through SDL2 diagnostics. - Populate
/etc/joystick2crsf.confwith the joystick index, channel map, inversion flags, deadbands, and UDP transport parameters. Enable SSE streaming if the autod UI should show live channels. - Launch autod (optionally via
systemd) so the HTML UI and helper endpoints are available. Configure joystick2crsf to run under autod supervision or as a companion service.
- Build and install the tools with
-
Vehicle Node
- Deploy autod alongside
ip2uart(frommake toolsormake install). - Wire the UART from the flight controller or ExpressLRS module and record its device path (for example
/dev/ttyUSB0). - Edit
/etc/ip2uart.confwith the desired UART settings and UDP peer parameters (baud rate commonly 420000 for CRSF). Confirm the daemon can reload the file withSIGHUPduring tuning. - Start autod to host status dashboards and expose control endpoints for remote management.
- Deploy autod alongside
-
Link Orchestration
- autod’s HTTP API (documented in
README.md) lets you start/stop helpers, stream telemetry, and push UDP packets. Use it to coordinate joystick2crsf start-up once a controller is detected and to monitor ip2uart statistics. - Establish an IP path between the nodes (direct Wi-Fi, Ethernet tether, or VPN).
- Use autod’s SSE feeds to visualize link health in the browser UI and surface alarms if cadence deviates from the expected rate configured in ip2uart.
- autod’s HTTP API (documented in
ip2uart can operate on either side of the network tunnel, enabling several CRSF workflows beyond direct joystick control.
- Attach an ExpressLRS receiver to the ground station via USB/UART.
- Configure
/etc/ip2uart.confwithuart_backend=ttypointed at the receiver and the appropriate UDP peer settings to stream frames outward. - Run autod to expose the helper’s status and, if desired, relay the CRSF stream to other tools through its
/udpendpoint or SSE announcements.
Use case: Bench testing a vehicle or forwarding live CRSF telemetry to a remote simulator while the airborne radio stays offline.
- Generate CRSF frames with joystick2crsf using the calibrated controller profile.
- Configure ip2uart in transmit mode (UART connected to the ELRS transmitter module) and point the UDP peer at the ground station.
- Employ autod to synchronize process lifecycles, adjust channel maps remotely, and log statistics for later analysis.
Use case: Replace a handheld transmitter with a networked ground station while retaining ELRS compatibility.
- Run ip2uart directly on the vehicle with
uart_backend=ttyconfigured for UDP peer mode to mirror CRSF traffic back to the ground station. - Capture and inspect the stream with another ip2uart or a UDP consumer to validate timing, packet integrity, and channel values.
Use case: Debugging intermittent control issues without modifying the primary control loop.
Budget USB controllers need careful tuning before they can safely drive CRSF outputs:
- Axis Mapping: SDL2 numbering may not match Linux joystick indices. Verify each axis/button using autod’s diagnostics or the
joystick2crsfconsole output and adjust themap[]array in the config. - Deadband and Drift: Cheap sticks drift; set the
dead[]entries (typically 2–5%) to avoid unintended motion. Revisit these values after transport or temperature changes. - Inversion and Range: Ensure throttle/tilt axes run in the expected direction by toggling
invert[]. Calibrate min/max travel so CRSF outputs span 172–1811. - Button Debounce: Some controllers register spurious presses. Use the
arm_togglelatch and hold timing to prevent accidental arming, and prefer long-press mappings for critical modes.
The build system already supports ARM cross compilation (make musl or make gnu). Porting to Android involves:
- Compiling the binaries for ARM64 and running them inside Termux or a similar environment.
- Granting USB permissions for SDL2 joystick access and for UART adapters attached via OTG.
- Keeping autod in the foreground or using Android’s background service allowances to host the CivetWeb HTTP endpoints and static UI.
- Monitoring power draw, as continual 250 Hz polling and Wi-Fi radios can tax phone batteries faster than on a Radxa SBC.
Each sync slot carries a generation counter that increments whenever the master’s slot configuration changes. Slot commands are delivered with that generation, and a slave reports the most recent one it executed as ack_generation on /sync/register.
The master uses this acknowledgement to decide whether to resend commands:
- If the acknowledgement is lower than the slot generation, the slave is behind and the commands are replayed.
- If the values match, the slave has already run the latest script and replay is skipped to avoid double execution.
- When a node moves between slots or reports an acknowledgement higher than the new slot’s generation (common when moving from a long-lived slot to a fresh one), the stored acknowledgement is cleared so the new slot’s commands are always sent.
This generation-based guard prevents stale acknowledgements from suppressing the scripts that should run after a slot move, without requiring per-command bookkeeping. A more foolproof alternative would be hashing the slot’s command bundle and replaying whenever the hash changes, but the generation counter already captures that intent with less state and payload.
autod orchestrates the joystick2crsf and ip2uart helpers so that low-cost controllers, CRSF radios, and IP links interoperate. By leveraging C-based utilities with small footprints, the same workflow can stretch from bench-top testing to field deployments, supporting joystick-driven control, CRSF forwarding, transmitter emulation, and diagnostic loopbacks without switching toolchains.