2

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:

enter image description here

To access the values indicated by the gyroscope, accelerometer and magnetometer, it is necessary to send the byte array 0x01 via UUID 0xFF35:

enter image description here

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:

enter image description here

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: enter image description here

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:

enter image description here

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:

enter image description here

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: enter image description here

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()
6
  • To help with debugging, have bluetoothctl open in a different terminal to see if that gives any more information about what is happening.. For very low level debugging, use sudo btmon in another terminal. Before that, it appears you followed a slightly different process when using nRF Connect. For the 0xFF38 characteristic nRF Connect has notifications enabled. With Python you are doing a read. Are you able to do a write on 0xFF35 and a read on 0xFF38 in nRF connect? Have you tried doing the "StartNotify" in Python?
    – ukBaz
    Dec 21, 2021 at 21:42
  • Good afternoon, first of all, I apologize for the confusion here on StackExchange. As I understand I have already enabled notifications on characteristc 0xFF38, however it is necessary to attaching an event handler or starting the eventloop/mainloop. Do you have examples with you, so that I can prepare Python code for that situation? Thank you.
    – jf_PT
    Dec 22, 2021 at 14:28
  • No need to apologize. You are a new contributor. You improved your question so that is good. There is an example of the Python you need for notification handler in the other question you linked to where you got the example from. It would also be interesting to know if in nRF Connect, if you turned off notifications (multiple arrow symbol), can you do a read (single down arrow)?
    – ukBaz
    Dec 22, 2021 at 14:46
  • I got the following results. When turning off the notifications, through the multiple-arrow button, I noticed that the blue box value of the image became static ( imagizer.imageshack.com/img922/779/s5rdS9.jpg). I notice that when pressing the single-arrow button nothing happens and the information received disappeared (imagizer.imageshack.com/img924/1042/FBLCgh.jpg). In the left logs page the following notifications appear : (imagizer.imageshack.com/img924/6691/TrinY5.jpg). Thank you.
    – jf_PT
    Dec 22, 2021 at 16:11
  • Thanks for doing that experiment. So that appears to show that you can't "read" the value in nRF Connect either. This means that you definitely need to do notifications in Python also. This means using onPropertiesChanged (to register the handler), StartNotify (to enable notifications), and mainloop.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
    – ukBaz
    Dec 22, 2021 at 16:40

1 Answer 1

2

gatttool is one of the tools that has been deprecated by the BlueZ project. If you are following a tutorial that uses those tools then you should be careful that the tutorial isn't incompatible with your system.

You might be seeing the error messages you are getting because gatttool bypasses the Bluetooth daemon running on your system causing there to be some kind of conflict.

You should be able to test your device is working using bluetoothctl. Using blutoothctl will help identify if the issue is with your system or with the code as bluetoothctl uses the same API to talk to the Bluetooth daemon as your Python code.

As you want to read and write to two different UUIDs it is often easier to have two instances of bluetoothctl running in different terminals to save you switching between the two characteristics.

In the first terminal start bluetothctl, connect, and turn on notifications for 0XFF38 characteristic:

connect 20:C3:8F:D0:CF:10
gatt.select-attribute 0000ff38-0000-1000-8000-00805f9b34fb
gatt.notify on

In the second terminal the bluetoothctl instance should already show that it is connected to your device. Select the other characteristic and write 0x01 to it as you did in nRF Connect:

gatt.select-attribute 0000ff35-0000-1000-8000-00805f9b34fb
gatt.write 0x01

In the first bluetoothctl that is monitoring 0xff38 you should see the IMU data start to appear.

Doing this test with bluetoothctl will help identify if it is an issue with your system or your Python code.


As a side note I would comment that your BLE device is using UUIDs in the value range reserved for officially adopted Characteristics (0000????-0000-1000-8000-00805F9B34FB). More information at: https://www.novelbits.io/uuid-for-custom-services-and-characteristics/ and in the 16-bit UUIDs document. Not a problem for your experiments at university but an issue if it is taken further.

8
  • Good morning, first of all thank you very much for the tip. I tested it and got the following result: imagizer.imageshack.com/img923/9999/p8uOdU.jpg . In the window on the right side, I selected the characteristic (0000ff35-0000-1000-8000-00805f9b34fb) and wrote the byte 0x01 just like in the nRF Connect app. Then, in the terminal on the left hand side, I ran the command lines you mentioned. I notice that every time I run the " gatt.notify on" command, the information is read , just like in nRF Connect. And if I give the sensor a few shakes, some values are modified.
    – jf_PT
    Dec 23, 2021 at 9:29
  • Given this situation, I ask if it will be necessary to install any packages that are missing in the Raspberry pi that might cause that error message to appear? Thank you.
    – jf_PT
    Dec 23, 2021 at 9:33
  • I am suspecting that the issue is more likely to be the sensor you are connecting to (or the order of steps you are following) given that there are errors reported when interacting with it with nRF Connect, bluetoothctl, and Python. Do you have any documentation for the sensor you can link to?
    – ukBaz
    Dec 23, 2021 at 10:20
  • Unfortunately they didn't provide us with much information. The only information I have is that the IMU sensor values are received when I write byte 0x01 to characteristic 0xFF35. And on the other hand, that sensor data is received by an array at characteristic 0xFF38.
    – jf_PT
    Dec 23, 2021 at 13:03
  • I was looking at the code and noticed two situations: First situation: Checking this image ( imagizer.imageshack.com/img924/6930/yAGFyw.jpg), I notice that the path to characteristic (0000ff38-0000-1000-8000-00805f9b34fb) is slightly different from what is in the code header indicated by the "adapter_path" variable (/org/bluez/hci0). I have changed the code to the following: imagizer.imageshack.com/img922/8017/qEUuiW.jpg Unfortunately it didn't change the behavior, and I still get the error message: imagizer.imageshack.com/img924/5214/CD94LC.jpg
    – jf_PT
    Dec 23, 2021 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.