Repository navigation
feat(ops243): add internal hardware trigger - #221
Conversation
Updated the Raspberry Pi retest checklist to clarify merging requirements and added a note about regression testing the sound trigger.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in OPS243 internal hardware-trigger strategy for rolling-buffer capture, threading new configuration flags through the kiosk script and server so installations without the external sound-trigger module can still capture I/Q history (while keeping sound-trigger as the default).
Changes:
- Add OPS243 driver support for configuring, waiting on, parsing, and re-arming the internal speed-trigger rolling-buffer dump (firmware-gated to v1.3.1).
- Extend server/kiosk CLI plumbing with
--trigger hardwareplus hardware-mode defaults/overrides (threshold, magnitude, pre-trigger split) and corresponding validation. - Add focused tests and documentation for the new trigger mode and its operational constraints.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_start_kiosk.py | Verifies kiosk CLI forwarding behavior for hardware trigger defaults and overrides. |
| tests/test_server.py | Adds server CLI parsing/forwarding tests for hardware trigger mode and its constraints. |
| tests/test_rolling_buffer.py | Registers/validates the new trigger strategy and adds behavior tests for accept/reject/re-arm flows. |
| tests/test_ops243.py | Adds focused OPS243 command-ordering, validation, re-arm, and recovery tests for internal triggering. |
| src/openflight/server.py | Adds hardware trigger CLI flags/validation and threads trigger kwargs into monitor startup. |
| src/openflight/rolling_buffer/trigger.py | Implements HardwareTriggeredCapture strategy around board-managed trigger + host-side validation + re-arm. |
| src/openflight/rolling_buffer/monitor.py | Configures OPS243 internal trigger on connect when trigger_type == "hardware". |
| src/openflight/rolling_buffer/init.py | Exposes HardwareTriggeredCapture from the rolling_buffer package. |
| src/openflight/ops243.py | Implements firmware validation, internal-trigger configuration, re-arm, and UART recovery behavior. |
| scripts/start-kiosk.sh | Adds kiosk flag parsing and forwarding for hardware-trigger-specific settings. |
| README.md | Documents the new opt-in hardware trigger mode and links to the new guide. |
| docs/rolling_buffer_spin_detection.md | Clarifies the default sound-trigger pipeline and references the new hardware trigger option. |
| docs/hardware-trigger.md | New guide describing command ordering, defaults, and retest expectations for internal triggering. |
| docs/CHANGELOG.md | Adds an Unreleased changelog entry describing the new hardware trigger mode and constraints. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
This is great, and much needed. However we are waiting on Omnipresence for a way to update OPS radars to this version that doesn't require their extra device. Will ping them again to see if this is a realistic ask. |
|
So do we have the hex file to flash with the new firmware? |
Send an email to Rob at customerservice@omnipresense.com. He will be able to get you the file once it's public and help you find the correct model SEGGER. |
|
Review note: Correcting my earlier version of this comment: I claimed boards reporting 1.5.2 would be blocked today. AN-010 rev AE Table 9 shows the OPS243-A line runs 1.2.3 → 1.3.1, with the |
|
Docs suggestion for
The J2 JTAG header physical is: When shopping for cables for your SEGGER programmer. Also worth a warning: AN-013 says never press Erase — it wipes factory settings and persistent memory, which is where Not blocking; the code change is unaffected. |
Possible cheap path. The OPS243's I/O is 3.3 V (this repo already wires Pi GPIO straight to J3 with no level shifter), and the XMC4500/4700 speaks SWD, which OpenOCD supports via its in-tree Caveats: XMCFlasher won't drive a CMSIS-DAP probe, so this is OpenOCD-only and off the vendor-supported path. Unverified on this board: BMI/boot-mode handling (J-Link does it automatically) and whether the part is read-protected. J2 is a keyed fine-pitch header, so it needs a proper cable — worth asking OmniPreSense for the pinout. Vendor-blessed alternative: the SEGGER J-Link EDU Mini, which AN-013 links directly (adafruit.com/product/3571, $75.95). Worth 30 minutes on a spare board. I wouldn't try it on the only radar in a build. |
Email sent! |
|
Instructions on how to apply the firmware update: |
…hain to the DS3502 PR open-flight#221 adds an opt-in OPS243 internal hardware trigger (firmware v1.3.1) that needs no SEN-14262, so call it out in the Sound Trigger section as an optional path that is not merged yet, and say under the Cost Summary that the Sound Trigger line drops out on that path. Totals are unchanged. Document that the Qwiic-to-Dupont cable's chain can continue from the LIS3DH's second STEMMA QT socket further down to the DS3502 digital potentiometer, and give the DS3502 row that wiring plan plus the note that it is moot if the sound trigger goes away. Claude-Session: https://claude.ai/code/session_01Y55yaLV4h2SVf5EVHN7Yz8 Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
…hain to the DS3502 PR open-flight#221 adds an opt-in OPS243 internal hardware trigger (firmware v1.3.1) that needs no SEN-14262, so call it out in the Sound Trigger section as an optional path that is not merged yet, and say under the Cost Summary that the Sound Trigger line drops out on that path. Totals are unchanged. Document that the Qwiic-to-Dupont cable's chain can continue from the LIS3DH's second STEMMA QT socket further down to the DS3502 digital potentiometer, and give the DS3502 row that wiring plan plus the note that it is moot if the sound trigger goes away. Claude-Session: https://claude.ai/code/session_01Y55yaLV4h2SVf5EVHN7Yz8 Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
|
New information from Sandy on the OPS team in the discord server:
JedS — 20:39
So we would need this code to gate to >1.3.2 and hoepfully we will be able to point people to update it - also we need to update the AN-XXX documents included in our repo when the new ones are released and point people in the direction on how to update their boards. |
|
@JPMarhefka Also was succesfull upgrading mine to 1.3.2 - I got an SEGGER jlink EDU mini and for that the Jlink driver install process need to be run as adminstrator and the legacy jlink usb driver need to be ticked when installing that. Other than that it went well and verified with Putty. |
Put the deprecated K-LD7 parts and wiring and the filament-estimate paragraph in collapsible details blocks. Remove the DS3502 digital potentiometer, its Qwiic-to-Qwiic cable, its two cable-length rows and the chain sentence in the Qwiic-to-Dupont row: the internal trigger in PR open-flight#221 makes a software gain trim on the sound detector moot. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Add the 200 mm Raspberry Pi display cable (SC1131) the Touch Display 2 needs in the openflight-enclosure v3 case, where the Pi and UPS mount to the shell rather than the screen and the ~100 mm ribbon in the box does not reach (openflight-enclosure#11). Document power input on the v3 case: the shell has no USB-C opening, so a USB-C supply is captive and a detachable supply is the DC route. Add the no-solder parts for it (a pre-wired 5.5 x 2.1 mm panel jack, Tensility 10-03609 on Mouser or Amazon DC-099 kits; two Wago 221-412 for the XH header option; the Adafruit 369 screw-terminal plug for the barrel option into the X1202's own jack, with the plug dimensions), fed at 12 V because the XH input is rated for about 3 A. Note that 5 A USB-C pass-throughs are hard to find. Give every Mouser ProductDetail link the ?qs= token from the shared Mouser project, which is the form Mouser still resolves, add Amazon options beside them, and move the Qwiic-to-Dupont row to the SparkFun CAB-17261 the project carries. Move every measured distance out of the product rows into the collapsible cable-lengths section and replace the v2 table with runs measured on the v3 STEP, including the display and camera ribbons, Ethernet, both DC jack options and both button-hole options. Record that both rear holes in the v3 shell are 12.5 mm, so the 16 mm Adafruit 1445 needs the CAD hole opened or a 12 mm button. Replace the v2 insert and screw table with a link to the enclosure repository's Required hardware page, re-base the filament estimate on the v3 print set from the STL mesh volumes (~750 g PETG, ~$17) inside a collapsible block, and fold the deprecated K-LD7 section into one too. Drop the DS3502 potentiometer and its cable, since the internal trigger in PR open-flight#221 makes a software gain trim moot; drop the IARC case and the X1206 input-range comparison; correct the internal-trigger firmware to OPS243-A 1.3.2. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Add the 200 mm Raspberry Pi display cable (SC1131) the Touch Display 2 needs in the openflight-enclosure v3 case, where the Pi and UPS mount to the shell rather than the screen and the ~100 mm ribbon in the box does not reach (openflight-enclosure#11). Document power input on the v3 case: the shell has no USB-C opening, so a USB-C supply is captive and a detachable supply is the DC route. Add the no-solder parts for it (a pre-wired 5.5 x 2.1 mm panel jack, Tensility 10-03609 on Mouser or Amazon DC-099 kits; two Wago 221-412 for the XH header option; the Adafruit 369 screw-terminal plug for the barrel option into the X1202's own jack, with the plug dimensions), fed at 12 V because the XH input is rated for about 3 A. Note that 5 A USB-C pass-throughs are hard to find. Give every Mouser ProductDetail link the ?qs= token from the shared Mouser project, which is the form Mouser still resolves, add Amazon options beside them, and move the Qwiic-to-Dupont row to the SparkFun CAB-17261 the project carries. Move every measured distance out of the product rows into the collapsible cable-lengths section and replace the v2 table with runs measured on the v3 STEP, including the display and camera ribbons, Ethernet, both DC jack options and both button-hole options. Record that both rear holes in the v3 shell are 12.5 mm, so the 16 mm Adafruit 1445 needs the CAD hole opened or a 12 mm button. Replace the v2 insert and screw table with a link to the enclosure repository's Required hardware page, re-base the filament estimate on the v3 print set from the STL mesh volumes (~750 g PETG, ~$17) inside a collapsible block, and fold the deprecated K-LD7 section into one too. Explain the PR open-flight#221 internal-trigger path next to the sound trigger: the OPS243-A fires the capture itself on firmware 1.3.2, every unit can be brought to that release, sensors shipping since September 2026 arrive with it, and older ones are flashed over the J2 JTAG header with a SEGGER J-Link and Infineon's XMCFlasher per AN-013 with the hex from OmniPreSense support. Add the J-Link EDU Mini row (about $76) so the cost of that choice sits next to the $18 detector. Drop the DS3502 potentiometer and its cable, since the internal trigger in PR open-flight#221 makes a software gain trim moot; drop the IARC case, the X1206 input-range comparison and the cost-summary paragraph that itemised the complete-build line; correct the internal-trigger firmware to OPS243-A 1.3.2. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Spell out, next to the sound trigger, what the PR #221 internal trigger changes and what it costs: the OPS243-A fires the rolling-buffer capture itself on firmware 1.3.2, every OPS243-A can be brought to that release, sensors shipping since OmniPreSense's September 2026 note arrive with it, and older units are flashed over the J2 JTAG header with a SEGGER J-Link and Infineon's XMCFlasher following AN-013, with the hex requested from OmniPreSense support. Add a J-Link EDU Mini row (about $76, tokenised Mouser link from the shared project, Amazon and Adafruit alternatives) so that cost sits next to the $18 detector, and say the same in the Cost Summary. Drop the Cost Summary paragraph that itemised the complete-build line; the table already carries it. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
docs(parts): explain the PR #221 internal-trigger firmware path
|
Will also add that I needed to fiddel with XMCflasher as the application is designed before javaFX was remove from the JDK - got it working with OpenJDK 27 + JavaFX 27 on windows 11 with the following: **Flashing the OPS243 on Windows: two blockers that hit before XMCFlasher even opens**
I got the SEGGER + XMCFlasher path working on Windows 11 (JDK 27, J-Link V9.78). Neither blocker is an OPS243 problem, but both cost time, so noting them here for whoever writes the firmware-update doc. 1. XMCFlasher does not start on any Java newer than 8XMCFlasher start /b javaw -jar %~dp0\xmcflasher.jar --guiJavaFX stopped shipping inside the JDK at Java 11, so on any current JDK it dies before showing a window: Installing a JavaFX SDK and setting javaw --module-path "<javafx-sdk>\lib" --add-modules javafx.controls,javafx.fxml ^
--enable-native-access=javafx.graphics,ALL-UNNAMED ^
-jar "<install-dir>\XMCFlasher.jar" --guiVerified working with OpenJDK 21 + JavaFX 21.0.7 and OpenJDK 27 + JavaFX 27. The app only uses Why
|
I'm working on fixing the hardware trigger with the current OpenFlight codebase and OPS 1.3.2. There was a slight regression, but it should be finished soon. Thanks for putting together the docs for this! |
…ight#221) Brings open-flight/openflight PR open-flight#221 (head 73eacc7) onto this branch as one change: --trigger hardware lets the OPS243-A start its rolling-buffer dump from its own speed trigger (ST/SM, 6 pre-trigger segments), so no sound detector is needed. Requires OPS243-A firmware 1.3.2+, checked at startup. Conflicts resolved by keeping this branch's orphaned-dump handling alongside the PR's re-arm recovery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Just dropping it here that Cormac and Jamorro is working on updating the firmware on the IWR to make it better and we could trigger the OPS through software via the pi on a detector on the IWr - also could be a way forward to deprecate the sound trigger. More discussion took place in the discord friday and saturday. I summarized it: https://discord.com/channels/1512485363875057674/1553678130537832499 (which would not require for people with both OPS and IWR to find a way to flash their OPS´s to 1.3.2) |
|
From OPSandy 2026-10-02 05:55:
|
Resolve conflicts:
- docs/get-started/parts.md: keep main's "why this is worth wanting" note
and expanded OmniPreSense contact details, with the PR's post-merge
framing ("1.3.2 or newer in the 1.3 train", --trigger hardware usage).
Drop the "mode is still unmerged" clause, which this PR makes stale.
- tests/test_server.py, tests/test_start_kiosk.py: keep both sides'
newly added tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On OPS243-A firmware 1.3.2 the board drops to idle power mode after an internal-trigger dump, with every detector setting intact. Re-arming with GC reset those settings, and motion during the ~2.5 s rebuild could fire the trigger: either in the unguarded gap right after GC, or just after the live ST threshold was restored. The rebuild's command replies and reset_input_buffer calls then consumed that dump, the wait loop never saw a capture marker, and the board sat idle with nothing left to re-arm it (blue LED stuck on). Reproduced on hardware by waving rapidly past the radar; it stuck after 1-3 triggers. - Re-arm with PA only: drain the finished dump, then resume sampling. Re-arm drops from ~2.5 s to ~0.4 s and has no unguarded window. - Never clear serial input once the board is live, after the startup arm or a re-arm. wait_for_hardware_trigger gains discard_pending_input; the hardware strategy passes False. Pre-marker output is skipped anyway. - Backstop: with detect_orphaned_dump, a Q array that arrives before any capture marker is reported as an orphaned dump, and the strategy re-arms the idle board and records an "orphaned_dump" diagnostic. The sound path keeps its existing behaviour (both options opt-in). - Fix the truncated Raspberry Pi retest checklist in the docs and add a rapid-motion re-arm check. Verified on a v1.3.2 OPS243-A over UART: 12/12 PA re-arms under continuous motion in a driver loop, then 9/9 dumps re-armed through the server with zero stuck states, orphans, or re-arm failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@JPMarhefka I merged current Bug (firmware 1.3.2): after an internal-trigger dump, the board drops to Fix (c4715a4):
Hardware results on a v1.3.2 OPS243-A over UART: 12/12 🤖 Generated with Claude Code |
main added a cost note linking to "#internal-trigger-instead-pr-221", but this PR renames that heading to "Internal Hardware Trigger", so the strict docs build failed on a missing anchor. Point the link at #internal-hardware-trigger and drop the "if PR open-flight#221 lands" framing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>





What does this PR do?
Adds an opt-in OPS243 internal hardware-trigger capture strategy for rolling-buffer mode.
--trigger hardwarewith hardware-mode defaults of a 25 mph outbound threshold,SM25,S#6, and 30 ksps.PA.orphaned_dump), so the trigger cannot get stuck idle.--sound-pre-triggerbehavior.Why was this required?
The existing rolling-buffer path depends on the external SEN-14262 sound-trigger edge. The OPS243 can perform the speed-trigger decision internally, allowing installations with the internal trigger wiring to capture the rolling I/Q history without requiring the sound-trigger module.
This is opt-in so existing installations continue to use sound mode unchanged. Hardware mode is intentionally limited to the OPS243 trigger path; unrelated experimental analysis, replay, board-diagnostic, clock, and UI work is not included.
Re-arm fix (firmware 1.3.2)
On 1.3.2 the board drops to idle power mode after an internal-trigger dump, with every detector setting intact. The original re-arm used
GC, which resets those settings and rebuilds them over about 2.5 s. Motion during the rebuild fired the trigger, either in the unguarded gap right afterGCor just after the liveSTthreshold was restored. The rebuild's command replies andreset_input_buffercalls consumed that dump, and the board sat idle with nothing left to re-arm it (blue LED stuck on). It reproduced reliably by waving rapidly past the radar: stuck after 1–3 triggers.Re-arm now sends only
PAafter draining the finished dump. That takes about 0.4 s instead of 2.5 s and has no unguarded window. Input is never cleared after the board is live. As a backstop, aQarray that arrives before any capture marker is treated as an orphaned dump and the idle board is re-armed. Both newwait_for_hardware_triggeroptions are opt-in, so the sound path is unchanged.Automated tests
uv run --extra camera pytest tests/ -q: 1804 passed, 10 skippeduv run ruff check src/openflight/: passeduv run pylint src/openflight/ --fail-under=9: passed, 9.60/10ruff format --checkflags 5 files with diffs that are already onmain; none come from this PR.The focused tests cover command ordering, firmware and parameter validation, dump-boundary filtering, dump parsing,
PA-only re-arm, dumps that start immediately after arming or re-arming, orphaned-dump detection and re-arm, re-arm recovery, accepted/rejected captures, strategy selection, and CLI forwarding.Manual (human) testing
Firmware v1.3.2 (OPS243-A rev D6 over the J3 UART at 230,400 baud, Raspberry Pi 5), merged with current
main:ST-25, S#6, S=30, SM25. No false triggers while idle.PowerMode: Idle or Pulse) after 1–3 triggers, reproduced three times.PA-only re-arm driver loop under continuous motion: 12/12 dumps captured, never stuck.--trigger hardware) with the fix: 9/9 dumps re-armed (6 accepted, 3 correctly rejected below 35 mph), 0 orphaned dumps, 0 re-arm failures, 0 errors. The LED cleared after every trigger.Earlier testing on firmware v1.3.1 by the original author: about 9/10 representative shots parsed correctly as swings with
SM25.S#6beatS#4andS#8. No ambient false triggers with magnitude above 20. About 8/10 club-only practice swings still produced full outputs, which is a known object-classification limitation.Sound mode was not physically tested on this branch. The sound-trigger code path is unchanged, and the new wait options are opt-in.
Checklist
🤖 Generated with Claude Code