Skip to content

Commit 112d5e4

Browse files
committed
fix(crow_alarm_panel): address Copilot review comments on PR #10
- Refresh arm_disarm_state_enter_ms_ before keypress() in the retry block, not after: keypress()->send_packet() can yield and re-enter loop(), so the watchdog could otherwise fire again on the stale timestamp and send extra keypresses (same fix already applied to the output-select retry). - Gate the ISR's bit-trace buffer swap on !bit_trace_ready_ so it can't overwrite bit_trace_buffer2_ while loop() (other core) is still copying it out. - Mark bit_trace_enabled_ volatile, matching ack_pending_/ is_transmitting_. - Reset bit_trace_len_/bit_trace_ready_ on every toggle so disabling mid-buffer and re-enabling can't mix stale bits into a trace chunk.
1 parent 3879100 commit 112d5e4

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

components/crow_alarm_panel/crow_alarm_panel.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ void IRAM_ATTR HOT CrowAlarmPanelStore::interrupt(CrowAlarmPanelStore *arg) {
150150
// Check for boundary
151151
arg->boundary_buffer_ = (uint8_t) ((arg->boundary_buffer_ << 1) | data_bit);
152152

153-
if (arg->bit_trace_enabled_) {
153+
// Gated on !bit_trace_ready_ so bit_trace_buffer2_ stays untouched while loop() (running on
154+
// the other core) is still copying it out — loop()'s InterruptLock only protects against
155+
// same-core reentrancy, not this ISR on the other core, so the flag itself is what prevents
156+
// the overwrite. Bits are simply dropped while the consumer is behind; accumulation resumes
157+
// as soon as it clears the flag (normally sub-millisecond later).
158+
if (arg->bit_trace_enabled_ && !arg->bit_trace_ready_) {
154159
arg->bit_trace_buffer_[arg->bit_trace_len_++] = data_bit ? '1' : '0';
155160
if (arg->bit_trace_len_ >= BIT_TRACE_BUFFER_BITS) {
156161
memcpy(arg->bit_trace_buffer2_, arg->bit_trace_buffer_, BIT_TRACE_BUFFER_BITS);
@@ -898,6 +903,11 @@ void CrowAlarmPanel::loop() {
898903
ESP_LOGW(TAG, "Arm/disarm: timeout in state %u, retrying (%u/%u)",
899904
static_cast<uint8_t>(this->arm_disarm_state_), this->arm_disarm_retry_count_,
900905
ARM_DISARM_MAX_RETRIES);
906+
// Refresh the watchdog timer BEFORE any keypress() below — keypress()->send_packet()
907+
// can delay()/yield() and re-enter this loop(), and with the old timestamp still in
908+
// place the watchdog would see itself as still timed out, firing again and sending an
909+
// extra keypress (same race the output-select retry above avoids the same way).
910+
this->arm_disarm_state_enter_ms_ = millis();
901911
switch (this->arm_disarm_state_) {
902912
case ArmDisarmState::ARM_AWAY_PENDING:
903913
this->keypress(KEY_ARM);
@@ -917,7 +927,6 @@ void CrowAlarmPanel::loop() {
917927
default:
918928
break;
919929
}
920-
this->arm_disarm_state_enter_ms_ = millis();
921930
} else {
922931
ESP_LOGW(TAG, "Arm/disarm: timeout in state %u, aborting after %u retries",
923932
static_cast<uint8_t>(this->arm_disarm_state_), this->arm_disarm_retry_count_);

components/crow_alarm_panel/crow_alarm_panel.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ class CrowAlarmPanelStore {
172172
// Independent of frame decoding, so mis-alignment/framing issues can be diagnosed
173173
// by hand from the literal bitstream instead of the already-decoded bytes.
174174
static const uint16_t BIT_TRACE_BUFFER_BITS = 128;
175-
bool bit_trace_enabled_{false};
175+
// Read in the ISR, written from the main loop via set_raw_bit_trace_enabled() — volatile like
176+
// ack_pending_/is_transmitting_ above, for the same cross-core visibility reason.
177+
volatile bool bit_trace_enabled_{false};
176178
char bit_trace_buffer_[BIT_TRACE_BUFFER_BITS + 1]{};
177179
char bit_trace_buffer2_[BIT_TRACE_BUFFER_BITS + 1]{};
178180
uint16_t bit_trace_len_{0};
@@ -264,8 +266,19 @@ class CrowAlarmPanel : public Component {
264266
// be toggled at runtime without recompiling with a higher logger level.
265267
void set_raw_frame_logging_enabled(bool enabled) { this->raw_frame_logging_enabled_ = enabled; }
266268

267-
// Enables the ISR-side raw bit trace (see CrowAlarmPanelStore::bit_trace_enabled_).
268-
void set_raw_bit_trace_enabled(bool enabled) { this->store_.bit_trace_enabled_ = enabled; }
269+
// Enables the ISR-side raw bit trace (see CrowAlarmPanelStore::bit_trace_enabled_). Disables
270+
// first, then resets the in-progress buffer position and any pending-but-unconsumed batch
271+
// before (re-)enabling, so a toggle never mixes bits captured before/after it into one trace
272+
// chunk, and never emits a stale ready buffer left over from before the toggle.
273+
void set_raw_bit_trace_enabled(bool enabled) {
274+
this->store_.bit_trace_enabled_ = false;
275+
{
276+
InterruptLock lock;
277+
this->store_.bit_trace_len_ = 0;
278+
this->store_.bit_trace_ready_ = false;
279+
}
280+
this->store_.bit_trace_enabled_ = enabled;
281+
}
269282

270283
protected:
271284
CrowAlarmPanelKeypad find_keypad_(uint8_t address);

0 commit comments

Comments
 (0)