Good afternoon,
First of all I apologize for asking questions in this thread but this is my first time writing on StackExchange. I am doing a university work where it is necessary through a Raspberry Pi to access information that is reported by a blueetooth sensor that has an IMU unit (with accelerometer, gyroscope and magnetometer).
Before starting to build code in python on the Raspberry Pi, I installed an Android application called nRF Connect that allows you to access information from blueetooth devices.
After successfully connecting to the Bluetooth device, I was able to access a number of bluetooth features, as can be seen from the figure below:
To access the values indicated by the gyroscope, accelerometer and magnetometer, it is necessary to send the byte array 0x01 via UUID 0xFF35:
After sending this byte 0x01 to UUID 0xFF35, the characteristic with UUID 0xFF38 immediately starts sending the required information. This value is constantly changing as it refers to the three sensors that are installed in the IMU unit. As can be seen by the red box in the figure below:
In the meantime I tried to create some python code on the Raspberry Pi in order to get the values reported by characteristic with UUID 0xFF38.
I checked the thread at the following link: BlueZ BTGATT-client.py WriteValue and tried to adapt the code to my situation.
The code I have at the moment is as follows:
import pydbus
import time
from gi.repository import GLib
dev_id = '20:C3:8F:D0:CF:10'
bluez_service = 'org.bluez'
adapter_path = '/org/bluez/hci0'
device_path = f"{adapter_path}/dev_{dev_id.replace(':', '_')}"
uuid = '0000ff35-0000-1000-8000-00805f9b34fb'
uuidnew= '0000ff38-0000-1000-8000-00805f9b34fb'
# Setup DBus informaton for adapter and remote device
bus = pydbus.SystemBus()
mngr = bus.get('org.bluez', '/')
adapter = bus.get('org.bluez', adapter_path)
device = bus.get('org.bluez', device_path)
# Connect to device (needs to have already been paired via bluetoothctl)
device.Connect()
#print(dir(adapter))
#print(dir(device))
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
#Read information from characteristic 0xFF35
char_path = get_characteristic_path(device._path, uuid)
btn = bus.get(bluez_service, char_path)
print(btn.ReadValue({}))
#Set the value 0x01, to activate the characteristic 0xFF38 in order to report the sensor values.
new_value = int(1).to_bytes(2, byteorder='little')
btn.WriteValue(new_value, {})
#Read the values of characteristic 0xFF38
newCaract = get_characteristic_path(device._path, uuidnew)
btnNew = bus.get(bluez_service, newCaract)
print(btnNew.ReadValue({}))
If I run this code in the Thonny IDE (which comes installed on the Raspberry pi), I am confronted with the following message:
Where line 43 is precisely the statement:
print(btnNew.ReadValue({}))
Essentially the following instructions, wrote the byte 0x01 to characteristic 0xFF35:
new_value = int(1).to_bytes(2, byteorder='little')
btn.WriteValue(new_value, {}),
However when analyzing the error message there seems to be no synchronism that allows the Raspberry Pi to read the values sent by characteristic 0xFF38.
On the other hand, if I run this code a second time the following output appears:
As you can see, the characteristic 0xFF35, reports the value 1, meaning that in fact byte 0x01 was written to this characteristic.
-Given this error message, I ask what might be the most appropriate procedure for reading the values reported by characteristic 0xFF38?
Thank you very much for your attention
[UPDATE]
Good afternoon,
Regarding the nRF Connect application, I confirm that I can do write at 0xFF35 and read from 0xFF38. In this case, I am writing 0x01 at characteristic 0xFF35 and automatically at 0xFF38 I see that the values reported by the IMU unit are being correctly received, as in the figure below:
On the other hand, I returned to the Rapberry Pi and did the following:
-In the Python code I added the following line of code:
btnNew.StartNotify()
Then I opened two terminals, in one of them I inserted the "bluetoothctl" command line, in another I inserted the "sudo btmon" command line.Then I pressed the Run button and got the following result:
Looking at the last image, we see that the "Data" field under the "Handle": 0x0024", is constantly changing.
When I give the sensor a few shakes, I notice that some of the "Data" fields change.
Considering this situation, at the Python code level, did I proceed correctly to access the value indicated by 0xFF38? Is any adjustment necessary?
Thank you for your attention
[UPDATE-2]
import pydbus
import time
from gi.repository import GLib
dev_id = '20:C3:8F:D0:CF:10'
bluez_service = 'org.bluez'
adapter_path = '/org/bluez/hci0'
device_path = f"{adapter_path}/dev_{dev_id.replace(':', '_')}"
uuid = '0000ff35-0000-1000-8000-00805f9b34fb'
uuidnew= '0000ff38-0000-1000-8000-00805f9b34fb'
# Setup DBus informaton for adapter and remote device
bus = pydbus.SystemBus()
mngr = bus.get('org.bluez', '/')
adapter = bus.get('org.bluez', adapter_path)
device = bus.get('org.bluez', device_path)
# Connect to device (needs to have already been paired via bluetoothctl)
device.Connect()
#print(dir(adapter))
#print(dir(device))
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
#Read information from characteristic 0xFF35
char_path = get_characteristic_path(device._path, uuid)
btn = bus.get(bluez_service, char_path)
print(btn.ReadValue({}))
#Set the value 0x01, to activate the characteristic 0xFF38 in order to report the sensor values.
new_value = int(1).to_bytes(2, byteorder='little')
btn.WriteValue(new_value, {})
#Read the values of characteristic 0xFF38
newCaract = get_characteristic_path(device._path, uuidnew)
btnNew = bus.get(bluez_service, newCaract)
# 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()
btnNew.onPropertiesChanged = temp_handler
btnNew.StartNotify()
try:
mainloop.run()
except KeyboardInterrupt:
mainloop.quit()
btnNew.StopNotify()
device.Disconnect()
bluetoothctl
open in a different terminal to see if that gives any more information about what is happening.. For very low level debugging, usesudo btmon
in another terminal. Before that, it appears you followed a slightly different process when using nRF Connect. For the0xFF38
characteristic nRF Connect has notifications enabled. With Python you are doing a read. Are you able to do a write on0xFF35
and a read on0xFF38
in nRF connect? Have you tried doing the "StartNotify" in Python?onPropertiesChanged
(to register the handler),StartNotify
(to enable notifications), andmainloop.run
(to start the event loop) at the bottom of raspberrypi.stackexchange.com/a/114175/121848. There is also another example at: stackoverflow.com/a/63751113/7721752