1

I had a successful attempts with BlueZ-5.50 > test > example-gatt-client.py with ESP32 set as HeartRate Sensor Server.I am able to read from ESP32 which i modified to suit the BlueZ example.

However i have a doubt. What if i need to send some byte or string to ESP32 from the existing example.I wrote in such manner (a simple function to see whether it throws an error):

def write_val():

value = 0x50

#Write to a UUID cg_ctrl_pt_chrc.WriteValue(value,reply_handler=body_sensor_val_cb, error_handler=generic_error_cb, dbus_interface=GATT_CHRC_IFACE)

I am still new & learning Dbus & Python.

OS - Raspbian Stretch

R.M.S.Sundram
  • 21
  • 1
  • 6

2 Answers2

0

The examples in the BlueZ test directory use the dbus-python library which is not the best of python libraries according to https://wiki.python.org/moin/DbusExamples.

A library from that page is pydbus https://pypi.org/project/pydbus/ which seems to work well with the BlueZ DBus API for a GATT client.

I don't have an ESP32 or Heart Rate Sensor, so I will do an example with a BBC micro:bit and hopefully it will be easy to modify to your needs.

The BlueZ DBus API documentation is available at:

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/device-api.txt

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt

Some useful things to know to get you started:

  1. The Dbus service for bluez is called org.bluez
  2. The Bluetooth adapter on a Raspberry Pi normally has the DBus object path of /org/bluez/hci0
  3. The DBus Object path to a device is the adapter path plus the mac address prepended by 'dev_' and the semi-colons replaced with underscores. i.e. 'DE:82:35:E7:43:BE' would be found at /org/bluez/hci0/dev_DE_82_35_E7_43_BE

This script assumes that the device has already been paired with Raspberry Pi. As the pairing is a one-off provisioning step, it makes things simplier to do it manually. I usually use bluetoothctl on the command line.

import pydbus
from gi.repository import GLib

Setup of device specific values

dev_id = 'DE:82:35:E7:43:BE' btn_a_uuid = 'e95dda90-251d-470a-a062-fa1922dfa9a8' temp_reading_uuid = 'e95d9250-251d-470a-a062-fa1922dfa9a8' temp_period_uuid = 'e95d1b25-251d-470a-a062-fa1922dfa9a8'

DBus object paths

bluez_service = 'org.bluez' adapter_path = '/org/bluez/hci0' device_path = f"{adapter_path}/dev_{dev_id.replace(':', '_')}"

bus = pydbus.SystemBus() adapter = bus.get(bluez_service, adapter_path) device = bus.get(bluez_service, device_path)

Assume device has been paired already so can use connect

device.Connect()

Get commands and properties available

print(dir(adapter)) print(dir(device))

To read and write you need to find the path to the characteristic which is a little more work. Typically you know the UUID of the characteristic you are interested in so to get the value from button A on a BBC micro:bit it would be:

mngr = bus.get(bluez_service, '/')

def get_characteristic_path(dev_path, uuid): mng_objs = mngr.GetManagedObjects() for path in mng_objs: chr_uuid = mng_objs[path].get('org.bluez.GattCharacteristic1', {}).get('UUID') if path.startswith(dev_path) and chr_uuid == uuid: return path

char_path = get_characteristic_path(device._path, btn_a_uuid) btn = bus.get(bluez_service, char_path) print(btn.ReadValue({}))

[0]

Writing to a characteristic is similar. Here is an example of reading and writing to the Temperature Period on a BBC micro:bit

tmp_period_path = get_characteristic_path(device._path, temp_period_uuid)
tmp_period = bus.get(bluez_service, tmp_period_path)
print(tmp_period.ReadValue({}))
# Result is:
# [232, 3]
# To get it as an integer:
print(int.from_bytes(tmp_period.ReadValue({}), byteorder='little'))
# 1000

To write a new value of 1500

new_value = int(1500).to_bytes(2, byteorder='little') tmp_period.WriteValue(new_value, {}) print(tmp_period.ReadValue({}))

[220, 5]

device.Disconnect()

If you want to run this in an eventloop with notifications from the remote device then remove the disconnect above and add the following code:

temp_reading_path = get_characteristic_path(device._path, temp_reading_uuid)
temp = bus.get(bluez_service, temp_reading_path)

Enable eventloop for notifications

def temp_handler(iface, prop_changed, prop_removed): """Notify event handler for temperature""" if 'Value' in prop_changed: print(f"Temp value: {as_int(prop_changed['Value'])} \u00B0C")

mainloop = GLib.MainLoop() temp.onPropertiesChanged = temp_handler temp.StartNotify() try: mainloop.run() except KeyboardInterrupt: mainloop.quit() temp.StopNotify() device.Disconnect()

Hope that is helpful.

ukBaz
  • 1,548
  • 1
  • 7
  • 23
0

I am posting another answer to see if this fits better given that you have posted the link which has the code you are using at the other end of the link:

https://platformio.org/lib/show/1841/ESP32%20BLE%20Arduino

Looking at the BLE_write.ino, my understanding is that if you write text to "beb5483e-36e1-4688-b7f5-ea07361b26a8" the ESP32 will display the text on its console.

import pydbus
from time import sleep

Setup of device specific values

dev_id = 'DE:82:35:E7:43:BE'

BBC micro:bit text display characteristic

txt_uuid = 'E95D93EE-251D-470A-A062-FA1922DFA9A8'

ESP32 BLE_write.ino text characteristic

txt_uuid = 'beb5483e-36e1-4688-b7f5-ea07361b26a8'

txt_to_send = 'This is a test'

DBus object paths

bluez_service = 'org.bluez' adapter_path = '/org/bluez/hci0' device_path = f"{adapter_path}/dev_{dev_id.replace(':', '_')}"

Get adapter and device objects

bus = pydbus.SystemBus() adapter = bus.get(bluez_service, adapter_path) device = bus.get(bluez_service, device_path)

Assume device has been paired already so can use connect

device.Connect()

Wait for the remote device to resolve its services

while not device.ServicesResolved: sleep(0.5)

mngr = bus.get(bluez_service, '/')

def get_characteristic_path(dev_path, uuid): """Get DBus object path for Characteristic UUID""" mng_objs = mngr.GetManagedObjects() for path in mng_objs: chr_uuid = mng_objs[path].get('org.bluez.GattCharacteristic1', {}).get('UUID') if path.startswith(dev_path) and chr_uuid == uuid.casefold(): return path

txt_path = get_characteristic_path(device._path, txt_uuid) txt_obj = bus.get(bluez_service, txt_path)

Turn text into

new_value = [ord(c) for c in txt_to_send]

Write a new value ensure it is not too long for Bluetooth

txt_obj.WriteValue(new_value[:19], {})

Disconnect

device.Disconnect()

ukBaz
  • 1,548
  • 1
  • 7
  • 23