A wireless head tracker that converts head movements into RC radio commands for FPV flying. Pitch, roll, and yaw from your head are sent as a PPM signal to your radio's trainer port β no PC required.
πΊ YouTube flight test:
π¨οΈ 3D printable case: Thingiverse #7389266
Head movement β BNO085 IMU β ESP32-C3 β PPM signal β 3.5mm cable β Radio trainer port β Sim / Aircraft
The head tracker plugs into the trainer port of any EdgeTX/OpenTX radio. A switch on the radio toggles between normal stick control and head-tracker input, so you can always take over manually.
- β Software emulator with 3D web interface
- β PPM output via RMT hardware (jitter free)
- β BNO085 real IMU firmware tested working
- β Calibration button for zeroing axes
- β Passive buzzer feedback (startup melody, calibration, errors, battery warnings)
- β Battery voltage monitoring with low/critical beep warnings
- β 3D printed case with headset mount and handheld variants β Thingiverse
- β Final assembly complete
- β³ Field testing (see YouTube channel for updates)
headtracking-module/
βββ 01-head_tracker_emulator/ # PPM emulator with WiFi + 3D web UI (test without IMU)
βββ 02-bno085_wiring_test/ # Verify BNO085 wiring and axis directions
βββ 03-buzzer_test/ # Test buzzer patterns and find resonant frequency
βββ 04-battery_wifi_test/ # Battery voltage monitor with web UI for calibration
βββ 05-head_tracker_bno085/ # Main firmware β IMU + PPM + buzzer + battery
βββ images/
β βββ wiring_diagram.svg # Wiring schematic
βββ ppm_demo.html # Interactive visualisation of PPM signal
βββ README.md
βββ WIRING.md # Detailed wiring guide
βββ LICENSE
The sketches are numbered in the recommended build order β flash and test each one to verify each subsystem before moving to the main firmware.
| Part | Details |
|---|---|
| ESP32-C3 SuperMini | Main controller |
| TENSTAR BNO085 | 9-axis IMU, VR/AR optimised |
| 1S Li-ion / LiPo battery | Power (any 3.7V battery works) |
| TP4056 module | Battery charging via USB-C |
| Slider switch | Power on/off |
| 6x6mm momentary button | IMU calibration |
| Passive buzzer | Audio feedback (~2kHz resonant) |
| 2x 10kΞ© resistors | Battery voltage divider |
| PJ-341 3.5mm TRS jack | Panel mount in case |
| Vention 3.5mm TRS cable 1.5m | PPM signal to radio |
| Perfboard 4x6cm | Switch/button mounting |
| Velcro straps | Goggle mounting |
See WIRING.md for the complete wiring diagram and pin assignments. 3D printable case files on Thingiverse.
- Add ESP32 board URL in File β Preferences β Additional Board Manager URLs:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
-
Install esp32 by Espressif Systems via Board Manager
-
Select board: ESP32C3 Dev Module
-
Set USB CDC On Boot β Enabled
-
Install libraries via Library Manager:
- WebSockets by Markus Sattler (needed for
01-head_tracker_emulatorand04-battery_wifi_test) - Adafruit BNO08x (needed for BNO085 firmware)
- Adafruit Unified Sensor (dependency)
- Adafruit BusIO (dependency)
- WebSockets by Markus Sattler (needed for
The numbered folders correspond to the recommended build sequence. Flash and verify each one before moving to the next:
| Step | Sketch | What to verify |
|---|---|---|
| 1 | 01-head_tracker_emulator |
PPM output reaches radio, channels respond in EdgeTX channel monitor |
| 2 | 02-bno085_wiring_test |
I2C communication works, all 3 axes respond to tilt |
| 3 | 03-buzzer_test |
Buzzer sounds, find loudest frequency for your specific buzzer |
| 4 | 04-battery_wifi_test |
Calibrate voltage divider against multimeter reading |
| 5 | 05-head_tracker_bno085 |
Main firmware with your calibrated values |
- Wire all components per WIRING.md
- Adjust calibration values in
05-head_tracker_bno085.ino:DIVIDER_RATIOβ from your resistor measurementsADC_CORRECTIONandBATT_CALIBβ from battery test sketchINVERT_PITCH/ROLL/YAWβ from BNO085 axis test
- Flash to ESP32-C3
- Power on β listen for the startup melody (4 ascending notes)
- Connect 3.5mm cable to radio trainer port
- In EdgeTX: Model β Trainer β Master/PPM, assign enable switch
- Put on headset, look forward, press calibration button (1 beep confirms)
- Fly!
| Sound | Meaning |
|---|---|
| 4 ascending notes (C-E-G-C) | Startup β board ready |
| 1 beep | Calibration confirmed |
| 2 spaced beeps | Battery LOW (<3.5V) β every 30s |
| 5 fast beeps | Battery CRITICAL (<3.3V) β every 5s |
| 1 long beep (looping) | BNO085 not found |
For testing without hardware (just ESP32 + 3.5mm cable β no IMU needed):
- Flash
01-head_tracker_emulator.inoto ESP32 - Connect to WiFi:
HeadTracker/ password:flyfpv123 - Open browser:
http://192.168.4.1 - Drag 3D head to control axes
Web UI controls:
| Input | Axis |
|---|---|
| Left drag horizontal | Yaw |
| Left drag vertical | Pitch |
| Right drag horizontal | Roll |
| Q / E keys | Roll left / right |
| Arrow keys | Pitch + Yaw |
| RESET CENTER button | Zero all axes |
| Channel | Axis | Range |
|---|---|---|
| CH1 | Roll (lean left/right) | Β±45Β° |
| CH2 | Pitch (nod up/down) | Β±45Β° |
| CH3 | β center β | |
| CH4 | Yaw (turn left/right) | Β±90Β° |
| CH5β8 | β center β |
- PPM pulse width: 300Β΅s (matches Radiomaster default)
- PPM frame: 22,500Β΅s
See ppm_demo.html for an interactive visualisation of the PPM signal.
- Connect 3.5mm TRS cable to trainer port on top of the radio
- Model Settings β Trainer β Master/PPM
- Map channels: CH1=Roll, CH2=Pitch, CH4=Yaw
- Assign trainer enable switch (SD or SH recommended for momentary override)
- Flip switch to activate head tracking, flip back for full stick control
Works with any EdgeTX/OpenTX radio that has a 3.5mm PPM trainer port.
Uses the ESP32 RMT (Remote Control Transceiver) hardware peripheral via a dedicated FreeRTOS task β pulse timing is handled in hardware, immune to WiFi stack interruptions.
Key details:
rmtSetEOT(PPM_PIN, 1)keeps pin HIGH between frames (prevents CH1 jitter)- Channel values updated atomically (locals computed first, then assigned)
- Exponential smoothing on all axes
The ESP32-C3 ADC needs some care:
- Use GPIO 0 for the battery ADC input β GPIO 2 has a hardware pullup that pollutes readings
- Firmware includes
ADC_CORRECTIONto compensate for ADC nonlinearity at 11dB attenuation - The
04-battery_wifi_testsketch helps calibrate all three values (divider ratio, ADC correction, offset) via a live web interface
In 05-head_tracker_bno085.ino, flip flags if an axis is backwards for your mount:
#define INVERT_PITCH false
#define INVERT_ROLL false
#define INVERT_YAW trueAdjust deflection range:
#define PITCH_RANGE 45.0f // Β±45Β° = full stick deflection
#define ROLL_RANGE 45.0f
#define YAW_RANGE 90.0f#define SMOOTH 0.5f // 0.1 = very smooth, 1.0 = no smoothingDetermined empirically using 04-battery_wifi_test:
#define DIVIDER_RATIO 2.077f // (R1 + R2) / R2
#define ADC_CORRECTION 0.886f // ESP32-C3 ADC nonlinearity fix
#define BATT_CALIB -0.13f // fine-tune offsetAny radio with a 3.5mm PPM trainer port and EdgeTX/OpenTX:
- RadioMaster TX16S β (tested)
- RadioMaster Pocket ELRS
- RadioMaster Zorro
- RadioMaster Boxer
- Any EdgeTX/OpenTX radio with trainer port
- πΊ YouTube build video: https://youtu.be/ZS9i_QNzIO0
- πΊ YouTube flight test: coming soon
- π₯ Channel: @flyuafpv
- π¨οΈ 3D printable case: Thingiverse #7389266
- πΎ Source code: this repo
MIT β see LICENSE
