Background: I'm building a distance sensor for my auto door lock which uses the bluetooth signal strength. The code runs on my raspiberry pi and detects my phone bluetooth address. It only needs to see if my phone is in a very close distance or a relatively far distance, so the RSSI should be stable enough for it.
Main issue: I couldn't find a way to detect the certain address RSSI efficiently without pairing and connecting since I only need the RSSI.
Working solution (not preferred):
The sudo btmgmt find | grep <address>
does the job, but the problem is that it take too long for a single scan loop - about 10 sec, because it scans all the address first, then filter.
What I need: Any linux (or python) command (or package) that allows me to pass address in and scan the RSSI only for that address in a short time.
Any idea is welcome.
I suspect that the slowness you are seeing is a combination of the Linux kernel doing some filtering of what devices are being shown to stop the output being too spammy and/or your phone not advertising rapidly to conserve battery power. Unless a phone has specifically been set to be discoverable, it will try to protect its address for privacy reasons to stop it being tracked.
You may want to investigate using something like a Bluetooth beacon keyring as an alternative to your phone.
For security reasons, you might also want to come up with a scheme that relies on more than just the address and RSSI.
On Linux the official Bluetooth stack is BlueZ which provides various API to access information. These APIs are accessed via D-Bus which is a message-oriented middleware mechanism that allows communication between your program and the Bluetooth Daemon running on the Raspberry Pi. Most programming languages have libraries to simplify communicating via D-Bus.
On the Linux command line you can use busctl
or gdbus
to access the D-Bus API.
In Python, pydbus is one of the easier libraries to get started with.
Bluez documents it's APIs at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/
To start the scanning for devices you will use
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/org.bluez.Adapter.rst
And to register for updates from a device (e.g. when the RSSI of your phone changes) you will need:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/org.bluez.Device.rst
The source tree also has an example of doing device discovery with Python:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/test-discovery
(Note: The BlueZ examples does not use pydbus
. They use the dbus-python
library)
As a simple example of using these APIs in Python using pydbus
:
(This example assumes the address is already in the bluetoothctl devices
list and you are using the default adapter of hci0
)
from gi.repository import GLib
from pydbus import SystemBus
def on_props_changed(iface, changed, invalidated):
# print(changed)
print(changed.get('RSSI')
def main(address):
bus = SystemBus()
adapter = bus.get('org.bluez', '/org/bluez/hci0')
device = bus.get('org.bluez', f'/org/bluez/hci0/dev_{address.replace(":", "_")}')
device.onPropertiesChanged = on_props_changed
mainloop = GLib.MainLoop()
adapter.SetDiscoveryFilter({'DuplicateData': GLib.Variant.new_boolean(True)})
adapter.StartDiscovery()
try:
mainloop.run()
except KeyboardInterrupt:
adapter.StopDiscovery()
mainloop.quit()
if __name__ == '__main__':
dev_addr = "xx:xx:xx:xx:xx:xx"
main(dev_addr)
If you wanted to experiment with beacons, you can create an Eddystone UID beacon with another Raspberry Pi using the following btmgmt
commands:
$ sudo btmgmt add-adv -u feaa -d 1516aafe0000112233445566778899AA112233445566 1
$ sudo btmgmt discov on
The on_props_changed
function might be updated to look like:
def on_props_changed(iface, changed, invalidated):
# print(changed)
service_data = changed.get('ServiceData', {}).get('0000feaa-0000-1000-8000-00805f9b34fb', [])
if service_data:
namespace_id = int.from_bytes(service_data[2:12], 'big')
instance_id = int.from_bytes(service_data[12:18], 'big')
print(f'{namespace_id=:x} - {instance_id=:x}')
Which in my test output:
namespace_id=112233445566778899aa - instance_id=112233445566
Longer term a project like this could be done with the AdvertisementMonitor API, but currently it is still hidden behind an experimental flag.
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/org.bluez.AdvertisementMonitor.rst