You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Blocks #29 (fix for #27). Both are parked until someone can capture Security+ 2.0 obstruction traffic from a live opener.
Background
The obstruction path has been reworked twice, each time trading away a signal:
Before Improve obstruction detection time. #21 — obstruction came only from the OBST bit in STATUS frames, applied unconditionally. Correct, but users reported it as too slow.
Improve obstruction detection time. #21 — added PAIR_3_RESP decode with both edges explicit: byte1 == 0x0e → obstructed, byte1 == 0x09 → cleared. The unconditional status path stayed as a backstop.
Use OBST events for immediate obstruction sensing. #22 — went after OBST_1, which lands earlier than PAIR_3_RESP. To stop the two mechanisms fighting it deleted the 0x09 clear branch, demoted 0x0e to only arming obst_clear_from_status, and gated the status backstop behind obstruction == MAX || obst_clear_from_status.
After #22 the state machine has no explicit "cleared" signal, so it infers one by toggling on each OBST_1 behind a 1-second debounce — and #27 is the direct result: a swallowed OBST_1 desyncs it, with no backstop left to repair it.
Separately, GDO_CMD_OBST_2 (0x085) is defined in gdo_priv.h and stringified in gdo_utils.c, but decode_packet() has never handled it. Nobody has looked at what it carries.
What we need from a device
The proposed fix (see #29) is to let each message do the one thing it's good at: OBST_1 sets obstructed, 0x0e confirms, 0x09 clears, STATUS reconciles. Two facts decide whether that design needs a debounce guard and a magic number, or whether it collapses into something simpler.
Q1 — how much latency does OBST_1 actually buy over PAIR_3_RESP 0x0e?
This is the load-bearing question. If 0x0e follows OBST_1 within tens of milliseconds, drop OBST_1 handling entirely, take both edges from PAIR_3_RESP, and no guard is needed — that's #21's design plus the restored backstop. If OBST_1 is meaningfully earlier (presumably what motivated #22), it stays as a set-only fast path and we need a guard against a clear-burst tail landing after 0x09.
Q2 — does OBST_1 fire on the clear edge, or only on the trip?
#22's comment claims "many obstruction events will be sent when a long obstruction is cleared", implying it fires on both. If it does, ordering matters: an OBST_1 arriving after0x09 would spuriously re-assert obstructed. (An OBST_1 arriving before0x09 is harmless — we're already obstructed, so setting obstructed is a no-op.) The guard window in Q1 only needs to cover the burst tail.
Q3 — what does OBST_2 carry?
If OBST_1/OBST_2 distinguish trip from clear, the whole edge-inference problem disappears. Its data bytes may encode state directly.
Capture plan
Log cmd, nibble, byte1, byte2 and a millisecond timestamp for every frame, then exercise:
Brief interruption (fast hand wave through the beam, well under 1s). Record the full frame sequence with timings.
Sustained interruption (box in the beam, 30s+), through trip, hold, and clear.
Repeated trips in quick succession, to see how bursts overlap.
From those captures, answer:
Q1: measured delta between OBST_1 and the following PAIR_3_RESP 0x0e.
Q2: does OBST_1 appear on the clear edge? Before or after 0x09? How long is the burst tail?
Q3: does OBST_2 ever appear, and does it correlate with trip vs. clear? What's in its data bytes?
Blocks #29 (fix for #27). Both are parked until someone can capture Security+ 2.0 obstruction traffic from a live opener.
Background
The obstruction path has been reworked twice, each time trading away a signal:
STATUSframes, applied unconditionally. Correct, but users reported it as too slow.PAIR_3_RESPdecode with both edges explicit:byte1 == 0x0e→ obstructed,byte1 == 0x09→ cleared. The unconditional status path stayed as a backstop.OBST_1, which lands earlier thanPAIR_3_RESP. To stop the two mechanisms fighting it deleted the0x09clear branch, demoted0x0eto only armingobst_clear_from_status, and gated the status backstop behindobstruction == MAX || obst_clear_from_status.After #22 the state machine has no explicit "cleared" signal, so it infers one by toggling on each
OBST_1behind a 1-second debounce — and #27 is the direct result: a swallowedOBST_1desyncs it, with no backstop left to repair it.Separately,
GDO_CMD_OBST_2(0x085) is defined ingdo_priv.hand stringified ingdo_utils.c, butdecode_packet()has never handled it. Nobody has looked at what it carries.What we need from a device
The proposed fix (see #29) is to let each message do the one thing it's good at:
OBST_1sets obstructed,0x0econfirms,0x09clears,STATUSreconciles. Two facts decide whether that design needs a debounce guard and a magic number, or whether it collapses into something simpler.Q1 — how much latency does
OBST_1actually buy overPAIR_3_RESP 0x0e?This is the load-bearing question. If
0x0efollowsOBST_1within tens of milliseconds, dropOBST_1handling entirely, take both edges fromPAIR_3_RESP, and no guard is needed — that's #21's design plus the restored backstop. IfOBST_1is meaningfully earlier (presumably what motivated #22), it stays as a set-only fast path and we need a guard against a clear-burst tail landing after0x09.Q2 — does
OBST_1fire on the clear edge, or only on the trip?#22's comment claims "many obstruction events will be sent when a long obstruction is cleared", implying it fires on both. If it does, ordering matters: an
OBST_1arriving after0x09would spuriously re-assert obstructed. (AnOBST_1arriving before0x09is harmless — we're already obstructed, so setting obstructed is a no-op.) The guard window in Q1 only needs to cover the burst tail.Q3 — what does
OBST_2carry?If
OBST_1/OBST_2distinguish trip from clear, the whole edge-inference problem disappears. Its data bytes may encode state directly.Capture plan
Log
cmd,nibble,byte1,byte2and a millisecond timestamp for every frame, then exercise:From those captures, answer:
OBST_1and the followingPAIR_3_RESP 0x0e.OBST_1appear on the clear edge? Before or after0x09? How long is the burst tail?OBST_2ever appear, and does it correlate with trip vs. clear? What's in its data bytes?0x09reliably emitted for short obstructions, or only long ones? (Use OBST events for immediate obstruction sensing. #22's "long duration" framing suggests0x0emay be long-only — if so, what marks a short trip?)STATUSOBST bit ((byte1 >> 6) & 1) latch, or does it track the beam in real time? Does it lag theOBST_*messages, and by how much?STATUSbit against whicheverOBST_*/PAIR_3_RESPpreceded it, to confirm it's safe to restore as an unconditional backstop.Also worth confirming on the device
GET_STATUSissued during an obstruction burst survives the TX path, or gets dropped by the 3-second staleness check atgdo.c:1671after repeated 150ms collision reschedules. This is the mechanism that sinks the round-trip approach in Fix Sec+2.0 obstruction reporting so it updates the moment the beam breaks #29.