基于CircuitPython与BLE的无线手势鼠标:从传感器到HID设备的实践
1. 项目概述与核心思路想没想过你手里的那块开发板除了点灯、读传感器还能直接变成你电脑的鼠标不是通过USB线而是像你的蓝牙耳机一样无线连接靠手腕的晃动来控制光标。这个想法听起来有点极客但实现起来其实比你想象的要简单。今天要聊的就是基于Adafruit Feather nRF52840 Sense这块“瑞士军刀”般的开发板配合CircuitPython打造一个完全自定义的无线手势鼠标。这个项目的核心逻辑非常直接利用板载的六轴传感器LSM6DS33感知你手部的倾斜和移动将这些物理运动数据通过蓝牙低功耗BLE协议实时发送给电脑并模拟成标准的鼠标HID人机接口设备指令。这样一来你的电脑就会把它识别为一个普通的蓝牙鼠标无需安装任何额外驱动。板载的按钮被映射为鼠标左键而接近传感器APDS9960则扮演了一个“模式切换”的角色——当你用手掌覆盖它时倾斜动作就从移动光标变为滚动页面。为什么选择这个方案首先低功耗蓝牙BLE是如今无线外设的绝对主流手机、平板、电脑全平台原生支持续航表现也远胜于传统蓝牙。其次CircuitPython的易用性在这里发挥得淋漓尽致。你不需要折腾复杂的嵌入式C开发环境、交叉编译和固件烧写就像操作U盘一样把写好的Python代码文件拖到开发板上就能运行调试信息直接通过串口打印对新手和快速迭代原型无比友好。最后Adafruit Feather nRF52840 Sense这块板子集成了我们所需的一切Nordic的nRF52840芯片提供了强大的BLE支持LSM6DS33加速度计/陀螺仪和APDS9960接近/手势传感器都是现成的Feather标准的电池接口也让它天生适合做成便携设备。所以无论你是想做一个炫酷的演示道具为特定场景比如演讲时远距离控制PPT制作一个专用控制器还是单纯想深入理解传感器数据如何通过BLE转化为实际的电脑输入这个项目都是一个绝佳的起点。它串联了传感器应用、无线通信和HID设备模拟这几个嵌入式开发中的经典课题。2. 硬件选型与核心组件解析工欲善其事必先利其器。这个项目的硬件清单非常精简但每一件都至关重要。理解它们的作用能帮助你在后续的调试和可能的改造中游刃有余。2.1 主控核心Adafruit Feather nRF52840 Sense这是整个项目的大脑和中枢。选择它并非仅仅因为教程用了它而是其特性完美匹配需求nRF52840 SoC这是核心中的核心。它内置了ARM Cortex-M4F处理器性能足够流畅处理传感器数据和BLE协议栈。最关键的是它的蓝牙5.1协议栈非常成熟稳定对于实现HID设备这类需要低延迟、可靠连接的应用至关重要。丰富的集成传感器板载的LSM6DS33加速度计陀螺仪和APDS9960接近、光感、手势让我们省去了额外焊接传感器的麻烦也保证了传感器与主控之间通过I2C通信的稳定性和一致性。Feather生态系统标准的Feather外形和引脚布局意味着丰富的扩展板翅膀可供选择。其内置的锂电池充电管理电路和JST-PH电池接口让设备“无线化”变得轻而易举。CircuitPython原生支持Adafruit官方为其提供了持续维护和优化的CircuitPython固件及驱动库软件生态有保障。注意市场上也有其他基于nRF52840的开发板。但Feather Sense的传感器是“原配”其I2C地址、引脚连接都已经在CircuitPython的board模块中预定义好了使用起来是import board; i2c board.I2C()一行代码的事。如果换用其他板子你可能需要手动配置I2C引脚和传感器地址会增加不必要的复杂度。2.2 供电与开关电池与滑动开关400mAh锂电池这是一个平衡了容量和体积的选择。对于这种间歇性发送小数据包的BLE HID设备续航可以轻松达到数十小时。电池的选型要点在于接口必须是JST-PH和电压3.7V。容量可以根据你希望的设备厚度和续航进行调整但要注意充放电电流是否在板载充电芯片的允许范围内。滑动开关它的作用是为整个设备提供一个物理断电开关。虽然代码里可以通过软件深度睡眠来省电但一个物理开关能彻底杜绝“静置耗电”对于需要长期存放的设备来说是必需品。选择三脚拨动开关我们只使用其中一侧和中间脚分别接GND和Enable引脚来实现电路的完全通断。2.3 结构件3D打印外壳与螺丝3D打印外壳外壳不仅仅是美观。它为精密的电子元件提供了物理保护将传感器、按钮的位置标准化确保了手势感应的可重复性。教程提供的STL文件已经为所有接口USB、开关、传感器接近传感器窗口和按钮顶盖的按压柱预留了开口并设计了卡扣固定方式省去了自己建模的麻烦。M2.5螺丝与螺母用于将Feather主板牢固地固定在外壳底座上。防止主板在壳内晃动对于依靠加速度计工作的设备来说稳定的机械连接是数据准确的基础。2.4 连接线材硅胶排线使用硅胶排线或任何细径、柔韧的导线来连接开关和主板是为了在有限的空间内实现整洁、可靠的布线。硅胶线皮耐高温在焊接时不易烫伤也更柔软便于在壳内弯折安置。3. 软件环境搭建与代码深度剖析硬件准备就绪后软件是赋予其灵魂的关键。CircuitPython的开发流程极其简单但理解代码背后的逻辑才能做到知其然更知其所以然。3.1 CircuitPython固件烧录这可能是整个项目中最简单的一步体现了CircuitPython“零门槛”的理念。获取固件访问CircuitPython官网找到Adafruit Feather nRF52840 Sense的页面下载最新的.uf2固件文件。进入引导模式用一条数据线强调必须是数据线充电线不行连接开发板和电脑。快速双击板上的RESET按钮。此时板载的NeoPixel LED会变绿电脑上会出现一个名为FTHRSNSBOOT或类似的U盘。拖拽烧录将下载好的.uf2文件直接拖入这个U盘。U盘会自动弹出随后出现一个新的名为CIRCUITPY的U盘。至此CircuitPython系统就安装完成了。整个过程无需任何安装程序或编译器。3.2 项目代码与库文件部署教程提供的“项目包”是一个ZIP文件解压后包含两个关键部分code.py这是主程序文件。CircuitPython启动后会自动执行根目录下的code.py文件。lib/文件夹里面包含了项目依赖的所有外部库文件。包括adafruit_lsm6ds/LSM6DS33加速度计驱动库。adafruit_apds9960/APDS9960接近传感器驱动库。adafruit_hid/这是核心库它使得CircuitPython设备可以模拟键盘、鼠标等HID设备。adafruit_ble/提供BLE通信能力。simpleio/提供一些简单的IO操作和转换函数如我们后面会用到的map_range。部署方法就是直接将code.py文件和整个lib文件夹复制到CIRCUITPYU盘的根目录。这种基于文件系统的管理方式使得代码修改和库更新变得像在电脑上编辑文本文件一样方便。3.3 核心代码逻辑逐行解读让我们深入code.py看看手势鼠标是如何“思考”和“行动”的。第一部分初始化与设置import time import board import digitalio import simpleio import adafruit_lsm6ds.lsm6ds33 import adafruit_apds9960.apds9960 from adafruit_hid.mouse import Mouse import adafruit_ble from adafruit_ble.advertising import Advertisement from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.standard.hid import HIDService from adafruit_ble.services.standard.device_info import DeviceInfoService开头是一连串的导入。这里的关键是Mouse、HIDService和ProvideServicesAdvertisement。它们共同定义了本设备的BLE身份一个提供HID服务的可连接设备。i2c board.I2C() lsm6ds33 adafruit_lsm6ds.lsm6ds33.LSM6DS33(i2c) apds9960 adafruit_apds9960.apds9960.APDS9960(i2c) apds9960.enable_proximity True初始化I2C总线并创建加速度计和接近传感器的对象。注意enable_proximity True这行代码启动了接近检测功能。click digitalio.DigitalInOut(board.SWITCH) click.direction digitalio.Direction.INPUT click.pull digitalio.Pull.UP配置板载按钮。这里设置为输入模式并启用内部上拉电阻。这意味着按钮未按下时读取到的值是True高电平按下时引脚接地值变为False低电平。所以后面的判断条件是if not click.value。第二部分数据预处理与BLE广播mouse_min -9 mouse_max 9 step (mouse_max - mouse_min) / 20.0 def steps(axis): return round((axis - mouse_min) / step)这是一个数据标准化函数。加速度计读出的原始值范围大且不稳定。这个函数将输入值axis映射到0-20的整数范围内。它先将-9到9的范围等分为20步然后计算当前加速度值落在哪一步。这相当于对原始数据进行了离散化和归一化为后续的移动速度映射奠定了基础。hid HIDService() device_info DeviceInfoService(software_revisionadafruit_ble.__version__, manufacturerAdafruit Industries) advertisement ProvideServicesAdvertisement(hid) advertisement.appearance 961 scan_response Advertisement() scan_response.complete_name CircuitPython HID ble adafruit_ble.BLERadio()创建HID服务、设备信息服务并设置广播参数。appearance 961是一个重要的魔法数字它告诉蓝牙主机你的电脑这个设备“看起来像”一个鼠标。complete_name设置了设备在蓝牙列表中显示的名字。if not ble.connected: print(advertising) ble.start_advertising(advertisement, scan_response)如果蓝牙未连接则开始广播。你的电脑在蓝牙设置里搜索到的就是这个设备。第三部分主循环——从传感器到鼠标动作这是整个程序的心脏在一个无限循环中不断执行。while ble.connected: y, x, z lsm6ds33.acceleration读取加速度计数据。注意这里y, x, z ...将加速度计的x和y值分别赋给了变量y和x。这是因为开发板在鼠标外壳中的安装方向导致物理坐标轴需要交换一下才能符合我们直觉上的前后倾斜对应光标上下移动左右倾斜对应光标左右移动。horizontal_mov simpleio.map_range(steps(x), 1.0, 20.0, -15.0, 15.0) vertical_mov simpleio.map_range(steps(y), 20.0, 1.0, -15.0, 15.0) scroll_dir simpleio.map_range(vertical_mov, -15.0, 15.0, 3.0, -3.0)这是运动映射的核心逻辑用了三层映射steps(x)将原始的X轴加速度值通过前面定义的steps函数转换为0-20的整数。simpleio.map_range(steps(x), 1.0, 20.0, -15.0, 15.0)将0-20的整数实际有效范围是1-20线性映射到-15到15。这意味着当设备从一侧倾斜到另一侧时horizontal_mov会从-15变化到15这个值将直接决定鼠标光标在水平方向上的移动速度和方向。vertical_mov同理但输入范围是20.0, 1.0这是因为原始加速度计数据的坐标系方向问题需要反转一下映射关系。scroll_dir将垂直移动的速度值-15到15再次映射到3到-3。这个值将作为滚轮的滚动速度。这里有一个精妙的设计当vertical_mov为正设备向前倾斜时映射出的scroll_dir为负这对应着页面向下滚动符合“向下拉”的直觉反之亦然。if not click.value: mouse.click(Mouse.LEFT_BUTTON) time.sleep(0.2)检测按钮是否被按下。按下则发送鼠标左键单击事件。time.sleep(0.2)是一个简单的防抖延时防止一次物理按压被误判为多次点击。if apds9960.proximity distance: mouse.move(wheelint(scroll_dir)) else: mouse.move(xint(horizontal_mov)) mouse.move(yint(vertical_mov))这是模式切换逻辑。程序不断读取接近传感器的值。当有物体比如你的手掌靠近传感器使其读数超过预设的distance阈值默认为245时系统进入“滚动模式”此时倾斜设备将触发mouse.move(wheel...)即滚动滚轮。否则处于正常的“光标移动模式”倾斜设备会触发mouse.move(x..., y...)来移动光标。实操心得distance 245这个阈值可能需要根据你的具体打印外壳透光性和环境光线进行调整。你可以通过在循环中打印apds9960.proximity的值观察手掌覆盖前后的数值变化来找到一个更可靠的触发点。例如在正常光线下无覆盖时值可能为10覆盖后激增到1000那么将阈值设为200-500之间会比较合适。4. 硬件制作与组装全流程代码跑通了接下来就是给这个“大脑”一个可靠的“身体”。组装过程是硬件项目从概念到实物的关键一步细节决定成败。4.1 开关焊接与线缆处理焊接是连接开关与主板的核心步骤良好的焊接能保证长期使用的可靠性。裁剪与剥线截取一段约9厘米的双芯硅胶排线。使用剥线钳小心地剥去每根线头约2-3毫米的绝缘皮。切忌损伤内部的金属绞线。预上锡搪锡这是保证焊接质量的关键一步。用烙铁温度建议320-350°C在剥开的线芯上熔化一点焊锡让焊锡均匀包裹所有铜丝。对开关的两个引脚选择一侧和中间脚也进行同样的预上锡操作。预上锡能防止“虚焊”让后续的焊接更快速、牢固。焊接开关将预上锡的线头分别对应焊接在开关的两个引脚上。可以使用“第三只手”夹具固定开关和电线。烙铁头同时接触引脚和线头待原有焊锡熔化后迅速移开烙铁保持不动直至焊点冷却凝固。一个理想的焊点应呈光滑的圆锥形完全包裹连接处。焊接至主板找到Feather主板背面的Enable和GND焊盘。同样先进行预上锡。然后将开关引出的两根线分别焊接到Enable和GND上。务必确认线序正确通常开关的中间脚公共端接GND一侧脚接Enable。这样当开关拨到“开”的位置时Enable引脚与GND断开板子得电拨到“关”时Enable与GND短接板子断电。注意事项焊接过程要快准狠避免长时间高温烫坏开关的塑料部件或主板焊盘。焊接完成后务必用力但不要暴力拉扯一下电线检查焊点是否牢固。用万用表的通断档检查开关功能是否正常开关断开时Enable与GND之间电阻应为无穷大开关闭合时电阻应接近0欧姆。4.2 3D打印件处理与主板固定打印参数使用PLA材料打印外壳的两部分底壳和顶盖。切片时无需支撑。建议参数层高0.2mm填充率10%-15%Gyroid填充图案在强度和耗材间取得很好平衡打印速度50-60mm/s。确保底壳上固定主板的四个立柱孔洞清晰。固定主板将M2.5x10mm的螺丝从底壳外部穿过固定孔。然后对齐Feather主板的四个安装孔将主板放上去。最后从主板上方拧入M2.5的尼龙螺母。不要一次性将一个螺丝拧到底建议先所有螺丝都带上一点然后再对角线交替地逐步拧紧确保主板平整受力避免因应力导致变形或焊接点开裂。安装开关将焊接好线的滑动开关从底壳内部对准侧面的方形开口以一定角度放入然后用力按压直至其卡入壳内预留的卡槽中。你应该能听到“咔哒”一声并且开关的拨杆能顺畅地从外壳侧面露出。卡扣设计使得开关无需螺丝即可固定得非常牢固。4.3 内部总装与合盖连接电池将400mAh锂电池的JST-PH插头以正确的方向通常红线对应插入Feather主板的电池接口。听到轻微的“咔”声表示插接到位。安放电池将电池平整地放入底壳内为电池预留的空间中。这个空间通常位于开关卡槽的上方。如果空间有富余可以使用一小块双面泡棉胶或可移除的蓝丁胶将电池稍微固定防止其在壳内晃动。安装顶盖这是最后一步。将顶盖与底壳的卡扣位置对齐。顶盖内部有一个专门设计的圆柱用于对准并按下板载的USR按钮同时还有一个开孔正对着APDS9960接近传感器。对准后用手均匀用力按压顶盖四周直到所有卡扣都啮合到位发出连续的“啪啪”声。合盖后检查USB口、开关拨杆、传感器窗口是否都对正没有遮挡。至此一个完整的、可独立工作的无线手势鼠标硬件就组装完成了。你可以拨动侧面的开关来开启它此时板载的红色电源LED应该会亮起。5. 蓝牙配对、校准与性能优化硬件组装完毕软件也已就绪最后一步就是让电脑认识这个新“鼠标”并把它调整到最顺手的状态。5.1 跨平台蓝牙配对指南配对过程本质上是让电脑的蓝牙HID主机识别并信任我们的设备。macOS打开系统设置-蓝牙。等待设备列表中出现名为“CircuitPython HID”的设备。点击其右侧的连接按钮。配对成功后设备名称旁会显示“已连接”。你可以在鼠标设置中调整跟踪速度等参数但主控权已在我们自制的鼠标上了。Windows 10/11打开设置-蓝牙和其他设备-添加设备-蓝牙。在列表中选择“CircuitPython HID”进行配对。配对成功后你可能会在“鼠标”设置里看到一个新的指针设备。主流Linux桌面如Ubuntu GNOME在系统设置的蓝牙模块中扫描设备并选择“CircuitPython HID”进行配对。通常需要授权或输入一个简单的配对码如0000但在HID设备模式下CircuitPython库通常已处理为“无密码配对”。常见问题排查如果电脑搜索不到设备请按以下步骤检查1. 确认设备已供电开关打开红灯亮。2. 确认代码正在运行CircuitPython启动后如果代码无错误板载的红色LED会开始缓慢呼吸表示正在广播。3. 检查电脑蓝牙是否已开启并尝试关闭再打开蓝牙功能重新扫描。4. 确保没有其他同名的BLE设备造成干扰。5.2 手势校准与灵敏度调优出厂代码提供了一个可用的默认配置但为了获得最佳体验你可能需要根据个人使用习惯进行微调。所有调整都在code.py文件中进行。光标移动速度与平滑度这由map_range函数中的输出范围-15.0, 15.0控制。这个值代表每次循环鼠标光标移动的最大像素数。增大绝对值如-25, 25会让光标移动更快但可能更“飘”减小绝对值如-8, 8会更慢更精细。你可以根据屏幕分辨率和操作精度进行调整。# 原代码 horizontal_mov simpleio.map_range(steps(x), 1.0, 20.0, -15.0, 15.0) # 调整为更慢更精细 horizontal_mov simpleio.map_range(steps(x), 1.0, 20.0, -8.0, 8.0)滚动速度与方向滚动速度由scroll_dir的映射范围3.0, -3.0控制。绝对值越大单次滚动的行数越多。如果你觉得滚动方向与直觉相反可以交换这两个值-3.0, 3.0。# 原代码向前倾斜vertical_mov为正页面向下滚scroll_dir为负 scroll_dir simpleio.map_range(vertical_mov, -15.0, 15.0, 3.0, -3.0) # 调整为向前倾斜页面向上滚 scroll_dir simpleio.map_range(vertical_mov, -15.0, 15.0, -3.0, 3.0)死区与灵敏度当前的steps函数将加速度映射到0-20。你可以调整mouse_min和mouse_max来改变这个映射的“零点”和“量程”。例如如果你希望设备在轻微倾斜时比如-2到2之间光标不动可以设置一个“死区”但这需要更复杂的逻辑判断而不是简单的线性映射。接近传感器阈值如前所述distance变量决定了触发滚动模式的灵敏度。在串口监视器使用Mu编辑器或VS Code的CircuitPython插件连接CIRCUITPY的串口中打印apds9960.proximity的值观察手掌覆盖前后的典型值然后将distance设置为一个介于两者之间的数例如(无覆盖值 覆盖值) / 2。5.3 高级功能扩展思路这个项目是一个完美的起点你可以基于它进行各种扩展增加更多按键Feather Sense上还有未使用的GPIO引脚。你可以焊接额外的微动开关或触摸传感器并在代码中为它们分配鼠标右键、中键甚至键盘快捷键需要导入Keyboard库的功能。手势识别APDS9960本身支持简单的手势识别上、下、左、右挥动。你可以启用其手势模式将不同的挥手动作映射为不同的快捷键如切换程序、调节音量。功耗优化目前的代码是持续运行、持续广播/连接的。对于电池供电设备可以加入time.sleep(0.01)来稍微降低循环频率或者在检测到长时间静止后让设备进入蓝牙低功耗广告模式甚至深度睡眠需要晃动时才唤醒连接这能极大延长续航。更换外壳与佩戴方式你可以重新设计3D模型把它做成戒指、手环或者固定在手套上实现真正“空中绘画”或特殊场景下的操控。这个基于CircuitPython与BLE的无线手势鼠标项目从核心原理到焊接组装完整地展示了一个创意硬件产品从构思到实现的全过程。它最迷人的地方在于你不仅得到了一个可用的工具更拥有了一个完全开源、可任意修改的平台。你可以随时打开code.py文件调整几行参数或者增加一段逻辑它的行为就会随之改变。这种即时反馈和高度可控性正是创客精神的精髓所在。

相关新闻

最新新闻

日新闻

周新闻

月新闻