bluetooth icon indicating copy to clipboard operation
bluetooth copied to clipboard

[question] bluetooth.DefaultAdapter.Scan Fails to Detect Devices After Prolonged macOS Screen Lock

Open wwwfeng opened this issue 5 months ago • 2 comments

I'm encountering an issue with the tinygo-org/bluetooth package on macOS when using the bluetooth.DefaultAdapter.Scan function. After the macOS system has been locked for an extended period (e.g., screen lock or sleep mode), the Scan function no longer detects any Bluetooth devices. This issue persists even after calling adapter.Enable() and attempting to rescan. Steps to Reproduce:

Set up the tinygo-org/bluetooth package on macOS and ensure Bluetooth is enabled. Use the bluetooth.DefaultAdapter to initiate a scan for Bluetooth devices (e.g., using the provided scanner example). Lock the macOS screen or let the system enter sleep mode for an extended period (e.g., 3-5 hours). Unlock the system and attempt to scan again using adapter.Scan. Optionally, call adapter.Enable() before rescanning.

Expected Behavior: The Scan function should detect nearby Bluetooth devices after unlocking the system, and the callback function should be invoked, allowing code within it (e.g., deviceName := device.LocalName()) to execute. Actual Behavior: After a prolonged lock period, no devices are detected by the Scan function, and the callback function is not triggered. When debugging with breakpoints, the code does not reach the line deviceName := device.LocalName() inside the callback, which it normally would under regular conditions. This persists even after calling adapter.Enable(). Code Snippet: Here’s the relevant code used for scanning:


err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
    deviceName := device.LocalName()
    log.Printf("Found device: %v, RSSI: %d", deviceName, device.RSSI)


    select {
    case <-closeChan:
        log.Printf("Stop signal received during scan callback")
        return
    default:
    }

    select {
    case data <- Device{
        Adapter:    adapter,
        Device:     device,
        DeviceName: deviceName,
        UUid:       device.Address.UUID.String(),
        Rssi:       device.RSSI,
    }:
    case <-closeChan:
        log.Printf("Stop signal received while sending device data")
        return
    default:
        log.Printf("Device channel full, skipping device: %v", deviceName)
    }
})

wwwfeng avatar Aug 01 '25 08:08 wwwfeng

On macOS (Darwin), I noticed that after closing and reopening the laptop lid (i.e., when the system goes to sleep and then wakes), the Bluetooth stack becomes unresponsive. Although cbgo.CentralManager.State() eventually returns PoweredOn, scanning and connecting still fail to work properly.

To resolve this, I added a Reset() method in the Adapter that reinitializes both the CentralManager and PeripheralManager after the system wakes. This allows the entire Bluetooth stack to recover fully from the off state and ensures that subsequent operations work as expected.

Key Differences from the Official Implementation New Reset() method:

Recreates the internal cbgo.CentralManager and cbgo.PeripheralManager instances.

Clears relevant internal state so that Enable() can reconfigure the stack from scratch.

Modified Enable() method logic:

Checks whether Bluetooth is already in the PoweredOn state and skips blocking logic if so, avoiding unnecessary wait.

Adds protection to prevent concurrent calls to Enable().

Always sets delegates regardless of the current Bluetooth state.

Non-blocking state notification: In CentralManagerDidUpdateState, state changes are sent to poweredChan using a non-blocking select to avoid potential deadlocks if the channel isn't being read:

select {
case cmd.a.poweredChan <- nil:
default:
}

These changes effectively resolve the issue where scanning fails after the system sleeps and wakes. I'm currently using this approach in a forked version of the code, but I’d be happy to open a PR if you’re open to reviewing it and discussing potential improvements or compatibility concerns.

wwwfeng avatar Aug 01 '25 11:08 wwwfeng

Hello @wwwfeng

These changes effectively resolve the issue where scanning fails after the system sleeps and wakes. I'm currently using this approach in a forked version of the code, but I’d be happy to open a PR if you’re open to reviewing it and discussing potential improvements or compatibility concerns.

Yes, please! we are happy to review fixes and improvements!

deadprogram avatar Aug 14 '25 06:08 deadprogram