From a639bde5beed2198fe5665b955969aa2d70c2c6b Mon Sep 17 00:00:00 2001 From: Mars-Ares <56955817+Mars-Ares@users.noreply.github.com> Date: Thu, 24 Oct 2019 10:33:26 +0200 Subject: [PATCH] Added BLE terminal Use sudo ./gattctl.py --connect address to open an terminal. 'ESC' + your text + ENTER to send a message --- examples/read_firmware_version.py | 1 + gatt/__init__.pyc | Bin 0 -> 288 bytes gatt/errors.pyc | Bin 0 -> 1762 bytes gatt/gatt.pyc | Bin 0 -> 409 bytes gatt/gatt_linux.py | 5 +- gatt/gatt_linux.pyc | Bin 0 -> 29337 bytes gattctl.py | 107 ++++++++++++++++++++++++++++-- 7 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 gatt/__init__.pyc create mode 100644 gatt/errors.pyc create mode 100644 gatt/gatt.pyc create mode 100644 gatt/gatt_linux.pyc diff --git a/examples/read_firmware_version.py b/examples/read_firmware_version.py index 3e5f031..8bb929f 100644 --- a/examples/read_firmware_version.py +++ b/examples/read_firmware_version.py @@ -29,4 +29,5 @@ def characteristic_value_updated(self, characteristic, value): device = AnyDevice(manager=manager, mac_address=args.mac_address) device.connect() + manager.run() diff --git a/gatt/__init__.pyc b/gatt/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67f78de1fb76b125d5caa69b5a8fc18683d1d15e GIT binary patch literal 288 zcmYjL!Ait15S_GD7h(Mj>17RjUqslw>Y?kY2qmOLnvgeX1{@Nwyh`NP|jEc5d>&njBOE z|0wP7)Z-LbAB^`LBkSk$$)8Ua&I1RIl5{p(y}rvh#Z@o~aX*soIv1UE-5q8N7;VDT LA3fja$OOL+dd@00g)WKm~9I za0~GBk~$4fn-y(<3O#LAv2Nmsr+V%8pMZ2K( zU^3h%;ckb_IF8ZUBh(41YJUNJ_(sKc$lT4QwnaGyTh$8%|Bn4xwynn~wI#nR1lJ<6nYXi5~JF}blH zi>2TRS(WAW+ktCS^<5_E=As1;7d(7R5;-VZRJ=s?%MX2K@|htIZ|d8v%M3|YZ%yVw zDf72X6Llfe1)iXuxp{5mvPTDNa=Cq)yYYg8#UwX1I)4sHzE=MH`I3-Qfl@7Nq>QyY%5j=;@GwV{`l`#=<4JF0+^5{2=)D k365#O;Uf4r1aDRFfbArvGFyLM<>xPD4&9}-4#V~L8AK&~00000 literal 0 HcmV?d00001 diff --git a/gatt/gatt.pyc b/gatt/gatt.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e9dec2da98826706af6f5034887da02d9839ce5 GIT binary patch literal 409 zcmYjM!AiqG5S`sLQ9^R*)gLHAjow9yc(5Q9+61KtAvSB1xVx#@iP&@h#9P0kpX3MB z*`U>7_RYLEFmK2C3jO`X=hFgy!l62%XB0tB$v^_k4?Tl91Hqu;upyDdD~Bcp0Z=(m z^&od3@4<#!w4*8HF616a7vi__mv|6s{~sZj+2DvA%RR*eU25-2-F{%-cH>F%m_Cf- z+xsL=Z?2OWGLp|rivtTCKkYUkCte#orm~V5zSNCEkBC_t6bh}2(C=kaYqZBM1)^_j zhM3CE^FR%@&t^bF_yaQ{8Wpt?QI$z4v=&jk+sfT`QDn_hD523rcH3~Z#-i3km637N eNgbS#Zhv>F>b#ZW${t_>f`A3Q&rVp#L+2Z*vs?WD literal 0 HcmV?d00001 diff --git a/gatt/gatt_linux.py b/gatt/gatt_linux.py index 5fdb9d7..44b31f5 100644 --- a/gatt/gatt_linux.py +++ b/gatt/gatt_linux.py @@ -63,6 +63,7 @@ def run(self): This call blocks until you call `stop()` to stop the main loop. """ + if self._main_loop: return @@ -86,7 +87,7 @@ def disconnect_signals(): self._properties_changed_signal.remove() self._interface_added_signal.remove() - self._main_loop = GObject.MainLoop() + self._main_loop = GObject.MainLoop.new(None, False) # modification to launch run in a thread try: self._main_loop.run() disconnect_signals() @@ -605,6 +606,7 @@ def write_value(self, value, offset=0): :param value: array of bytes to be written :param offset: offset from where to start writing the bytes (defaults to 0) """ + print("Sent : ", value) bytes = [dbus.Byte(b) for b in value] try: @@ -640,6 +642,7 @@ def enable_notifications(self, enabled=True): Each time when the device notifies a new value, `characteristic_value_updated()` of the related device will be called. """ + print("enable_notifications called") try: if enabled: self._object.StartNotify( diff --git a/gatt/gatt_linux.pyc b/gatt/gatt_linux.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56c1faf6adc6d084647026986e108f9ebb9638f1 GIT binary patch literal 29337 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 K{@@b_-~GRS&SFCV literal 0 HcmV?d00001 diff --git a/gattctl.py b/gattctl.py index 9f57947..2acf672 100755 --- a/gattctl.py +++ b/gattctl.py @@ -1,7 +1,12 @@ #!/usr/bin/env python3 from argparse import ArgumentParser +from threading import Thread +import keyboard +from termios import tcflush, TCIFLUSH import gatt +import time +import sys device_manager = None @@ -11,13 +16,28 @@ class AnyDeviceManager(gatt.DeviceManager): An implementation of ``gatt.DeviceManager`` that discovers any GATT device and prints all discovered devices. """ + def __init__(self, adapter_name): + super().__init__(adapter_name) + self.nb_device_connected = 0 + self.discover_filter = 'True' def device_discovered(self, device): - print("[%s] Discovered, alias = %s" % (device.mac_address, device.alias())) + if self.discover_filter == 'True': + print("[%s] Discovered, alias = %s" % (device.mac_address, device.alias())) + elif self.discover_filter in device.alias() or self.discover_filter in device.mac_address: + print("[%s] Discovered, alias = %s" % (device.mac_address, device.alias())) def make_device(self, mac_address): return AnyDevice(mac_address=mac_address, manager=self) + def run(self): + print("Running Manager") + super().run() + + def quit(self): + print("Stopping Manager") + super().stop() + class AnyDevice(gatt.Device): """ @@ -28,6 +48,9 @@ class AnyDevice(gatt.Device): def __init__(self, mac_address, manager, auto_reconnect=False): super().__init__(mac_address=mac_address, manager=manager) self.auto_reconnect = auto_reconnect + self.message = '' + self.transmition = "" + self.reception = "" def connect(self): print("Connecting...") @@ -35,6 +58,7 @@ def connect(self): def connect_succeeded(self): super().connect_succeeded() + device_manager.nb_device_connected += 1 print("[%s] Connected" % (self.mac_address)) def connect_failed(self, error): @@ -42,6 +66,8 @@ def connect_failed(self, error): print("[%s] Connection failed: %s" % (self.mac_address, str(error))) def disconnect_succeeded(self): + if device_manager.nb_device_connected: + device_manager.nb_device_connected -= 1 super().disconnect_succeeded() print("[%s] Disconnected" % (self.mac_address)) @@ -55,7 +81,48 @@ def services_resolved(self): for service in self.services: print("[%s] Service [%s]" % (self.mac_address, service.uuid)) for characteristic in service.characteristics: + if characteristic.uuid == '6e400003-b5a3-f393-e0a9-e50e24dcca9e': + characteristic.enable_notifications() + print("[%s] Characteristic [%s]" % (self.mac_address, characteristic.uuid)) + print("[%s] Resolved services" % (self.mac_address)) + + device_service = next( + s for s in self.services + if s.uuid == '6e400001-b5a3-f393-e0a9-e50e24dcca9e') + + self.reception = next( + c for c in device_service.characteristics + if c.uuid == '6e400003-b5a3-f393-e0a9-e50e24dcca9e') + + self.transmition = next( + c for c in device_service.characteristics + if c.uuid == '6e400002-b5a3-f393-e0a9-e50e24dcca9e') + + if self.message: + bar = bytearray(self.message.encode()) + self.transmition.write_value(bar) + + time.sleep(2) + self.reception.read_value() + + def characteristic_value_updated(self, characteristic, value): + print("RX:", (value.decode("utf-8")).replace('\n', '')) + + def characteristic_read_value_failed(self, characteristic, error): + print("RX fail : ", error) + + def characteristic_enable_notifications_succeeded(self, characteristic): + print("enable notification succeeded for characteristic ", characteristic.uuid) + + def characteristic_enable_notifications_failed(self, characteristic, error): + print("enable notification succeeded for characteristic ", characteristic.uuid, ", error, ", error) + + def characteristic_write_value_succeeded(self, characteristic): + print("write value succeeded for characteristic ", characteristic.uuid) + + def characteristic_write_value_failed(self, characteristic, error): + print("write value fail for characteristic ", characteristic.uuid, ", error, ", error) def main(): @@ -64,6 +131,11 @@ def main(): '--adapter', default='hci0', help="Name of Bluetooth adapter, defaults to 'hci0'") + arg_parser.add_argument( + '--message', + metavar='string', + type=str, + help="String send by ble, usable with --connect only. All 'n' of the message will be remplace by '\ n' ") arg_commands_group = arg_parser.add_mutually_exclusive_group(required=True) arg_commands_group.add_argument( '--power-on', @@ -81,6 +153,11 @@ def main(): '--discover', action='store_true', help="Lists all nearby GATT devices") + arg_commands_group.add_argument( + '--discover-filter', + metavar='filter', + type=str, + help="Lists all nearby GATT devices containing filter in their MAC address or alias") arg_commands_group.add_argument( '--connect', metavar='address', @@ -114,8 +191,14 @@ def main(): return if args.discover: device_manager.start_discovery() + if args.discover_filter: + print("discover filter : ", args.discover_filter) + device_manager.discover_filter = args.discover_filter + device_manager.start_discovery() elif args.connect: device = AnyDevice(mac_address=args.connect, manager=device_manager) + if args.message: + device.message = (args.message.replace('%n','\n')) device.connect() elif args.auto: device = AnyDevice(mac_address=args.auto, manager=device_manager, auto_reconnect=True) @@ -125,12 +208,28 @@ def main(): device.disconnect() return + thread = Thread(target=device_manager.run) + thread.start() + print("Terminate with Ctrl+C") + print("Started Thread.") + try: - device_manager.run() + while True: + if device_manager.nb_device_connected: + # If ESCAPE key is pressed you can start to write the message, ENTER to send it to the device + if keyboard.is_pressed(chr(27)): + tcflush(sys.stdin, TCIFLUSH) + message = bytearray(input(">> ").replace('%n','\n').encode()) + device.transmition.write_value(message) + + pass except KeyboardInterrupt: - pass - + print('KeyboardInterrupt!') + if device_manager.nb_device_connected: + device.disconnect() + device_manager.quit() + thread.join() if __name__ == '__main__': main()