adafruit / adafruit/Adafruit_nRF52_Arduino

Bug: macOS serial open() blocks indefinitely when Bluefruit.begin() is called

Open
#861 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.