Closed David-Afik closed 1 year ago
Asking the question here is fine. There are not enough questions on the repo to justify setting anything else up.
There is nothing obviously wrong from just looking at the extract you have posted.
A couple of candidates for where things might be going wrong are the CPU_TMP_CHRC and CPU_FMT_DSCP are set to values from the Bluetooth Standard that relate to sending temperature values. I would remove the descriptor and set the characteristic to a randomly generated UUID. Look at https://github.com/ukBaz/python-bluezero/blob/main/examples/ble_uart.py for an example.
The other thing is that characteristics can only send bytes. Try generating some random bytes and sending them rather than a string.
When running your script have separate terminals open with the following running to get more debug information:
bluetoothctl
journalctl -f -u bluetooth
sudo busctl monitor org.bluez
sudo btmon
If that doesn't help I'll try and take a look the weekend.
The below test has worked successfully for me. I used nRF Connect app as the client on my phone.
The main thing to change was the value of the characteristic to be sent in bytes. Without that it was not sending data.
I have also removed the GATT descriptor and changed the GATT characteristic to a custom UUID just to follow best practice. This is also why I changed the name of your function to snake_case.
"""Example of how to create a Peripheral device/GATT Server"""
# Standard modules
import logging
import random
# Bluezero modules
from bluezero import async_tools
from bluezero import adapter
from bluezero import peripheral
# constants
# Custom service uuid
STRING_SRVC = '12341000-1234-1234-1234-123456789abc'
STRING_CHRC = '12341001-1234-1234-1234-123456789abc'
def get_sample_from_device():
data = str(random.randrange(0, 5678))
return data
return data
def read_value():
sample = get_sample_from_device()
print('read value function -> data is : ', sample)
return sample
def update_value(characteristic):
print('before')
new_value = read_value()
print('after')
characteristic.set_value(new_value.encode())
print('new value is ', new_value)
return characteristic.is_notifying
def notify_callback(notifying, characteristic):
if notifying:
async_tools.add_timer_seconds(2, update_value, characteristic)
def main(adapter_address):
"""Creation of peripheral"""
logger = logging.getLogger('localGATT')
logger.setLevel(logging.DEBUG)
# Example of the output from read_value
print(f'String is {read_value()}')
# Create peripheral
str_monitor = peripheral.Peripheral(adapter_address,
local_name='String Monitor',
appearance=1344)
# Add service
str_monitor.add_service(srv_id=1, uuid=STRING_SRVC, primary=True)
# Add characteristic
str_monitor.add_characteristic(srv_id=1, chr_id=1, uuid=STRING_CHRC,
value=[], notifying=False,
flags=['read', 'notify'],
read_callback=read_value,
write_callback=None,
notify_callback=notify_callback
)
# Publish peripheral and start event loop
str_monitor.publish()
if __name__ == '__main__':
# Get the default adapter address and pass it to main
main(list(adapter.Adapter.available())[0].address)
The transcript from the session was:
$ python3 peripheral_str_characteristic.py
read value function -> data is : 4547
String is 4547
Failed to register advertisement: org.bluez.Error.Failed: Failed to register advertisement
before
read value function -> data is : 2460
after
new value is 2460
before
read value function -> data is : 1286
after
new value is 1286
before
read value function -> data is : 516
after
new value is 516
before
read value function -> data is : 3665
after
new value is 3665
read value function -> data is : 3499
Please let me know if this works for you and I'll go ahead and close this issue.
I've just realised that it is an integer value are sending as a string. This is very inefficient of Bluetooth data rate over the air. Sending it as an integer value with to_bytes
is far more efficient (although I accept you can't then use a general client).
great , thank you ! didn't understand that I need to send it as bytes and not as string
now it's working !
another question - it there a unique \custom UUID for sending GPS data ? and can I add my own data to the coordinates? or it's best to use my own UUID and send it as string ?
Thanks,
The Bluetooth SIG website is the place to look for this kind of information. https://www.bluetooth.com/specifications/assigned-numbers/
The "16-Bit UUIDs" and "GATT Specification Supplement" should have the majority of information you might need.
There is also the specifications at https://www.bluetooth.com/specifications/specs/
There is a GNSS (uses the older style "classic" Bluetooth Serial Port Profile (SPP)) and LNS.
If you want to structure your own data then you are probably best sticking with a custom UUID.
There is some more information on customer UUIDs at: https://novelbits.io/uuid-for-custom-services-and-characteristics/
OK I'm going to use my own data structure (adding more data to the GPS)
2 more questions that are not related for the same project:
Thanks ,
there is the ability to set more secure flags on characteristics as documented at https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt#n267
They use this in one of the BlueZ example GATT server characteristics: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/example-gatt-server#n606
What the difference is between "read", "encrypt-read", "encrypt-authenticated-read", and "secure-read" is not clear to me as I haven't found this documented anywhere. I think it matches up with the four security levels BLE has: Level 1: No Security (No authentication and no encryption) Level 2: Unauthenticated pairing with encryption Level 3: Authenticated pairing with encryption Level 4: Authenticated LE Secure Connections pairing with encryption
This is like a BLE beacon does? They do it by putting data in the advertisement. This is either in the "service data" or "manufacturing data". You can do this with BlueZ by setting one of those values in your advert. Couple of things to watch out for. BlueZ stops advertising once a connection is made. If you want to change the value in the advert you have to stop the advertisement, change the value, and then start the advert again. By the very nature of this being broadcast data, it does not encrypt your data. You would have to encrypt the values yourself.
OK you gave me some things to think about , I will see what is better solution for me and what I need
Thanks!
if there is another place to ask - please tell me
I'm trying to use the example of "cpu_temperature.py " in order to publish my own data (not Temp, but a string) I can see the data is "changing" from the read_value function on every read but nothing in the update_value
it doesn't even enter the update_value function
what am I missing ?
Thanks,
(if there is a forum I can ask and used please inform me)