Skip to content

Instantly share code, notes, and snippets.

@JakubAndrysek
Last active July 30, 2023 10:35
Show Gist options
  • Save JakubAndrysek/090fb71fd97aaa12006522626f02b03c to your computer and use it in GitHub Desktop.
Save JakubAndrysek/090fb71fd97aaa12006522626f02b03c to your computer and use it in GitHub Desktop.
Gattol + HciTool

Install PyQt5 with BLE

sudo apt install python3-pyqt5
sudo apt install python3-pyqt5.qtbluetooth

Scan

sudo hcitool lescan

Connect interactive

https://www.jaredwolff.com/get-started-with-bluetooth-low-energy/

sudo gatttool -b E5:76:86:2B:B4:F8  -I

[E5:76:86:2B:B4:F8][LE]> connect
Attempting to connect to E5:76:86:2B:B4:F8
Connection successful

[E5:76:86:2B:B4:F8][LE]> primary
attr handle: 0x0001, end grp handle: 0x0005 uuid: b2bbc642-46da-11ed-b878-0242ac120002

[E5:76:86:2B:B4:F8][LE]> char-desc
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0003, uuid: c9af9c76-46de-11ed-b878-0242ac120002
handle: 0x0004, uuid: 00002902-0000-1000-8000-00805f9b34fb

[E5:76:86:2B:B4:F8][LE]> char-write-req 0x0003 hi
Characteristic value was written successfully

[E5:76:86:2B:B4:F8][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 49 20 61 6d 20 63 68 61 72 61 63 74 65 72 69 73 74 69 63 20 76 61

[E5:76:86:2B:B4:F8][LE]> disconnect

(gatttool:59038): GLib-WARNING **: 12:23:05.002: Invalid file descriptor.
[E5:76:86:2B:B4:F8][LE]> 

[E5:76:86:2B:B4:F8][LE]> help
help                                           Show this help
exit                                           Exit interactive mode
quit                                           Exit interactive mode
connect         [address [address type]]       Connect to a remote device
disconnect                                     Disconnect from a remote device
primary         [UUID]                         Primary Service Discovery
included        [start hnd [end hnd]]          Find Included Services
characteristics [start hnd [end hnd [UUID]]]   Characteristics Discovery
char-desc       [start hnd] [end hnd]          Characteristics Descriptor Discovery
char-read-hnd   <handle>                       Characteristics Value/Descriptor Read by handle
char-read-uuid  <UUID> [start hnd] [end hnd]   Characteristics Value/Descriptor Read by UUID
char-write-req  <handle> <new value>           Characteristic Value Write (Write Request)
char-write-cmd  <handle> <new value>           Characteristic Value Write (No response)
sec-level       [low | medium | high]          Set security level. Default: low
mtu             <value>                        Exchange MTU for GATT/ATT

Restart

sudo systemctl restart bluetooth
udo hciconfig hci0 reset

BT Status

sudo systemctl status bluetooth

PyQT5 QTBluetooth BLE

# not work during my test
import sys
from PyQt5.QtBluetooth import QBluetoothLocalDevice, QBluetoothDeviceDiscoveryAgent, QBluetoothDeviceInfo, QLowEnergyController, QBluetoothUuid, QLowEnergyService
from PyQt5.QtCore import QObject, QCoreApplication, pyqtSlot
class BLEApp(QObject):
def __init__(self, parent=None):
super(BLEApp, self).__init__(parent)
self.deviceAddress = "E5:76:86:2B:B4:F8"
self.deviceInfo = QBluetoothDeviceInfo()
self.deviceDiscoveryAgent = QBluetoothDeviceDiscoveryAgent(self)
self.deviceDiscoveryAgent.deviceDiscovered.connect(self.deviceDiscovered)
self.deviceDiscoveryAgent.start()
@pyqtSlot(QBluetoothDeviceInfo)
def deviceDiscovered(self, device):
if device.address().toString() == self.deviceAddress:
self.deviceInfo = device
self.deviceDiscoveryAgent.stop()
self.controller = QLowEnergyController.createCentral(self.deviceInfo)
self.controller.connected.connect(self.deviceConnected)
self.controller.discoveryFinished.connect(self.serviceScanDone)
#self.controller.error.connect(self.controllerError)
self.controller.connectToDevice()
@pyqtSlot()
def deviceConnected(self):
print("Device connected, now discovering services...")
self.controller.discoverServices()
@pyqtSlot()
def serviceScanDone(self):
print("Service scan done.")
services = self.controller.services()
for serviceUuid in services:
service = self.controller.createServiceObject(serviceUuid)
if service:
service.stateChanged.connect(self.serviceDetailsDiscovered)
service.discoverDetails()
@pyqtSlot(QLowEnergyService.ServiceState)
def serviceDetailsDiscovered(self, newState):
if newState != QLowEnergyService.ServiceDiscovered:
return
service = self.sender()
# Get all characteristics
for char in service.characteristics():
print(f'Characteristic: {char.uuid().toString()}')
# Get descriptors for this characteristic
for desc in char.descriptors():
print(f'Descriptor: {desc.uuid().toString()}')
# Proceed to read a characteristic as before
characteristic = service.characteristic(QBluetoothUuid(0x0003)) # replace with actual characteristic UUID
if characteristic.isValid():
print("Reading characteristic...")
service.readCharacteristic(characteristic)
service.characteristicRead.connect(lambda c, a: print(f"Read complete, value: {a}"))
@pyqtSlot(QLowEnergyController.Error)
def controllerError(self, error):
print(f"Controller Error: {error}")
def main():
app = QCoreApplication([])
ble_app = BLEApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
#!/usr/bin/python3
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2014, Oscar Acena <oscaracena@gmail.com>
# This software is under the terms of Apache License v2 or later.
from __future__ import print_function
import sys
from gattlib import GATTRequester
class Reader(object):
def __init__(self, address):
self.requester = GATTRequester(address, False)
self.connect()
self.request_data()
def connect(self):
print("Connecting...", end=' ')
sys.stdout.flush()
self.requester.connect(True)
print("OK!")
def request_data(self):
data = self.requester.read_by_handle(0x0003)[0]
print("bytes received:", end=' ')
# convert byte array to string
print(data.decode('utf-8'))
#for b in data:
#print(str(hex(b)).decode("hex"), end=' ')
#print(str(hex(b)).encode('ascii').decode('ascii'), end=' ')
#print(hex(ord(b)), end=' ')
print("")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: {} <addr>".format(sys.argv[0]))
sys.exit(1)
Reader(sys.argv[1])
print("Done.")
#!/usr/bin/python3
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2014, Oscar Acena <oscaracena@gmail.com>
# This software is under the terms of Apache License v2 or later.
from __future__ import print_function
import sys
from gattlib import GATTRequester
class Reader(object):
def __init__(self, address):
self.requester = GATTRequester(address, False)
self.connect()
self.send_data()
def connect(self):
print("Connecting...", end=' ')
sys.stdout.flush()
self.requester.connect(True)
print("OK!")
def send_data(self):
self.requester.write_by_handle(0x0003, str("hi there"))
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: {} <addr>".format(sys.argv[0]))
sys.exit(1)
Reader(sys.argv[1])
print("Done.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment