Skip to content

bledev: fast reconnects, and REPL output that arrives intact - #84

Merged
bdbarnett merged 3 commits into
mainfrom
bledev-reconnect
Sep 25, 2026
Merged

bdbarnett merged 3 commits into
mainfrom
bledev-reconnect

Conversation

@bdbarnett

@bdbarnett bdbarnett commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Two v1 cleanups for #81, stacked on bledev-host. Merge #78 and #81 first.

The reconnect flake. The REPL gate once failed one run in five: bleak couldn't subscribe on a connection made 1.5 s after the last one closed. tests/bledev_board/reconnect_loop.py (log in, run a line, close, wait 1.5 s, from the laptop) caught it at about one cycle in fifty, always the same shape: bleak's connect returned in 70-440 ms instead of the usual 1.7 s or more, Windows said the device was connected, the board never saw a connection (its IRQ log shows it advertising the whole time), and the first GATT operation hung until Windows gave up nine seconds later.

  • Against bledev-host's repl.py on the LCD-7: 7 failures in 465 cycles (5/315 earlier today, 2/150 as a re-check just now).
  • Against this branch's repl.py: 0 in 270 (120 with the retry available and never needed, 150 with it switched off).

So the board side was the cause, and the fix is the REPL change below. I haven't found how the old timer handling made Windows believe in a connection the board never saw, so bledev.connect_and_set_up() stays as a defence: it bounds setup at 4 s and retries up to three times. nus.connect() uses it, and midi.connect() does on #85. The contract checks both the retry and giving up, and fails with the retry planted out.

REPL output. Printing a 16,890-character list over the BLE REPL came back corrupt 5 times out of 5: the retry timer's callback could land inside a flush() that print() had started, so a chunk went out twice and the next never did. A re-entry guard fixed that. Then the board panicked twice in about 45 long prints (InstrFetchProhibited in esp_timer's task): re-arming a one-shot machine.Timer(-1) while its alarm is being dispatched makes the esp32 port call a NULL handler. The REPL now starts one periodic timer and never re-arms it. After both fixes: 60 long prints intact, no panic, with the console captured throughout. The REPL gate now includes the long print.

Fake contract 43/43 on CPython and unix MicroPython; test_bledev passes, including every plant.

Not done: the esp32 machine.Timer re-init race is upstream MicroPython's (ports/esp32/machine_timer.c: machine_timer_deinit() sets handler = NULL while esp_timer may be dispatching it). bledev no longer triggers it. No upstream draft was written for it in this PR.

flush() could be re-entered by the retry timer's callback, which sent one
chunk twice and dropped the next: a 16,890-character print came back corrupt
five times out of five. And re-arming a one-shot machine.Timer while its
alarm was being dispatched panicked the S3 in esp_timer's task. One periodic
timer, never re-armed, and a re-entry guard. The REPL gate checks a long
print.
About one reconnect in fifty from Windows came up dead: bleak's connect
returned in 70-440 ms (a real one takes 1.7 s or more) with the device
reported connected and the session active, the board never saw a
connection, and the first GATT operation hung until Windows gave up nine
seconds later. connect_and_set_up() bounds the setup and tries again; nus
uses it. tests/bledev_board/reconnect_loop.py is the rapid-reconnect gate.
Base automatically changed from bledev-host to main September 25, 2026 05:37
@bdbarnett
bdbarnett merged commit 979ece1 into main Sep 25, 2026
3 checks passed
@bdbarnett
bdbarnett deleted the bledev-reconnect branch September 25, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant