diff --git a/boards/default_src/micropython/origin/build/lib/rtctime.py b/boards/default_src/micropython/origin/build/lib/rtctime.py new file mode 100644 index 00000000..4c4aa60a --- /dev/null +++ b/boards/default_src/micropython/origin/build/lib/rtctime.py @@ -0,0 +1,45 @@ +"""RTC Time""" +import gc +from time import * +from machine import RTC +import usocket as socket +import ustruct as struct + +# NTP_DELTA (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60 +NTP_DELTA = 3155673600 + +def ntptime(host="pool.ntp.org", utc=28800): + NTP_QUERY = bytearray(48) + NTP_QUERY[0] = 0x1B + addr = socket.getaddrinfo(host, 123)[0][-1] + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.settimeout(1) + res = s.sendto(NTP_QUERY, addr) + msg = s.recv(48) + finally: + del addr + s.close() + gc.collect() + val = struct.unpack("!I", msg[40:44])[0] + return gmtime(val - NTP_DELTA + utc) + +# There's currently no timezone support in MicroPython, and the RTC is set in UTC time. +def settime(times): + if isinstance(times, str): + try: + val = eval(times) + if len(val) >= 6: + times=(val[0], val[1], val[2], 0, val[3], val[4], val[5], 0) + else: + raise ValueError("Clock information format error") + except: + raise ValueError("Clock information format error, use ',' to separate at least 6 numerical values") + if isinstance(times, tuple): + if 6 <= len(times) <= 8: + RTC().datetime((times[0], times[1], times[2], 0, times[3], times[4], times[5], 0)) + else: + raise ValueError("Settime needs a tuple of length 6~8") + +def strtime(): + return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format(*localtime()) diff --git a/boards/default_src/micropython_esp32/origin/build/Generic_ESP32-0x1000-V1.19.1-lib.bin b/boards/default_src/micropython_esp32/origin/build/Generic_ESP32-0x1000-V1.19.1-lib.bin index 50af1698..95181b0e 100644 Binary files a/boards/default_src/micropython_esp32/origin/build/Generic_ESP32-0x1000-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32/origin/build/Generic_ESP32-0x1000-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32/origin/build/MixGo-0x1000-V1.19.1-lib.bin b/boards/default_src/micropython_esp32/origin/build/MixGo-0x1000-V1.19.1-lib.bin index 27b5294a..f11a27f1 100644 Binary files a/boards/default_src/micropython_esp32/origin/build/MixGo-0x1000-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32/origin/build/MixGo-0x1000-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32/origin/build/MixGo_PE-0x1000-V1.19.1-lib.bin b/boards/default_src/micropython_esp32/origin/build/MixGo_PE-0x1000-V1.19.1-lib.bin index 9f675e7d..d1ed88d6 100644 Binary files a/boards/default_src/micropython_esp32/origin/build/MixGo_PE-0x1000-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32/origin/build/MixGo_PE-0x1000-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32/origin/build/mPython-0x1000-V1.19.1-lib.bin b/boards/default_src/micropython_esp32/origin/build/mPython-0x1000-V1.19.1-lib.bin index 178a3d25..157805ef 100644 Binary files a/boards/default_src/micropython_esp32/origin/build/mPython-0x1000-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32/origin/build/mPython-0x1000-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin index dd021b73..942fa25b 100644 Binary files a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin index e10fb064..97e5656d 100644 Binary files a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin index 92010fb0..ae599f75 100644 Binary files a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/lib/ble_central.py b/boards/default_src/micropython_esp32c2/origin/build/lib/ble_central.py new file mode 100644 index 00000000..117f4ad2 --- /dev/null +++ b/boards/default_src/micropython_esp32c2/origin/build/lib/ble_central.py @@ -0,0 +1,217 @@ +""" +Bluetooth-Central + +Micropython library for the Bluetooth-Central(ESP32-C2) +======================================================= +#https://github.com/micropython/micropython/tree/master/examples/bluetooth + +@dahanzimin From the Mixly Team +""" +import time,gc +import bluetooth +from micropython import const +from ubinascii import hexlify,unhexlify +from ble_advertising import decode_services, decode_name + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_GATTS_READ_REQUEST = const(4) +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_DESCRIPTOR_RESULT = const(13) +_IRQ_GATTC_DESCRIPTOR_DONE = const(14) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) +_IRQ_GATTC_INDICATE = const(19) + +_ADV_IND = const(0x00) +_ADV_DIRECT_IND = const(0x01) +_ADV_SCAN_IND = const(0x02) +_ADV_NONCONN_IND = const(0x03) + +_UART_SERVICE_UUID = bluetooth.UUID(0x1101) +_UART_RX_CHAR_UUID = bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") +_UART_TX_CHAR_UUID = bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") + + +class BLESimpleCentral: + def __init__(self): + self._ble = bluetooth.BLE() + self._scan_flg = True + self._ble.active(True) + self._ble.irq(self._irq) + self._reset() + self.scan() + + def _reset(self): + # Cached name and address from a successful scan. + self._name = None + self._addr_type = None + self._addr = None + + # Callbacks for completion of various operations. + # These reset back to None after being invoked. + self._conn_callback = None + self._read_callback = None + + # Persistent callback for when new data is notified from the device. + self._notify_callback = None + self._write_data=None + + # Connected device. + self._conn_handle = None + self._start_handle = None + self._end_handle = None + self._tx_handle = None + self._rx_handle = None + + def _irq(self, event, data): + if event == _IRQ_SCAN_RESULT: + addr_type, addr, adv_type, rssi, adv_data = data + + if adv_type in (_ADV_IND, _ADV_DIRECT_IND) and _UART_SERVICE_UUID in decode_services(adv_data): + # Found a potential device, remember it and stop scanning. + self._addr_type = addr_type + self._addr = bytes(addr) # Note: addr buffer is owned by caller so need to copy it. + self._name = decode_name(adv_data) or "?" + if self._addr in self._info[2]: + #self._ble.gap_scan(None) + return None + else: + self._info[0].append(self._name) + self._info[1].append(self._addr_type) + self._info[2].append(self._addr) + self._info[3].append(rssi) + + elif event == _IRQ_SCAN_DONE: + self._scan_flg = False + + elif event == _IRQ_PERIPHERAL_CONNECT: + # Connect successful. + conn_handle, addr_type, addr = data + if addr_type == self._addr_type and addr == self._addr: + self._conn_handle = conn_handle + self._ble.gattc_discover_services(self._conn_handle) + + elif event == _IRQ_PERIPHERAL_DISCONNECT: + # Disconnect (either initiated by us or the remote end). + conn_handle, _, _ = data + if conn_handle == self._conn_handle: + # If it was initiated by us, it'll already be reset. + self._reset() + + elif event == _IRQ_GATTC_SERVICE_RESULT: + # Connected device returned a service. + conn_handle, start_handle, end_handle, uuid = data + print("service", data) + if conn_handle == self._conn_handle and uuid == _UART_SERVICE_UUID: + self._start_handle, self._end_handle = start_handle, end_handle + + elif event == _IRQ_GATTC_SERVICE_DONE: + # Service query complete. + if self._start_handle and self._end_handle: + self._ble.gattc_discover_characteristics( + self._conn_handle, self._start_handle, self._end_handle + ) + else: + print("Failed to find uart service.") + + elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: + # Connected device returned a characteristic. + conn_handle, def_handle, value_handle, properties, uuid = data + if conn_handle == self._conn_handle and uuid == _UART_RX_CHAR_UUID: + self._rx_handle = value_handle + if conn_handle == self._conn_handle and uuid == _UART_TX_CHAR_UUID: + self._tx_handle = value_handle + + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: + # Characteristic query complete. + if self._tx_handle is not None and self._rx_handle is not None: + # We've finished connecting and discovering device, fire the connect callback. + if self._conn_callback: + self._conn_callback() + else: + print("Failed to find uart rx characteristic.") + + elif event == _IRQ_GATTC_WRITE_DONE: + conn_handle, value_handle, status = data + print("TX complete") + + elif event == _IRQ_GATTC_NOTIFY: + conn_handle, value_handle, notify_data = data + if conn_handle == self._conn_handle and value_handle == self._tx_handle: + try: + self._write_data=bytes(notify_data).decode().strip() + except: + self._write_data=bytes(notify_data) + if self._notify_callback: + self._notify_callback(self._write_data) + + # Returns true if we've successfully connected and discovered characteristics. + def is_connected(self): + return (self._conn_handle is not None and self._tx_handle is not None and self._rx_handle is not None) + + # Find a device advertising the environmental sensor service. + def scan(self): + self._info = [[],[],[],[]] + self._ble.gap_scan(5000, 30000, 30000) + while self._scan_flg: + time.sleep_ms(10) + self._scan_flg = True + info=[] + for i in range(len(self._info[0])): + info.append([self._info[0][i],self._info[1][i],hexlify(self._info[2][i]).decode(),self._info[3][i]]) + return info + + # Connect to the specified device (otherwise use cached address from a scan). + def connect(self, name=None,mac=None, callback=None): + if mac and unhexlify(mac) in self._info[2]: + index=self._info[2].index(unhexlify(mac)) + self._addr_type=self._info[1][index] + self._addr=unhexlify(mac) + elif name and name in self._info[0]: + index=self._info[0].index(name) + self._addr_type=self._info[1][index] + self._addr=self._info[2][index] + else: + raise ValueError("Bluetooth was not found") + self._conn_callback = callback + self._ble.gap_connect(self._addr_type, self._addr) + return True + + # Disconnect from current device. + def disconnect(self): + if not self._conn_handle: + return + self._ble.gap_disconnect(self._conn_handle) + self._reset() + gc.collect() + + # Send data over the UART + def send(self, v, response=False): + if not self.is_connected(): + return + self._ble.gattc_write(self._conn_handle, self._rx_handle, v, 1 if response else 0) + + # Set handler for when data is received over the UART. + def recv(self, callback= None): + if callback: + self._notify_callback = callback + else: + write_data=self._write_data + self._write_data=None + return write_data + + @property + def mac(self): + '''Get mac address''' + return hexlify(self._ble.config('mac')[1]).decode() diff --git a/boards/default_src/micropython_esp32c3/origin/build/Generic_C3_UART-0x0-V1.19.1-lib.bin b/boards/default_src/micropython_esp32c3/origin/build/Generic_C3_UART-0x0-V1.19.1-lib.bin index 6509f1db..26d67a2a 100644 Binary files a/boards/default_src/micropython_esp32c3/origin/build/Generic_C3_UART-0x0-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32c3/origin/build/Generic_C3_UART-0x0-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32c3/origin/build/MixGo_CC-0x0-V1.19.1-lib.bin b/boards/default_src/micropython_esp32c3/origin/build/MixGo_CC-0x0-V1.19.1-lib.bin index 1dc7a168..767270de 100644 Binary files a/boards/default_src/micropython_esp32c3/origin/build/MixGo_CC-0x0-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32c3/origin/build/MixGo_CC-0x0-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32c3/origin/build/MixGo_Car-0x0-V1.19.1-lib.bin b/boards/default_src/micropython_esp32c3/origin/build/MixGo_Car-0x0-V1.19.1-lib.bin index 82fc1584..b9a3f3a6 100644 Binary files a/boards/default_src/micropython_esp32c3/origin/build/MixGo_Car-0x0-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32c3/origin/build/MixGo_Car-0x0-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32c3/origin/build/MixGo_ME-0x0-V1.19.1-lib.bin b/boards/default_src/micropython_esp32c3/origin/build/MixGo_ME-0x0-V1.19.1-lib.bin index 2bd37fd5..d075a7da 100644 Binary files a/boards/default_src/micropython_esp32c3/origin/build/MixGo_ME-0x0-V1.19.1-lib.bin and b/boards/default_src/micropython_esp32c3/origin/build/MixGo_ME-0x0-V1.19.1-lib.bin differ diff --git a/boards/default_src/micropython_esp32c3/origin/build/lib/hid_services.py b/boards/default_src/micropython_esp32c3/origin/build/lib/hid_services.py deleted file mode 100644 index 3feef122..00000000 --- a/boards/default_src/micropython_esp32c3/origin/build/lib/hid_services.py +++ /dev/null @@ -1,919 +0,0 @@ -# MicroPython Human Interface Device library - -from micropython import const -import struct -import bluetooth -import json -import binascii -from bluetooth import UUID - -F_READ = bluetooth.FLAG_READ -F_WRITE = bluetooth.FLAG_WRITE -F_READ_WRITE = bluetooth.FLAG_READ | bluetooth.FLAG_WRITE -F_READ_NOTIFY = bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY - -ATT_F_READ = 0x01 -ATT_F_WRITE = 0x02 - - -_ADV_TYPE_FLAGS = const(0x01) -_ADV_TYPE_NAME = const(0x09) -_ADV_TYPE_UUID16_COMPLETE = const(0x3) -_ADV_TYPE_UUID32_COMPLETE = const(0x5) -_ADV_TYPE_UUID128_COMPLETE = const(0x7) -_ADV_TYPE_UUID16_MORE = const(0x2) -_ADV_TYPE_UUID32_MORE = const(0x4) -_ADV_TYPE_UUID128_MORE = const(0x6) -_ADV_TYPE_APPEARANCE = const(0x19) - -# IRQ peripheral role event codes -_IRQ_CENTRAL_CONNECT = const(1) -_IRQ_CENTRAL_DISCONNECT = const(2) -_IRQ_GATTS_WRITE = const(3) -_IRQ_GATTS_READ_REQUEST = const(4) -_IRQ_SCAN_RESULT = const(5) -_IRQ_SCAN_DONE = const(6) -_IRQ_PERIPHERAL_CONNECT = const(7) -_IRQ_PERIPHERAL_DISCONNECT = const(8) -_IRQ_GATTC_SERVICE_RESULT = const(9) -_IRQ_GATTC_SERVICE_DONE = const(10) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) -_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) -_IRQ_GATTC_DESCRIPTOR_RESULT = const(13) -_IRQ_GATTC_DESCRIPTOR_DONE = const(14) -_IRQ_GATTC_READ_RESULT = const(15) -_IRQ_GATTC_READ_DONE = const(16) -_IRQ_GATTC_WRITE_DONE = const(17) -_IRQ_GATTC_NOTIFY = const(18) -_IRQ_GATTC_INDICATE = const(19) -_IRQ_GATTS_INDICATE_DONE = const(20) -_IRQ_MTU_EXCHANGED = const(21) -_IRQ_L2CAP_ACCEPT = const(22) -_IRQ_L2CAP_CONNECT = const(23) -_IRQ_L2CAP_DISCONNECT = const(24) -_IRQ_L2CAP_RECV = const(25) -_IRQ_L2CAP_SEND_READY = const(26) -_IRQ_CONNECTION_UPDATE = const(27) -_IRQ_ENCRYPTION_UPDATE = const(28) -_IRQ_GET_SECRET = const(29) -_IRQ_SET_SECRET = const(30) -_IRQ_PASSKEY_ACTION = const(31) - -_IO_CAPABILITY_DISPLAY_ONLY = const(0) -_IO_CAPABILITY_DISPLAY_YESNO = const(1) -_IO_CAPABILITY_KEYBOARD_ONLY = const(2) -_IO_CAPABILITY_NO_INPUT_OUTPUT = const(3) -_IO_CAPABILITY_KEYBOARD_DISPLAY = const(4) - -_PASSKEY_ACTION_INPUT = const(2) -_PASSKEY_ACTION_DISP = const(3) -_PASSKEY_ACTION_NUMCMP = const(4) - -class Advertiser: - - # Generate a payload to be passed to gap_advertise(adv_data=...). - def advertising_payload(self, limited_disc=False, br_edr=False, name=None, services=None, appearance=0): - payload = bytearray() - - def _append(adv_type, value): - nonlocal payload - payload += struct.pack("BB", len(value) + 1, adv_type) + value - - _append( - _ADV_TYPE_FLAGS, - struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)), - ) - - if name: - _append(_ADV_TYPE_NAME, name) - - if services: - for uuid in services: - b = bytes(uuid) - if len(b) == 2: - _append(_ADV_TYPE_UUID16_COMPLETE, b) - elif len(b) == 4: - _append(_ADV_TYPE_UUID32_COMPLETE, b) - elif len(b) == 16: - _append(_ADV_TYPE_UUID128_COMPLETE, b) - - # See org.bluetooth.characteristic.gap.appearance.xml - if appearance: - _append(_ADV_TYPE_APPEARANCE, struct.pack(" 100: - self.battery_level = 100 - elif level < 0: - self.battery_level = 0 - else: - self.battery_level = level - - # Set device information - # Must be called before calling Start() - # Variables must be Strings - def set_device_information(self, manufacture_name="Homebrew", model_number="1", serial_number="1"): - self.manufacture_name = manufacture_name - self.model_number = model_number - self.serial_number = serial_number - - # Set device revision - # Must be called before calling Start() - # Variables must be Strings - def set_device_revision(self, firmware_revision="1", hardware_revision="1", software_revision="1"): - self.firmware_revision = firmware_revision - self.hardware_revision = hardware_revision - self.software_revision = software_revision - - # Set device pnp information - # Must be called before calling Start() - # Must use the following format: - # pnp_manufacturer_source: 0x01 for manufacturers uuid from the Bluetooth uuid list OR 0x02 from the USBs id list - # pnp_manufacturer_uuid: 0xFEB2 for Microsoft, 0xFE61 for Logitech, 0xFD65 for Razer with source 0x01 - # pnp_product_id: One byte, user defined - # pnp_product_version: Two bytes, user defined, format as 0xJJMN which corresponds to version JJ.M.N - def set_device_pnp_information(self, pnp_manufacturer_source=0x01, pnp_manufacturer_uuid=0xFE61, pnp_product_id=0x01, pnp_product_version=0x0123): - self.pnp_manufacturer_source = pnp_manufacturer_source - self.pnp_manufacturer_uuid = pnp_manufacturer_uuid - self.pnp_product_id = pnp_product_id - self.pnp_product_version = pnp_product_version - - # Set whether to use Bluetooth bonding - def set_bonding(self, bond): - self.bond = bond - - # Set whether to use LE secure pairing - def set_le_secure(self, le_secure): - self.le_secure = le_secure - - # Set input/output capability of this device - - def set_io_capability(self, io_capability): - self.io_capability = io_capability - - # Set callback function for pairing events - # Depending on the I/O capability used, the callback function should return either a - # - boolean to accept or deny a connection, or a - # - passkey that was displayed by the main - def set_passkey_callback(self, passkey_callback): - self.passkey_callback = passkey_callback - - # Set the passkey used during pairing when entering a passkey at the main - def set_passkey(self, passkey): - self.passkey = passkey - - # Notifies the central by writing to the battery level handle - def notify_battery_level(self): - if self.is_connected(): - print("Notify battery level: ", self.battery_level) - self._ble.gatts_notify(self.conn_handle, self.h_bat, struct.pack(" 127: - x = 127 - elif x < -127: - x = -127 - - if y > 127: - y = 127 - elif y < -127: - y = -127 - - self.x = x - self.y = y - - def set_buttons(self, b1=0, b2=0, b3=0, b4=0, b5=0, b6=0, b7=0, b8=0): - self.button1 = b1 - self.button2 = b2 - self.button3 = b3 - self.button4 = b4 - self.button5 = b5 - self.button6 = b6 - self.button7 = b7 - self.button8 = b8 - -# Class that represents the Mouse service -class Mouse(HumanInterfaceDevice): - def __init__(self, name="Bluetooth Mouse"): - super(Mouse, self).__init__(name) # Set up the general HID services in super - self.device_appearance = 962 # Device appearance ID, 962 = mouse - - self.HIDS = ( # Service description: describes the service and how we communicate - UUID(0x1812), # Human Interface Device - ( - (UUID(0x2A4A), F_READ), # HID information - (UUID(0x2A4B), F_READ), # HID report map - (UUID(0x2A4C), F_WRITE), # HID control point - (UUID(0x2A4D), F_READ_NOTIFY, ((UUID(0x2908), ATT_F_READ),)), # HID report / reference - (UUID(0x2A4E), F_READ_WRITE), # HID protocol mode - ), - ) - - # fmt: off - self.HID_INPUT_REPORT = bytes([ # Report Description: describes what we communicate - 0x05, 0x01, # USAGE_PAGE (Generic Desktop) - 0x09, 0x02, # USAGE (Mouse) - 0xa1, 0x01, # COLLECTION (Application) - 0x85, 0x01, # REPORT_ID (1) - 0x09, 0x01, # USAGE (Pointer) - 0xa1, 0x00, # COLLECTION (Physical) - 0x05, 0x09, # Usage Page (Buttons) - 0x19, 0x01, # Usage Minimum (1) - 0x29, 0x03, # Usage Maximum (3) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x95, 0x03, # Report Count (3) - 0x75, 0x01, # Report Size (1) - 0x81, 0x02, # Input(Data, Variable, Absolute); 3 button bits - 0x95, 0x01, # Report Count(1) - 0x75, 0x05, # Report Size(5) - 0x81, 0x03, # Input(Constant); 5 bit padding - 0x05, 0x01, # Usage Page (Generic Desktop) - 0x09, 0x30, # Usage (X) - 0x09, 0x31, # Usage (Y) - 0x09, 0x38, # Usage (Wheel) - 0x15, 0x81, # Logical Minimum (-127) - 0x25, 0x7F, # Logical Maximum (127) - 0x75, 0x08, # Report Size (8) - 0x95, 0x03, # Report Count (3) - 0x81, 0x06, # Input(Data, Variable, Relative); 3 position bytes (X,Y,Wheel) - 0xc0, # END_COLLECTION - 0xc0 # END_COLLECTION - ]) - # fmt: on - - # Define the initial mouse state - self.x = 0 - self.y = 0 - self.w = 0 - - self.button1 = 0 - self.button2 = 0 - self.button3 = 0 - - self.services = [self.DIS, self.BAS, self.HIDS] # List of service descriptions - - # Overwrite super to register HID specific service - # Call super to register DIS and BAS services - def start(self): - super(Mouse, self).start() # Start super - - print("Registering services") - # Register services and get read/write handles for all services - handles = self._ble.gatts_register_services(self.services) - # Write the values for the characteristics - self.write_service_characteristics(handles) - - # Create an Advertiser - # Only advertise the top level service, i.e., the HIDS - self.adv = Advertiser(self._ble, [UUID(0x1812)], self.device_appearance, self.device_name) - - print("Server started") - - # Overwrite super to write HID specific characteristics - # Call super to write DIS and BAS characteristics - def write_service_characteristics(self, handles): - super(Mouse, self).write_service_characteristics(handles) - - # Get the handles from the hids, the third service after DIS and BAS - # These correspond directly to self.HIDS - (h_info, h_hid, _, self.h_rep, h_d1, h_proto,) = handles[2] - - # Pack the initial mouse state as described by the input report - b = self.button1 + self.button2 * 2 + self.button3 * 4 - state = struct.pack("Bbbb", b, self.x, self.y, self.w) - - print("Writing hid service characteristics") - # Write service characteristics - self._ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal - self._ble.gatts_write(h_hid, self.HID_INPUT_REPORT) # HID input report map - self._ble.gatts_write(self.h_rep, state) # HID report - self._ble.gatts_write(h_d1, struct.pack(" 127: - x = 127 - elif x < -127: - x = -127 - - if y > 127: - y = 127 - elif y < -127: - y = -127 - - self.x = x - self.y = y - - def set_wheel(self, w=0): - if w > 127: - w = 127 - elif w < -127: - w = -127 - - self.w = w - - def set_buttons(self, b1=0, b2=0, b3=0): - self.button1 = b1 - self.button2 = b2 - self.button3 = b3 - -# Class that represents the Keyboard service -class Keyboard(HumanInterfaceDevice): - def __init__(self, name="Bluetooth Keyboard"): - super(Keyboard, self).__init__(name) # Set up the general HID services in super - self.device_appearance = 961 # Device appearance ID, 961 = keyboard - - self.HIDS = ( # Service description: describes the service and how we communicate - UUID(0x1812), # Human Interface Device - ( - (UUID(0x2A4A), F_READ), # HID information - (UUID(0x2A4B), F_READ), # HID report map - (UUID(0x2A4C), F_WRITE), # HID control point - (UUID(0x2A4D), F_READ_NOTIFY, ((UUID(0x2908), ATT_F_READ),)), # HID report / reference - (UUID(0x2A4D), F_READ_WRITE, ((UUID(0x2908), ATT_F_READ),)), # HID report / reference - (UUID(0x2A4E), F_READ_WRITE), # HID protocol mode - ), - ) - - # fmt: off - self.HID_INPUT_REPORT = bytes([ # Report Description: describes what we communicate - 0x05, 0x01, # USAGE_PAGE (Generic Desktop) - 0x09, 0x06, # USAGE (Keyboard) - 0xa1, 0x01, # COLLECTION (Application) - 0x85, 0x01, # REPORT_ID (1) - 0x75, 0x01, # Report Size (1) - 0x95, 0x08, # Report Count (8) - 0x05, 0x07, # Usage Page (Key Codes) - 0x19, 0xE0, # Usage Minimum (224) - 0x29, 0xE7, # Usage Maximum (231) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x01, # Logical Maximum (1) - 0x81, 0x02, # Input (Data, Variable, Absolute); Modifier byte - 0x95, 0x01, # Report Count (1) - 0x75, 0x08, # Report Size (8) - 0x81, 0x01, # Input (Constant); Reserved byte - 0x95, 0x05, # Report Count (5) - 0x75, 0x01, # Report Size (1) - 0x05, 0x08, # Usage Page (LEDs) - 0x19, 0x01, # Usage Minimum (1) - 0x29, 0x05, # Usage Maximum (5) - 0x91, 0x02, # Output (Data, Variable, Absolute); LED report - 0x95, 0x01, # Report Count (1) - 0x75, 0x03, # Report Size (3) - 0x91, 0x01, # Output (Constant); LED report padding - 0x95, 0x06, # Report Count (6) - 0x75, 0x08, # Report Size (8) - 0x15, 0x00, # Logical Minimum (0) - 0x25, 0x65, # Logical Maximum (101) - 0x05, 0x07, # Usage Page (Key Codes) - 0x19, 0x00, # Usage Minimum (0) - 0x29, 0x65, # Usage Maximum (101) - 0x81, 0x00, # Input (Data, Array); Key array (6 bytes) - 0xc0 # END_COLLECTION - ]) - # fmt: on - - # Define the initial keyboard state - self.modifiers = 0 # 8 bits signifying Right GUI(Win/Command), Right ALT/Option, Right Shift, Right Control, Left GUI, Left ALT, Left Shift, Left Control - self.keypresses = [0x00] * 6 # 6 keys to hold - - # Callback function for keyboard messages from central - self.kb_callback = None - - self.services = [self.DIS, self.BAS, self.HIDS] # List of service descriptions - - # Interrupt request callback function - # Overwrite super to catch keyboard report write events by the central - def ble_irq(self, event, data): - if event == _IRQ_GATTS_WRITE: # If a client has written to a characteristic or descriptor. - print("Keyboard changed by Central") - conn_handle, attr_handle = data # Get the handle to the characteristic that was written - report = self._ble.gatts_read(attr_handle) # Read the report - bytes = struct.unpack("B", report) # Unpack the report - if self.kb_callback is not None: # Call the callback function - self.kb_callback(bytes) - else: # Else let super handle the event - super(Keyboard, self).ble_irq(event, data) - - # Overwrite super to register HID specific service - # Call super to register DIS and BAS services - def start(self): - super(Keyboard, self).start() # Start super - - print("Registering services") - # Register services and get read/write handles for all services - handles = self._ble.gatts_register_services(self.services) - # Write the values for the characteristics - self.write_service_characteristics(handles) - - # Create an Advertiser - # Only advertise the top level service, i.e., the HIDS - self.adv = Advertiser(self._ble, [UUID(0x1812)], self.device_appearance, self.device_name) - - print("Server started") - - # Overwrite super to write HID specific characteristics - # Call super to write DIS and BAS characteristics - def write_service_characteristics(self, handles): - super(Keyboard, self).write_service_characteristics(handles) - - # Get the handles from the hids, the third service after DIS and BAS - # These correspond directly to self.HIDS - (h_info, h_hid, _, self.h_rep, h_d1, self.h_repout, h_d2, h_proto,) = handles[2] - - print("Writing hid service characteristics") - # Write service characteristics - self._ble.gatts_write(h_info, b"\x01\x01\x00\x02") # HID info: ver=1.1, country=0, flags=normal - self._ble.gatts_write(h_hid, self.HID_INPUT_REPORT) # HID input report map - self._ble.gatts_write(h_d1, struct.pack("