IanHarvey / bluepy

Python interface to Bluetooth LE on Linux
Other
1.61k stars 490 forks source link

Passive scanning #198

Closed gandy92 closed 7 years ago

gandy92 commented 7 years ago

For my project I need the Scanner to perform a passive scan for LE devices rather than an active scan in order to save resources. As far as I can tell, bluepy can not do that yet. I tried to figure out how to tell mgmt to do the scan passively so I could develop a patch. Long Story short, I could not find any obvious hints. @IanHarvey, do you recon this can be done? Thanks, Andy.

nitrag commented 7 years ago

Bump

EDIT: Perhaps by passive you mean "asynchronous", that's what I'm looking for at least.

PrzemoF commented 7 years ago

@gandy92 How do you know that bluepy performs active scan?

From bluez blog [1] from 2014: "When run on a kernel that supports it, instead of doing a kind of “fake” background scanning using the Start Discovery command, bluetoothd will now tell the kernel the relevant information and the kernel will then commence passive scanning for devices."

For asynchronous scan- maybe you can run bluepy scan in a separate thread for X seconds every minute or so?

[1] http://www.bluez.org/release-of-bluez-5-21/

gandy92 commented 7 years ago

thank you for the feedback, actually, what I meant by 'passive' is what hcitool does when called with option '--passive'. For example, when i start hcitool lescan (aka active low enery scan) and run btmon, I get the following (Note, how the ADV_IND packet is followed by a SCAN_RSP packet. This would be the case for any BLE device advertising itself.):

Bluetooth monitor ver 5.43
= New Index: 9C:B6:D0:D1:21:2A (Primary,USB,hci0)                                                   [hci0] 0.142928
= Open Index: 9C:B6:D0:D1:21:2A                                                                     [hci0] 0.142930
= Index Info: 9C:B6:D0:D1:21:2A (Qualcomm)                                                          [hci0] 0.142931
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7                                         [hci0] 41.888293
        Type: Active (0x01)
        Interval: 10.000 msec (0x0010)
        Window: 10.000 msec (0x0010)
        Own address type: Public (0x00)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                                                        [hci0] 41.889096
      LE Set Scan Parameters (0x08|0x000b) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2                                             [hci0] 41.889223
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                                                        [hci0] 41.890092
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 40                                                          [hci0] 42.135098
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Connectable undirected - ADV_IND (0x00)
        Address type: Public (0x00)
        Address: 90:03:B7:E8:1B:DB (PARROT)
        Data length: 28
        Flags: 0x06
          LE General Discoverable Mode
          BR/EDR Not Supported
        128-bit Service UUIDs (partial): 1 entry
          Vendor specific (39e1fa00-84a8-11e2-afba-0002a5d5c51b)
        Company: PARROT SA (67)
          Data: 014102
        RSSI: -58 dBm (0xc6)
> HCI Event: LE Meta Event (0x3e) plen 40                                                          [hci0] 42.138096
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Scan response - SCAN_RSP (0x04)
        Address type: Public (0x00)
        Address: 90:03:B7:E8:1B:DB (PARROT)
        Data length: 28
        Name (complete): Flower power 1BDB
        Slave Conn. Interval: 0x000a - 0x0064
        TX power: 0 dBm
        RSSI: -55 dBm (0xc9)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2                                            [hci0] 176.434543
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                                                       [hci0] 176.435420
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)

On the other hand, calling hcitool with option --passive shows only ADV_IND packets, because the kernel no longer requests SCAN_RSP from the advertising devices:

Bluetooth monitor ver 5.43
= New Index: 9C:B6:D0:D1:21:2A (Primary,USB,hci0)                                                   [hci0] 0.650323
= Open Index: 9C:B6:D0:D1:21:2A                                                                     [hci0] 0.650324
= Index Info: 9C:B6:D0:D1:21:2A (Qualcomm)                                                          [hci0] 0.650324
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7                                          [hci0] 3.665715
        Type: Passive (0x00)
        Interval: 10.000 msec (0x0010)
        Window: 10.000 msec (0x0010)
        Own address type: Public (0x00)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                                                         [hci0] 3.666663
      LE Set Scan Parameters (0x08|0x000b) ncmd 1
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2                                              [hci0] 3.666737
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                                                         [hci0] 3.667627
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)
> HCI Event: LE Meta Event (0x3e) plen 40                                                           [hci0] 4.830661
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Connectable undirected - ADV_IND (0x00)
        Address type: Public (0x00)
        Address: A0:14:3D:07:CF:D6 (PARROT SA)
        Data length: 28
        Flags: 0x06
          LE General Discoverable Mode
          BR/EDR Not Supported
        128-bit Service UUIDs (partial): 1 entry
          Vendor specific (39e1fa00-84a8-11e2-afba-0002a5d5c51b)
        Company: PARROT SA (67)
          Data: 014100
        RSSI: -75 dBm (0xb5)
> HCI Event: LE Meta Event (0x3e) plen 40                                                           [hci0] 5.369685
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Connectable undirected - ADV_IND (0x00)
        Address type: Public (0x00)
        Address: A0:14:3D:07:D0:4D (PARROT SA)
        Data length: 28
        Flags: 0x06
          LE General Discoverable Mode
          BR/EDR Not Supported
        128-bit Service UUIDs (partial): 1 entry
          Vendor specific (39e1fa00-84a8-11e2-afba-0002a5d5c51b)
        Company: PARROT SA (67)
          Data: 014102
        RSSI: -66 dBm (0xbe)
> HCI Event: LE Meta Event (0x3e) plen 40                                                           [hci0] 6.628669
      LE Advertising Report (0x02)
        Num reports: 1
        Event type: Connectable undirected - ADV_IND (0x00)
        Address type: Public (0x00)
        Address: 90:03:B7:E8:1B:DB (PARROT)
        Data length: 28
        Flags: 0x06
          LE General Discoverable Mode
          BR/EDR Not Supported
        128-bit Service UUIDs (partial): 1 entry
          Vendor specific (39e1fa00-84a8-11e2-afba-0002a5d5c51b)
        Company: PARROT SA (67)
          Data: 014102
        RSSI: -62 dBm (0xc2)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2                                              [hci0] 7.269400
        Scanning: Disabled (0x00)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                                                         [hci0] 7.270669
      LE Set Scan Enable (0x08|0x000c) ncmd 1
        Status: Success (0x00)

The difference really boils down to the scan-type specified in HCI Command: LE Set Scan Parameters:

< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7
        Type: Active (0x01)
        Interval: 10.000 msec (0x0010)
        Window: 10.000 msec (0x0010)
        Own address type: Public (0x00)
        Filter policy: Accept all advertisement (0x00)

vs.

< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7 
        Type: Passive (0x00)
        Interval: 10.000 msec (0x0010)
        Window: 10.000 msec (0x0010)
        Own address type: Public (0x00)
        Filter policy: Accept all advertisement (0x00)

Obviously, the HCI interface allows choosing the scan type as active or passive, while a scan w/ bluepy always runs an active scan.

In my application I would like to keep track of the BLE devices in the environment, keeping the listening instance as quiet as possible, going active only to retrieve SCAN_RSP packets from yet unknown devices. My question is, can this be achieved through the mgmt socket bluepy is using (after patching the latter) or do I have to resort to using HCI?

Sorry for the rather lengthy post.

PrzemoF commented 7 years ago

I did some research on the bluez side and setting the passive scanning after calling hcitool happens here: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/tools/hcitool.c#n2504 scan_type = 0x00 - passive or 0x01 active (default) The typs is used by hci_le_set_scan_parameters defined here (lib/hci.c): https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/lib/hci.c#n2944 header file (lib/hci_lib.h) https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/lib/hci_lib.h#n119

We have hci.c/hci_lib.h in bluepy, so I guess we should implement passive scan in bluepy-helper.c

@IanHarvey: is bluepy-helper the right place to add new scan options?

IanHarvey commented 7 years ago

If you can do this with code in bluepy-helper.c itself, that's fine - go ahead.

If we have to change the BlueZ code itself, that's slightly harder. I think I'd want to update to a newer version of BlueZ first.

Thanks Ian

gandy92 commented 7 years ago

@PrzemoF I did some research with pyBluez and came to pretty much the same conclusions, thanks for confirming this. An implementation in bluepy-helper.c would be preferable, so I'll give it a try and offer a patch when I have a working solution. Thanks @IanHarvey for the heads up.

gandy92 commented 7 years ago

This turned out to be a bit more complicated than anticipated, but with PR #214 comes a way to do passive scans. I've tested it thoroughly over the last days and so far it simply works. @IanHarvey, I'd be happy if this could make it into the official version one way or the other.

Thanks, Andy.

IanHarvey commented 7 years ago

Hi Andy,

Thanks for the patch. I merged it to master a couple of weeks ago and it's now been released as 1.1.2.

Cheers Ian

aqualx commented 4 years ago

@IanHarvey. For some reason manual doesn't mention new parameter passive for scan function implemented on this commit