adafruit / adafruit/Adafruit_nRF52_Arduino
Bug: macOS serial open() blocks indefinitely when Bluefruit.begin() is called
- Dominant language
- C
- Stars
- 699
- Forks
- 570
- PR merge metrics
- No merged PRs in 30d
Description
## Bug: macOS serial open() blocks indefinitely when Bluefruit.begin() is called
### Environment
- **Board:** Adafruit Feather nRF52840 Sense (2 units tested)
- **Bootloader:** 0.9.1 and 0.9.2
- **BSP Version:** 1.7.0 (via PlatformIO)
- **TinyUSB Version:** 0.18.0 (bundled), also tested with 0.20.0
- **Host OS:** macOS Darwin 24.1.0 (Sequoia)
- **Python:** pyserial for serial communication
### Problem Description
When firmware calls `Bluefruit.begin()`, the macOS host cannot open the USB CDC serial port. The POSIX `open()` call blocks indefinitely (not timeout - completely hangs). This affects `pyserial`, `screen`, and any serial communication tool.
**Key observation:** Firmware WITHOUT `Bluefruit.begin()` works perfectly. The exact same firmware WITH `Bluefruit.begin()` causes the hang.
### Minimal Reproduction
**This works (serial opens in ~18ms):**
```cpp
#include
#include "Adafruit_TinyUSB.h"
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 3000) delay(10);
Serial.println("Hello");
}
void loop() {
Serial.println("PING");
delay(1000);
}
```
**This hangs (serial open blocks forever):**
```cpp
#include
#include
#include "Adafruit_TinyUSB.h"
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 3000) delay(10);
Serial.println("Before BLE");
Bluefruit.begin(); // <-- Adding this line causes host-side hang
Serial.println("After BLE");
}
void loop() {
Serial.println("PING");
delay(1000);
}
```
### Test Results
| Firmware | Flash Method | Serial Open |
|----------|--------------|-------------|
| No BLE | DFU | ✅ Works (18ms) |
| No BLE | UF2 | ✅ Works (18ms) |
| With `Bluefruit.begin()` | DFU | ❌ Blocks forever |
| With `Bluefruit.begin()` | UF2 | ❌ Blocks forever |
| Bootloader mode (0x0087) | N/A | ✅ Always works |
### What Does NOT Help
- Manual button reset (single or double click)
- Physical USB unplug/replug
- Waiting any amount of time
- Using `O_NONBLOCK` flag (still blocks in kernel)
- Updating TinyUSB to 0.20.0
- Different baud rates
- Different bootloader versions (tested 0.8.0, 0.9.1, 0.9.2)
### Process State During Hang
The process enters **uninterruptible sleep (D state)** in the macOS kernel. It cannot be killed with SIGKILL - only physical USB disconnect releases it.
```
$ ps aux | grep python
user 12345 0.0 0.0 ... Ds ... python3 -c "serial.Serial('/dev/cu.usbmodem...')"
^^ D state = uninterruptible sleep
```
### Related Issues
This appears related to previously reported issues:
- #544 - Bluefruit.begin() and USB Device Not recognized (closed 2020)
- #522 - Random lockup on Serial Activity and BLE Enabled (closed 2020)
The fixes from 2020 (PR #542, #546) may have regressed, or the fix was incomplete for macOS specifically.
### Hypothesis
The issue appears to be a race condition between:
1. Nordic SoftDevice (BLE stack) initialization
2. USB CDC power event handling
3. macOS CDC driver expecting a handshake response
When `Bluefruit.begin()` runs, something in the USB CDC state causes macOS's `com.apple.driver.usb.cdc.acm` driver to block waiting for a response that never comes.
### Workaround
Currently the only workaround is to not use USB Serial with BLE-enabled firmware. The bootloader's CDC implementation works correctly, suggesting the issue is specific to the application-mode USB stack when SoftDevice is active.
### Request
Could you please investigate:
1. Whether the SoftDevice initialization timing affects USB CDC enumeration on macOS
2. If there's a way to ensure USB CDC is fully stable before calling `Bluefruit.begin()`
3. Whether the fixes from PR #542/#546 are still effective or have regressed
Thank you!
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the two minimal firmware reproductions centered on Bluefruit.begin() and compare USB CDC behavior on macOS. Review the changes in PR #542 and PR #546, then investigate whether the application-mode serial port can open reliably after BLE initialization; done means pyserial, screen, and other serial tools no longer block indefinitely.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, macos
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100