Cannot read/write to characteristics in BLE - python-3.x

I have a BLED112 dongle and a BLE device which contains a GATT profile with Services-Characteristics.
I have installed pygatt to communicate with my BLE device.
Following is my python code to connect to my BLE device and read/write to a characteristic:-
import pygatt
adapter = pygatt.BGAPIBackend()
#adapter = pygatt.BGAPIBackend()
adapter.start()
adapter.scan(timeout=1)
device = adapter.connect('30:F5:6A:01:2D:05',address_type=pygatt.BLEAddressType.public)
characteristic = "f1126ec4-6e99-5552-5286-55bc21d65776"
device.char_write(characteristic, bytearray([0x00]), wait_for_response=True)
value = device.char_read(characteristic)
print(value)
adapter.stop()
I am able to connect to my device.
However, i get the following error while reading or writing:
C:\Python\Python37-32\lib\site-packages\pygatt\backends\bgapi\bgapi.py",
line 570, in expect_any raise exc
pygatt.backends.bgapi.exceptions.ExpectedResponseTimeout: Timed out
after 10.000000s waiting for
[]
I have included wait_for_response=True in my write instruction, but i still get this issue.
How do i read/write to the characteristic without any error?
PS: While scanning, i observed that the UUID above belongs to a "descriptor".
Is this the reason why i am getting the error?
Is there a way to write to "descriptor" (and not characteristic) in pygatt?

I finally got it by changing the characteristic UUID to the characteristic handle.
Following is the working code:-
import pygatt
import time
adapter = pygatt.BGAPIBackend()
#adapter = pygatt.BGAPIBackend()
adapter.start()
adapter.scan(timeout=1)
device = adapter.connect('30:F5:6A:01:2D:05',address_type=pygatt.BLEAddressType.public)
characteristic = "26"
device.char_write_handle(characteristic, bytearray([0x00, 0x09]), wait_for_response=True)
time.sleep(2)
value = device.char_read_handle(characteristic)
print(value)
adapter.stop()

Related

I'm trying to read sensor data through serial communication but when I try to read it just kept on runnin in python but it doesn't display any value

I have light detector sensors connected to a data acquisition box and it is connected to my laptop via RS232 usb cable. I have stabilized serial communication to that port in python. But when I try to read the data it just keeps on running and it doesn't display any value. I have tried this same think in MATALB and it works properly, so I know that port and sensors are working fine. I am just not able to read the data in python. I have three ways(shown below in python code) but nothing works. Please help me.
Here is my python code:
import serial
from serial import Serial
s = serial.Serial('COM3') # open serial port
print(ser.name)
# Set the serial port to desired COM
s = serial.Serial(port = 'COM3', bytesize = serial.EIGHTBITS, parity = serial.PARITY_NONE, baudrate = 9600, stopbits = serial.STOPBITS_ONE)
# Read the data(method 1)
while 1:
while (s.inWaiting() > 0):
try:
inc = s.readline().strip()
print(inc)
# Read the data(method 2)
data = str(s.read(size = 1))
print(data)
# Read all the data(method 3)
while i <10:
b = s.readlines(100) # reading all the lines
in matlab it gave values with a space but in same line
That indicates there are no newline characters sent by the device.
I've never used your device or the Python serial module. But from the docs, this is what I would do:
from time import sleep
while s.is_open():
n = s.in_waiting()
if n == 0:
print "no data"
sleep(1)
continue
try:
inc = s.read(n)
print(inc)
catch serial.SerialException as oops:
print(oops)
catch serial.SerialTimeoutException as oops:
print(oops)
print("serial port closed")
This gives you feedback for each condition: port open/closed, data ready/not, and the data, if any. From there you can figure out what to do.
The documentation says inWaiting is deprecated, so I used in_waiting.
I wouldn't be surprised if the problem lies in configuring the serial port in the first place. Serial ports are tricky like that.

Can't Send Command to BLE Device Philips Hue Bulb (Connection Drops)

I am trying to turn my bluetooth Hue bulb on/off and change brightness using my Raspberry Pi 4B. The bulb is on, and I have successfully connected to it using bluez. When I try to run 'char-write-req 0x0027 01' to turn it on, I get this message:
GLib-WARNING **: 22:53:34.807: Invalid file descriptor
I can see that the connection is successful, but whenever I try to write a value to it, I just get this message and it disconnects. Running bluetoothctl 5.50. I have seen the patch conversation here: https://www.spinics.net/lists/linux-bluetooth/msg67617.html. But I am not sure it applies and I also wouldn't even know how to apply it. Can someone please help me!
EDIT I ditched the gatttool and am using bluetoothctl to connect to the bulb and menu gatt to send commands to it.
I figured out that the characteristic for toggling the light on and off is 932c32bd-0002-47a2-835a-a8d455b859dd (For my Philips Hue A19). After connecting to the bulb, I was able to select this attribute and use 'write 01' to turn it on and 'write 00' to turn it off.
The brightness characteristic is 932c32bd-0002-47a2-835a-a8d455b859dd. When I read, it outputs 'fe', which is HEX for 254. This is the highest brightness setting, which it was already set to. I can use 'write ' where value ranges from 1-254 to change the brightness.
Using acquire-write in bluetoothctl is typically not the correct command. read and write are what you want.
After starting starting bluetoothctl I would expect the series of commands to be:
connect <Address of bulb>
menu gatt
select-attribute 932c32bd-0002-47a2-835a-a8d455b859dd
write 1
write 0
If you wanted to script this, then below is a Python3 script that I would expect to turn the bulb on then off.
from time import sleep
from pydbus import SystemBus
BLUEZ_SERVICE = 'org.bluez'
BLUEZ_DEV_IFACE = 'org.bluez.Device1'
BLUEZ_CHR_IFACE = 'org.bluez.GattCharacteristic1'
class Central:
def __init__(self, address):
self.bus = SystemBus()
self.mngr = self.bus.get(BLUEZ_SERVICE, '/')
self.dev_path = self._from_device_address(address)
self.device = self.bus.get(BLUEZ_SERVICE, self.dev_path)
self.chars = {}
def _from_device_address(self, addr):
"""Look up D-Bus object path from device address"""
mng_objs = self.mngr.GetManagedObjects()
for path in mng_objs:
dev_addr = mng_objs[path].get(BLUEZ_DEV_IFACE, {}).get('Address', '')
if addr.casefold() == dev_addr.casefold():
return path
def _get_device_chars(self):
mng_objs = self.mngr.GetManagedObjects()
for path in mng_objs:
chr_uuid = mng_objs[path].get(BLUEZ_CHR_IFACE, {}).get('UUID')
if path.startswith(self.dev_path) and chr_uuid:
self.chars[chr_uuid] = self.bus.get(BLUEZ_SERVICE, path)
def connect(self):
"""
Connect to device.
Wait for GATT services to be resolved before returning
"""
self.device.Connect()
while not self.device.ServicesResolved:
sleep(0.5)
self._get_device_chars()
def disconnect(self):
"""Disconnect from device"""
self.device.Disconnect()
def char_write(self, uuid, value):
"""Write value to given GATT characteristic UUID"""
if uuid.casefold() in self.chars:
self.chars[uuid.casefold()].WriteValue(value, {})
else:
raise KeyError(f'UUID {uuid} not found')
def char_read(self, uuid):
"""Read value of given GATT characteristic UUID"""
if uuid.casefold() in self.chars:
return self.chars[uuid.casefold()].ReadValue({})
else:
raise KeyError(f'UUID {uuid} not found')
device_address = '11:22:33:44:55:66'
light_state = '932c32bd-0002-47a2-835a-a8d455b859dd'
dev = Central(device_address )
dev.connect()
dev.char_write(light_state , [1])
sleep(5)
dev.char_write(light_state , [0])
print(dev.char_read(light_state ))
dev.disconnect()
As I don't have a bulb the above is untested. But should be a good outline of what is required.
It worked for me after I reset the Bulb with the bluetooth app to factory settings.
The bulb appears to be able to be paired/bonded to a single device only. If other devices try to communicate with the bulb, the connection is lost, as #ukBaz mentioned in his comment.

Reading data from a Bluetooth device low energy rfcomm python3

Faced a problem. There is a bluetooth device with low power consumption. BLE. The goal is to send a command to the device and get the data back.
For example: command - 0x** 0x** 0x**, where first 0x** - code command, second 0x - data lenght. Response mast be - 0x** 0x** 0x**. I can not send a command to the device. The device works by RFCOMM. Actually the code that is available, but it does not give a result - it says that the device is off.
from bluetooth import *
import socket
class Work:
def __init__(self):
self.rfcon = BluetoothSocket(RFCOMM)
# self.socket = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM)
self.server = '**:**:**:**:**:**'
self.port = 1
self.data = '0x01 0x00 0x00'
def scan_device(self):
connection = self.rfcon.connect((self.server, self.port))
print('con ===>>> ', connection)
senddata = self.rfcon.send(self.data)
print('senddata ====>>>> ', senddata)
data = self.rfcon.recv(256)
if not data:
print("no data!!!")
self.rfcon.close()
else:
print(data)
self.rfcon.close()
if __name__ == '__main__':
a = Work()
a.scan_device()
a.rfcon.close()
I made it through another library - the code:
from bluepy.btle import *
class Work:
def __init__(self):
self.initialize = Peripheral()
def scan_devices(self):
scanner = Scanner()
devices = scanner.scan(10.0)
for dev in devices:
print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
for (adtype, desc, value) in dev.getScanData():
print(" %s = %s" % (desc, value))
def connect_to_device(self):
print('start con')
connection = self.initialize.connect('**:**:**:**:**:**', 'public')
print('initialize complite')
return connection
def check_device_status(self):
print('test ====>>> ', self.initialize.getCharacteristics())
cmd = '0x01 0x00 0x00 0x20 0x00'.encode('UTF-8')
print(cmd)
status_device = self.initialize.readCharacteristic(cmd)
print('Device status => ', status_device)
def diconnect(self):
self.initialize.disconnect()
if __name__ == '__main__':
a = Work()
a.connect_to_device()
a.check_device_status()
a.diconnect()
It gives a connection but does not send a command and does not return a value since this library does not know what RFCOMM is. Perhaps someone faced this problem in the python and knows how to solve it?
RFCOMM is a protocol of Bluetooth Classic, BLE does not support it. It is impossible to use RFCOMM for communicating with a BLE device.
You should read an introduction to BLE, it will give you a basic understanding of BLE.
Anything further will be guessing, it depends on how the BLE device is configured.
If you are using your own device that you can configure, one possibility is to create a characteristic that supports Write and Indicate. You can indicate (to be notified when the characteristic value changes and what is the new value) and write the command. The response will be received via an indication.
For most practical purposes the answer that RFCOMM is only available in Bluetooth "Classic" is true: most Bluetooth LE devices don't support RFCOMM. However, in principle the Bluetooth Core Spec describes how a channel for RFCOMM can be opened between two LE devices using LE credit-based control flow with an L2CAP_LE_CREDIT_BASED_CONNECTION_REQ command (introduced in Core Spec v4.1 Vol 3 Part A Chapter 4.22 and the Assigned Numbers document).

Is it possible to refer to a serial device by vendor and device ID in pySerial?

The reason I ask is that right now I'm referring to the port using the 'by-id' string. 'by-id' refers to the serial device's serial number.
This would be fine, but what if I want to replace that serial device with the exact same brand and model? It won't work because each serial device has a unique serial number.
Maybe there is a better way to do this? The serial device is a barcode scanner by the way...
So for those looking to accomplish the same thing as me, this was the best way I found to refer to a serial device by vendor id (VID) and product id (PID)
Also note, the following requires pyserial version 3 or greater.
from serial.tools import list_ports
VID = 1234
PID = 5678
device_list = list_ports.comports()
for device in device_list:
if (device.vid != None or device.pid != None):
if ('{:04X}'.format(device.vid) == VID and
'{:04X}'.format(device.pid) == PID):
port = device.device
break
port = None
I have found that the list_ports.grep function comes in handy, like
import serial.tools.list_ports as list_ports
device_signature = '0403:6014'
def find_serial_device():
"""Return the device path based on vender & product ID.
The device is something like (like COM4, /dev/ttyUSB0 or /dev/cu.usbserial-1430)
"""
candidates = list(list_ports.grep(device_signature))
if not candidates:
raise ValueError(f'No device with signature {device_signature} found')
if len(candidates) > 1:
raise ValueError(f'More than one device with signature {device_signature} found')
return candidates[0].device

Obtain bluetooth signal strength on Raspberry Pi of BT device without pairing

I like to create a kind of indoor-tracking-system for my already existing home automation system. I thought of using BLE. I already successfully set up hcitool on my Raspberry Pi and I can connect to my iPhone without any problems. But how can I obtain the signal strength between my Raspberry Pi and my iPhone without connecting them. I already tried to use sudo hcitool cc [BTADDRESS] to connect to my iPhone without authentication, but it looks like the iPhone don't allow those connection to stay open. I think that must be a way to get the signal strength without connecting both devices. I want to use it to determine the distance from my Raspberry Pi to my iPhone. May I am able to calculate the distance from the time I need to discover my iPhone?
There are two ways to go, and by now I have been able to get both work reliably only on Android devices.
Exploiting the Bluetooth friendly name of the smartphone and set the discoverability to infinite. I have done this writing a simple app. Works in background, also after that the app has been killed, since the discoverability setting is preserved. At the best of my knowledge, this is not possible in iOS.
Advertising a UUID in a BLE packet from the phone. This can be done by both Android and iOS devices. However, while in background, iPhones switch the advertising to a shrinked mode that makes the packet unidentifiable. The problem of identifying an advertising iOS devices in background is still open.
On the raspberry, I used PyBluez to scan and looking for the presence of smartphones running (1) or (2). I report a code example:
import bluetooth
import bluetooth._bluetooth as bluez
import struct, socket, sys, select
def hci_enable_le_scan(sock):
hci_toggle_le_scan(sock, 0x01)
#Discover name and RSS of enabled BLE devices
class MyDiscoverer(bluetooth.DeviceDiscoverer):
def pre_inquiry(self):
self.done = False
def device_discovered(self, address, device_class, rssi, name):
discovery_logger.info("Discovered %s" % (address, ))
if name == "YOUR-DEVICE-FRIENDLY_NAME":
#Use the RSS for your detection / localization system
def inquiry_complete(self):
self.done = True
#Performs inquiry for name request
def async_inquiry():
d = MyDiscoverer()
while True:
d.find_devices(lookup_names = True)
readfiles = [ d, ]
while True:
rfds = select.select( readfiles, [], [] )[0]
if d in rfds:
d.process_event()
if d.done:
break
time.sleep(DISCOVERY_INTERVAL)
#Parse received advertising packets
def parse_events(sock):
# save current filter
old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
flt = bluez.hci_filter_new()
bluez.hci_filter_all_events(flt)
bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )
while True:
pkt = sock.recv(255)
ptype, event, plen = struct.unpack("BBB", pkt[:3])
if event == LE_META_EVENT:
subevent, = struct.unpack("B", pkt[3])
pkt = pkt[4:]
if subevent == EVT_LE_CONN_COMPLETE:
le_handle_connection_complete(pkt)
elif subevent == EVT_LE_ADVERTISING_REPORT:
#Check if the advertisement is the one we are searching for
if getASCII(pkt[start:end]) == "YOUR-UUID"
report_pkt_offset = 0
report_data_length, = struct.unpack("B", pkt[report_pkt_offset + 9])
# each report is 2 (event type, bdaddr type) + 6 (the address)
# + 1 (data length field) + report_data length + 1 (rssi)
report_pkt_offset = report_pkt_offset + 10 + report_data_length + 1
rssi, = struct.unpack("b", pkt[report_pkt_offset -1])
#Now you have the RSS indicator, use it for monitoring / localization
sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
dev_id = 0
try:
sock = bluez.hci_open_dev(dev_id)
except:
print "error accessing bluetooth device..."
sys.exit(1)
p = threading.Thread(group=None, target=parse_events, name='parsing', args=(sock, ))
d = threading.Thread(group=None, target=async_inquiry, name='async_inquiry', args=())
try:
p.start()
except:
print "Error: unable to start parsing thread"
try:
d.start()
except:
print "Error: unable to start asynchronous discovery thread"

Resources