Complete guide for setting up OpenFlight on a Raspberry Pi 5 with the 7" touchscreen display.
Make sure you have all the hardware. See the Parts List for what to buy.
Required:
- Raspberry Pi 5 (4GB+ recommended)
- 7" Touchscreen Display
- MicroSD Card (32GB+)
- 27W USB-C Power Supply (official Pi 5 PSU recommended)
- OPS243-A Doppler Radar + USB cable
- SparkFun SEN-14262 sound detector (wired per the Sound Trigger Wiring Guide)
Optional (deprecated):
- K-LD7 + FTDI adapter (×2) — for launch angle and club path (see Parts List). Deprecated — superseded by a more capable radar chip; don't buy for a new build. Supported for existing builds only.
Use Raspberry Pi Imager to flash Raspberry Pi OS (64-bit) to your SD card.
Plug in the OPS243-A (and the K-LD7 adapters if you have them), then:
cd ~
git clone https://github.com/jewbetcha/openflight.git
cd openflight
./scripts/setup/setup.shThe script installs everything, then walks you through the one-time hardware configuration with prompts:
- Dependencies — Python venv, packages, UI build, test run
- OPS243-A radar — saves rolling buffer mode to the radar's flash (you'll be asked to unplug/replug the radar once)
- K-LD7 radars (deprecated; if you have them) — identifies each radar by plugging them in one at a time, so OpenFlight always knows which is which
- Auto-start on boot — optional systemd service
- Desktop shortcut — optional
Every step can be skipped and the script is safe to re-run any time — it picks up where you left off.
./scripts/start-kiosk.sh # Default: rolling buffer + sound trigger
./scripts/start-kiosk.sh --kld7 # With K-LD7 angle radars (deprecated)
./scripts/start-kiosk.sh --mock # Mock mode (no hardware)Then open http://localhost:8080 or use the touchscreen.
To send shots to GSPro, OpenGolfSim, or another supported sim, copy the example config and enable your simulator:
cp config/sim.example.json config/sim.json # then edit host/port + "enabled": trueSee Simulator Connectors for the full guide.
You don't need this section unless something went wrong or you prefer to do things by hand.
The OPS243-A needs a one-time configuration to enable rolling buffer mode with hardware sound triggering, saved to flash so it boots correctly every time.
Why? The OPS243-A has a firmware bug where the HOST_INT pin mode switches unexpectedly when entering rolling buffer mode at runtime. Saving to flash and power cycling bypasses this. Confirmed by OmniPreSense engineering.
Manual steps
# 1. Configure and save to flash
uv run python scripts/hardware-test/test_rolling_buffer_persist.py --setup
# 2. Power cycle the radar, wait 3 seconds, reconnect (see note below)
# 3. Verify — make a sound near the SEN-14262, you should see I/Q trigger data
uv run python scripts/hardware-test/test_rolling_buffer_persist.py --testHow you power cycle depends on how the OPS243 is connected:
| Connection | Power cycle by |
|---|---|
| USB | Unplugging the USB cable |
| Pi GPIO UART | Disconnecting 5V from OPS J3 pin 9 — not by rebooting the Pi, which does not necessarily drop the header rail |
If the OPS243 is on the GPIO UART, add --port /dev/ttyAMA0 to both commands
above.
The setup script does not configure the IWR6843 — it needs custom firmware flashed over the ROM bootloader, which requires physically moving a switch on the board. That is covered end to end in the IWR6843 Operator Guide.
Do it in this order, and confirm each step works before starting the next:
- Move the OPS243 to the Pi GPIO UART — migration guide. The Pi cannot power both radars over USB, so the OPS243 has to vacate the USB port. Validate the OPS on its own after rewiring, before the TI board is involved at all.
- Flash the IWR6843 — operator guide, Flash The IWR6843 Firmware. A
validated prebuilt image is in
firmware/releases/, so the TI toolchain is not required. You only need the firmware developer guide to build from source. - Mount, aim, and measure geometry — operator guide. The geometry values are passed on the command line and a wrong one silently biases the launch angle instead of erroring, so measure rather than estimate.
Re-flashing is only needed if the image in firmware/releases/ changes. A
software update alone does not require it — compare the release filename against
what you flashed.
Warning
A WiFi-equipped OPS243-A cannot use the GPIO UART. Its WiFi module already drives the radar processor's UART receive line, so the Pi cannot send it commands, and OpenFlight must be able to reconfigure and rearm the OPS after every capture. Use a separately powered USB hub for both radars instead (operator guide, Option B).
USB serial adapters can swap between /dev/ttyUSB0 and /dev/ttyUSB1 after a
reboot, so OpenFlight needs fixed names (/dev/kld7_vertical and
/dev/kld7_horizontal) to tell the two radars apart. The wizard handles this —
you just plug each radar in when asked:
./scripts/setup/setup_kld7_devices.sh # run / redo the mapping
./scripts/setup/setup_kld7_devices.sh --show # check the current mappingIt also installs the FTDI low-latency rule (the K-LD7 RADC stream runs at
3 Mbaud and needs latency_timer=1ms instead of the Linux default 16ms).
On startup, the server logs should show both radars at 1ms:
[KLD7:vertical] USB serial latency_timer=1ms ...
[KLD7:horizontal] USB serial latency_timer=1ms ...
Manual steps (what the wizard does)
Find each adapter's serial number:
udevadm info -a /dev/ttyUSB0 | grep '{serial}' | head -1
udevadm info -a /dev/ttyUSB1 | grep '{serial}' | head -1Create a udev rule with the serial numbers (replace FTXXXXXX/FTYYYYYY):
sudo tee /etc/udev/rules.d/99-kld7.rules << 'EOF'
SUBSYSTEM=="tty", ATTRS{serial}=="FTXXXXXX", SYMLINK+="kld7_vertical"
SUBSYSTEM=="tty", ATTRS{serial}=="FTYYYYYY", SYMLINK+="kld7_horizontal"
EOF
sudo udevadm control --reload-rules && sudo udevadm triggerThen install the latency rule:
sudo scripts/setup/setup_kld7_latency.shUse --dry-run to preview the rule, or --all-ftdi if the /dev/kld7_*
names aren't set up yet.
The setup script installs and enables a systemd service configured for your username and install path.
Manual steps and service management
# Install (adjust User= and paths in the file if your username isn't the default)
sudo cp ~/openflight/scripts/setup/openflight.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable openflight
sudo systemctl start openflightManagement:
sudo systemctl status openflight --no-pager # Check status
journalctl -u openflight -f # View logs
sudo systemctl stop openflight # Stop
sudo systemctl restart openflight # Restart
sudo systemctl disable openflight # Disable auto-startTo modify the service:
sudo nano /etc/systemd/system/openflight.service
sudo systemctl daemon-reload
sudo systemctl restart openflight
⚠️ DEPRECATED: The K-LD7 angle radars are deprecated — this section applies to existing K-LD7 builds only.
- Vertical unit — measures launch angle. Mount with the antenna plane vertical, aimed at the hitting area.
- Horizontal unit — measures club path / aim direction. Mount with the antenna plane horizontal.
Both should be positioned near the OPS243-A, 3-5 feet behind the tee.
The vertical K-LD7 geometry estimator needs the physical mount tilt, ball-to-radar distance, and boresight offset to match real launch angles.
- Start a session with
--kld7-geometry - Hit 5-10 shots with a known club (7-iron recommended)
- Compare reported launch angles to expected values:
- Wedge: 24-30°, 7-iron: 16-18°, 5-iron: 12-14°, Driver: 10-14°
- Keep
--kld7-mount-tiltand--kld7-ball-distancematched to the physical setup; adjust--kld7-angle-offsetfor a stable boresight bias.
The current field preset is mount tilt 10°, ball distance 5ft, and angle
offset +2.5°. The exact values depend on your mounting position.
See K-LD7 Troubleshooting for more details.
./scripts/start-kiosk.sh # Default: rolling buffer + sound trigger
./scripts/start-kiosk.sh --kld7-geometry # With K-LD7 launch-angle geometry defaults
./scripts/start-kiosk.sh --mock # Mock mode (no hardware needed)openflight-server # With radar
openflight-server --mock # No hardwareThen open http://localhost:8080.
DISPLAY=:0 ./scripts/start-kiosk.shOpenFlight can ship session logs to Grafana Cloud for long-term analysis.
sudo ./scripts/setup/setup_alloy.sh
sudo vim /etc/alloy/credentials.envSee observability.md for full setup and LogQL queries.
ls /dev/ttyACM* /dev/ttyUSB*
openflight --port /dev/ttyACM0 --infoSee the Sound Trigger Wiring Guide — Troubleshooting.
# Check the device mapping
./scripts/setup/setup_kld7_devices.sh --show
# Test standalone
uv run python scripts/hardware-test/test_kld7.pyIf the mapping is missing or points at the wrong radar, re-run the wizard:
./scripts/setup/setup_kld7_devices.sh. Look for [KLD7] Connected on /dev/ttyUSB... in the server logs. See K-LD7 Troubleshooting
for "Wrong length reply" and other connection issues.
journalctl -u openflight --no-pager -n 50
# If service is masked
sudo systemctl unmask openflight
sudo cp ~/openflight/scripts/setup/openflight.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable openflightCheck for WebSocket instability:
journalctl -u openflight -fLook for "Client disconnected/connected" messages.
Use DISPLAY=:0 prefix for commands that need the Pi's display.
./scripts/start-kiosk.sh # Default
./scripts/start-kiosk.sh --mock # No hardware
./scripts/start-kiosk.sh --kld7-geometry # With angle radar
./scripts/start-kiosk.sh --port 3000 # Custom portopenflight-server # Start with radar
openflight-server --mock # Mock mode
openflight-server --web-port 3000 # Custom port./scripts/setup/setup.sh # Full interactive setup (re-run safe)
./scripts/setup/setup.sh --deps-only # Dependencies only
./scripts/setup/setup_kld7_devices.sh # K-LD7 device naming wizard
./scripts/setup/setup_kld7_devices.sh --show # Show current K-LD7 mappinguv run python scripts/hardware-test/test_rolling_buffer_persist.py --test # Sound trigger
uv run python scripts/hardware-test/test_sound_trigger_hardware.py # Direct trigger test
uv run python scripts/hardware-test/test_kld7.py # K-LD7 standalone
uv run pytest tests/ -v # Full test suite