Bluezv5.42 Dbus C API for Ble

BluezV5.42 DBUS C API for BLE?

As you mention in the other post I updated gattlib to support Bluez DBUS API.

gattlib is written in C. If you do not want to use it looking at its code should help you to progress.

Since Bluez v5.42, GATT D-BUS API is not longer mark as experimental.

The DBUS API is described here: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc. There are also (python) examples that demonstrate the API.

  • Reading RSSI
  • Connecting and Disconnecting
  • Pairing
  • Discovery
  • Scanning
  • Advertising
  • Write/Reading to Characteristics
  • Notifications and Indications
  • Bonding and Deleting Bonding information: it looks it is transparent when using DBUS API.

Active BLE Scanning (BlueZ) - Issue with DBus

I can't really reproduce your error exactly but my system is not happy running that fast while loop repeatedly getting the data from GetManagedObjects.
Below is the code I ran based on your code with a little bit of refactoring...

import dbus

BLUEZ_SERVICE_NAME = "org.bluez"
DBUS_OM_IFACE = "org.freedesktop.DBus.ObjectManager"
ADAPTER_IFACE = "org.bluez.Adapter1"
DEVICES_IFACE = "org.bluez.Device1"

def main_loop():
devinfo = None
objects = None

dbussys = dbus.SystemBus()
dbusconnection = dbussys.get_object(BLUEZ_SERVICE_NAME, "/")
bluezInterface = dbus.Interface(dbusconnection, DBUS_OM_IFACE)

while True:
objects = bluezInterface.GetManagedObjects()
for path in objects:
name = objects[path].get(DEVICES_IFACE, {}).get('Name')
rssi = objects[path].get(DEVICES_IFACE, {}).get('RSSI')
service_data = objects[path].get(DEVICES_IFACE, {}).get('ServiceData')
if all((name, rssi, service_data)):
print(f'{name} @ {rssi} = {service_data}')
#[... Do someting...]

if __name__ == '__main__':
main_loop()

I'm not sure what you are trying to do in the broader project but if I can make some recommendations...

A more typical way of scanning for service/manufacturer data is to subscribe to signals in D-Bus that trigger callbacks when something of interest happens.

Below is some code I use to look for iBeacons and Eddystone beacons. This runs using the GLib event loop which is maybe something you have ruled out but is more efficient on resources.

It does use different Python dbus bindings as I find pydbus more "pythonic".

I have left the code in processing the beacons as it might be a useful reference.

import argparse
from gi.repository import GLib
from pydbus import SystemBus
import uuid

DEVICE_INTERFACE = 'org.bluez.Device1'

remove_list = set()

def stop_scan():
"""Stop device discovery and quit event loop"""
adapter.StopDiscovery()
mainloop.quit()

def clean_beacons():
"""
BlueZ D-Bus API does not show duplicates. This is a
workaround that removes devices that have been found
during discovery
"""
not_found = set()
for rm_dev in remove_list:
try:
adapter.RemoveDevice(rm_dev)
except GLib.Error as err:
not_found.add(rm_dev)
for lost in not_found:
remove_list.remove(lost)

def process_eddystone(data):
"""Print Eddystone data in human readable format"""
_url_prefix_scheme = ['http://www.', 'https://www.',
'http://', 'https://', ]
_url_encoding = ['.com/', '.org/', '.edu/', '.net/', '.info/',
'.biz/', '.gov/', '.com', '.org', '.edu',
'.net', '.info', '.biz', '.gov']
tx_pwr = int.from_bytes([data[1]], 'big', signed=True)
# Eddystone UID Beacon format
if data[0] == 0x00:
namespace_id = int.from_bytes(data[2:12], 'big')
instance_id = int.from_bytes(data[12:18], 'big')
print(f'\t\tEddystone UID: {namespace_id} - {instance_id} \u2197 {tx_pwr}')
# Eddystone URL beacon format
elif data[0] == 0x10:
prefix = data[2]
encoded_url = data[3:]
full_url = _url_prefix_scheme[prefix]
for letter in encoded_url:
if letter < len(_url_encoding):
full_url += _url_encoding[letter]
else:
full_url += chr(letter)
print(f'\t\tEddystone URL: {full_url} \u2197 {tx_pwr}')

def process_ibeacon(data, beacon_type='iBeacon'):
"""Print iBeacon data in human readable format"""
print('DATA:', data)
beacon_uuid = uuid.UUID(bytes=bytes(data[2:18]))
major = int.from_bytes(bytearray(data[18:20]), 'big', signed=False)
minor = int.from_bytes(bytearray(data[20:22]), 'big', signed=False)
tx_pwr = int.from_bytes([data[22]], 'big', signed=True)
print(f'\t\t{beacon_type}: {beacon_uuid} - {major} - {minor} \u2197 {tx_pwr}')

def ble_16bit_match(uuid_16, srv_data):
"""Expand 16 bit UUID to full 128 bit UUID"""
uuid_128 = f'0000{uuid_16}-0000-1000-8000-00805f9b34fb'
return uuid_128 == list(srv_data.keys())[0]

def on_iface_added(owner, path, iface, signal, interfaces_and_properties):
"""
Event handler for D-Bus interface added.
Test to see if it is a new Bluetooth device
"""
iface_path, iface_props = interfaces_and_properties
if DEVICE_INTERFACE in iface_props:
on_device_found(iface_path, iface_props[DEVICE_INTERFACE])

def on_device_found(device_path, device_props):
"""
Handle new Bluetooth device being discover.
If it is a beacon of type iBeacon, Eddystone, AltBeacon
then process it
"""
address = device_props.get('Address')
address_type = device_props.get('AddressType')
name = device_props.get('Name')
alias = device_props.get('Alias')
paired = device_props.get('Paired')
trusted = device_props.get('Trusted')
rssi = device_props.get('RSSI')
service_data = device_props.get('ServiceData')
manufacturer_data = device_props.get('ManufacturerData')
if address.casefold() == '00:c3:f4:f1:58:69':
print('Found mac address of interest')
if service_data and ble_16bit_match('feaa', service_data):
process_eddystone(service_data['0000feaa-0000-1000-8000-00805f9b34fb'])
remove_list.add(device_path)
elif manufacturer_data:
for mfg_id in manufacturer_data:
# iBeacon 0x004c
if mfg_id == 0x004c and manufacturer_data[mfg_id][0] == 0x02:
process_ibeacon(manufacturer_data[mfg_id])
remove_list.add(device_path)
# AltBeacon 0xacbe
elif mfg_id == 0xffff and manufacturer_data[mfg_id][0:2] == [0xbe, 0xac]:
process_ibeacon(manufacturer_data[mfg_id], beacon_type='AltBeacon')
remove_list.add(device_path)
clean_beacons()

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--duration', type=int, default=0,
help='Duration of scan [0 for continuous]')
args = parser.parse_args()
bus = SystemBus()
adapter = bus.get('org.bluez', '/org/bluez/hci0')

bus.subscribe(iface='org.freedesktop.DBus.ObjectManager',
signal='InterfacesAdded',
signal_fired=on_iface_added)

mainloop = GLib.MainLoop()

if args.duration > 0:
GLib.timeout_add_seconds(args.duration, stop_scan)
adapter.SetDiscoveryFilter({'DuplicateData': GLib.Variant.new_boolean(False)})
adapter.StartDiscovery()

try:
print('\n\tUse CTRL-C to stop discovery\n')
mainloop.run()
except KeyboardInterrupt:
stop_scan()

BLE communication after pairing

The connection parameters for a BLE connection is a set of parameters that determine when and how the Central and a Peripheral in a link transmits data. It is always the Central that actually sets the connection parameters used, but the Peripheral can send a so-called Connection Parameter Update Request, that the Central can then accept or reject.

Connection supervision timeout: This timeout determines the timeout from the last data exchange till a link is considered lost. A Central will not start trying to reconnect before the timeout has passed, so if you have a device which goes in and out of range often, and you need to notice when that happens, it might make sense to have a short timeout.

for more read this thread

gatttool LTK, IRK, GAP doubts?

You don't have to build you application around gatttool and what's included in it.

Bluez5 exposes interfaces in the DBus. Using this dbus api and with the dbus bindings in the language of your choice (C, python, C#Mono) you can pretty much do everything =)

You can find a description of the dbus api exposed by bluez here :
https://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc

You can find the source code of bluetoothctl (a tool that you can use for pairing, connecting, services discovery, gatt attributes reading & writing etc) here : https://git.kernel.org/cgit/bluetooth/bluez.git/tree/client/main.c

bluetoothctl was built using GLib GDBus (dbus bindings for Glib in C) and you will find code examples for pretty much everything.

BLE Smart 4.2 Automation - How the by-pass security layer if possible [For Test Automation]

To answer your questions:-

1- You are right, connection is mandatory for communication while pairing is not. However, pairing has many advantages such as securing the connection and aiding in future device discoverability. Have a look at the links below for more information:-

  • Should One Create a Bond with a Bluetooth LE Device
  • Is Bonding Required for BLE Android iOS Device Communication

2- You can use bluetoothctl itself to do the pairing/bonding for you. If you don't want to manually intervene in the pairing process yourself then you might have to set up and automated process or write some shell scripts to take care of this for you. The whole idea of the pairing process is to prevent attacks which is why manual security entry is always preferred to the automated one. Have a look at the links below for more information on pairing using bluetoothctl:-

  • Raspberry Pi BLE Encryption/Pairing
  • Configuring Bluetooth devices with bluetoothctl
  • How to Encrypt Data Using BLE Connection

I hope this helps.



Related Topics



Leave a reply



Submit