Reading data from a Bluetooth device low energy rfcomm python3 - python-3.x

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).

Related

Interrupting a bluetooth connection in a programmable way

I am wanting to interrupt this bluetooth connect in a programmable way in python if I can. I have read many articles online and cannot seem to find a way to send an interrupt, other than a keyboard interrupt, to the client_sock, clientInfo = server_sock.accept() so that this line of code stops its bluetooth connectivity. The end game is to use my GUI side of this program by implementing a "Stop Connection" button in my GUI to halt the bluetooth connection. Is there a way to do that in python3 at all, or is this something that can only be handled via command line???
size = 1024
while True:
self.bluetooth_information.append("Waiting for connection")
self.bluetooth_information.append(str(datetime.now().time()))
client_sock, clientInfo = server_sock.accept()
try:
data = client_sock.recv(size) # receives data from client
if len(data) == 0:
break
client_sock.send(self.parse.process_data(data)) # Echo response back to client
# except bluetooth.btcommon.BluetoothError:
# pass
if self.stop:
client_sock.close()
server_sock.close()
self.stop = False
self.bluetooth_information.clear()
break
except KeyboardInterrupt:
client_sock.close()
server_sock.close()
break

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.

Camera Control via RS-422 Serial Port

I am currently doing research and writing a simple program in controlling a camera through the serial port, RS-422 to be more specific. I got the camera control protocol documentation from the manufacturer. I've been reading and testing for a few days now but unfortunately, I haven't managed to make it work. I used Python (3.7) to speed up the testing. Below is my sample code:
import serial
ser_port = serial.Serial(
port='COM4',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
sccp_cmd_null = [0x00]
sccp_cmd = [0xE7, 0x40, 0x11, 0x43, 0x00, 0x00] # Chroma Key "GREEN"
if ser_port.isOpen():
# Try sending a break code first
try:
print("Sending Break Code: ", serial.to_bytes(sccp_cmd_null))
ser_port.write(serial.to_bytes(sccp_cmd_null))
except:
print("Failed Sending Break Code!")
# Try Sending The Commands
try:
print("Sending Commands: ", serial.to_bytes(sccp_cmd))
ser_port.write(serial.to_bytes(sccp_cmd))
except:
print("Failed Sending Commands!")
ser_port.close()
Please note that this is my first time writing code that deals with Serial Ports.
Thank you in advanced for those who can provide help ^_^

Cannot read/write to characteristics in BLE

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()

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