Closed arist0v closed 1 year ago
additional informatoin:
after some test, look like my trigger.wait never received a set() i'll keep looking so as far as i understand, the wait never stop so the reste of the code never keep going until i ctrl_c
look like wait block the server to manage the write_request
new information:
i also tested with both of the example code, and have the same issue!
Sorry for the delayed response.
Some systems use threading and coroutines differently. If the code is ran entirely on asynchronous coroutines, my understanding is that blocking the thread will block other coroutines that need to process. To test this: have you tried changing the trigger to be an asyncio.Event
rather than a threading.Event
? This way, when you await
the asyncio.Event
the background function for bless can continue working.
Hello, no worries for the delay, we're all busy ;-)
i'll just try and got the following error:
/home/pi/hotspot-python/pkg/ble/ble_server.py:75: RuntimeWarning: coroutine 'Event.wait' was never awaited
self._trigger.wait()#This one seem to block
Edit: My bad , i'll just add await in front and it's work, but:
Edit 2:
My service are still not advertise when i goes back to threading, so i must look further(i just did some quick fix so maybe i did something wrong) i'll came back to you for this.
if you can help with the device name advertise it would be great)
Edit 3:
look like i wasn'T able to connect to the device at all, so client list remain empty
Edit 4:
i'll try to make my code look more like https://github.com/kevincar/bless/blob/master/examples/server.py but still no service discoverded when i try to connect
I've edited your code and it runs fine (see below). The await Event.wait()
command was blocking the code from getting to the rest of the server setup. The changes below address it. You should see write requests coming through now.
I still need to check why the service name isn't broadcasting. This can be tricky though depending on your system.
import asyncio
import logging
from typing import Any, Dict
from bless import ( # type: ignore
BlessServer,
BlessGATTCharacteristic,
GATTCharacteristicProperties,
GATTAttributePermissions
)
GENERIC_ACCESS_SERVICE = "00001800-0000-1000-8000-00805F9B34FB"
DEVICE_NAME = "00002A00-0000-1000-8000-00805F9B34FB"
APPEARANCE = "00002A01-0000-1000-8000-00805F9B34FB"
PERIPH_PREF_CONN_PARAM = "00002A04-0000-1000-8000-00805F9B34FB"
RX_UART = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
TX_UART = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
SERVICE_UART ="6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
GET_ALL_DATAS = 'download mem'
GET_LAST_GPS = 'get position'
ERASE_MEME = 'erase mem'
UART_SAFE_SIZE = 20
DATA_PATTERN = ('ID', 'crc', 'hour', 'minute', 'seconds', 'day', 'month', 'year', 'lat', 'long', 'HDOP', 'time_to_fix', 'nb_sat')
logger = logging.getLogger(__name__)
class BleServer:
def __init__(self, serviceName) -> None:
self._serviceName = serviceName
self._trigger: asyncio.Event = asyncio.Event()
self._gatt: Dict = {
f"{SERVICE_UART}":{
f"{RX_UART}":{
"Properties": (GATTCharacteristicProperties.write |
GATTCharacteristicProperties.write_without_response),
"Permissions": (GATTAttributePermissions.readable | GATTAttributePermissions.writeable)
},
f"{TX_UART}": {
"Properties": (GATTCharacteristicProperties.notify),
"Permissions": (GATTAttributePermissions.readable),
"Value": None
}
}
}
def startServer(self):
loop = asyncio.get_event_loop()
logger.debug("Starting Server loop")
try:
loop.run_until_complete(self._setServerUp(loop))
except KeyboardInterrupt:
loop.run_until_complete(self.stopServer())
async def _setServerUp(self, loop):
logger.debug("Setting up server")
self._trigger.clear()
self._server = BlessServer(name=self._serviceName, loop=loop)
#TODO fix service name not showing
self._server.read_request_func = self.read_request
self._server.write_request_func = self.write_request
#logger.debug(self._gatt)
await self._server.add_gatt(self._gatt)
logger.debug("Gatt added to the server")
await self._server.start()
logger.debug("server started")
#logger.debug(self._server.get_characteristic(RX_UART))
logger.debug("advertising")
# self._server.get_characteristic(DEVICE_NAME).value = (
# bytearray(self._serviceName.encode())
# )
# self._server.update_value(GENERIC_ACCESS_SERVICE, DEVICE_NAME)
await self._trigger.wait()
logger.debug("after wait trigger")
def read_request(self,
characteristic: BlessGATTCharacteristic,
**kwargs
) -> bytearray:
logger.debug(f"Reading {characteristic.value}")
return characteristic.value
def write_request(self,
characteristic: BlessGATTCharacteristic,
value: Any,
**kwargs
):
logger.debug("Write Request")
logger.debug(value)
characteristic.value = value
logger.debug(f"Char value set to {characteristic.value}")
if characteristic.value :
logger.debug("Nice write request")
self._trigger.set()
async def stopServer(self):
logger.debug("stopping server")
await self._server.stop()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug("Starting Server in self running mode")
server = BleServer("GATT_SERVICE")
server.startServer()
i tested it, but unfortunately, when trying to connect with nrfConnect, i got disconnected after launching discover service, so i was not able to even try to send a write request, there is still something wrong, i'll copy paste you the last version of my code :
import logging
import asyncio
from typing import Any, Dict
from bless import ( # type: ignore
BlessServer,
BlessGATTCharacteristic,
GATTCharacteristicProperties,
GATTAttributePermissions
)
GENERIC_ACCESS_SERVICE = "00001800-0000-1000-8000-00805F9B34FB"
DEVICE_NAME = "00002A00-0000-1000-8000-00805F9B34FB"
APPEARANCE = "00002A01-0000-1000-8000-00805F9B34FB"
PERIPH_PREF_CONN_PARAM = "00002A04-0000-1000-8000-00805F9B34FB"
RX_UART = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
TX_UART = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
SERVICE_UART ="6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
GET_ALL_DATAS = 'download mem'
GET_LAST_GPS = 'get position'
ERASE_MEME = 'erase mem'
UART_SAFE_SIZE = 20
DATA_PATTERN = ('ID', 'crc', 'hour', 'minute', 'seconds', 'day', 'month', 'year', 'lat', 'long', 'HDOP', 'time_to_fix', 'nb_sat')
logger = logging.getLogger(__name__)
class BleServer:
def __init__(self, serviceName) -> None:
self._serviceName = serviceName
self._trigger: asyncio.Event = asyncio.Event()
self._gatt: Dict = {
f"{SERVICE_UART}":{
f"{RX_UART}":{
"Properties": (GATTCharacteristicProperties.write |
GATTCharacteristicProperties.write_without_response)
},
f"{TX_UART}": {
"Properties": (GATTCharacteristicProperties.notify),
"Permissions": (GATTAttributePermissions.readable),
"Value": None
}
}
}
def startServer(self):
loop = asyncio.get_event_loop()
logger.debug("Starting Server loop")
try:
loop.run_until_complete(self._setServerUp(loop))
except KeyboardInterrupt:
loop.run_until_complete(self.stopServer())
async def _setServerUp(self, loop):
logger.debug("Setting up server")
self._trigger.clear()
self._server = BlessServer(name=self._serviceName, loop=loop)
#TODO fix service name not showing
self._server.read_request_func = self.read_request
self._server.write_request_func = self.write_request
await self._server.add_gatt(self._gatt)
logger.debug("Gatt added to the server")
await self._server.start()
logger.debug("server started")
logger.debug("advertising")
await self._trigger.wait()
logger.debug("after wait trigger")
def read_request(self,
characteristic: BlessGATTCharacteristic,
**kwargs
) -> bytearray:
logger.debug(f"Reading {characteristic.value}")
return characteristic.value
def write_request(self,
characteristic: BlessGATTCharacteristic,
value: Any,
**kwargs
):
logger.debug("Write Request")
logger.debug(value)
characteristic.value = value
logger.debug(f"Char value set to {characteristic.value}")
if characteristic.value == bytearray(b'allo'):
self.printSomething()
elif characteristic.value :
logger.debug("Nice write request")
self._trigger.set()
def printSomething(self):
logger.debug("TEST")
print("TEST")
async def stopServer(self):
logger.debug("stopping server")
await self._server.stop()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug("Starting Server in self running mode")
server = BleServer("GATT_SERVICE")
server.startServer()
When you try to connect and discover services, does the python code continue to run or does it crash?
the python code keep running without issue.
(the version i past earlier)
Does the examples/server.py
example work for you?
no, i got the same issue
but with a Nordic dev board (so not using python and bless) i receive a gatt service client list on nrfConnect
Does your raspberry pi have a built in bluetooth module or are you using the nordic board as the adapter?
O'm using the onboard device, the nordic board are for a linked but dofferent project
Le jeu. 10 nov. 2022, 16 h 00, Kevin Davis @.***> a écrit :
Does your raspberry pi have a built in bluetooth module or are you using the nordic board as the adapter?
— Reply to this email directly, view it on GitHub https://github.com/kevincar/bless/issues/87#issuecomment-1310886788, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSPAACNLGMCSAZVNARZWRLWHVO6BANCNFSM53VQDGVA . You are receiving this because you authored the thread.Message ID: @.***>
Any chance I could get a screen ship of the nRF connect error? Do you also have this issue if you use PunchThrough's LightBlue app?
I haven't tested on Raspian, I'll see if I can flash a copy later and test.
I'll gave you more details later today
Le jeu. 10 nov. 2022, 16 h 21, Kevin Davis @.***> a écrit :
Any chance I could get a screen ship of the nRF connect error? Do you also have this issue if you use PunchThrough's LightBlue app https://punchthrough.com/lightblue/?
I haven't tested on Raspian, I'll see if I can flash a copy later and test.
— Reply to this email directly, view it on GitHub https://github.com/kevincar/bless/issues/87#issuecomment-1310906265, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSPAAGPSEJ7W53HIQDVARLWHVRNNANCNFSM53VQDGVA . You are receiving this because you authored the thread.Message ID: @.***>
So i will explain you the whole situation first:
we have the nordic board(dev board) that is use to make a standalone BLE device that my work will use.
in a first stage, i have to code a Android Application that will have to fetch the Device (named puck) data and store it locally until it have network and can be forwarded to our server.(the device will be in a zone with low to no cellular coverage)
in a second stage, i will have to code a Raspberry pi device that will automatically fetch data from the Puck when they are in Ble range (and later probably add LoRa support for better coverage of a site)
So since we will still have the network coverage issue (not cellular data so the Raspberry pi can'T automatically send data) we will have to manually go get the data using an android App, or another raspberry pi that will be mounted on car or stuff.
So my idea to work smarter not hard was to reproduce the puck BLE Behavior (UUID, Command, etc.....) of the Puck so the current android app and raspberry pi python app will be able to fetch the data the exact same way it currently does with the puck.
There is the screenshot
And the log from a current Working nordic dev board
nRF Connect, 2022-11-10
AxlStick (C1:50:68:11:28:B7)
D 18:29:06.422 gatt.close()
D 18:29:06.426 wait(200)
V 18:29:06.627 Connecting to C1:50:68:11:28:B7...
D 18:29:06.627 gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, preferred PHY = LE 1M)
D 18:29:06.766 [Server callback] Connection state changed with status: 0 and new state: CONNECTED (2)
I 18:29:06.766 [Server] Device with address C1:50:68:11:28:B7 connected
I 18:29:06.767 [Server] MTU changed to 247
D 18:29:06.778 [Callback] Connection state changed with status: 0 and new state: CONNECTED (2)
I 18:29:06.778 Connected to C1:50:68:11:28:B7
D 18:29:06.786 [Broadcast] Action received: android.bluetooth.device.action.ACL_CONNECTED
V 18:29:06.796 Discovering services...
D 18:29:06.796 gatt.discoverServices()
I 18:29:07.472 Connection parameters updated (interval: 7.5ms, latency: 0, timeout: 5000ms)
D 18:29:07.703 [Callback] Services discovered with status: 0
I 18:29:07.703 Services discovered
V 18:29:07.707 Generic Access (0x1800)
- Device Name [R W] (0x2A00)
- Appearance [R] (0x2A01)
- Peripheral Preferred Connection Parameters [R] (0x2A04)
- Central Address Resolution [R] (0x2AA6)
Generic Attribute (0x1801)
- Service Changed [I] (0x2A05)
Client Characteristic Configuration (0x2902)
Nordic UART Service (6e400001-b5a3-f393-e0a9-e50e24dcca9e)
- RX Characteristic [W WNR] (6e400002-b5a3-f393-e0a9-e50e24dcca9e)
- TX Characteristic [N] (6e400003-b5a3-f393-e0a9-e50e24dcca9e)
Client Characteristic Configuration (0x2902)
Secure DFU Service (0xFE59)
- Buttonless DFU [I W] (8ec90003-f315-4f60-9fb8-838830daea50)
Client Characteristic Configuration (0x2902)
D 18:29:07.708 gatt.setCharacteristicNotification(00002a05-0000-1000-8000-00805f9b34fb, true)
D 18:29:07.711 gatt.setCharacteristicNotification(6e400003-b5a3-f393-e0a9-e50e24dcca9e, true)
I 18:29:07.763 Connection parameters updated (interval: 50.0ms, latency: 0, timeout: 5000ms)
Thanks for the clarification. I think I understand your situation. So that we can pin point whether the issue directly associated with bless and not another piece of your workflow, can you post the following:
example/server.py
The log have already been posted, it was when using my code, but they are the same.
I will try with your app
Le jeu. 10 nov. 2022, 19 h 41, Kevin Davis @.***> a écrit :
Thanks for the clarification. I think I understand your situation. So that we can pin point whether the issue directly associated with bless and not another piece of your workflow, can you post the following:
- nRF connect logs when connecting to the Raspberry Pi running the example/server.py
- Confirm that the same behavior occurs Im when using LightBlue app
— Reply to this email directly, view it on GitHub https://github.com/kevincar/bless/issues/87#issuecomment-1311085409, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSPAABHIT3N5CZOT3SLLP3WHWI3HANCNFSM53VQDGVA . You are receiving this because you authored the thread.Message ID: @.***>
I can confirm the same issue with lightblue
Le jeu. 10 nov. 2022, 20 h 09, Martin Verret @.***> a écrit :
The log have already been posted, it was when using my code, but they are the same.
I will try with your app
Le jeu. 10 nov. 2022, 19 h 41, Kevin Davis @.***> a écrit :
Thanks for the clarification. I think I understand your situation. So that we can pin point whether the issue directly associated with bless and not another piece of your workflow, can you post the following:
- nRF connect logs when connecting to the Raspberry Pi running the example/server.py
- Confirm that the same behavior occurs Im when using LightBlue app
— Reply to this email directly, view it on GitHub https://github.com/kevincar/bless/issues/87#issuecomment-1311085409, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSPAABHIT3N5CZOT3SLLP3WHWI3HANCNFSM53VQDGVA . You are receiving this because you authored the thread.Message ID: @.***>
Thanks for the update.
I'll see if I can whip up my Raspberry Pi to run Raspian. I've currently tested bless on Raspberry Pi running Ubuntu and it works properly, but haven't checked Raspian. I'll keep you posted
In the mean time, do you have any way to confirm this isn't an android issue? See here
I have no issue connecting to the nordic board, so android is not the issue since the same app try to connect to 2 different device (raspberry pi and nordic board) that (should) publish the same gatt service
I can confirm this problem on RPI 4 (python:3.8 based docker container managed by balena)
Usingasyncio.Event
solved the problem
I would at least suggest a comment in the example file regarding this possible issue.
Thanks for this confirmation @tobias-dv-lnu. I've pushed an update on a development branch to include your suggestion. See here
However, I'm not sure this solves @arist0v 's issue on Raspian though. To confirm, did the switch to using an asyncio.Event
work for you while using Ubuntu on RPI 4, or are you using Raspian?
Our project who use this is on hold for i don't know how many time, so i won't be able to confiirm anything soon, if you really need confirmation from me please tell me and i will setup stuff to try to help as much as i can
@arist0v sounds good. No need for now but I’ll let you know. I still need to confirm for myself first. Thanks!
@kevincar nice!
Our docker container is FROM: python:3.11 -> i guess this is debian based in the bottom of things.
FROM python:3.11
RUN apt-get update
RUN apt-get install -y build-essential bluetooth libdbus-1-dev libgirepository1.0-dev libudev-dev libical-dev
ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
WORKDIR /code
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
RUN pip install dbus-python pycairo PyGObject
RUN pip install python-networkmanager
COPY . .
ENTRYPOINT sh docker_entrypoint.sh
docker_entrypoint.sh:
#!/bin/bash
service dbus start
bluetoothd &
python ble_service.py
/bin/bash
To follow up. I tested the updated server.py example file on Raspbian v6.1.21 released on 2023-5-3 and it worked without issues. I test this using an iOS device as a central and first ensured that the devices were paired prior to testing.
I'm going to close this since it is working as expected. If anyone is still having issues with this please reopen and we'll continue the discussion.
The problem i created a gatt server based on the gattserverexample.py and i must kill the server(KeyboardInterrupt) to get the write_request code to log on screen
Reproduction
` from concurrent.futures import thread import logging import asyncio import threading
`
Expected behavior I excpected that the write_request section run in real time so i could them code the function triggered by the update of the RX_UART
Screenshots If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information): -Rspbian (rpi 4b 8gb) -Python 3.9.2
Additional context i'm trying to setup a service similar to another one for specific need, this is why i use the Nordic UART service
Other small isue My service name are never advertised, i only got N/A