Слияние кода завершено, страница обновится автоматически
"""
The MIT License (MIT)
Copyright © 2020 Walkline Wang (https://walkline.wang)
https://gitee.com/walkline/esp32-ble
"""
import ubluetooth as bt
from ble.profile.manager import ProfileManager
from ble.services import *
from ble.characteristics import *
from ble.descriptors import *
from ble.const import BLEConst
from ble.tools import BLETools
import struct
import gc
PACK = struct.pack
# http://www.freebsddiary.org/APC/usb_hid_usages
REPORT_MAP_DATA = [
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 (01) - Button 1
0x29, 0x03, # Usage Maximum (03) - Button 3
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x03, # Report Count (3)
0x81, 0x02, # Input (Data, Variable, Absolute) - Button states
0x75, 0x05, # Report Size (5)
0x95, 0x01, # Report Count (1)
0x81, 0x01, # Input (Constant) - Padding or Reserved bits
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) - X & Y coordinate
0xC0, # End Collection
0xC0, # End Collection
0x05, 0x01, # USAGE_PAGE (Generic Desktop)
0x09, 0x06, # USAGE (Keyboard)
0xA1, 0x01, # COLLECTION (Application)
# 8 keycode 控制键键位
0x85, 0x02, # Report Id (2)
0x05, 0x07, # USAGE_PAGE (Keyboard)
0x19, 0xE0, # USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xE7, # USAGE_MAXIMUM (Keyboard Right GUI)
# keycode value 0 and 1
0x15, 0x00, # LOGICAL_MINIMUM (0)
0x25, 0x01, # LOGICAL_MAXIMUM (1)
# Modifier byte
0x75, 0x01, # REPORT_SIZE (1)
0x95, 0x08, # REPORT_COUNT (8)
0x81, 0x02, # INPUT (Data,Var,Abs)
# Reserved byte 保留键位
0x95, 0x01, # REPORT_COUNT (1)
0x75, 0x08, # REPORT_SIZE (8)
0x81, 0x01, # INPUT (Cnst,Var,Abs)
# Key arrays (6 bytes) 常规键键位
0x05, 0x07, # USAGE_PAGE (Keyboard)
0x19, 0x00, # USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0xA4, # USAGE_MAXIMUM (Keyboard ExSel)
0x15, 0x00, # LOGICAL_MINIMUM (0)
0x25, 0xA4, # LOGICAL_MAXIMUM (164)
0x95, 0x06, # REPORT_COUNT (6)
0x75, 0x08, # REPORT_SIZE (8)
0x81, 0x00, # INPUT (Data,Ary,Abs)
# # LED report 不要被动灯光控制
# 0x05, 0x08, # USAGE_PAGE (LEDs)
# 0x19, 0x01, # USAGE_MINIMUM (Num Lock)
# 0x29, 0x05, # USAGE_MAXIMUM (Kana)
# 0x95, 0x05, # REPORT_COUNT (5)
# 0x75, 0x01, # REPORT_SIZE (1)
# 0x91, 0x02, # OUTPUT (Data,Var,Abs)
# # LED report padding 补位
# 0x95, 0x01, # REPORT_COUNT (1)
# 0x75, 0x03, # REPORT_SIZE (3)
# 0x91, 0x03, # OUTPUT (Cnst,Var,Abs)
0xC0, # END_COLLECTION
]
class GenericAccessValues(object):
APPEARANCE = PACK("<h", BLEConst.Appearance.KEYBOARD)
PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = PACK("<4h", 40, 80, 10, 300) # (6, 6, 60, 300) <- ble mouse # (min, max, latency, timeout), for min & max, 1 = 1.25ms
CENTRAL_ADDRESS_RESOLUTION = PACK("<b", 1)
class BatteryServiceValues(object):
BATTERY_LEVEL_INT = PACK("<B", int(100))
class DeviceInformationValues(object):
MANUFACTURER_NAME_STRING = PACK("<17s", "Walkline Hardware".encode())
MODEL_NUMBER_STRING = PACK("<11s", "WKBP-B20".encode())
SERIAL_NUMBER_STRING = PACK("<36s", "4e897424-061d-4dd9-a798-454f79b37245".encode())
FIRMWARE_REVISION_STRING = PACK("<4s", "v0.1".encode())
HARDWARE_REVISION_STRING = PACK("<4s", "v0.2.1".encode())
SOFTWARE_REVISION_STRING = PACK("<4s", "v0.1".encode())
# key="1" value="Bluetooth SIG assigned Company Identifier value from the Assigned Numbers document"
# key="2" value="USB Implementer’s Forum assigned Vendor ID value"
# VendorID: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/
PNP_ID = PACK("<BHHH", 1, 0x02E5, 0x01, 0x01) # 0x02E5: Espressif, 0x0006: Microsoft
class HumanInterfaceDeviceValues(object):
# key="0" value="Boot Protocol Mode"
# key="1" value="Report Protocol Mode"
PROTOCOL_MODE = PACK("<B", int(1))
HID_INFORMATION = PACK("<H2b", 0x0111, 0x00, 0b0011)
# param 1: Report ID
# param 2: Report Type
# value="Input Report" key="1"
# value="Output report" key="2"
# value="Feature Report" key="3"
REPORT_REFERANCE_MOUSE = PACK("<BB", int(1), int(1))
REPORT_REFERANCE_KEYBOARD = PACK("<BB", int(2), int(1))
class BLE_HID(object):
def __init__(self, ble, name='Walkline KPB20'):
self.__ble = ble
self.__device_name = name
self.__conn_handle = None
self.__profile_manager_adv = ProfileManager()
self.__profile_manager_resp = ProfileManager()
self.__write = self.__ble.gatts_write
self.__read = self.__ble.gatts_read
self.__notify = self.__ble.gatts_notify
self.__ble.active(False)
print("activating ble...")
self.__ble.active(True)
print("ble activated")
self.__ble.config(rxbuf=256)
print("mac: [{}]".format(BLETools.decode_mac(self.__ble.config("mac"))))
print("rxbuf: [{}]".format(self.__ble.config("rxbuf")))
self.__profile_manager_adv.add_services(
BatteryService().add_characteristics(
Characteristics(BLEConst.Characteristics.BATTERY_LEVEL, BLEConst.CharacteristicFlags.BATTERY_LEVEL_),
),
DeviceInformation().add_characteristics(
Characteristics(BLEConst.Characteristics.MODEL_NUMBER_STRING, BLEConst.CharacteristicFlags.MODEL_NUMBER_STRING_),
Characteristics(BLEConst.Characteristics.SERIAL_NUMBER_STRING, BLEConst.CharacteristicFlags.SERIAL_NUMBER_STRING_),
Characteristics(BLEConst.Characteristics.FIRMWARE_REVISION_STRING, BLEConst.CharacteristicFlags.FIRMWARE_REVISION_STRING_),
Characteristics(BLEConst.Characteristics.HARDWARE_REVISION_STRING, BLEConst.CharacteristicFlags.HARDWARE_REVISION_STRING_),
Characteristics(BLEConst.Characteristics.SOFTWARE_REVISION_STRING, BLEConst.CharacteristicFlags.SOFTWARE_REVISION_STRING_),
Characteristics(BLEConst.Characteristics.MANUFACTURER_NAME_STRING, BLEConst.CharacteristicFlags.MANUFACTURER_NAME_STRING_),
Characteristics(BLEConst.Characteristics.PNP_ID, BLEConst.CharacteristicFlags.PNP_ID_),
),
HumanInterfaceDevice().add_characteristics(
Characteristics(BLEConst.Characteristics.PROTOCOL_MODE, BLEConst.CharacteristicFlags.PROTOCOL_MODE_),
Characteristics(BLEConst.Characteristics.REPORT, BLEConst.CharacteristicFlags.REPORT_).add_descriptors(
# ReportReference(),
Descriptor(bt.UUID(BLEConst.Descriptors.REPORT_REFERENCE), bt.FLAG_READ | bt.FLAG_WRITE | bt.FLAG_NOTIFY),
),
Characteristics(BLEConst.Characteristics.REPORT, BLEConst.CharacteristicFlags.REPORT_).add_descriptors(
# ReportReference(),
Descriptor(bt.UUID(BLEConst.Descriptors.REPORT_REFERENCE), bt.FLAG_READ | bt.FLAG_WRITE | bt.FLAG_NOTIFY),
),
Characteristics(BLEConst.Characteristics.REPORT_MAP, BLEConst.CharacteristicFlags.REPORT_MAP_),
Characteristics(BLEConst.Characteristics.BOOT_KEYBOARD_INPUT_REPORT, BLEConst.CharacteristicFlags.BOOT_KEYBOARD_INPUT_REPORT_),
Characteristics(BLEConst.Characteristics.BOOT_KEYBOARD_OUTPUT_REPORT, BLEConst.CharacteristicFlags.BOOT_KEYBOARD_OUTPUT_REPORT_),
Characteristics(BLEConst.Characteristics.HID_INFORMATION, BLEConst.CharacteristicFlags.HID_INFORMATION_),
Characteristics(BLEConst.Characteristics.HID_CONTROL_POINT, BLEConst.CharacteristicFlags.HID_CONTROL_POINT_),
),
)
self.__profile_manager_resp.add_services(
GenericAccess().add_characteristics(
Characteristics(BLEConst.Characteristics.DEVICE_NAME, BLEConst.CharacteristicFlags.DEVICE_NAME_),
Characteristics(BLEConst.Characteristics.APPEARANCE, BLEConst.CharacteristicFlags.APPEARANCE_),
# Characteristics(BLEConst.Characteristics.PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, BLEConst.CharacteristicFlags.PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_),
Characteristics(BLEConst.Characteristics.CENTRAL_ADDRESS_RESOLUTION, BLEConst.CharacteristicFlags.CENTRAL_ADDRESS_RESOLUTION_),
),
GenericAttribute().add_characteristics(
Characteristics(BLEConst.Characteristics.SERVICE_CHANGED, BLEConst.CharacteristicFlags.SERVICE_CHANGED_),
),
)
self.__services = self.__profile_manager_adv.get_services() + self.__profile_manager_resp.get_services()
self.__ble.irq(handler=self.__irq)
self.__register_services()
self.__adv_payload = self.advertising_hid_payload(
services=self.__profile_manager_adv.get_services_uuid(),
appearance=BLEConst.Appearance.KEYBOARD,
# tx_power=-57
)
self.__resp_payload = BLETools.advertising_resp_payload(
services=self.__profile_manager_resp.get_services_uuid(),
name=self.__device_name
)
self.__advertise()
def advertising_hid_payload(self, services=None, appearance=0, tx_power=None):
"""
Generate paylaod for HID device
"""
payload = bytearray()
def _append(adv_type, value):
nonlocal payload
payload += PACK('BB', len(value) + 1, adv_type) + value
_append(BLEConst.ADType.AD_TYPE_FLAGS, PACK('B', 0x06))
if services:
for uuid in services:
b = bytes(uuid)
if len(b) == 2:
_append(BLEConst.ADType.AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, b)
elif len(b) == 4:
_append(BLEConst.ADType.AD_TYPE_32BIT_SERVICE_UUID_COMPLETE, b)
elif len(b) == 16:
_append(BLEConst.ADType.AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, b)
_append(BLEConst.ADType.AD_TYPE_APPEARANCE, PACK('<h', appearance))
# _append(BLEConst.ADType.AD_TYPE_MANUFACTURER_SPECIFIC_DATA, PACK('<h3b', 0x0006, 0x03, 0x00, 0x80)) # 0x004c, 0x02, 0x00, 0x80))
if tx_power:
_append(BLEConst.ADType.AD_TYPE_TX_POWER_LEVEL, PACK('<h', BLETools.convert_tx_power_level(tx_power)))
return payload
def __advertise(self, interval_us=100000):
self.__ble.gap_advertise(None)
self.__ble.gap_advertise(interval_us, adv_data=self.__adv_payload, resp_data=self.__resp_payload)
print("advertising...")
def __register_services(self):
(
(
self.__handle_battery_level,
),
(
self.__handle_model,
self.__handle_serial,
self.__handle_firmware,
self.__handle_hardware,
self.__handle_software,
self.__handle_manufacturer,
self.__handle_pnp_id,
),
(
self.__handle_protocol_mode,
self.__handle_report_mouse,
self.__handle_report_report_reference_mouse_descriptor,
self.__handle_report_keyboard,
self.__handle_report_report_reference_keyboard_descriptor,
self.__handle_report_map,
self.__handle_boot_kb_input_report,
self.__handle_boot_kb_output_report,
self.__handle_hid_information,
self.__handle_hid_control_point,
# self.handle___boot_kb_input_report_descriptor,
),
(
self.__handle_device_name,
self.__handle_appearance,
# self.__handle_peripheral_preferred_connection_parameters,
self.__handle_central_address_resolution,
),
(
self.__handle_service_changed,
),
) = self.__ble.gatts_register_services(self.__services)
print("services registed")
self.__setup_generic_access()
# self.set_battery_level()
self.__setup_device_info()
self.__setup_hid()
print("characteristic values setup")
def __irq(self, event, data):
if event == BLEConst.IRQ.IRQ_CENTRAL_CONNECT:
self.__conn_handle, addr_type, addr, = data
print("[{}] connected, handle: {}".format(BLETools.decode_mac(addr), self.__conn_handle))
self.__ble.gap_advertise(None)
self.__ble.start_pairing(self.__conn_handle)
elif event == BLEConst.IRQ.IRQ_CENTRAL_DISCONNECT:
self.__conn_handle, _, addr, = data
print("[{}] disconnected, handle: {}".format(BLETools.decode_mac(addr), self.__conn_handle))
self.__conn_handle = None
self.__advertise()
else:
print("event: {}, data: {}".format(event, data))
gc.collect()
def __setup_generic_access(self):
self.__write(self.__handle_device_name, PACK("<{}s".format(len(self.__device_name)), self.__device_name.encode()))
self.__write(self.__handle_appearance, GenericAccessValues.APPEARANCE)
# self.__write(self.__handle_peripheral_preferred_connection_parameters, GenericAccessValues.PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS)
self.__write(self.__handle_central_address_resolution, GenericAccessValues.CENTRAL_ADDRESS_RESOLUTION)
def __setup_device_info(self):
"""
used for Device Information
"""
self.__write(self.__handle_manufacturer, DeviceInformationValues.MANUFACTURER_NAME_STRING)
self.__write(self.__handle_model, DeviceInformationValues.MODEL_NUMBER_STRING)
self.__write(self.__handle_serial, DeviceInformationValues.SERIAL_NUMBER_STRING)
self.__write(self.__handle_firmware, DeviceInformationValues.FIRMWARE_REVISION_STRING)
self.__write(self.__handle_hardware, DeviceInformationValues.HARDWARE_REVISION_STRING)
self.__write(self.__handle_software, DeviceInformationValues.SOFTWARE_REVISION_STRING)
self.__write(self.__handle_pnp_id, DeviceInformationValues.PNP_ID)
def __setup_hid(self):
self.__write(self.__handle_protocol_mode, HumanInterfaceDeviceValues.PROTOCOL_MODE)
self.__write(self.__handle_report_report_reference_mouse_descriptor, HumanInterfaceDeviceValues.REPORT_REFERANCE_MOUSE)
self.__write(self.__handle_report_report_reference_keyboard_descriptor, HumanInterfaceDeviceValues.REPORT_REFERANCE_KEYBOARD)
self.__write(self.__handle_hid_information, HumanInterfaceDeviceValues.HID_INFORMATION)
self.__write(self.__handle_report_map, bytes(REPORT_MAP_DATA))
def update_battery_level(self, value):
self.__write(self.__handle_battery_level, BatteryServiceValues.BATTERY_LEVEL_INT)
if self.__conn_handle is not None:
self.__notify(self.__conn_handle, self.__handle_battery_level)
def send_key(self, keycode):
self.__write(self.__handle_report_keyboard, bytes([0x0, 0x0, keycode, 0x0, 0x0, 0x0, 0x0, 0x0]))
if self.__conn_handle is not None:
self.__notify(self.__conn_handle, self.__handle_report_keyboard)
self.__write(self.__handle_report_keyboard, bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]))
if self.__conn_handle is not None:
self.__notify(self.__conn_handle, self.__handle_report_keyboard)
def send_key_down(self, keycodes, modifier):
self.__write(self.__handle_report_keyboard, bytes([0x2, modifier , 0x0] + keycodes))
if self.__conn_handle is not None:
self.__notify(self.__conn_handle, self.__handle_report_keyboard)
def send_key_up(self, keycodes, modifier):
self.__write(self.__handle_report_keyboard, bytes([0x2, modifier, 0x0] + keycodes))
if self.__conn_handle is not None:
self.__notify(self.__conn_handle, self.__handle_report_keyboard)
def main():
ble = bt.BLE()
hid = BLE_HID(ble)
if __name__ == "__main__":
main()
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарий ( 0 )