ukBaz / python-bluezero

A simple Python interface to Bluez
MIT License
387 stars 112 forks source link

Uuid and little endian format #373

Closed ZaoTX closed 2 years ago

ZaoTX commented 2 years ago

Dear all,

I read through the code in examples/heartrate_monitor_peripheral.py Here you defined a uuid HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb' which means "Heart Rate Control Point" I would like to ask how you managed to generate such uuid from string? There is a package called uuid. I don't know which one should I use for this translation. https://docs.python.org/3/library/uuid.html

And if I send a message in string (from phone to the peripheral). It returns numbers in little endian format. How can I translate the little endian numbers into string again?

ukBaz commented 2 years ago

I am not sure I understand your question about UUID. There is is no requirement to use the UUID library as BlueZ (the library underneath Bluezero) expects the UUIDs in string format. If you can clarify your question I will try to answer it. Is something not doing what you are expecting? Are you trying to create a UUID for a custom service? You can do like the following:

pi@raspberry:~ $ python3
Python 3.9.2 (default, Mar 12 2021, 04:06:34) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import uuid
>>> str(uuid.uuid4())
'7a2e8776-a11f-49c4-85f7-3dce19cf010f'`

Bluetooth always sends bytes in little endian format. Most things can be done with the struct library https://docs.python.org/3/library/struct.html If you can share what the data is your getting (and what you are sending/expecting) I can take a look to see how it could be unpacked.

ZaoTX commented 2 years ago

Thank you very much for your quick answer.

Using examples/heartrate_monitor_peripheral.py as an instance, I want to firstly create a writeable characteristic with name like "send message". Here I want to change the uuid so that it can show a desired characteristic name.

Then, I want to read a string sent from iPhone to the peripheral (here I used lightBlue). I rewrite the function write_control_point in

def write_control_point(value, options):
     print(value)

But I got a list of integers when I send the text via lightBlue. Is that possible to convert this list of integers back into string?

ukBaz commented 2 years ago

Maybe the ble_uart.py is a better example for you to start from for what you want to do.

The RX_CHARACTERISTIC can be written to and it has a callback to convert the list of integers to text:

https://github.com/ukBaz/python-bluezero/blob/1fb7a0e7df18b785fe073aa55cba1354633f6880/examples/ble_uart.py#L38-L43

ZaoTX commented 2 years ago

Here in my test code based on your examples:

from gi.repository import GLib
from bluezero import adapter
from bluezero import peripheral
from bluezero import device

HRM_SRV = '0000180D-0000-1000-8000-00805f9b34fb'
HR_CTRL_PT_UUID = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'

def write_control_point(value,option):
    """
    Called when a central writes to our write characteristic.
    :param value: data sent by the central
    :param options:
    """

    print('raw bytes:', value)
    print('With options:', options)
    print('Text value:', bytes(value).decode('utf-8'))
    #print(option)

adapter_address = list(adapter.Adapter.available())[0].address
# Create peripheral
hr_monitor = peripheral.Peripheral(adapter_address,
                                   local_name='SendMeText',
                                   appearance=0x0340)
# Add service
hr_monitor.add_service(srv_id=1, uuid=HRM_SRV, primary=True)

# Add characteristics
hr_monitor.add_characteristic(srv_id=1, chr_id=1, uuid=HR_CTRL_PT_UUID,
                              value="", notifying=False,
                              flags=['write'],
                              read_callback=None,
                              write_callback=write_control_point,
                              notify_callback=None
                              )

# Publish peripheral and start event loop
hr_monitor.publish()

The line print('Text value:', bytes(value).decode('utf-8')) is not working but print('raw bytes:', value) does. Can you tell me what could be missing here?

ZaoTX commented 2 years ago

test output

What I did is send a string "Test" to the peripheral. However, I only got the [84,101,115,116] as output. Is that possible to translate that back to "Test"?

ukBaz commented 2 years ago

Those raw byte values seem like they should do what you want.

This is my experiment:

>>> value = [84, 101, 115, 116]
>>> bytes(value)
b'Test'
>>> bytes(value).decode('utf-8')
'Test'

When you say:

The line print('Text value:', bytes(value).decode('utf-8')) is not working

What does that mean? Are you getting an error message? Can you share that message?

ZaoTX commented 2 years ago

Hi ukBaz, Your experiment code works. But somehow there is no message printed from that line. The photo I took is the only message I got

ZaoTX commented 2 years ago

Hi ukBaz, I don't know why when I delete the code from my demo

print('raw bytes:', value)
print('With options:', options)

It works again. Seems it only fits only one line print

ZaoTX commented 2 years ago

heartrate

Hi ukBaz, Thank you for answering my question before. My last question is how you can label the characteristics in peripheral? Is that because of the uuid? If so, I don't know how to create the uuid so that I can name that whatever I want.

ukBaz commented 2 years ago

There isn't really the ability to label the characteristic. As you suspect, there are some known UUIDs used in Bluetooth that are published by the Bluetooth SIG. See the 16-bit UUIDs document on their website for the full list.

There is also the following article with guidance on how to choose a UUID: https://www.novelbits.io/uuid-for-custom-services-and-characteristics/

ZaoTX commented 2 years ago

Thank you very much! I think I found one suitable for my case! You feel free to close the issue. Have a nice day!