ukBaz / python-bluezero

A simple Python interface to Bluez
MIT License
387 stars 112 forks source link

Documentation about PIN on pairing #399

Closed bolinocroustibat closed 9 months ago

bolinocroustibat commented 10 months ago

I'm trying to implement a BLE peripheral with python-bluezero.

When using bluezero.peripheral.Peripheral, is there a way to make the peripheral appairable with no PIN? For now the central is asking for a PIN or says "Couldn't pair because of incorrect PIN". I couldn't find the info in the documentation.

ukBaz commented 10 months ago

The Bluezero library is targeted at STEM projects and people learning to use Bluetooth on Linux with Python. Pairing can quickly become a tangled web of complexity and that is why the examples in the library avoid the requirement to pair.

If you can give me a little bit more context about the characteristics you are using and the flags you are setting then we can have a more meaningful discussion.

If I was to speculate, could it be that you are using an iPhone to connect and you are seeing the "battery service" issue?

bolinocroustibat commented 9 months ago

Thanks for your answer and sorry for late reply. We are trying to set up a Raspberry Pi 4 (Bluetooth chipset Cypress CYW43455) as a BLE peripheral and decided to use the python-bluezero library, it seems it's the only Python one which is being maintained, as far as our searches went.

We are running our tests from an Android phone (Google Pixel) as the BLE central. We didn't try with an iPhone yet.

Those are the tests settings we are using:

adapter_address = list(adapter.Adapter.available())[0].address

ble = peripheral.Peripheral(adapter_address, local_name="Test peripheral")

ble.add_service(srv_id=1, uuid="00001111-1234-1234-1234-123456789abc", primary=True)

ble.add_characteristic(
    srv_id=1,
    chr_id=1,
    uuid="00002222-1234-1234-1234-123456789abc",
    value=[],
    notifying=False,
    flags=["write"],
    write_callback=callback_write,
)

ble.add_characteristic(
    srv_id=1,
    chr_id=2,
    uuid="00003333-1234-1234-1234-123456789abc",
    value=[],
    notifying=False,
    flags=["read"],
    read_callback=callback_read,
)

It successfully advertises, but when we try to connect with the central (sorry, I might still be confused between "connecting" and pairing" btw), the central is asking for a PIN or says "Couldn't pair with raspberry pi because of incorrect PIN".

It seems also the advertisement name displayed on the central switch back to "raspberrypi" from "Test peripheral" after trying, or after a few seconds.

ukBaz commented 9 months ago

I see nothing in your peripheral code that would trigger pairing being required.

Pairing is the one-time exchange of keys to enable secure communication. You don't have any secure characteristics so I would not expect it to prompt for pairing.

This makes me wonder how you are connecting from the Android phone. I would suggest that initially you use a generic Bluetooth Low Energy scanning and exploration tool such as nRF Connect from Nordic. This will try to connect to the Raspberry Pi in BLE and you can see if you peripheral is as you expect.

If that doesn't work then follow the below instructions to make the RPi BLE only: https://github.com/ukBaz/python-bluezero/blob/main/docs/install_bluez.rst#switch-controller-to-bluetooth-low-energy-only

When running your script have separate terminals open with the following running to get more debug information:

- bluetoothctl
- journalctl -f -u bluetooth
- sudo busctl monitor org.bluez
- sudo btmon

I suspect that writing the output from btmon to a btsnoop file and importing it to wireshark might be out next step if there isn't anything helpful from the above experiment with nRF Connect

bolinocroustibat commented 9 months ago

Thanks, it's very helpful. I've been testing using the tools you mentioned, and after launching the above mentioned script using python-bluezero, connecting works fine, and I can get much more information using logs. Indeed I was confused between connecting and pairing (the latter seems to be also named "bonding" in nRF Connect).

Now that connecting works fine, I guess I need to set up pairing. Can pairing configuration be done using python-bluezero, and if yes does it have documentation and/or example about that? Or does it have to be done not using python-bluezero, but using bluetothctl CLI?

ukBaz commented 9 months ago

Pairing is only necessary if a connection is encrypted. If you create a peripheral device that requires security measures on one of its characteristics then Android will prompt the user of your app with a pairing request automatically.

In the GATT API, setting the enhanced security is done by setting the appropriate value for flags in the characteristic: The list of possible values is in the BlueZ API documentation: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt#n270

There is more information about pairing and bonding in the answers to the following question on Stackoverflow: https://stackoverflow.com/q/65835536

As pairing is normally a one-off provisioning step, and Bluezero is using unsecured characteristics, this library doesn't have code in it to help with pairing.

However, on stackoverflow there are answers that explain the example provided by Bluez repo: https://stackoverflow.com/a/71255445

Does that help?

bolinocroustibat commented 9 months ago

It does help a lot, thank you!