精华内容
下载资源
问答
  • 2022-05-13 08:25:50

    同学,别退出呀,我可是全网最牛逼的 Android 蓝牙分析博主,我写了上百篇蓝牙文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。

    HID/HOGP完整安卓源码分析(word文件)请点击下面链接:Android 蓝牙低功耗ble hogp 广播、扫描、连接、数据读写源码流程分析大全,详细的从btif-bta-btm-hci

    L2CAP完整安卓源码分析(word文件)请点击下面链接:Android 蓝牙L2cap协议源码分析大全,非常详细的从btif-bta-btm-hci 数据流程走向,以及从control

    一、简述

            在工作中,我发现大多数同事对HID/HOGP的概念不是很清楚,尤其是HOGP。网上也没有人将这两个协议的概念和安卓源码流程总结出来,今天我将用

    更多相关内容
  • 低功耗蓝牙hid协议中文版
  • android 蓝牙hid协议开发,实现手机连接蓝牙鼠标、键盘、扫描枪.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  • 蓝牙HID协议

    热门讨论 2011-12-06 17:29:08
    完整的蓝牙HID协议,好不容易才找到的。
  • bt蓝牙hid协议.zip

    2022-01-04 19:34:09
    标准蓝牙hid协议中文版
  • 本文章主要介绍了蓝牙HID协议的实现方法,基于ESP32平台实现了蓝牙键盘,蓝牙鼠标,蓝牙自拍杆和蓝牙游戏手柄等设备,是初学者学习BLE HID协议很好的参考文章。 HID设备 HID(Human Interface Device)人体学接口...

    摘要

    本文章主要介绍了蓝牙HID协议的实现方法,基于ESP32平台实现了蓝牙键盘,蓝牙鼠标,蓝牙自拍杆和蓝牙游戏手柄等设备,是初学者学习BLE HID协议很好的参考文章。

    HID设备

    常见HID设备

    HID(Human Interface Device)人体学接口设备,是生活中常见的输入设备,比如键盘鼠标游戏手柄等等。早期的HID是设备大部分都是通过USB接口来实现,蓝牙技术出现后,通过蓝牙作为传输层,实现了无线HID设备。通过低功耗蓝牙实现的HID功能一般简称为HOGP(HID over Gatt Profile)。

    蓝牙HID设备的实现

    任何低功耗蓝牙模块都可以通过软件开发实现HID功能,一个蓝牙模块要实现HID的功能,一般需满足如下两个条件:

    ①、在广播数据中广播HID的UUID,设备外观,设备名称等信息。
    蓝牙HID广播数据
    HID服务的UUID是0x1812,键盘的外观是0x03C1,鼠标的外观是0x03C2,游戏手柄的外观是0x03C3
    UUID参考资料:https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
    设备外观参考资料:https://specificationrefs.bluetooth.com/assigned-values/Appearance%20Values.pdf

    ②、在GATT中实现HID要求的服务和特性。

    蓝牙HIDS UUID

    0x1812是HID Service的UUID,必须要使用该UUID实现服务。

    0x2A4A是HID Information 的特性UUID,主要功能是展示HID的信息,其值为4个字节:
    前两个字节是HID版本,一般填入0x01,0x01,表示版本号为1.1
    第三个字节是Country Code,一般填入0x00
    第四个字节是HID Flags,一般填入0x02,表示Normally Connectable。

    0x2A4B是Report Map的特性UUID,主要功能是描述HID设备与HID主机数据交互的方式,即二者之间所发送的数据每一位的含义。

    0x2A4C是Control Point的特性UUID,该特性一定要可Write ,HID主机通过该特性告知HID设备主机的状态,比如电脑休眠后会告知蓝牙键盘也进入低功耗模式。

    0x2A4D是HID设备与HID主机之间交互数据(Report)的特性UUID。
    对于键盘设备,当某个按键按下时,HID设备发送数据到HID主机;当开关CapsLock,NumsLook和ScrollLook功能时,HID主机将相关指示灯的状态发送给HID键盘。所以键盘设备需要两个UUID为0x2A4D的特性,一个用于发送数据,另一个用于接收数据(使用UUID为0x2908的描述来区分)。
    而对于鼠标,游戏手柄这种只发送数据给电脑的设备,只需要定义一个0x2A4D的特性用来发送数据就可以了(当然定义两个也不会出错)。

    0x2A4E是协议模式的特性UUID,对于键盘和鼠标这两种设备,可能也会在电脑BIOS阶段使用,此阶段的计算机没有进入系统,难以支持复杂的设备。所以键盘鼠标就有两种模式,分别是Report模式和Boot模式,系统启动前使用的是Boot模式,HID设备与HID主机之间使用固定的数据格式进行交互。系统启动完成后,HID主机会通过该特性发数据给HID设备,通知HID设备切换成Report模式,在Report模式下,数据格式由 Report Map 决定。
    该特性的数据值为0x00表示Boot模式,0x01表示Report模式。

    在Boot模式下,Keyboard Input 的特性UUID是0x2A22,Output 的特性UUID是0x2A32;Mous Input 的特性UUID是0x2A33

    本文章只讨论Report模式,暂不讨论Boot模式!

    蓝牙键盘

    下列代码基于ESP32芯片MicroPython固件(V1.16以上)给出了简单的蓝牙键盘案例:

    from machine import Pin
    import ubluetooth #导入BLE功能模块
    from bluetooth import UUID
    
    ble = ubluetooth.BLE()  #创建BLE设备
    ble.active(True)  #打开BLE
    
    ble.config(gap_name="ESP Keyboard")
    ble.config(mtu=23)
    
    HIDS = (                              # Service description: describes the service and how we communicate
        UUID(0x1812),                     # Human Interface Device
        (
            (UUID(0x2A4A), ubluetooth.FLAG_READ),       # HID information
            (UUID(0x2A4B), ubluetooth.FLAG_READ),       # HID report map
            (UUID(0x2A4C), ubluetooth.FLAG_WRITE),      # HID control point
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,  ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode
        ),
    )
    
    services = (HIDS,)
    handles = ble.gatts_register_services(services)
    
    KEYBOARD_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
            ])
    
    #设置BLE广播数据并开始广播
    ble.gap_advertise(100, adv_data = b'\x02\x01\x05'
                                    + b'\x03\x03\x12\x18' #HID UUID
                                    + b'\x03\x19\xC1\x03' #设备外观为键盘
                                    + b'\x0D\x09' + "ESP Keyboard".encode("UTF-8"))
    
    (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0]
    # Write service characteristics
    ble.gatts_write(h_info, b"\x01\x01\x00\x02")     # HID info: ver=1.1, country=0, flags=normal
    ble.gatts_write(h_map, KEYBOARD_REPORT)    # HID input report map
    ble.gatts_write(h_d1, b"\x01\x01")  # HID reference: id=1, type=input
    ble.gatts_write(h_d2, b"\x01\x02")  # HID reference: id=1, type=output
    ble.gatts_write(h_model, b"\x01")   # HID Protocol Model: 0=Boot Model, 1=Report Model
    
    
    key = Pin(0,Pin.IN)#IO 0 用作按键
    while True:
      if key.value() == 0:
        while key.value() == 0:
          pass
        ble.gatts_notify(0, h_repin, b'\x00\x00\x04\x00\x00\x00\x00\x00')#按键A按下
        ble.gatts_notify(0, h_repin, b'\x00\x00\x00\x00\x00\x00\x00\x00')#按键A抬起
    

    上述代码实现了一个简单的蓝牙键盘,按下设备上IO0对应的按键,发送按键A到HID主机。
    上述代码的ReportMap中定义了HID设备发给HID主机的数据为8个字节,定义如下图:

    Keyboard Report Data
    第一个字节是Modifier按键,相应的位为1表示对应的按键按下(GUI在Windows下是Win键);第二个字节保留,默认为零。第三到第八字节可表示六个按键,数据值为零表示无按键按下,按键对应的代码可搜索HID KeyCode,比如按键A对应的代码是0x04,按键B对应的代码是0x05,Enter键对应的代码是0x28

    比如:

    0x80,0x00,0x06,0x00,0x00,0x00,0x00,0x00  //表示同时按下Ctrl键和字母C键。
    
    0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00  //表示同时按下Shift键和字母A键。
    

    需要注意的是,发送某个按键按下的数据后,需及时发送按键抬起的数据,否则系统会认为按键未抬起,从而触发长按操作。

    蓝牙自拍杆

    自拍杆是最近几年出现的产品,其实蓝牙自拍杆的本质就是一个超简易的蓝牙键盘。我们知道在手机拍照页面,音量调节按键可以触发快门拍照,蓝牙自拍杆本质上就是一个只能调节音量的蓝牙键盘。调节音量是键盘的高级功能,可通过如下代码试下:

    from machine import Pin
    import ubluetooth #导入BLE功能模块
    from bluetooth import UUID
    
    ble = ubluetooth.BLE()  #创建BLE设备
    ble.active(True)  #打开BLE
    
    ble.config(gap_name="ESP Keyboard")
    ble.config(mtu=23)
    
    HIDS = (                              # Service description: describes the service and how we communicate
        UUID(0x1812),                     # Human Interface Device
        (
            (UUID(0x2A4A), ubluetooth.FLAG_READ),       # HID information
            (UUID(0x2A4B), ubluetooth.FLAG_READ),       # HID report map
            (UUID(0x2A4C), ubluetooth.FLAG_WRITE),      # HID control point
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,  ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode
        ),
    )
    
    services = (HIDS,)
    handles = ble.gatts_register_services(services)
    
    MEDIA_REPORT = bytes([    # Report Description: describes what we communicate
                # Report ID 1: Advanced buttons
                0x05, 0x0C,       # Usage Page (Consumer)
                0x09, 0x01,       # Usage (Consumer Control)
                0xA1, 0x01,       # Collection (Application)
                0x85, 0x01,       #     Report Id (1)
                0x15, 0x00,       #     Logical minimum (0)
                0x25, 0x01,       #     Logical maximum (1)
                0x75, 0x01,       #     Report Size (1)
                0x95, 0x01,       #     Report Count (1)
    
                0x09, 0xCD,       #     Usage (Play/Pause)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x0A, 0x83, 0x01, #     Usage (AL Consumer Control Configuration)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x09, 0xB5,       #     Usage (Scan Next Track)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x09, 0xB6,       #     Usage (Scan Previous Track)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
    
                0x09, 0xEA,       #     Usage (Volume Down)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x09, 0xE9,       #     Usage (Volume Up)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x0A, 0x25, 0x02, #     Usage (AC Forward)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0x0A, 0x24, 0x02, #     Usage (AC Back)
                0x81, 0x06,       #     Input (Data,Value,Relative,Bit Field)
                0xC0              # End Collection
            ])
    
    #设置BLE广播数据并开始广播
    ble.gap_advertise(100, adv_data = b'\x02\x01\x05'
                                    + b'\x03\x03\x12\x18' #HID UUID
                                    + b'\x03\x19\xC1\x03' #设备外观为键盘
                                    + b'\x0D\x09' + "ESP Keyboard".encode("UTF-8"))
    
    (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0]
    # Write service characteristics
    ble.gatts_write(h_info, b"\x01\x01\x00\x02")     # HID info: ver=1.1, country=0, flags=normal
    ble.gatts_write(h_map, MEDIA_REPORT)    # HID input report map
    ble.gatts_write(h_d1, b"\x01\x01")  # HID reference: id=1, type=input
    ble.gatts_write(h_d2, b"\x01\x02")  # HID reference: id=1, type=output
    ble.gatts_write(h_model, b"\x01")   # HID Protocol Model: 0=Boot Model, 1=Report Model
    
    
    key = Pin(0,Pin.IN)#IO 0 用作按键
    while True:
      if key.value() == 0:
        while key.value() == 0:
          pass
        ble.gatts_notify(0, h_repin, b'\x10')#音量- 按下
        ble.gatts_notify(0, h_repin, b'\x00')#音量- 抬起
    

    上述代码试下了媒体控制的功能,按下设备上IO0对应的按键,执行音量-的动作。上述Report Map中定义了键盘的高级按键,媒体控制的功能,发送给HID主机的数据为一个字节,每一位定义如下:

    Media Control 按键

    0x10表示音量-,0x04表示下一曲。

    蓝牙鼠标

    下列代码给出了简单的蓝牙鼠标的示例代码:

    from machine import Pin
    import ubluetooth #导入BLE功能模块
    from bluetooth import UUID
    
    ble = ubluetooth.BLE()  #创建BLE设备
    ble.active(True)  #打开BLE
    
    ble.config(gap_name="ESP Mouse")
    ble.config(mtu=23)
    
    HIDS = (                              # Service description: describes the service and how we communicate
        UUID(0x1812),                     # Human Interface Device
        (
            (UUID(0x2A4A), ubluetooth.FLAG_READ),       # HID information
            (UUID(0x2A4B), ubluetooth.FLAG_READ),       # HID report map
            (UUID(0x2A4C), ubluetooth.FLAG_WRITE),      # HID control point
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,  ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode
        ),
    )
    
    services = (HIDS,)
    handles = ble.gatts_register_services(services)
    
    MOUSE_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
            ])
    
    #设置BLE广播数据并开始广播
    ble.gap_advertise(100, adv_data = b'\x02\x01\x05'
                                    + b'\x03\x03\x12\x18' #HID UUID
                                    + b'\x03\x19\xC2\x03' #设备外观为鼠标
                                    + b'\x0A\x09' + "ESP Mouse".encode("UTF-8"))
    
    (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0]
    # Write service characteristics
    ble.gatts_write(h_info, b"\x01\x01\x00\x02")     # HID info: ver=1.1, country=0, flags=normal
    ble.gatts_write(h_map, MOUSE_REPORT)    # HID input report map
    ble.gatts_write(h_d1, b"\x01\x01")  # HID reference: id=1, type=input
    ble.gatts_write(h_d2, b"\x01\x02")  # HID reference: id=1, type=output
    ble.gatts_write(h_model, b"\x01")   # HID Protocol Model: 0=Boot Model, 1=Report Model
    
    
    key = Pin(0,Pin.IN)#IO 0 用作按键
    while True:
      if key.value() == 0:
        while key.value() == 0:
          pass
        ble.gatts_notify(0, h_repin, b'\x00\x0A\xF6\x00')#X正方向和Y的负方向各移动10像素
        ble.gatts_notify(0, h_repin, b'\x00\x00\x00\x00')#
    

    上述代码实现了简单的蓝牙鼠标的功能,按下设备上IO0对应的按键,鼠标指针将向右和向上各移动10个像素单位。上述代码中的Report Map定义了HID设备发给HID主机的数据为4个字节,每个字节含义如下:

    Mouse Report

    一个标准的鼠标上面有三个按键(左键右键中间键)一个滑轮和一个光标。上述数据中,第一个字节的第三位表示三个按键按下与否(具体如何对应未知),第二个字节表示鼠标指针在X方向的移动量,取值范围-127 ~ 127,第三字节表示鼠标指针在Y方向的移动量,取值范围是-127 ~ 127,第四个字节表示鼠标滑轮的滚动量,取值范围是-127 ~ 128。

    比如:

    0x01,0x00,0x00,0x00 表示鼠标上的某个按键按下。

    0x00,0x09,0x09,0x00 表示鼠标指针向X正方向和Y正方向个移动9个像素单位。

    0x00,0x00,0x00,0x01 表示鼠标滑轮滚动一格。

    蓝牙游戏手柄

    下列代码给出了简单的蓝牙游戏手柄案例:

    from machine import Pin
    from time import sleep_ms
    import ubluetooth #导入BLE功能模块
    from bluetooth import UUID
    import struct
    
    ble = ubluetooth.BLE()  #创建BLE设备
    ble.active(True)  #打开BLE
    
    ble.config(gap_name="ESP Joystick")
    ble.config(mtu=23)
    
    HIDS = (                              # Service description: describes the service and how we communicate
        UUID(0x1812),                     # Human Interface Device
        (
            (UUID(0x2A4A), ubluetooth.FLAG_READ),       # HID information
            (UUID(0x2A4B), ubluetooth.FLAG_READ),       # HID report map
            (UUID(0x2A4C), ubluetooth.FLAG_WRITE),      # HID control point
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4D), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,  ((UUID(0x2908), 1),)),  # HID report / reference
            (UUID(0x2A4E), ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE), # HID protocol mode
        ),
    )
    
    services = (HIDS,)
    handles = ble.gatts_register_services(services)
    
    JOY_REPORT = bytes([    # Report Description: describes what we communicate
                0x05, 0x01,                    # USAGE_PAGE (Generic Desktop)
                0x09, 0x04,                    # USAGE (Joystick)
                0xa1, 0x01,                    # COLLECTION (Application)
                0x85, 0x01,                    #   REPORT_ID (1)
                0xa1, 0x00,                    #   COLLECTION (Physical)
                0x09, 0x30,                    #     USAGE (X)
                0x09, 0x31,                    #     USAGE (Y)
                0x15, 0x81,                    #     LOGICAL_MINIMUM (-127)
                0x25, 0x7f,                    #     LOGICAL_MAXIMUM (127)
                0x75, 0x08,                    #     REPORT_SIZE (8)
                0x95, 0x02,                    #     REPORT_COUNT (2)
                0x81, 0x02,                    #     INPUT (Data,Var,Abs)
                0x05, 0x09,                    #     USAGE_PAGE (Button)
                0x29, 0x08,                    #     USAGE_MAXIMUM (Button 8)
                0x19, 0x01,                    #     USAGE_MINIMUM (Button 1)
                0x95, 0x08,                    #     REPORT_COUNT (8)
                0x75, 0x01,                    #     REPORT_SIZE (1)
                0x25, 0x01,                    #     LOGICAL_MAXIMUM (1)
                0x15, 0x00,                    #     LOGICAL_MINIMUM (0)
                0x81, 0x02,                    #     Input (Data, Variable, Absolute)
                0xc0,                          #   END_COLLECTION
                0xc0                           # END_COLLECTION
            ])                  #         Report Size (1)
    
    #设置BLE广播数据并开始广播
    ble.gap_advertise(100, adv_data = b'\x02\x01\x05'
                                    + b'\x03\x03\x12\x18' #HID UUID
                                    + b'\x03\x19\xC3\x03' #设备外观为手柄
                                    + b'\x0D\x09' + "ESP Joystick".encode("UTF-8"))
    
    (h_info, h_map, _, h_repin, h_d1, h_repout, h_d2, h_model,) = handles[0]
    # Write service characteristics
    ble.gatts_write(h_info, b"\x01\x01\x00\x02")     # HID info: ver=1.1, country=0, flags=normal
    ble.gatts_write(h_map, JOY_REPORT)    # HID input report map
    ble.gatts_write(h_d1, struct.pack("<BB", 1, 1))  # HID reference: id=1, type=input
    ble.gatts_write(h_d2, struct.pack("<BB", 1, 2))  # HID reference: id=1, type=output
    ble.gatts_write(h_model, b"\x01") # HID Protocol Model: 0=Boot Model, 1=Report Model
    
    
    key = Pin(0,Pin.IN)#IO 0 用作按键
    while True:
      if key.value() == 0:
        while key.value() == 0:
          pass
        ble.gatts_notify(0, h_repin, b'\xf5\xf5\x00')
        ble.gatts_notify(0, h_repin, b'\x00\x00\x00')
    

    上述代码实现了一个简单的蓝牙游戏手柄的功能,ReportMap中定义了HID设备发给HID主机的数据为3个字节,每个字节的含义如下:
    在这里插入图片描述
    游戏手柄上一般由推杆和按键组成,上面数据的第一字节表示推杆在X方向的偏移量,范围是-127 ~ 127,第二字节表示推杆在Y方向的偏移量,范围是-127 ~ 128;第三字节表示是否有按键按下,最多支持八个按键。

    上述代码中的BUG

    一般的蓝牙设备在配对成功后,下次可以自动连接,上述代码运行后,需要在电脑或手机上删除该设备,再次连接才能生效,只有首次连接可以使用。断开连接后需要删除设备重新配对。

    上述文档中的数据格式和取值范围和代码中的ReportMap一一对应,修改ReportMap后,数据格式和取值返回也要做对应的变化。

    一般正常的蓝牙HID设备也要包含DIS(Device Information Service)设备信息服务,和BAS(Battery Service)电池电量服务,读者可自行添加。

    上一章节:【低功耗蓝牙】④ 蓝牙MIDI协议

    作者:我是鹏老师

    展开全文
  • 蓝牙HID协议笔记

    2022-05-16 14:45:02
    The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。 2.一些概念 (1)HID Reports:...

    1.概述

        The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。

        

    2.一些概念

    (1)HID Reports:Bluetooth HID devices支持三种Report:Input, Output, and Feature。

    (2)HID建立Control Channel和Interrupt Channel两个通道,report可以在这两条channel上传输,在Control channel上传输的report称为synchronous reports ;在Interrupt channel上传输的report称为asynchronous reports。

    (3)Feature reports are always transferred synchronously using GET_REPORT or SET_REPORT requests。

    (4)Report Protocol Mode和Boot Protocol Mode。Bluetooth HID Hosts至少支持一种,Bluetooth HID Device则需要支持Report Protocol Mode,并且Report Protocol Mode是Bluetooth HID Device的默认Mode。 

    3.Bluetooth HID Protocol Messages

    这些message不能超过L2CAP的MTU,大小超过MTU的message将被忽略。

    Message Header的格式如下:



    (1)HANDSHAKE

    该Message用来acknowledgeSET_REPORT, SET_IDLE and SET_PROTOCOL等request。只在Control Channel上传输,只由Bluetooth HID device。

    Parameter部分定义如下:

    (2)HID_CONTROL

    控制Bluetooth HID device改变状态。

    Parameter部分定义如下:

    (3)GET_REPORT

    Bluetooth HID Host用来请求Bluetooth HID device的传输。

    定义如下:

    (4)SET_REPORT

    Bluetooth HID Host用来向Bluetooth HID device发起传输。

    格式如下:

    (5)GET_PROTOCOL

    用来获取Bluetooth HID device的Protocol Mode,然后Bluetooth HID device response一个DATA payload说明当前的Protocol Mode。

    格式如下:

    GET_PROTOCOL Data Definition格式如下:

    (6)SET_PROTOCOL

    用来设置Bluetooth HID device的Bluetooth HID device。格式如下:

    (7)DATA

    代表一个a HID payload。格式如下:

    4.Transfers

    以HID Protocol messages的形式传输。

    (1)Control Channel Transfers

    分为Acknowledged和Unacknowledged两种,格式如下:

    (2)Interrupt Channel Transfers

    Interrupt IN和Interrupt OUT两种,可以在任意时刻发送。【中断嘛】

    5.其余各层对于HID的要求

    HID与L2CAP的交互如下:

     

    展开全文
  • 蓝牙标准HID协议

    2018-08-06 13:24:15
    蓝牙标准的HID协议,英文原版。可以用于蓝牙产品开发。
  • 蓝牙hid协议源码解析

    万次阅读 2017-01-11 21:47:03
    1.1 HID协议 HID协议: Hunman Interface Device Profile人机交互设备协议 使用场景:支持人机交互设备之间的控制 市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏手柄等。 1.2 代码路径 客户端: frameworks\base\core\...

    1,概述

    1.1 HID协议

    HID协议: Hunman Interface Device Profile人机交互设备协议

    使用场景:支持人机交互设备之间的控制

    市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏手柄等。

    1.2 代码路径

    客户端: frameworks\base\core\java\android\bluetooth


    服务端: packages\apps\Bluetooth\src\com\android\bluetooth\ hid

    HidDevService.java                 hid协议的服务端

    开发流程和健康设备类似,但是稍微麻烦

    2,接口

    接口如下


    3,开发步骤

    在官方文档中有一个建立通信的流程:

    1、调用getProfileProxy(Context,BluetoothProfile.ServiceListener, int)来获取代理对象的连接。

    2、创建BluetoothHidDeviceAppSdpSettings, BluetoothHidDeviceAppQosSettings对象,创建BluetoothHidDeviceCallback回调,调用registerApp方法注册

    3、将手机与设备配对,并且进行连接。

    4、实现BluetoothHidDeviceCallback的7个回调方法

    5、调用sendReport方法分别实现蓝牙鼠标,蓝牙键盘等。

    3.1 获取客户端代理对象

    一般在oncreate方法中,直接调用getProfileProxy方法,这个没什么好说的。

    BluetoothAdapter.getDefaultAdapter().getProfileProxy(getApplicationContext(), 
    				mProfileServiceListener,BluetoothProfile. HID_DEVICE);
    
    
    private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {
    		
    		@Override
    		public void onServiceDisconnected(int profile) {
    				if (profile == BluetoothProfile.HEALTH){
    				     mBluetoothHealth = null;
    			}
    		}
    		
    		@SuppressLint("NewApi")
    		@Override
    		public void onServiceConnected(int profile, BluetoothProfile proxy) {
    			if (profile == BluetoothProfile.HEALTH) {
    				mHidDevice = (BluetoothHidDevice) proxy;
                     // 获取代理对象之后就进行注册
                     ...
    			}
    		}
    	};

    一般经过这个步骤,客户端的BluetoothHidDevice对象已经和服务端的HidDevService对象绑定了。

    3.2 注册registerApp

    BluetoothHidDeviceAppSdpSettings sdp = new BluetoothHidDeviceAppSdpSettings(
                                HidConsts.NAME, HidConsts.DESCRIPTION, HidConsts.PROVIDER,
                                BluetoothHidDevice.SUBCLASS1_COMBO, HidConsts.DESCRIPTOR);
    BluetoothHidDeviceAppQosSettings inQos = new BluetoothHidDeviceAppQosSettings(
                            BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 200, 2, 200,
                            10000 /* 10 ms */, 10000 /* 10 ms */);
    BluetoothHidDeviceAppQosSettings outQos = new BluetoothHidDeviceAppQosSettings(
                            BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 900, 9, 900,
                            10000 /* 10 ms */, 10000 /* 10 ms */);
    boolean result = mHidDevice.registerApp(sdp, inQos, outQos, mCallback);

    HidConsts类的定义如下:

    public class HidConsts {
    
        public final static String NAME = "HID Device Testapp";
    
        public final static String DESCRIPTION = "";
    
        public final static String PROVIDER = "Codeaurora";
    
        /* @formatter:off */
        public final static byte[] DESCRIPTOR = {
            (byte) 0x05, (byte) 0x01,                    // USAGE_PAGE (Generic Desktop)
            (byte) 0x09, (byte) 0x02,                    // USAGE (Mouse)
            (byte) 0xa1, (byte) 0x01,                    // COLLECTION (Application)
            (byte) 0x09, (byte) 0x01,                    //   USAGE (Pointer)
            (byte) 0xa1, (byte) 0x00,                    //   COLLECTION (Physical)
            (byte) 0x85, (byte) 0x02,                    //     REPORT_ID (2)
            (byte) 0x05, (byte) 0x09,                    //     USAGE_PAGE (Button)
            (byte) 0x19, (byte) 0x01,                    //     USAGE_MINIMUM (Button 1)
            (byte) 0x29, (byte) 0x03,                    //     USAGE_MAXIMUM (Button 3)
            (byte) 0x15, (byte) 0x00,                    //     LOGICAL_MINIMUM (0)
            (byte) 0x25, (byte) 0x01,                    //     LOGICAL_MAXIMUM (1)
            (byte) 0x95, (byte) 0x03,                    //     REPORT_COUNT (3)
            (byte) 0x75, (byte) 0x01,                    //     REPORT_SIZE (1)
            (byte) 0x81, (byte) 0x02,                    //     INPUT (Data,Var,Abs)
            (byte) 0x95, (byte) 0x01,                    //     REPORT_COUNT (1)
            (byte) 0x75, (byte) 0x05,                    //     REPORT_SIZE (5)
            (byte) 0x81, (byte) 0x03,                    //     INPUT (Cnst,Var,Abs)
            (byte) 0x05, (byte) 0x01,                    //     USAGE_PAGE (Generic Desktop)
            (byte) 0x09, (byte) 0x30,                    //     USAGE (X)
            (byte) 0x09, (byte) 0x31,                    //     USAGE (Y)
            (byte) 0x15, (byte) 0x81,                    //     LOGICAL_MINIMUM (-127)
            (byte) 0x25, (byte) 0x7f,                    //     LOGICAL_MAXIMUM (127)
            (byte) 0x75, (byte) 0x08,                    //     REPORT_SIZE (8)
            (byte) 0x95, (byte) 0x02,                    //     REPORT_COUNT (2)
            (byte) 0x81, (byte) 0x06,                    //     INPUT (Data,Var,Rel)
            (byte) 0x09, (byte) 0x38,                    //     USAGE (Wheel)
            (byte) 0x15, (byte) 0x81,                    //     LOGICAL_MINIMUM (-127)
            (byte) 0x25, (byte) 0x7f,                    //     LOGICAL_MAXIMUM (127)
            (byte) 0x75, (byte) 0x08,                    //     REPORT_SIZE (8)
            (byte) 0x95, (byte) 0x01,                    //     REPORT_COUNT (1)
            (byte) 0x81, (byte) 0x06,                    //     INPUT (Data,Var,Rel)
            (byte) 0xc0,                                 //   END_COLLECTION
            (byte) 0xc0,                                 // END_COLLECTION
    
            // battery strength
            (byte) 0x05, (byte) 0x0c,
            (byte) 0x09, (byte) 0x01,
            (byte) 0xa1, (byte) 0x01,
            (byte) 0x85, (byte) 0x20,                    //   REPORT_ID (32)
            (byte) 0x05, (byte) 0x01,
            (byte) 0x09, (byte) 0x06,
            (byte) 0xa1, (byte) 0x02,
            (byte) 0x05, (byte) 0x06,                    // USAGE_PAGE (Generic Device Controls)
            (byte) 0x09, (byte) 0x20,                    // USAGE (Battery Strength)
            (byte) 0x15, (byte) 0x00,                    // LOGICAL_MINIMUM (0)
            (byte) 0x26, (byte) 0xff, (byte) 0x00,      // LOGICAL_MAXIMUM (100)
            (byte) 0x75, (byte) 0x08,                    // REPORT_SIZE (8)
            (byte) 0x95, (byte) 0x01,                    // REPORT_COUNT (1)
            (byte) 0x81, (byte) 0x02,                    // INPUT (Data,Var,Abs)
            (byte) 0xc0,
            (byte) 0xc0,
    
            (byte) 0x05, (byte) 0x01,                    // USAGE_PAGE (Generic Desktop)
            (byte) 0x09, (byte) 0x06,                    // USAGE (Keyboard)
            (byte) 0xa1, (byte) 0x01,                    // COLLECTION (Application)
            (byte) 0x85, (byte) 0x01,                    //   REPORT_ID (1)
            (byte) 0x05, (byte) 0x07,                    //   USAGE_PAGE (Keyboard)
            (byte) 0x19, (byte) 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
            (byte) 0x29, (byte) 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
            (byte) 0x15, (byte) 0x00,                    //   LOGICAL_MINIMUM (0)
            (byte) 0x25, (byte) 0x01,                    //   LOGICAL_MAXIMUM (1)
            (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
            (byte) 0x95, (byte) 0x08,                    //   REPORT_COUNT (8)
            (byte) 0x81, (byte) 0x02,                    //   INPUT (Data,Var,Abs)
            (byte) 0x05, (byte) 0x0c,                    //   USAGE_PAGE (Consumer Devices)
            (byte) 0x15, (byte) 0x00,                    //   LOGICAL_MINIMUM (0)
            (byte) 0x25, (byte) 0x01,                    //   LOGICAL_MAXIMUM (1)
            (byte) 0x95, (byte) 0x07,                    //   REPORT_COUNT (7)
            (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
            (byte) 0x09, (byte) 0xb6,                    //   USAGE (Scan Previous Track)
            (byte) 0x09, (byte) 0xb5,                    //   USAGE (Scan Next Track)
            (byte) 0x09, (byte) 0xb7,                    //   USAGE (Stop)
            (byte) 0x09, (byte) 0xcd,                    //   USAGE (Play/Pause)
            (byte) 0x09, (byte) 0xe2,                    //   USAGE (Mute)
            (byte) 0x09, (byte) 0xe9,                    //   USAGE (Volume Up)
            (byte) 0x09, (byte) 0xea,                    //   USAGE (Volume Down)
            (byte) 0x81, (byte) 0x02,                    //   INPUT (Data,Var,Abs)
            (byte) 0x95, (byte) 0x01,                    //   REPORT_COUNT (1)
            (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
            (byte) 0x81, (byte) 0x03,                    //   INPUT (Constant,Var,Abs)
            (byte) 0x05, (byte) 0x07,                    //   USAGE_PAGE (Keyboard)
            (byte) 0x95, (byte) 0x05,                    //   REPORT_COUNT (5)
            (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
            (byte) 0x85, (byte) 0x01,                    //   REPORT_ID (1)
            (byte) 0x05, (byte) 0x08,                    //   USAGE_PAGE (LEDs)
            (byte) 0x19, (byte) 0x01,                    //   USAGE_MINIMUM (Num Lock)
            (byte) 0x29, (byte) 0x05,                    //   USAGE_MAXIMUM (Kana)
            (byte) 0x91, (byte) 0x02,                    //   OUTPUT (Data,Var,Abs)
            (byte) 0x95, (byte) 0x01,                    //   REPORT_COUNT (1)
            (byte) 0x75, (byte) 0x03,                    //   REPORT_SIZE (3)
            (byte) 0x91, (byte) 0x03,                    //   OUTPUT (Cnst,Var,Abs)
            (byte) 0x95, (byte) 0x06,                    //   REPORT_COUNT (6)
            (byte) 0x75, (byte) 0x08,                    //   REPORT_SIZE (8)

    连接很简单,直接调用connect方法就可以了。

    public void connect() {
            if (mHidDevice == null) return;
            mHidDevice.connect();
    }

    3.4 BluetoothHidDeviceCallback

    BluetoothHidDeviceCallback这个抽象类有7个回调方法,

    private byte[] mBuffer = new byte[1];
    private final BluetoothHidDeviceCallback mCallback = new BluetoothHidDeviceCallback() {
            @Override
      public void onAppStatusChanged(BluetoothDevice pluggedDevice,
                        BluetoothHidDeviceAppConfiguration config, boolean registered) {
               // 一般在registerApp和unregisterApp方法之后回调    
              // registered 表示是否注册上      
            }
    
            @Override
            public void onConnectionStateChanged(BluetoothDevice device, int state) {
                // device 远程蓝牙设备  state连接状态
              mBuffer = (byte) 63
               mHidDevice.sendReport(32, mBuffer);  // 不知道为啥子这样写?
            }
              // 其他5个方法就可以不管了。
            @Override
            public void onIntrData(byte reportId, byte[] data) {
              Log.v(TAG, "intr data: reportId=" + reportId + " data=" + Arrays.toString(data));
            }
    
            @Override
            public void onSetProtocol(byte protocol) {
                Log.d(TAG, "protocol set to " + protocol);
            }
            @Override
            public void onVirtualCableUnplug() {
                
            }
            @Override
            public void onGetReport(byte type, byte id, int bufferSize) {
                
            }
            @Override
            public void onSetReport(byte type, byte id, byte[] data) {
             
            }
        };

    BluetoothHidDeviceCallback7个方法都是C/C++等通过JNI机制的回调。

    4,蓝牙鼠标

    4.1 蓝牙鼠标滑动

    实现鼠标在整个界面前后左右上下滑动

    mTouchpad = view.findViewById(R.id.touchpad);
            mTouchpad.setOnTouchListener(new OnTouchListener() {
                private int mPrevX; 
                private int mPrevY; 
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
    
                        case MotionEvent.ACTION_DOWN:
                            mPrevX = (int) (event.getX() * mSpeed);
                            mPrevY = (int) (event.getY() * mSpeed);
                            break;
    
                        case MotionEvent.ACTION_MOVE:
                            int x = (int) (event.getX() * mSpeed);
                            int y = (int) (event.getY() * mSpeed);
    
                            mouseMove((byte) (x – mPrevX), (byte) (y - mPrevY)); 
                            mPrevX = x;
                            mPrevY = y;
                            break;
                    }
                    return true;
                }
            });


    private int mSpeed = 3;

    mSpeed的值控制鼠标移动的速度。

    private byte[] mBuffer = new byte[4];
    byte id = 2;

    public synchronized void move(byte dx, byte dy) {
                // leave buttons state unchanged
                mBuffer[1] = dx;
                mBuffer[2] = dy;
                
                mHidDevice.sendReport(id, mBuffer);
            }

    4.2 蓝牙鼠标点击

    将鼠标滑动到目标后,点击按钮可以选中目标。

    button.setOnTouchListener(new OnTouchListener() {
    
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            int which = 0;
                            switch (event.getAction()) {
                                case MotionEvent.ACTION_DOWN:
                                   mouseButtonDown(which);
                                    break;
    
                                case MotionEvent.ACTION_UP:
                                    mouseButtonUp(which);
                                    break;
                            }
    
                            return false;
                        }
    
                    });

    Which的值有三种,分别是0,1,2 之间好像没什么差别。

    public synchronized void buttonDown(int which) {
                mBuffer[0] |= (1 << which);
                mBuffer[1] = 0;
                mBuffer[2] = 0;
    
                mHidDevice.sendReport(id, mBuffer);
            }
    
      public synchronized void buttonUp(int which) {
                mBuffer[0] &= ~(1 << which);
                mBuffer[1] = 0;
                mBuffer[2] = 0; 
     
                mHidDevice.sendReport(id, mBuffer);
            }

    4.3 蓝牙鼠标翻页

    比如在手机中,有时候界面不止一页,这样就需要翻页来显示了。

    private int mScrollSpeed = 3;   // 控制翻页的速度
    
    
    mScrollZone.setOnTouchListener(new OnTouchListener() {
    
                private int mPrevY;
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            mPrevY = (int) (event.getY() * mScrollSpeed);
                            break;
    
                        case MotionEvent.ACTION_MOVE:
                            int y = (int) (event.getY() * mScrollSpeed);
    
                            mouseScroll((byte) (mPrevY - y));
    
                            mPrevY = y;
                            break;
                    }
    
                    return true;
                }
            });
    
    
    public synchronized void scroll(byte delta) {
                mBuffer[3] = delta;
                mHidDevice.sendReport(id, mBuffer);
    
                mBuffer[3] = 0x00;
            }

    5,蓝牙键盘

    市面上的键盘类型较多,实现了3种蓝牙键盘,

                                               标准键盘

    lable以及对应keyCode如下:

    <Keyboard>
        <Layout>
            <Key keyLabel="Esc" keyCode="0x29"/>
            <Key keyLabel="F1" keyCode="0x3A"/>
            <Key keyLabel="F2" keyCode="0x3B"/>
            <Key keyLabel="F3" keyCode="0x3C"/>
            <Key keyLabel="F4" keyCode="0x3D"/>
            <Key keyLabel="F5" keyCode="0x3E"/>
            <Key keyLabel="F6" keyCode="0x3F"/>
            <Key keyLabel="F7" keyCode="0x40"/>
            <Key keyLabel="F8" keyCode="0x41"/>
            <Key keyLabel="F9" keyCode="0x42"/>
            <Key keyLabel="F10" keyCode="0x43"/>
            <Key keyLabel="F11" keyCode="0x44"/>
            <Key keyLabel="F12" keyCode="0x45"/>
            <Key keyLabel="Del" keyCode="0x4C"/>
        </Layout>
        <Layout>
            <Key keyLabel="'" shiftLabel="~" keyCode="0x35"/>
            <Key keyLabel="1" shiftLabel="!" keyCode="0x1E"/>
            <Key keyLabel="2" shiftLabel="\@" keyCode="0x1F"/>
            <Key keyLabel="3" shiftLabel="\#" keyCode="0x20"/>
            <Key keyLabel="4" shiftLabel="$" keyCode="0x21"/>
            <Key keyLabel="5" shiftLabel="%" keyCode="0x22"/>
            <Key keyLabel="6" shiftLabel="^" keyCode="0x23"/>
            <Key keyLabel="7" shiftLabel="&" keyCode="0x24"/>
            <Key keyLabel="8" shiftLabel="*" keyCode="0x25"/>
            <Key keyLabel="9" shiftLabel="(" keyCode="0x26"/>
            <Key keyLabel="0" shiftLabel=")" keyCode="0x27"/>
            <Key keyLabel="-" shiftLabel="_" keyCode="0x2D"/>
            <Key keyLabel="=" shiftLabel="+" keyCode="0x2E"/>
            <Key keyLabel="Backspace ←" keyCode="0x2A" weight="1.5"/>
        </Layout>
        <Layout>
            <Key keyLabel="Tab ↹" keyCode="0x2B" weight="1.5"/>
            <Key keyLabel="Q" keyCode="0x14"/>
            <Key keyLabel="W" keyCode="0x1A"/>
            <Key keyLabel="E" keyCode="0x08"/>
            <Key keyLabel="R" keyCode="0x15"/>
            <Key keyLabel="T" keyCode="0x17"/>
            <Key keyLabel="Y" keyCode="0x1C"/>
            <Key keyLabel="U" keyCode="0x18"/>
            <Key keyLabel="I" keyCode="0x0C"/>
            <Key keyLabel="O" keyCode="0x12"/>
            <Key keyLabel="P" keyCode="0x13"/>
            <Key keyLabel="[" keyCode="0x2F" shiftLabel="{"/>
            <Key keyLabel="]" keyCode="0x30" shiftLabel="}"/>
            <Key keyLabel="\\" keyCode="0x31" shiftLabel="|"/>
        </Layout>
        <Layout>
            <Key keyLabel="Caps Lock" keyCode="0x39" weight="1.5"/>
            <Key keyLabel="A" keyCode="0x04"/>
            <Key keyLabel="S" keyCode="0x16"/>
            <Key keyLabel="D" keyCode="0x07"/>
            <Key keyLabel="F" keyCode="0x09"/>
            <Key keyLabel="G" keyCode="0x0A"/>
            <Key keyLabel="H" keyCode="0x0B"/>
            <Key keyLabel="J" keyCode="0x0D"/>
            <Key keyLabel="K" keyCode="0x0E"/>
            <Key keyLabel="L" keyCode="0x0F"/>
            <Key keyLabel=";" keyCode="0x33" shiftLabel=":"/>
            <Key keyLabel="'" keyCode="0x34" shiftLabel="""/>
            <Key keyLabel="Enter ↵" keyCode="0x28" weight="3.0"/>
        </Layout>
        <Layout>
            <Key keyLabel="Shift ⇧" keyCode="0xE1" keyFunc="Shift" weight="1.5"/>
            <Key keyLabel="Z" keyCode="0x1D"/>
            <Key keyLabel="X" keyCode="0x1B"/>
            <Key keyLabel="C" keyCode="0x06"/>
            <Key keyLabel="V" keyCode="0x19"/>
            <Key keyLabel="B" keyCode="0x05"/>
            <Key keyLabel="N" keyCode="0x11"/>
            <Key keyLabel="M" keyCode="0x10"/>
            <Key keyLabel="," keyCode="0x36" shiftLabel="<"/>
            <Key keyLabel="." keyCode="0x37" shiftLabel=">"/>
            <Key keyLabel="/" keyCode="0x38" shiftLabel="\?"/>
            <Key keyLabel="Shift ⇧" keyCode="0xE5" keyFunc="Shift" weight="1.5"/>
        </Layout>
        <Layout>
            <Key keyLabel="Ctrl" keyCode="0xE0" weight="1.5"/>
            <Key keyLabel="Win" keyCode="0xE3"/>
            <Key keyLabel="Alt" keyCode="0xE2"/>
            <Key keyLabel=" " keyCode="0x2C" weight="10.0"/>
            <Key keyLabel="Alt Gr" keyCode="0xE6"/>
            <Key keyLabel="Win" keyCode="0xE7"/>
            <Key keyLabel="Menu" keyCode="0x76"/>
            <Key keyLabel="Ctrl" keyCode="0xE4" weight="1.5"/>
        </Layout>
    </Keyboard>

                                        左边是导航键盘  右边是数字键盘

    导航键盘的lable以及对应keyCode如下:

    <Keyboard>
        <Layout>
            <Key keyLabel="Vol Down ⇩" keyCode="0x86"/>
            <Key keyLabel="Mute" keyCode="0x84"/>
            <Key keyLabel="Vol Up ⇧" keyCode="0x85"/>
        </Layout>
        <Layout>
            <Key keyLabel="Print Screen" keyCode="0x46"/>
            <Key keyLabel="Scroll Lock" keyCode="0x47"/>
            <Key keyLabel="Pause" keyCode="0x48"/>
        </Layout>
        <Layout>
            <Key keyLabel="Insert" keyCode="0x49"/>
            <Key keyLabel="Home" keyCode="0x4A"/>
            <Key keyLabel="Page Up" keyCode="0x4B"/>
        </Layout>
        <Layout>
            <Key keyLabel="Delete" keyCode="0x4C"/>
            <Key keyLabel="End" keyCode="0x4D"/>
            <Key keyLabel="Page Down" keyCode="0x4E"/>
        </Layout>
        <Layout>
            <Key visible="false"/>
            <Key keyLabel="↑" keyCode="0x52"/>
            <Key visible="false"/>
        </Layout>
        <Layout>
            <Key keyLabel="←" keyCode="0x50"/>
            <Key keyLabel="↓" keyCode="0x51"/>
            <Key keyLabel="→" keyCode="0x4F"/>
        </Layout>
    </Keyboard>

    数字键盘的lable以及对应keyCode如下:

    <Keyboard>
        <Layout>
            <Key keyLabel="Prev\n⇦" keyCode="0x80"/>
            <Key keyLabel="Next\n⇨" keyCode="0x81"/>
            <Key keyLabel="Stop" keyCode="0x82"/>
            <Key keyLabel="Play\nPause" keyCode="0x83"/>
            <Key visible="false"/>
        </Layout>
        <Layout>
            <Key keyLabel="Num\nLock" keyCode="0x53"/>
            <Key keyLabel="/" keyCode="0x54"/>
            <Key keyLabel="*" keyCode="0x55"/>
            <Key keyLabel="-" keyCode="0x56"/>
        </Layout>
        <Layout>
            <Key keyLabel="7\nHome" keyCode="0x5F"/>
            <Key keyLabel="8 ↑" keyCode="0x60"/>
            <Key keyLabel="9\nPgUp" keyCode="0x61"/>
            <Key keyLabel="+" keyCode="0x57"/>
        </Layout>
        <Layout>
            <Key keyLabel="4\n←" keyCode="0x5C"/>
            <Key keyLabel="5" keyCode="0x5D"/>
            <Key keyLabel="6\n→" keyCode="0x5E"/>
            <Key visible="false"/>
        </Layout>
        <Layout>
            <Key keyLabel="1\nEnd" keyCode="0x59"/>
            <Key keyLabel="2 ↓" keyCode="0x5A"/>
            <Key keyLabel="3\nPgDn" keyCode="0x5B"/>
            <Key visible="false"/>
        </Layout>
        <Layout>
            <Key keyLabel="0\nIns" keyCode="0x62" weight="2.0"/>
            <Key keyLabel=".\nDel" keyCode="0x63"/>
            <Key keyLabel="Enter" keyCode="0x58"/>
        </Layout>
    </Keyboard>

    按键事件都是一样的,主要就是keyCode不同,

    @Override
                public void onKeyUp(byte keyCode) {
                    keyboardKeyUp(keyCode);
                }
    
                @Override
                public void onKeyDown(byte keyCode) {
                    keyboardKeyDown(keyCode);
                }
    
    private final static byte MODIFIER_BASE = (byte) 0xe0;
            private final static byte MODIFIER_COUNT = 8; /* left ctrl -> right gui */
            private byte[] mBuffer = new byte[8];
            byte id = 1;
    public synchronized void keyDown(byte key) {
                if (key >= MODIFIER_BASE && key <= MODIFIER_BASE + MODIFIER_COUNT) {
                    mBuffer[0] |= (1 << (key - MODIFIER_BASE));
                } else if ((key & 0x80) != 0) {
                    mBuffer[1] |= (1 << (key & 0x07));
                } else {
                    for (int i = 2; i < 8; i++) {
                        if (mBuffer[i] == 0x00) {
                            mBuffer[i] = key;
                            break;
                        }
                    }
                }
                mHidDevice.sendReport(id, mBuffer);
            }
    
            public synchronized void keyUp(byte key) {
                if (key >= MODIFIER_BASE && key <= MODIFIER_BASE + MODIFIER_COUNT) {
                    mBuffer[0] &= ~(1 << (key - MODIFIER_BASE));
                } else if ((key & 0x80) != 0) {
                    mBuffer[1] &= ~(1 << (key & 0x07));
                } else {
                    for (int i = 2; i < 8; i++) {
                        if (mBuffer[i] == key) {
                            mBuffer[i] = 0x00;
                            break;
                        }
                    }
                }
                mHidDevice.sendReport(id, mBuffer);
            }

    这样就可以了,很简单吧。

    6,源码解析

    客户端的BluetoothHidDevice和服务端的HidDevService都比较简单,很直接,完全没有拐弯抹角的地方, sendReport方法从客户端到服务端的过程如下,

    回调的7个方法流程如下,以onAppStatusChanged回调为例,


    7,小节

    问题:1,回调的几个方法以及2个类还未弄清楚其作用。

    2,客户端的BluetoothInputDevice以及客户端的HidService和HID协议的文件在一个文件夹下,那么这2个类的作用是什么呢?如何使用。

    展开全文
  • HID协议官方文档,蓝牙5.0协议,存点C币下东西用,见谅。
  • 蓝牙HID协议介绍

    万次阅读 2015-12-18 10:26:28
     The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。   2.一些概念 (1)HID ...
  • android-蓝牙A2dp-avrcp-hfp-opp-...HID:human interface device :基于经典蓝牙实现的。 HOGP:human over gatt prifile :基于ble gatt协议实现的。 HID/HOGP设备,即人机交互设备,常见的有鼠标,键盘,游戏......
  • 没有接触过蓝牙开发,最近需要开发蓝牙相关的项目,需要用到HID实现蓝牙解锁,求指点
  • The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。该协议改编自USB HID Protocol。 13.1.1 HID角色 A Bluetooth HID deviceis a device providing the service of human or ...
  • BLE HID协议规范是以USB HID协议规范为蓝本的,可以认为是USB HID的无线方式。那么作为人机交互设备(HID),我们常见产品有:蓝牙鼠标,蓝牙键盘,蓝牙游戏手柄,蓝牙自拍杆等。另外有一点就是,对于Android系统的...
  • STM32 USB-HID 协议源码

    2016-03-10 18:21:34
    STM32 USB-HID 协议源代码 上位机和下位机
  • BLE HID协议-----蓝牙鼠标代码流分析

    千次阅读 2021-06-08 05:34:38
    隐藏了蓝牙写在前面: 使用SDK版本NRF 12.3.0,nRF52 \ nRF52832包\ nRF5_SDK_12.3.0_d7731ad \ examples \ ble_peripheral \ ble_app_hids_mouse,本文仅用于代码分析,如果没有足够的理解,欢迎交流,QQ: 993650814...
  • 蓝牙HID——将android设备变成蓝牙键盘(BluetoothHidDevice) 可先了解上篇文章的内容再来浏览此篇。首先需要建立HID键盘连接,参考上篇。 其次要进行密码设置并保存,这里设置了3种类型的密码:4位数字、6位数字、...
  • Android 蓝牙 BLE 连接(connect)数据流程图(协议栈),非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据...
  • 接到这个任务,我还是挺有把握的,因为在研究HID鼠标协议的时候听说有绝对位置的鼠标这个么东西,心想只要实现绝对定位的鼠标不就行了,于是当时就答应了那哥们,说没问题。经过一段时间的折腾后,我搞出了符合绝对...
  • 蓝牙HID协议笔记 转自:http://blog.sina.com.cn/s/blog_69b5d2a50101emll.html 1.概述 The Human Interface Device (HID)定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘...
  • iOS HID蓝牙协议

    千次阅读 2021-12-17 14:12:37
    蓝牙HID协议笔记【转】- 阿里云开发社区 资料蓝牙开发 - 51博客
  • HID协议有很多应用,比如蓝牙鼠标,键盘,手柄,自拍杆等都会用到HID协议!   一. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念...
  • BM4342 模块是一款符合蓝牙标准的蓝牙产品,单模双协议, 基于BK方案开发的.目前支持的协议包括 BR/SPP/HID, 支持 UART 通信( 流控功能可定制),主要应用于IOT、无线扫描枪、无线测量监控、医疗、蓝牙POS机.
  • bluetooth hid协议

    2011-08-02 10:25:21
    开发蓝牙HID设备的必备资料啊,吐血奉献

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,994
精华内容 797
关键字:

蓝牙hid协议

友情链接: modbus调试精灵.zip