alexandrebarachant / muse-lsl

Python script to stream EEG data from the muse 2016 headset
BSD 3-Clause "New" or "Revised" License
616 stars 182 forks source link

Workaround for list_muses failing when backend='gatt' on linux #141

Closed hubertjb closed 3 years ago

hubertjb commented 3 years ago

This PR introduces a workaround to use list_muses with gatt on Linux.

Some context: When using backend='gatt' on Linux, pygatt relies on the command line tool hcitool to scan for BLE devices. hcitool is however deprecated, and seems to fail on Bluetooth 5 devices. This workaround roughly replicates the functionality of pygatt.backends.gatttool.gatttool.GATTToolBackend.scan() using the more modern bluetoothctl tool.

I had to rely on pexpect to run bluetoothctl scan on as subprocess.run seems to be returning before any device can be found when used in a jupyter notebook environment, which doesn't make for a very elegant approach. Also, I suppose this might be of interest to the pygatt folks, but wanted to make a quick fix available for the eeg-notebooks sprint at BrainHack Ontario.

Deprecation of hcitool: https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/?id=b1eb2c4cd057624312e0412f6c4be000f7fc3617

@m9h @JohnGriffiths

ErikBjare commented 3 years ago

Some background on the hcitool issue, and a workaround for Arch Linux: https://github.com/alexandrebarachant/muse-lsl/issues/119#issuecomment-701289365

hubertjb commented 3 years ago

Awesome, thanks for trying it out @ErikBjare! I think sudo is still required because the code first tries scanning with pygatt (which asks for sudo), and only if that fails will it use bluetoothctl. Not sure if that holds for muselsl stream though.

xloem commented 3 years ago

I've merged this into https://github.com/xloem/muse-lsl .

jdpigeon commented 3 years ago

Fantastic. Thanks for this, @hubertjb.

I'm going to test this myself and make a small follow-up PR that makes this the standard way to search for muses on Linux and avoid the pygatt failure step.

jdpigeon commented 3 years ago

I'm running into a little issue with this change where the bluetoothctl subprocess will not complete until I type quit into the terminal to end the interactive session it inits.

I'm not too familiar with bluetoothctrl. Is there a way to make it return directly?

xloem commented 3 years ago

This change could also be good as a patch to the https://github.com/peplin/pygatt project itself; then more people will review, benefit from, and maintain it.

xloem commented 3 years ago

Note: to make it terminate on its own pass --timeout: bluetoothctl --timeout 5 scan on scans for only 5 seconds. Passing that would be a good decision, but note @jdpigeon it works fine for me, each launch of the subprocess is passing individual commands and not opening an interactive session.

hubertjb commented 3 years ago

@jdpigeon interesting, I haven't had that problem (as seems to be the case for @xloem too). Can you try with the timeout as @xloem suggests?

hubertjb commented 3 years ago

@xloem yes good point, I was thinking this workaround could be useful in the pygatt project itself. I'll look into submitting a PR there too.

xloem commented 3 years ago

Just to clarify and restate, I didn't have the issue jdpigeon had, it sounds like they have some version of bluetoothctl where an interactive shell is launched when a command is passed?

jdpigeon commented 3 years ago

I just tried it out on another more up to date linux machine and bluetoothctl devices successfully prints to stdout.