成品效果:
省流版
详细版
🛠️ 一杯咖啡的时间,让你的 AI 助手学会「打信号」
本文适合:正在用 Claude Code 写代码,想给 AI 加个硬件外设的折腾党
阅读时间:约 6 分钟
最终效果:显示器旁一盏三色 LED,余光一扫就知道 AI 在干什么
🤔 起因:AI 在「思考」还是「卡住了」?
用 Claude Code 写代码时,你有没有遇到过这种场景:
发完指令后切到浏览器查资料,几分钟后切回来——发现它还在「思考」。更糟的是,它弹出权限请求卡住了,而你浑然不知,白白等了半天。
于是我决定搞一个实体状态灯,放在显示器旁边,用余光就能判断 AI 的工作状态:
一 🛒 硬件清单(总价 ≈ ¥15)
T 如果你手头有 Arduino Nano 或 ESP32 开发板,完全可以替换,接线一样简单。
接线图(文字版)
Arduino Uno LED
D2 ──→ 220Ω ──→ 红色 LED 正极,负极 → GND
D3 ──→ 220Ω ──→ 黄色 LED 正极,负极 → GND
D8 ──→ 220Ω ──→ 绿色 LED 正极,负极 → GND
三条线,三个电阻,接完就能用,不需要任何额外模块。
二 💻 Arduino 固件(直接烧录)
Arduino 端的逻辑非常简单:监听串口指令,控制对应 LED 闪烁。
指令集只有四个字母:
R → 红灯快闪 Y → 黄灯慢闪 G → 绿灯常亮 X → 全部熄灭
完整代码如下(可直接复制到 Arduino IDE 烧录):
Arduino C++ — led_indicator.ino
// 串口指令:R=红灯快闪, Y=黄灯慢闪, G=绿灯, X=全灭
#define RED_PIN 2
#define YELLOW_PIN 3
#define GREEN_PIN 8
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
Serial.begin(9600);
// 启动时绿灯快闪 3 次表示就绪
for (int i = 0; i < 3; i++) {
digitalWrite(GREEN_PIN, HIGH); delay(100);
digitalWrite(GREEN_PIN, LOW); delay(100);
}
digitalWrite(GREEN_PIN, HIGH);
}
void loop() {
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'R': blinkRed(100); break;
case 'Y': blinkYellow(600); break;
case 'G': allOff();
digitalWrite(GREEN_PIN, HIGH); break;
case 'X': allOff(); break;
}
}
}
void blinkRed(int ms) {
while (!Serial.available()) {
digitalWrite(RED_PIN, HIGH); delay(ms);
digitalWrite(RED_PIN, LOW); delay(ms);
}
}
void blinkYellow(int ms) {
while (!Serial.available()) {
digitalWrite(YELLOW_PIN, HIGH); delay(ms);
digitalWrite(YELLOW_PIN, LOW); delay(ms);
}
}
void allOff() {
digitalWrite(RED_PIN, LOW);
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(GREEN_PIN, LOW);
}
烧录到 Arduino Uno(COM5,9600bps)即可,一行命令搞定:
Shell
arduino-cli compile --fqbn arduino:avr:uno led_indicator/
arduino-cli upload -p COM5 --fqbn arduino:avr:uno led_indicator/
三 🖥️ 电脑端方案演进(踩坑全记录)
这部分是本文的精华所在。从最初的想法到最终跑通,踩了四个方案的坑才找到正确答案。
❌ 方案 1:Python + HTTP 守护进程
思路:Python HTTP 服务常驻 → Claude Code hooks 触发 bat 脚本 → bat 调用 curl → HTTP 请求 → 串口写指令。
O 翻车原因:
• 每次 Python 重新打开 COM5 会触发 Arduino 硬件复位(DTR 信号),有 3 秒延迟
• 升级为「守护进程模式」独占串口后,又遇到 Windows 串口权限问题
❌ 方案 2:Node.js TCP 桥接
思路:参考开源项目 agent-light,用 Node.js 做 TCP 桥接:serial-bridge.mjs 常驻进程独占 COM5,hook-client.mjs 被 hooks 调用时发 TCP 指令。
O 翻车原因:桥接本身工作完美——手动测试串口通信、TCP 转发全正常。但 hooks 始终不触发。
❌ 方案 3:opencode-led 框架改造
思路:参考 opencode-led 的 MQTT + ESP32 方案,保留 hooks + daemon 架构,只把 MQTT 输出换成串口。
关键发现:/hooks 命令显示 hooks 配置正确,但实际从未执行——stdin JSON 事件数据根本没有传入。
! 重大发现:经过数十次调试(不同格式、完整路径、timeout 参数、bash/cmd/node 各种命令),最终确认:
Claude Code v2.1.187 + DeepSeek API 后端,hooks 系统不工作。 这是一个客户端无法修复的根因问题。
✅ 方案 4:文件系统监控(最终方案)
既然 hooks 不可用,那就彻底绕过它。
灵光一闪:Claude Code 的每次交互都会写入 session JSONL 文件(位于 ~/.claude/projects/),实时记录每一条事件。那为什么不直接监控这个文件?
Claude Code 输入框
│
↓ (自动写入 session JSONL)
监控脚本轮询文件变化
│
↓ (检测事件类型)
通过串口发送指令 (R/Y/G/X)
│
↓
Arduino → LED 亮起 ✨
核心监控逻辑(仅约 120 行 Node.js)
JavaScript — led-monitor.mjs 核心代码
// 每 500ms 轮询最新的 session JSONL 文件
function poll() {
const latest = findLatestSession(); // 找最新修改的 .jsonl
if (latest.mtime > lastMtime || latest.size !== lastSize) {
const lastLine = readLastLine(latest.path);
const event = JSON.parse(lastLine);
if (event.type === "user") {
setState("thinking"); // 🟡 用户发消息 → 黄灯
} else if (event.subtype === "turn_duration") {
setState("done"); // 🟢 任务完成 → 绿灯
}
}
// 3 秒无活动 → 自动切绿灯
if (idleFor(3000)) setState("idle");
}
X 这个方案的优势:
✅ 完全不依赖 hooks,无需任何权限配置
✅ 500ms 轮询延迟几乎无感知
✅ Session JSONL 比 hooks 更可靠——它是 Claude Code 的核心日志
四 📁 完整项目结构
E:\ccdeng\
├── arduino-led\ # Arduino 固件
│ └── led_indicator.ino
└── opencode-led\ # 电脑端
├── led-monitor.mjs # 🔑 文件监控脚本(核心)
├── start-monitor.bat # 启动脚本(双击即用)
├── claude-led-daemon-serial.mjs # HTTP daemon(备选)
└── claude-led-hook.mjs # hooks 客户端(待修复后可用)
五 🚀 三步跑起来
Step 1:烧录 Arduino
Shell
arduino-cli compile --fqbn arduino:avr:uno led_indicator/
arduino-cli upload -p COM5 --fqbn arduino:avr:uno led_indicator/
Step 2:安装电脑端依赖
Shell
cd E:\ccdeng\opencode-led
npm install
Step 3:启动监控
双击 start-monitor.bat,或者命令行:
Shell
node led-monitor.mjs
启动后,监控脚本自动检测 session 文件变化,LED 实时反映 Claude Code 的工作状态。不需要任何额外配置。
六 📊 状态映射表
七 📝 踩坑总结(6 条血泪教训)
八 🔮 后续优化方向
1支持权限请求检测 → 红灯提醒(需要用 claude 普通模式跳过 xiaocc 权限模式)
2添加声音提示(任务完成时「叮」一声 🎵)
3多项目同时运行时合并状态显示
43D 打印一个好看的外壳(告别「杜邦线裸奔」)
📎 相关链接
📝 写于 2026 年 6 月,一个调试 hooks 到凌晨的夜晚。如果你也在折腾 Claude Code 的硬件外设,欢迎交流。
如果这篇文章对你有帮助,欢迎 点赞、在看、转发 🙌
夜雨聆风