Issue
I'm trying to send data from my Arduino to my Raspberry Pi over BLE. However, when I run the following script, I sometimes get one of the two errors:
[org.bluez.Error.Failed] Software caused connection abort
or This service is already present in this BleakGATTServiceCollection!
When I exit the program, I get the following error as the last line: bleak.exc.BleakError: Characteristic 00001143-0000-1000-8000-00805f9b34fb not found!
or bleak.exc.BleakError: Not connected
I have tried rebooting the Raspberry Pi and Arduino as well as restarting the bluetooth service with sudo systemctl restart bluetooth
and sudo systemctl daemon-reload
without avail.
The weird thing is, if I run the script on a different Pi that I set up in a similar way, the script runs as expected.
What could be causing this problem?
Here's the script (some irrelevant bits removed):
# Adapted from: https://github.com/Ladvien/arduino_ble_sense
import os
import sys
import asyncio
import pyrebase
import platform
import bluetooth
from datetime import datetime
from typing import Callable, Any, List
from time import sleep
from aioconsole import ainput
from bleak import BleakClient, discover
class Connection:
client: BleakClient = None
def __init__(
self,
loop: asyncio.AbstractEventLoop,
read_characteristic: str,
write_characteristic: str,
data_dump_handler: Callable[[str, str], None]
):
self.loop = loop
self.read_characteristic = read_characteristic
self.write_characteristic = write_characteristic
self.data_dump_handler = data_dump_handler
self.connected = False
self.connected_device = None
def on_disconnect(self, client: BleakClient):
self.connected = False
# Put code here to handle what happens on disconnect.
print(f"Disconnected from {self.connected_device.name}!")
async def cleanup(self):
if self.client:
await self.client.stop_notify(read_characteristic)
await self.client.disconnect()
async def manager(self):
print("Starting connection manager.")
while True:
if self.client:
await self.connect()
else:
await self.select_device()
await asyncio.sleep(15.0)
async def connect(self):
if self.connected:
return
try:
await self.client.connect()
self.connected = self.client.is_connected
if self.connected:
print(F"Connected to {self.connected_device.name}")
self.client.set_disconnected_callback(self.on_disconnect)
await self.client.start_notify(
self.read_characteristic, self.notification_handler,
)
while True:
if not self.connected:
break
await asyncio.sleep(3.0)
else:
print(f"Failed to connect to {self.connected_device.name}")
except Exception as e:
print(e)
async def select_device(self):
print("Bluetooh LE hardware warming up...")
await asyncio.sleep(2.0) # Wait for BLE to initialize.
devices = await discover()
print("Please select device: ")
for i, device in enumerate(devices):
print(f"{i}: {device.name}")
response = -1
while True:
response = await ainput("Select device: ")
try:
response = int(response.strip())
except:
print("Please make valid selection.")
if response > -1 and response < len(devices):
break
else:
print("Please make valid selection.")
print(f"Connecting to {devices[response].name}")
self.connected_device = devices[response]
self.client = BleakClient(devices[response].address, loop=self.loop)
#############
# Loops
#############
async def user_console_manager(connection: Connection):
if connection.client and connection.connected:
input_str = await ainput("Enter command: ")
bytes_to_send = bytearray(map(ord, input_str))
await connection.client.write_gatt_char(write_characteristic, bytes_to_send)
else:
await asyncio.sleep(2.0)
async def main():
while True:
await asyncio.sleep(5)
#############
# App Main
#############
read_characteristic = "00001143-0000-1000-8000-00805f9b34fb"
write_characteristic = "00001142-0000-1000-8000-00805f9b34fb"
if __name__ == "__main__":
# Create the event loop.
loop = asyncio.get_event_loop()
db = Database()
connection = Connection(
loop, read_characteristic, write_characteristic, db.writeToDB
)
try:
asyncio.ensure_future(connection.manager())
asyncio.ensure_future(user_console_manager(connection))
asyncio.ensure_future(main())
loop.run_forever()
except KeyboardInterrupt:
print()
print("User stopped program.")
finally:
print("Disconnecting...")
loop.run_until_complete(connection.cleanup())
exit()
Edit:
After @ukBaz's suggestion, I distilled the script to the following:
import asyncio
from bleak import discover
from bleak import BleakClient
address = "ARDUINO_ADDRESS"
async def connect(address, loop):
async with BleakClient(address, loop=loop) as client:
services = await client.get_services()
for ser in services:
print(ser.uuid)
loop = asyncio.get_event_loop()
loop.run_until_complete(connect(address, loop))
However, I still ran into the error:
Traceback (most recent call last):
File "/home/pi/smart-home-pi/test2.py", line 15, in <module>
loop.run_until_complete(connect(address, loop))
File "/home/pi/.pyenv/versions/3.9.7/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/home/pi/smart-home-pi/test2.py", line 9, in connect
async with BleakClient(address, loop=loop) as client:
File "/home/pi/.pyenv/versions/3.9.7/lib/python3.9/site-packages/bleak/backends/client.py", line 61, in __aenter__
await self.connect()
File "/home/pi/.pyenv/versions/3.9.7/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 298, in connect
assert_reply(reply)
File "/home/pi/.pyenv/versions/3.9.7/lib/python3.9/site-packages/bleak/backends/bluezdbus/utils.py", line 23, in assert_reply
raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.Failed] Software caused connection abort
Could it be the Python version I'm using?
Solution
I have run your second script and it worked for me although I'm not using a RPi or an Arduino. I'm also using Python 3.8.10 on Linux.
To get Bluetooth debug information on Linux:
- Are you able to connect to the device using bluetoothctl?
- Does
service bluetooth status
show errors?
When running your script have separate terminals open with the following running to get more debug information:
- bluetootctl
- journalctl -f -u bluetooth
- sudo busctl monitor org.bluez
- sudo btmon
I also took a look at your original script to simplify it. I came up with the following:
import asyncio
from aioconsole import ainput
from bleak import BleakClient, discover
async def data_client(device):
def handle_rx(_: int, data: bytearray):
print("received:", data)
async with BleakClient(device) as client:
await client.start_notify(read_characteristic, handle_rx)
while client.is_connected:
await asyncio.sleep(1)
input_str = await ainput("Enter command: ")
bytes_to_send = input_str.encode()
if input_str == 'exit':
await client.stop_notify(read_characteristic)
await client.disconnect()
else:
await client.write_gatt_char(write_characteristic, bytes_to_send)
async def select_device():
print("Scanning for Bluetooh LE hardware...")
await asyncio.sleep(2.0) # Wait for BLE to initialize.
devices = await discover()
print("Please select device: ")
for i, device in enumerate(devices):
print(f"{i}: {device.name}")
print("99: Exit program")
print("-1: Re-scan for BLE devices")
response = await ainput("Select device: ")
try:
response = int(response.strip())
except ValueError:
print("Please make valid selection.")
response = -1
print('response', type(response), response)
if -1 < response < len(devices):
return devices[response].address
elif response == 99:
return 99
print("Please make valid selection.")
async def main():
device = None
keep_alive = True
while keep_alive:
print('Device:', device)
if device is None:
device = await select_device()
if device == 99:
keep_alive = False
elif device:
await data_client(device)
device = None
print('Device disconnected.\n')
# read_characteristic = "00001143-0000-1000-8000-00805f9b34fb"
read_characteristic = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
# write_characteristic = "00001142-0000-1000-8000-00805f9b34fb"
write_characteristic = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
if __name__ == "__main__":
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
Answered By - ukBaz Answer Checked By - Clifford M. (WPSolving Volunteer)