实现一个BLE HID鼠标

  • 这个程序将 ESP32 或类似设备变成了一个简单的蓝牙鼠标,通过 4 个 GPIO 引脚来控制鼠标的上下左右移动。
  • 连接到蓝牙后,按下相应的按键会发送 HID 鼠标移动事件。
  • 包含了一个简单的测试函数,用于验证鼠标的移动和点击功能。
# MicroPython Human Interface Device library
# Copyright (C) 2021 H. Groefsema
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
import time
from machine import SoftSPI, Pin
from hid_services import Mouse

class Device:
    def __init__(self):
        # 定义初始坐标
        self.x = 0 # 用于存储鼠标的 X 轴和 Y 轴移动量。
        self.y = 0

        self.prev_x = 0
        self.prev_y = 0

        # 定义按键引脚(GPIO)
        # 对应于鼠标的上、下、右、左移动,分别连接到 GPIO 引脚 5、23、19 和 18
        self.pin_forward = Pin(5, Pin.IN)
        self.pin_reverse = Pin(23, Pin.IN)
        self.pin_right = Pin(19, Pin.IN)
        self.pin_left = Pin(18, Pin.IN)

        # 创建鼠标设备实例
        self.mouse = Mouse("Mouse")
        # 设置状态变化回调函数 mouse_state_callback
        self.mouse.set_state_change_callback(self.mouse_state_callback)
        # 启动鼠标设备
        self.mouse.start()

    # Function that catches device status events
    # 鼠标状态回调函数,用于处理鼠标设备状态变化。可能会根据设备状态执行不同的操作。
    def mouse_state_callback(self):
        if self.mouse.get_state() is Mouse.DEVICE_IDLE: # 设备处于空闲状态
            return
        elif self.mouse.get_state() is Mouse.DEVICE_ADVERTISING: # 设备正在广播蓝牙信号,等待连接
            return 
        elif self.mouse.get_state() is Mouse.DEVICE_CONNECTED: # 设备已连接
            return
        else:
            return

    def advertise(self): #启动蓝牙广播,使设备可以被发现和连接
        self.mouse.start_advertising()

    def stop_advertise(self): #停止蓝牙广播
        self.mouse.stop_advertising()

    # Main loop
    def start(self):
        while True:
            # 根据按键状态计算 x 和 y 的变化量
            # 右键按下 (pin_right.value() == 1) 时,x = 127,表示向右移动最大速度
            self.x = self.pin_right.value() * 127 - self.pin_left.value() * 127
            self.y = self.pin_forward.value() * 127 - self.pin_reverse.value() * 127

            # 如果检测到坐标发生变化且鼠标设备已连接,则发送 HID 报告,通知移动更新。
            # 检查是否发生变化
            if (self.x != self.prev_x) or (self.y != self.prev_y):
                # 更新上一次的坐标值
                self.prev_x = self.x
                self.prev_y = self.y

                # 如果鼠标已连接,更新坐标并通知
                # If idle start advertising for 30s or until connected
                if self.mouse.get_state() is Mouse.DEVICE_CONNECTED:
                    self.mouse.set_axes(self.x, self.y)
                    self.mouse.notify_hid_report()
                # 如果设备处于空闲状态,则开始广播
                elif self.mouse.get_state() is Mouse.DEVICE_IDLE:
                    self.mouse.start_advertising()
                    i = 10
                    while i > 0 and self.mouse.get_state() is Mouse.DEVICE_ADVERTISING:
                        time.sleep(3)
                        i -= 1
                    if self.mouse.get_state() is Mouse.DEVICE_ADVERTISING:
                        self.mouse.stop_advertising()

             # 如果鼠标已连接,休眠 20ms 后退出循环,进行测试
            if self.mouse.get_state() is Mouse.DEVICE_CONNECTED:
                time.sleep_ms(20)
                break
            else:
                time.sleep(2)
        self.test()
    # 停止设备
    def stop(self):
        self.mouse.stop()

    # Test routine
    def test(self):
        self.mouse.set_battery_level(50)
        self.mouse.notify_battery_level()

        for i in range(30):
            self.mouse.set_axes(100,100) # 向右上移动 (100, 100) 并按下左键
            self.mouse.set_buttons(1)
            self.mouse.notify_hid_report()
            time.sleep_ms(500)

            self.mouse.set_axes(100,-100) # 向右下移动 (100, -100) 释放按键
            self.mouse.set_buttons()
            self.mouse.notify_hid_report()
            time.sleep_ms(500)

            self.mouse.set_axes(-100,-100) # 向左下移动 (-100, -100) 并按下右键
            self.mouse.set_buttons(b2=1)
            self.mouse.notify_hid_report()
            time.sleep_ms(500)

            self.mouse.set_axes(-100,100) # 向左上移动 (-100, 100) 释放按键
            self.mouse.set_buttons()
            self.mouse.notify_hid_report()
            time.sleep_ms(500)

        self.mouse.set_axes(0,0) # 最后将鼠标位置重置为 (0, 0),并模拟将电池电量更新为 100%
        self.mouse.set_buttons()
        self.mouse.notify_hid_report()

        self.mouse.set_battery_level(100) # 设置电池电量(模拟电池状态
        self.mouse.notify_battery_level()

if __name__ == "__main__":
    # 创建 Device 实例并启动主循环,等待蓝牙连接和处理按键输入。
    d = Device()
    d.start()

你可以通过修改 pin_forwardpin_reversepin_rightpin_left 引脚编号,来匹配你的硬件设置,并根据需要调整鼠标移动的速度(例如更改 127 的值)。

### Android BLE HID 鼠标事件处理 在Android平台中,为了处理来自BLE HID鼠标的输入事件,应用程序需要注册并监听特定类型的广播消息以及设置相应的回调函数。当接收到HID设备发送的数据包时,这些数据会被解析成标准的鼠标动作,比如移动指针、点击按钮等。 对于BLE HID外设而言,在建立连接之后,主机端(即运行应用的安卓设备)会定期接收到来自外围设备的状态更新报告。每一个状态更新都包含了当前按键状态或者是相对位置变化的信息。这部分逻辑通常是在服务层面上实现,并且依赖于`BluetoothGattCallback`类中的方法重载来捕获重要的通信时刻[^1]。 下面是一个简单的代码片段展示如何配置一个Activity去响应BLE HID鼠标产生的事件: ```java import android.bluetooth.BluetoothDevice; import android.hardware.input.InputManager; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class BleMouseActivity extends AppCompatActivity { private BluetoothDevice mBleDevice; // 假定此变量已经被正确初始化 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); InputManager inputManager = (InputManager)getSystemService(INPUT_SERVICE); // 注册用于接收BLE HID输入事件的服务 registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ("android.intent.action.INPUT_METHOD_CHANGED".equals(action)) { Bundle extras = intent.getExtras(); if(extras != null){ int[] motionEvent = extras.getIntArray("motion_event"); // 处理鼠标移动事件... } } } }, new IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED)); connectToDevice(mBleDevice); // 这里应该有一个实际的方法用来发起与指定BLE设备之间的连接请求 } /** * 此处省略connectToDevice的具体实现细节, * 它应当负责创建必要的GATT客户端实例并与目标BLE设备完成配对过程。 */ } ``` 需要注意的是上述代码仅为示意用途,具体实现可能因应用场景而异。此外,由于不同版本之间API有所差异,开发者应查阅官方文档获取最准确的支持信息[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cici_ovo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值