【低功耗蓝牙】⑤ 蓝牙HID协议

2023-10-31

摘要

本文章主要介绍了蓝牙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协议

作者:我是鹏老师

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【低功耗蓝牙】⑤ 蓝牙HID协议 的相关文章

  • 安卓蓝牙广播大全

    android bluetooth 提供了皆如扫描设备 连接设备以及对设备间的数据传输进行管理的类 这些类对蓝牙设备进行功能性管理 蓝牙模块API提供的应用包括了 扫描其它蓝牙设备 通过查询本地蓝牙适配器来匹配蓝牙设备 建立RFCOMM 无
  • ESP32/ESP8266使用MicroPython控制DHT11/DHT22

    本教程介绍了如何使用MicroPython固件将DHT11或DHT22温度和湿度传感器与ESP32和ESP8266开发板一起使用 DHT模块 刷新MicroPython固件 要遵循本教程 您需要在ESP32或ESP8266板上安装Micro
  • nRF52832学习记录(一、外设初识之 GPIOTE)

    添加GPIO和GPIOTE寄存器表 对于应用的理解对着寄存器查看会比较明了 这个不管是在哪款芯片上都是如此 2021 9 27 这些年蓝牙5 0的应用越来越多 最近也是想着把以前Enocean的低功耗设备有过的产品 用蓝牙做一套匹配的版本
  • ESP32引脚参考

    原文链接 ESP32引脚参考 您应该使用哪个GPIO引脚 360doc个人图书馆 总结的相当全面 ESP32简单易懂的GPIO使用注意事项 首先上图 GPIO建议列表 特别的在硬件上要注意使用外接模块时不能将GPIO12拉高 否则将导致ES
  • ESP32连接阿里云MQTT

    ESP32连接阿里云的github链接 ESP32官网文档 可下载开发文档 文章目录 一 ESP32介绍 二 搭建ESP32开发环境 一 调出终端 二 代码补全 三 ESP32接入阿里云 一 编译项目 二 配置项目 三 烧录程序 四 配置四
  • 基于STM8的ADC0832采集及蓝牙通信系统

    为了方便大家学习 工程已经打包上传 http download csdn net detail devintt 8029389 最近在淘宝逛的时候发现了一款单片机 STM8 相比之前一直使用的也是8位的AVR相比 感觉STM8更为强大 芯片
  • 树莓派Pico入手

    树莓派Pico入手 树莓派 Pico 中文站 一 介绍 1 1 价格 开发板 19 00 1 2 型号 目前主要有两个型号 Pico 基础版 Pico W 板载WIFI芯片 H的是指给你焊好排针 1 3 性能 核心 双核 Arm Corte
  • 蓝牙之十七-bluedroid scan流程

    蓝牙扫描过程是指扫描蓝牙设备 app层 这里有两张截图 第一张图显示的是安卓设置setting菜单栏中有Bluetooth这一项 点进去以后 点击右上角显示如下的截图 其中Refresh就是刷新设备列表 也就会扫描设备信息 上图显示的三个菜
  • pulseaudio使用过程中遇到的问题

    W pulseaudio main c This program is not intended to be run as root unless system is specified E pulseaudio core util c H
  • ESP32-C3 应用程序的启动流程

    网上说明一大堆 基本是官网文档复制没有额外解释 对于ESP32 C3的 risc v 内核 是我选择他的原因之一 了解芯片上电后的启动流程 有利于我们更加深入理解芯片 目录 前言 一 应用程序启动阶段 1 1 app main c 1 2
  • ESP32(Micro Python) LVGL 传感器数值显示

    本程序用于显示SR04超声波传感器和BMP280气压温度传感器的读数 由于高度数值类型不符合要求 BMP280改为显示气压和温度值 气压值分两部分显示 分别为千帕值 100 避免超出表盘显示范围 和千帕值的两位小数 由于标签不能显示动态数值
  • 使用Arduino开发ESP32(22):蓝牙作为客户端使用

    文章目录 目的 基础准备 搜索蓝牙设备 搜索设备 信息查询 连接与交互 总结 目的 ESP32的蓝牙除了作为服务器 从设备 使用还可以作为客户端 主机 使用 这篇文章将对相关内容做个简单说明 基础准备 这篇文章中测试需要先准备一个蓝牙服务器
  • 蓝牙协议规范--L2CAP

    L2CAP 分析 记住一点 软件和硬件分开理解 数据经由物理通道交互 上层通道由各层协议打通 L2CAP 全称为逻辑链路控制与适配协议 Logical Link Control and Adaptation Protocol 位于基带层之上
  • 蓝牙之十三-HFPclient JNI层

    JNI到app JAVA
  • SimpleFOC无刷电机平衡小车

    前言 本科毕业于自动化 期间学习了各种电机运动控制原理 自动控制原理 但是只会考试而未究其理 最近接触到simplefoc这个基于arduino的开源无刷电机驱动库 想正好借此机会将本科学到的内容用于实际 于是就有了这个小项目 基于无刷电机
  • ESP-IDF库开发教程之(4)--FLASH、BootLoader和FreeRTOS

    在之前的文章中 我描述了新的esp32芯片 并解释了如何设置开发环境 包括使用一种可选的图形IDE Eclipse 在开始开发程序之前 您需要了解框架的三个关键组件 flash bootloader和FreeRTOS操作系统 但别担心 在本
  • 如何在树莓派 pico 自动运行上编写 micropython 程序?

    我使用 Thonny 软件将程序发送到我的树莓派皮克 我正在尝试在插入 pico 时自动运行特定程序 目前 pico 上的另一个程序会自动运行 但我希望运行另一个程序 将要运行的程序命名为 main py
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • VSCode 上的 PlatformIO 未编译:collect2.exe:错误:ld 返回 1 退出状态

    我最近不得不擦拭我的计算机 在一切准备就绪并运行之后 是时候打开我之前正在处理的一些 ESP32 程序了 发现 VSCode 上的 Platform IO 将不再编译 运行编译器后 我收到以下错误 Compiling pio build e
  • 使用什么 ffmpeg 命令将无符号整数列表转换为音频文件?

    我有一个文件 其中包含大约四万个整数的列表 这些整数以空格分隔 每个整数的值都在 0 到 255 之间 这里是这个文件 https github com johnlai2004 sound project blob master integ

随机推荐

  • 修复Yum依赖冲突

    警告 RPM 数据库已被非 yum 程序修改 发现 个已存在的 RPM 数据库问题 yum check 输出如下 列出重复的包 package cleanup dupes 移除旧的重复包 package cleanup cleandupes
  • Python Import 详解

    import 绝对是我们在使用python时最常用的语句之一了 但其实关于import 需要注意的地方还真不少 如导入第三方库 导入自己写的库 导入相对路径下文件中的方法 在包内部的相对与绝对导入等导入源 有导入的顺序 有Lazy Load
  • 经验:如何快速地写出格雷码

    经验 如何快速地写出格雷码 更新历史 201901212 首次发布 格雷码 Binary Gray Code 的特点是 相邻两个码之间 只相差了一个比特 由于这个特性 格雷码在数字电路中使用甚广 不过 令人尴尬的是 格雷码似乎不好记 以4比
  • C语言数组和指针笔试题(二)(一定要看)

    目录 字符数组二 例题1 例题2 例题3 例题4 例题5 例题6 例题7 总结 字符数组三 例题1 例题2 例题3 例题4 例题5 例题6 例题7 字符数组二 char arr a b c d e f 1 printf d n strlen
  • 黑马程序员MySQL学习笔记一(超详细版)-MySQL基础篇:MySQL概述、SQL、函数、约束、多表查询、事务

    名称 黑马程序员MySQL数据库入门到精通 从MYSQL安装到MYSQL高级 MYSQL优化全囊括 来源 B站黑马程序员 链接 https www bilibili com video BV1Kr4y1i7ru p 37 share sou
  • 区块链之java(六.1) 合约监听

    之前写的那一篇呢 好像有点点问题 就是在设定监听的时候 没有编写具体监听的方法 今天带来一篇新的方式的合约监听 前面的就不赘述了 合约上的监听是一样的 在java中 根据abi生产的文件 其实在里面是有监听方法的 代码如下 public F
  • 服务器如何生成文件夹,服务器如何生成数据库文件夹

    服务器如何生成数据库文件夹 内容精选 换一换 将GaussDB for openGauss 提供的ODBC DRIVER psqlodbcw so 配置到数据源中便可使用 配置数据源需要配置 odbc ini 和 odbcinst ini
  • 二阶振荡环节的谐振频率_自动控制系统时域分析十三:对数频率特性

    一 对数频率特性曲线 波德图 Bode图 Bode图由对数幅频特性和对数相频特性两条曲线组成 波德图坐标 横坐标是频率 纵坐标是幅值和相角 的分度 1 横坐标分度 称为频率轴 它是以频率w的对数值logw进行线性分度的 但为了便于观察仍标以
  • 图书管理系统C语言

    C语言简单编写图书管理系统 两种方法 链表 线性表 主要内容 开发一个图书管理系统 基本信息包括图书的书名 作者 ISBN号 基本实现 输出 输入 删除 查询 插入的基本功能 代码如下 运行结果省略 链表 include
  • 《CTFshow-Web入门》03. Web 21~30

    Web 21 30 索引 web21 题解 原理 web22 题解 原理 web23 题解 原理 web24 题解 原理 web25 题解 原理 web26 题解 web27 题解 web28 题解 web29 题解 原理 web30 题解
  • 【C#基础】C# 文件与IO

    序号 系列文章 9 C 基础 C 异常处理操作 10 C 基础 C 正则表达式 11 C 基础 C 预处理器指令 文章目录 前言 1 文件和IO的概念 2 文本文件操作 2 1 File 类 2 2 FileInfo 类 2 3 FileS
  • 不要消费信任

    消费是一种用来满足人们各种需求的过程 人们可以通过消费来满足吃 穿 住 行等物质上的需求 同样的 人们也可以通过消费来满足非物质需求 如 消费人情 权力等 当今世界 消费 是一种需要 而会消费 是一种艺术 但无论怎样 有一种 物品 是绝对经
  • xctf攻防世界 MISC高手进阶区 low

    1 进入环境 下载附件 给出的一张bmp图片 没有其他信息 2 问题分析 扔进binwalk中 没有发现有用信息 使用zsteg 没有有用信息 塞入StegSolve中 发现有点猫腻 如图 有点二维码的感觉 emmm 需要像素处理 但不知道
  • css 获取当前类的子类,删除子类列表项的CSS类属性

    我一直试图在Html中创建一个无序列表 css类将附加 ul 元素及其子元素 li 元素 问题是如果另一个 无序列表 成为这个父无序列表的子元素 删除子类列表项的CSS类属性 我创建了下面的示例 以显示我的问题 的Javascript fu
  • 超详解“二分法查找”,一看就会!

    目录 一 二分法概念用途 二 超详思维图解 三 超详使用方法实现代码运行操作 四 总结 五 结语 一 二分法概念用途 什么是二分法 有什么作用 一般用在何处 概念 二分查找法算法 也叫折半查找算法 对半处理会提高寻找目标数字的效率 作用 在
  • 计算机视觉——图像增强

    图像增强的定义非常广泛 一般来说 图像增强是有目的地强调图像的整体或局部特性 例如改善图像的颜色 亮度和对比度等 将原来不清晰的图像变得清晰或强调某些感兴趣的特征 扩大图像中不同物体特征之间的差别 抑制不感兴趣的特征 提高图像的视觉效果 传
  • “范式杯”2023牛客暑期多校训练营10

    K First Last 一共有n个人 然后每一局拿到第一名或者最后一名的概率为2 n 然后连续m局拿到第一名或者最后一名的概率为pow 2 n m 特判一下当n为1时 拿第一名或者最后一名的概率为1 注意 输出1时要写1 0 写1的话就w
  • Jinja 2模板引擎

    如何使用Flask渲染摸板 在模板中传递一个或多个参数 if 语句在摸板中的使用 for 语句在模板中的使用 Flask 提供了Jiaja 2模板引擎渲染模板 下面逐步介绍其模板渲染机制 templates index html user
  • Phpstorm 插件

    Symfony Plugin 支持 Symfony 2 3 4 Laravel Plugin 支持 Laravel env files support 支持 env 文件BashSupport 支持 BashEditorConfig 支持
  • 【低功耗蓝牙】⑤ 蓝牙HID协议

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