设计一个桌面助手:可实时响应你的AI所执行的操作
这个想法
如果你经常使用 Claude Code 或 Claude 的桌面应用,你一定熟悉这种节奏:你启动一个任务,Claude 开始运行,然后出现提示——“ping”,弹出权限请求。Claude 是否可以执行这个 Bash 命令?写入那个文件?调用这个 API?你转到电脑上查看提示内容,选择同意或拒绝,然后再回到之前正在做的事情。
这并不疼,但会打断流程。如果你是个在做其他事情时,比如做饭、画画或阅读,背景里不断播放克洛德的课程的人,那你要么会不时地瞥一眼屏幕,要么完全错过提示,导致整个会话停滞不前,被卡住,等待着继续。
Claude Desktop Buddy 是解决这一问题的方案。它是一款小巧的硬件设备,放置在你的桌面上,通过蓝牙连接到 Claude 桌面应用程序,让你随时掌握信息,而无需完全分心。当 Claude 正在忙碌工作时,屏幕上的小角色会不断出汗。一旦出现权限提示,它就会立刻引起注意,LED 灯开始闪烁。你只需瞥一眼,读取 Claude 的请求,点击“允许”或“拒绝”按钮,然后继续之前的工作即可。就这么简单。
哦,它还有18种不同的ASCII宠物种类,并支持自定义动画GIF角色。为什么不呢。
它到底是什么
Claude Desktop Buddy 的核心是一个基于 ESP32 开发板的开源固件。适用于 macOS 和 Windows 的 Claude 桌面应用程序提供了一个蓝牙 LE API——硬件伙伴 API,用于广播实时会话数据:当前运行的会话数量、是否有会话被阻塞等待审批、最近的对话记录、累积令牌数量,以及任何待处理权限请求的详细信息。
固件连接到该API,解析传入的数据,并通过一系列反映克洛德实际行为的情绪状态来驱动一个动画角色。它还会监听设备上的物理按钮,能够将批准或拒绝的决策发送回桌面应用程序,而该应用程序则直接将这些信息转发至克洛德的会话管理器。
整个过程是一个反馈回路,即一个约15美元的开发板与运行在你电脑上的AI助手之间,完全通过蓝牙JSON进行通信和交互。
连接的工作原理
在 Claude 桌面应用中启用开发者模式(帮助 → 问题排查 → 启用开发者模式)后,会弹出一个开发者菜单。从中打开“硬件伙伴”窗口,该窗口会扫描附近提供正确服务的 BLE 设备。从列表中选择您的设备,在首次连接时授权蓝牙权限,配对即完成。此后,当双方都处于唤醒状态时,桥接器将自动重新连接,您无需再次操作配对窗口。
在无线通信中,所有数据均通过蓝牙低功耗(BLE)使用 Nordic UART Service——这是一种广泛支持于嵌入式生态系统的标准串行-BLE配置文件。Claude 应用程序通过该特性每行发送一个 UTF-8 JSON 对象。设备会累积字节,直到遇到换行符,然后解析 JSON 并据此执行相应操作。
心跳消息看起来像这样:
当Claude端发生任何变化时,设备会收到此信息,并每10秒发送一次保持连接的ping请求,以确认连接仍然有效。固件需要了解的所有关于Claude状态的信息都包含在这条消息中。running > 0表示Claude正在积极生成内容;waiting > 0表示某个权限提示正在等待用户决定;total == 0表示所有会话均已关闭。只有在确实有决策等待时,prompt字段才会显示。
如果设备30秒内没有收到消息,就会认为连接已断开并返回睡眠状态。
批准和拒绝提示
正是这部分让设备真正实用,而不仅仅是装饰品。
当出现权限提示时,固件会显示工具名称和提示信息——即Claude希望执行的具体命令或路径——并使字符进入注意状态。LED灯开始闪烁。屏幕保持亮起(通常在30秒无操作后会变暗,但在等待提示时不会变暗)。
你查看一下,读取克洛德想要执行的操作。如果看起来没问题,就按下A键;如果你想阻止它,就按下B键。设备会返回:
或
ID 必须与心跳检测中的 prompt.id 完全匹配。桌面应用程序会检查这一点,并将结果转发给 Claude 的会话管理器。会话随即解封并继续运行。如果你稍加留意,整个交互过程大约只需两秒钟。
五秒内通过即可获得奖励:角色会短暂切换至“心”状态。虽然只是微小的细节,但正是这种反馈让设备显得更加生动,而不仅仅是功能性的存在。
七州
该角色居住在七个州中的一个,每个州都有其独特的动画:
当BLE连接建立的瞬间,就会触发从睡眠状态到空闲状态的切换,因此你会获得一个小小的视觉确认,表明配对成功了。庆祝状态确实很有趣,达到某个里程碑的感觉就像通关游戏升级一样,而且它意外地成为持续使用的好动力。而“眩晕状态”则完全是因为有人觉得摇晃设备看看会发生什么会很有趣,他们猜对了。
ASCII宠物与GIF角色
出厂时,固件自带18种ASCII宠物物种,每种都配有七种动画,所有动画均由角色艺术手工制作而成。您可通过设置菜单切换这些动画。您的选择会保存在非易失性存储中,因此在断电后仍可保留。
但更有趣的功能是自定义GIF角色。将文件夹拖放到Claude桌面应用中的硬件伙伴窗口,它会通过分块传输协议,利用BLE将文件内容流式传输到您的设备。设备会实时切换至GIF模式,无需重新刷新。
角色包的结构很简单:一个 manifest.json 文件,用于定义角色名称和配色方案,以及 96 像素宽的 GIF 文件——每个状态对应一个 GIF,或者对于像空闲状态这样需要多样性的状态,可以使用多个 GIF 的数组,以避免同一个片段无限循环。
整个文件包的大小必须控制在1.8MB以内,这比看起来的要严格得多。使用命令 `gifsicle --lossy=80 -O3 --colors 64` 通常可以将文件大小减少40%到60%,且不会出现明显的画质损失。该仓库包含一个Python工具(tools/prep_character.py),用于将源GIF图像调整为合适的宽度,并标准化比例,使角色在七种状态中看起来一致——否则动画中的忙碌状态会比睡眠状态大出三倍。
原始硬件:M5StickC Plus
固件的首个版本是为M5StickC Plus设计的,原因显而易见。这是一款非常小巧的设备。仅需约15美元,你就能获得一个ESP32-PICO-D4芯片、一块1.14英寸(135×240)TFT显示屏、内置的MPU6886惯性测量单元、带充电电路的电池、红外发射器、实时时钟以及两个物理按键——所有这些都集成在一个比U盘还小的外壳中。它自成一体,库存充足,拥有成熟的Arduino库,可处理从显示屏驱动到电源管理等所有功能,而且创客社区多年来一直在此基础上开发各种项目。
对于一只坐在显示器旁、显示克洛德状态的桌面宠物?老实说,这确实可行。角色动画虽小但清晰可辨,按钮布局覆盖了“批准”和“拒绝”,而轻摇手势在如此轻薄的设备上也令人满意。
限制在于屏幕。1.14英寸面板上的135×240像素,物理上非常小。滚动视图中的文字行被压缩得几乎像计数器一样,而非实际内容。权限提示中的工具提示——即Claude真正想执行的命令——常常被截断。你或许能读到足够多的内容做出判断,但这种阅读方式比日常使用时更费眼力。而且如果你正在运行GIF角色,可用的画布空间也更少。
从端口到UniHiker K10:我们为何搬迁
将固件移植到DFRobot UniHiker K10的决定源于对M5StickC Plus的三个具体不满,而K10正是针对这些问题进行了直接改进。
你真正能看懂的屏幕
K10 配备了一块 2.8 英寸的 IPS 显示屏,分辨率为 240×320 像素。这是一块明显更大的画布,像素面积超过 M5StickC Plus 屏幕的四倍以上。宠物动画有充足的空间自由表现。转录滚动条显示真实内容,而非压缩的摘要。权限提示界面会显示工具名称和提示信息,同时仍保留了用于角色绘制的剩余像素。
从1.14英寸到2.8英寸看起来在纸上是小幅增长,但实际上却像是从瞥一眼设备,到不得不凑近才能看清屏幕的差别。
用于批准/拒绝的合理按钮
M5StickC Plus 配有两个按钮:正面的主按钮和右侧面板上的一个较小按钮。对于大多数 M5Stick 项目来说,这已经足够,但对于那些核心功能是二元确认/拒绝交互的设备而言,这种设计显得不够顺手。侧边按钮需要调整握持方式,快速决策——比如触发心形动画的场景——则容易显得笨拙。
K10 采用自然并排的双前侧按钮布局,左键、右键,分别对应“拒绝”和“批准”。无需看一眼,手指便能准确操作。
K10搭载了ESP32-S3芯片,其内存和闪存空间比M5StickC Plus中的ESP32-PICO-D4更大。这一点在解码动画GIF并同时保持BLE连接时尤为重要。原版手柄在处理复杂的字符包时可能出现卡顿,尤其是在GIF渲染器与BLE栈争夺CPU周期的切换状态下。而在K10上,即使在有BLE数据传输的情况下,字符动画也能流畅运行。
理想之家:定制3D打印外壳
Unihiker K10 是一款性能出色的电路板,但仅在桌面上摆放裸露的PCB板,并不能算作一件成品。为了给这款桌面伴侣一个永久归属,我们使用 Fusion 360 软件设计了这款外壳,并采用青铜色PLA打印而成。青铜PLA具有比普通线材更重、更温暖的质感,它像一件属于桌面的小物件一样自然地安放其中,而不是被遗忘在角落的开发板。该设计为简洁的直立式支架,屏幕以自然视角朝向用户,两侧设有按钮(A和B)的开孔,顶部还设有一个通道,可让USB-C数据线整洁地从其中引出。
打印无需支撑,公差足够紧密,K10无需螺丝即可固定。整个打印过程在0.4mm喷嘴下大约需要三个小时,青铜色表面处理效果良好。
端口实际涉及的内容
固件移植并非完全重写——应用程序逻辑、BLE桥接、JSON协议处理、状态机以及全部18种ASCII宠物物种均保持完全不变。唯一改变的是硬件接口层。
原始代码与 M5StickCPlus.h 库紧密耦合,该库为显示屏、IMU、按钮、蜂鸣器和电源管理提供了便捷的封装。而 K10 使用的是完全不同的 BSP:DFRobot 的 unihiker_k10.h,搭配 TFT_eSPI 显示屏驱动。
解决方案是在 src/k10_hal.cpp 和 src/k10_hal.h 中实现了一个硬件抽象层。该文件将所有 M5 特定的调用替换为等效的 K10 调用:
•M5 IMU.getAccelData() 变成 k10.getAccelerometerX/Y/Z()
•M5单色红色LED变为K10的RGB LED(闪烁红色表示注意状态)
•M5 M5.BtnA 和 M5 BtnB 变成了 K10Button 封装器,提供了固件其余部分所期望的边缘检测 API。
•屏幕亮度通过PWM控制K10的背光GPIO
•由于K10没有硬件实时时钟(RTC),软件RTC会根据BLE桥接器在连接时接收到的时间同步信息,来合成GetTime()和GetDate()。
K10 也使用了 LittleFS 而不是 SPIFFS,由于需要更大的闪存容量,因此需要自定义分区表(partitions_k10.csv),以确保文件系统拥有足够的空间来存储完整的 GIF 字符集,同时保持固件分区的完整性。
其他所有内容——platformio.ini 中新增了一个 [env:unihiker-k10] 目标,就这些了。
画廊(演示图片)
默认界面显示当前时间和日期。当无活动时,还会显示一个休眠的面孔。
这显示了状态:
当提示正在运行时:
当你被提示批准或拒绝命令时:
打造属于自己的
最重要的是:使用 Hardware Buddy API 并不需要这个代码库。BLE 协议完全开放,任何能够广播 Nordic UART 服务并解析以换行符分隔的 JSON 的设备都可以连接。Arduino、ESP32、nRF52,以及配备 USB BLE 路由器的 Raspberry Pi Zero 都是有效的设备。完整的协议规范位于仓库中的 REFERENCE.md 文件中。
如果您想在支持的某一块板上使用此固件:
•在 Claude 中启用开发者模式:帮助 → 问题排查 → 启用开发者模式
•安装 PlatformIO Core
•使用 pio run -t upload(用于新设备)或 pio run -t erase && pio run -t upload(先清除已刷机的设备)
•在 Claude 桌面应用程序中,打开“开发者”→“硬件伙伴”,点击“连接”,然后从扫描列表中选择您的设备。
关于安全的一个重要说明:BLE 链路会携带会话片段和工具调用提示,这些内容来自您与 Claude 的真实会话。固件会请求 LE Secure Connections 绑定,这意味着从首次配对开始,链路将使用 AES-CCM 加密。首次连接时,桌面端会提示您输入一个 6 位数的密码,该密码由设备显示——这是标准的 BLE 配对方式。重新连接时,系统会自动使用已存储的密钥。如果您正在开发自己的实现方案,也应采用相同的方法。未加密的设备在无线电范围内,任何配备廉价 BLE 路由器的人都可以截获其数据。
本文编译自hackster.io





