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
{{ message }}
Repository navigation
P4 as BLE central loses notifications below MicroPython when Python is busy #87
When the P4 panel is the BLE central, it sometimes loses notifications, and nothing reports it. The lost packets never reach MicroPython's IRQ handler, so the loss happens below Python: in NimBLE on the P4, the ESP-Hosted transport, or the C6.
How it shows up. In the bledev.hid board-to-board gate (#83), an S3 (the LCD-7) plays the keyboard and the P4 is the host. At a 7.5-15 ms connection interval it lost reports in 6 of 6 runs, and at the default interval in 1 of 4. The loss starts in the same place every time: the 26th to 28th report, where ! is typed. Sometimes two reports go missing, sometimes most of the rest of the script, and then reports arrive again. Pacing the device at 10 ms per key didn't help. A user IRQ handler (ble.irq) counting _IRQ_GATTC_NOTIFY saw exactly the reports that were decoded (44 of 70 sent), so they're missing before Python ever runs.
The reverse doesn't lose anything. With the P4 as the keyboard and an S3 as the host, the S3 got every report in every run. S3 to S3 is clean too.
A smaller reproduction (a characteristic notifying numbered packets back to back, 7.5-15 ms interval):
The esp32 port runs BLE events synchronously (MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS), so NimBLE's host task waits while Python is busy. On the S3 the controller then holds the link back and nothing is lost. The P4's path through ESP-Hosted appears to drop packets instead. This is probably the same thing as #79 (the P4 losing writes while it notifies), seen from the other role. What the HID gate does at report 26 to trigger it at the default interval isn't known yet.
What to try next: the ESP-Hosted and NimBLE buffer counts in the P4 hosted config (ACL buffers, flow control between host and controller), and whether HCI flow control is on over the hosted link at all.
A correction to the count above: at the default interval the P4 host passed 2 of 5 runs, not 1 of 4. The two diagnostic runs (raw reports, then the IRQ count) were at the default interval, and both lost reports. The fast-interval count stays at 6 of 6.
Pairing doesn't change it. A bonded reconnect with the P4 as host lost 14 events in the same place.
The scripts are tests/bledev_board/burst_source.py and burst_sink.py on bledev-hid (#83).
The packets are dropped in ESP-Hosted's receive path on the P4 (vhci_drv.c, hci_rx_handler). It throws away an ACL packet when NimBLE's ACL pool is empty, and that happens whenever the NimBLE host task waits on Python. On an S3, IDF's own transport waits for a buffer instead, so the controller holds the link back. Every lost packet shows up on the P4's UART as E vhci_drv: Rx: alloc_acl_from_ll failed, one for one, but only from the friendly REPL. The raw REPL (every mpftp run) mutes ESP-IDF's log, which is why nobody saw it.
CONFIG_BT_NIMBLE_HS_FLOW_CTRL=y makes the C6's controller respect the host's buffer count. I used the burst repro from this issue: the T-Embed notifies 940 packets, and the P4 receives them with 1.7 ms of Python work each.
Two things are left open. First, 4 of 20 connects from the P4 as a central timed out at the default interval (1 of 10 before). Second, a Python reader that falls 128 packets behind still gets bledev's loud "queue overran" error, which is by design. I didn't rerun the HID gate itself, only this repro.
With micropython-pydevices#12 on the P4, the P4 no longer loses keystrokes as a HID host. I reran #83's full board-to-board gate with the LCD-7 as the keyboard and the P4 as the host. Each run typed 30 characters with Shift, two chords, two media keys and five gamepad reports, and the P4's events had to match all 112 exactly.
10 of 10 pass, 112/112 each (11 attempts; one never connected)
Planted lost Shift
fails
fails at event 0, as it must
No run logged an alloc_acl_from_ll failed. The runs were at the friendly REPL, where ESP-IDF's log reaches the UART. The planted run is what proves the gate. Nobody has checked that the drop counter catches a drop, because the #10-only image wasn't reflashed.
The round trip, key press to the host's LED write, had a median of 69-85 ms at the default interval and 29-37 ms at 7.5-15 ms, close to the S3-to-S3 numbers.
The P4 was hard-reset before every run (micropython-pydevices#13). Its boot.py joins Wi-Fi, so Wi-Fi was associated throughout. The P4 ran the #12 kitchen sink + speaker-usermod image, which I checked by hashing the first 4 KB of the running partition against the build (fe5c70c3…). bledev was the bledev-pairing branch as .mpy. The rig is tests/bledev_board/p4_hid_gate.py in #89.
Fixed by micropython-pydevices#12, merged 2026-09-25. On the flow-control image, the full HID gate with an S3 typing into the P4 passed 10 of 10 runs at the default interval and 10 of 10 at 7.5–15 ms, 112/112 events each. Before the fix, 3 of 5 and 6 of 6 runs lost keystrokes.
Activity
bledev.hid board gate: pacing, connection interval and security loggi…
bdbarnett commentedon Sep 24, 2026
A correction to the count above: at the default interval the P4 host passed 2 of 5 runs, not 1 of 4. The two diagnostic runs (raw reports, then the IRQ count) were at the default interval, and both lost reports. The fast-interval count stays at 6 of 6.
Pairing doesn't change it. A bonded reconnect with the P4 as host lost 14 events in the same place.
The scripts are
tests/bledev_board/burst_source.pyandburst_sink.pyonbledev-hid(#83).bdbarnett commentedon Sep 25, 2026
Found and fixed: micropython-pydevices#12, one sdkconfig line stacked on #10.
The packets are dropped in ESP-Hosted's receive path on the P4 (
vhci_drv.c,hci_rx_handler). It throws away an ACL packet when NimBLE's ACL pool is empty, and that happens whenever the NimBLE host task waits on Python. On an S3, IDF's own transport waits for a buffer instead, so the controller holds the link back. Every lost packet shows up on the P4's UART asE vhci_drv: Rx: alloc_acl_from_ll failed, one for one, but only from the friendly REPL. The raw REPL (every mpftp run) mutes ESP-IDF's log, which is why nobody saw it.CONFIG_BT_NIMBLE_HS_FLOW_CTRL=ymakes the C6's controller respect the host's buffer count. I used the burst repro from this issue: the T-Embed notifies 940 packets, and the P4 receives them with 1.7 ms of Python work each.Two things are left open. First, 4 of 20 connects from the P4 as a central timed out at the default interval (1 of 10 before). Second, a Python reader that falls 128 packets behind still gets bledev's loud "queue overran" error, which is by design. I didn't rerun the HID gate itself, only this repro.
bdbarnett commentedon Sep 25, 2026
With micropython-pydevices#12 on the P4, the P4 no longer loses keystrokes as a HID host. I reran #83's full board-to-board gate with the LCD-7 as the keyboard and the P4 as the host. Each run typed 30 characters with Shift, two chords, two media keys and five gamepad reports, and the P4's
eventshad to match all 112 exactly.No run logged an
alloc_acl_from_ll failed. The runs were at the friendly REPL, where ESP-IDF's log reaches the UART. The planted run is what proves the gate. Nobody has checked that the drop counter catches a drop, because the #10-only image wasn't reflashed.The round trip, key press to the host's LED write, had a median of 69-85 ms at the default interval and 29-37 ms at 7.5-15 ms, close to the S3-to-S3 numbers.
The P4 was hard-reset before every run (micropython-pydevices#13). Its boot.py joins Wi-Fi, so Wi-Fi was associated throughout. The P4 ran the #12 kitchen sink + speaker-usermod image, which I checked by hashing the first 4 KB of the running partition against the build (
fe5c70c3…). bledev was thebledev-pairingbranch as.mpy. The rig istests/bledev_board/p4_hid_gate.pyin #89.bdbarnett commentedon Sep 25, 2026
Fixed by micropython-pydevices#12, merged 2026-09-25. On the flow-control image, the full HID gate with an S3 typing into the P4 passed 10 of 10 runs at the default interval and 10 of 10 at 7.5–15 ms, 112/112 events each. Before the fix, 3 of 5 and 6 of 6 runs lost keystrokes.