Skip to content

fix: clear IRQ register after RX restore to prevent gpiod polling dea…#79

Open
zindello wants to merge 1 commit into
pyMC-dev:devfrom
zindello:fix/gpiod-stuck-irq-rx-restore-clear
Open

fix: clear IRQ register after RX restore to prevent gpiod polling dea…#79
zindello wants to merge 1 commit into
pyMC-dev:devfrom
zindello:fix/gpiod-stuck-irq-rx-restore-clear

Conversation

@zindello
Copy link
Copy Markdown

@zindello zindello commented Jun 1, 2026

fix: radio deafness on gpiod devices after back-to-back corrupted packets

Problem

On devices using the gpiod GPIO backend, the radio would intermittently go completely deaf — receiving no packets until the next transmit cycle or a restart.

The root cause is a race between back-to-back LoRa IRQs and the gpiod polling interval. The gpiod backend detects interrupts by polling DIO1 every 20ms, looking for a LOW→HIGH transition. When two IRQs arrive close together (e.g. a corrupted preamble followed immediately by another), DIO1 is already HIGH when the second IRQ fires. The polling thread sees HIGH→HIGH, not LOW→HIGH, so the second interrupt is silently lost. DIO1 stays HIGH indefinitely, _handle_interrupt is never called again, and the radio stops receiving.

The condition was confirmed in production logs: DIO1 remained stuck HIGH for over 100 seconds with no recovery.

Fix

After calling request(RX_CONTINUOUS) to restore the radio at the end of IRQ handling, immediately call clearIrqStatus(0xFFFF). This sends an SPI command to the SX1262 to clear all pending IRQ flags, which causes the chip to de-assert DIO1. The line goes LOW, and the polling thread can now detect the next real rising edge normally.

Why this isn't guarded to gpiod only

The periphery backend uses kernel-level edge detection (hardware FIFO), so it doesn't suffer from missed edges. However, the clearIrqStatus call is an SPI transaction that executes in approximately 8µs — far shorter than the minimum possible LoRa Time-on-Air (~5ms at SF5/250kHz). No valid packet can arrive and complete reception in that window on any supported SF or bandwidth combination, making the call safe to apply unconditionally on both backends.

…fness

When two back-to-back packets arrive within the gpiod polling window
(20ms), the IRQ line stays HIGH after the first interrupt is handled.
The polling thread never sees a LOW→HIGH transition for the second
event, so _handle_interrupt is never called and the radio goes deaf
indefinitely.

Calling clearIrqStatus(0xFFFF) immediately after request(RX_CONTINUOUS)
forces DIO1 LOW, allowing the polling thread to detect the next real
rising edge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant