Skip to content

Add FrSky BLE trainer (head tracker) support to the TX backpack - #248

Open
Amigache wants to merge 1 commit into
ExpressLRS:masterfrom
Amigache:feat/ble-headtracker
Open

Amigache wants to merge 1 commit into
ExpressLRS:masterfrom
Amigache:feat/ble-headtracker

Conversation

@Amigache

@Amigache Amigache commented Oct 5, 2026

Copy link
Copy Markdown

Summary

Adds an opt-in BLE central to the TX backpack so a device advertising the
FrSky trainer service (0xFFF0 / 0xFFF6) - most notably a HeadTracker
in Wireless Transmitter (Head) mode - can feed the radio's trainer input
without the BTWifiModule hardware.

The backpack connects over BLE, parses the FrSky trainer frame (8 channels),
converts it to CRSF values and emits MSP_ELRS_BACKPACK_SET_PTR to the TX
module over the backpack UART. The module already understands that opcode
(HT Enabled / HT Start Channel = EdgeTX in the ELRS Lua), so no ExpressLRS
change is required
for the core feature.

Although head tracking is the main use case, the implementation is generic:
any FrSky trainer source works. Hence the name FrSky BLE trainer
(BleTrainer).

Scope

  • TX backpack only (ESP32-C3; the component is also valid for ESP32/S3).
    VRX and Timer backpacks are unchanged.
  • Opt-in per target via HAS_BLE_TRAINER; currently enabled for
    ESP32C3_TX_Backpack_via_UART.
  • The native (ESP-NOW/VRX) head-tracking path is untouched; a source selector
    chooses Auto / Native / BLE.

Changes

  • src/BleTrainer.cpp + include/BleTrainer.h: NimBLE central (scan for
    0xFFF0, pair strongest RSSI, store MAC, reconnect on boot, Forget),
    per-notification FrSky parser (0x7E/0x7D byte-stuff, 0x80 + 8 channels,
    XOR CRC), PWM->CRSF conversion and SET_PTR emission.
  • Steady 50 Hz republication of the last frame, so EdgeTX's trainer input does
    not time out between irregular BLE notifications.
  • Connection params: 8 ms default interval (re-applied ~1.2 s after connect,
    since the trainer asks for its own first) and 4 s supervision timeout.
  • BLE + MAVLink WiFi coexistence: esp_coex_preference_set(ESP_COEX_PREFER_WIFI)
    and reduced BLE TX power (P3) in MAVLINK_TX.
  • Config (v6): trainerPeerMac / trainerPeerType / trainerIntervalMs /
    trainerSource (Auto / Native / BLE).
  • WebUI tab FrSky BLE Trainer (status, paired MAC, interval, Forget, source)
    using the existing /config + callback() patterns; new POST /settrainer;
    JSON config.trainer.
  • Long button press forgets the paired peer.
  • HAS_BLE_TRAINER + h2zero/NimBLE-Arduino for the C3 TX backpack target.
  • MAVLink WiFi mode is now persistent by telemMode (restored on every boot),
    so entering the updater no longer leaves the backpack without an AP.
  • Dedup on a repeated MSP_ELRS_BACKPACK_CONFIG telemetry-mode message so the
    module cannot trigger a reboot loop.

Testing

Hardware: RadioMaster Nomad (ESP32-C3 TX backpack) + TX16S + Arduino Nano 33 BLE
running HeadTracker (Wireless Transmitter / Head); Mission Planner as GCS.

Validated:

  • Trainer: linked:true, SET_PTR ~ 50/s (setptr 5917 -> 6119 in 4 s),
    EdgeTX trainer channels move.
  • HT + MAVLink WiFi simultaneously: 10-min soak, 0 resets, 0 BLE
    disconnects, GCS telemetry continuous (packets_down ~ 55/s,
    overflows_down: 0).
  • Module power 10/25/50/100/250 mW stable; cold boot at 250 mW; updater
    enter/exit returns to MAVLink WiFi.
  • Build: 1.186.832 B (60.4 % of min_spiffs), RAM 55.980 B.

Notes for reviewers

  • Naming is intentionally generic (FrSky BLE trainer): it implements the
    FrSky trainer profile, not head tracking specifically. Head tracker is
    documented as the main use case.
  • NimBLE-Arduino stays on the 1.4.x branch because the repo pins
    espressif32@6.7.0 (Arduino-ESP32 2.0.x / IDF 4.4); 2.x needs a newer core.
  • Config layout is additive within v6: no EEPROM migration and no need to
    re-pair.
  • In Link Mode = MAVLINK the trainer needs a live RF link: without a linked
    receiver the module does not drain its UART FIFO. Worth a user-facing note;
    not a code issue.

Related

  • Optional follow-up (separate ExpressLRS PR): a Forget trainer action in the
    Lua Backpack menu. The Lua builds the menu from the module's parameter
    structure, so only a module-side field plus a new MSP is needed. Not required
    for this PR.

Adds an opt-in BLE central to the TX backpack so a device advertising the
FrSky trainer service (0xFFF0 / 0xFFF6), most notably a HeadTracker in
"Wireless Transmitter (Head)" mode, can feed the radio's trainer input
without the BTWifiModule hardware.

The backpack connects over BLE, parses the FrSky trainer frame (8 channels),
converts it to CRSF values and emits MSP_ELRS_BACKPACK_SET_PTR to the TX
module over the backpack UART. The module already understands that opcode
(HT Enabled / HT Start Channel = EdgeTX), so no ExpressLRS change is needed
for the core feature.

Although head tracking is the main use case, the implementation is generic:
any FrSky trainer source works, hence the name "FrSky BLE trainer".

Highlights:
- src/BleTrainer.cpp + include/BleTrainer.h: NimBLE central (scan 0xFFF0,
  pair strongest RSSI, store MAC, reconnect on boot, Forget), per-notification
  FrSky parser, PWM->CRSF conversion, SET_PTR emission.
- Steady 50 Hz republication so EdgeTX's trainer input does not time out.
- 8 ms default connection interval, 4 s supervision timeout.
- BLE + MAVLink WiFi coexistence (WiFi preference, BLE P3 in MAVLINK_TX).
- Config v6: trainerPeerMac/Type/IntervalMs/Source (Auto/Native/BLE).
- WebUI tab "FrSky BLE Trainer"; POST /settrainer; JSON config.trainer.
- Long button press forgets the peer.
- HAS_BLE_TRAINER + NimBLE-Arduino for the C3 TX backpack target.
- MAVLink WiFi mode persistent across boots (telemMode).
- Dedup repeated MSP_ELRS_BACKPACK_CONFIG telemetry-mode to avoid reboot loops.

Validated on the Nomad: SET_PTR ~50/s, EdgeTX trainer channels move, HT +
MAVLink WiFi simultaneously (10-min soak, 0 resets), module power up to
250 mW, cold boot and updater cycle. Build: ~1.19 MB (60.4% of min_spiffs),
RAM 55.980 B.
@rotorman

rotorman commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Amigache added a commit to Amigache/Backpack that referenced this pull request Oct 5, 2026
- Config v7: add bleTrainerEnable (default Off). Bumping the version resets
  the backpack EEPROM (re-pair required).
- Handle MSP_ELRS_BACKPACK_CONFIG_BLE_TRAINER (enable) and
  MSP_ELRS_BACKPACK_TRAINER_PAIR (scan and pair with the strongest).
- BLE trainer is now lazily initialized only when enabled; disabling stops
  all activity (no scan/connect). It auto-connects to the paired peer and
  never drops the stored MAC on connection failure (manual pairing).
- WebUI tab: Enable checkbox and Pair button; /config exposes "enabled".

Built on top of the FrSky BLE trainer feature (ExpressLRS#248).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants