|
针对淘宝 1003 MLight 单灯珠版 USB 状态灯编写的驱动。 |
对应硬件商品宣传图 |
驱动程序工程/
├── mlight/ # 驱动包(Python,纯脚本,无需编译)
│ ├── protocol.py # 协议层:命令表、校验、组帧
│ ├── portfinder.py # 串口端口发现(WMI / VID_2E8A / 环境变量)
│ ├── driver.py # 驱动核心:连接、发送、超时、断线重连、自检、软件呼吸
│ ├── cli.py # 命令行入口
│ └── server.py # 本地 HTTP 服务(供 Agent Skill 调用)
├── skill/ # 控制程序(Agent Skill 形式)
│ ├── SKILL.md
│ └── scripts/light.py
├── tests/ # 单元测试(可离线运行,不依赖硬件)
├── images/ # README 配图
├── 启动服务.bat # 一键启动服务(前台窗口)
├── 启动服务-隐藏.ps1 # 隐藏启动(开机自启用)
├── 注册开机自启.bat # 注册到 HKCU Run(当前用户,无需管理员)
├── 取消开机自启.bat # 取消开机自启
├── 使用说明书.md # 日常使用说明(面向非开发者)
└── README.md
- 物理层:USB 串口(CDC),115200 波特,8N1,读写超时 500ms
- 帧格式:ASCII 命令文本 +
\n(0x0A),无帧头/校验/应答 (卖家代码:SerialPort.WriteLine(command),NewLine = "\n") - 命令表(13 个基础命令 + 亮度):
| 命令 | 效果 |
|---|---|
GREEN_ON / GREEN_OFF / GREEN_BLINK / GREEN_BREATH |
绿灯 常亮/熄灭/闪烁/呼吸 |
YELLOW_ON / YELLOW_OFF / YELLOW_BLINK / YELLOW_BREATH |
黄灯 常亮/熄灭/闪烁/呼吸 |
RED_ON / RED_OFF / RED_BLINK / RED_BREATH |
红灯 常亮/熄灭/闪烁/呼吸 |
OFF |
全部熄灭 |
BRIGHTNESS_0 ~ BRIGHTNESS_100 |
亮度 0-100% |
- 设备为单灯珠(1003 单灯珠版),同一时刻只显示一种主色
- 设备芯片方案:Raspberry Pi Pico(USB VID
2E8A),端口发现时优先匹配
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10/11(开发与实测环境) |
| Python | 3.7+(推荐 3.10+,实测 Windows 11 + Python 3.14) |
| 第三方依赖 | 仅 pyserial |
| 硬件 | 1003 MLight 单灯珠版 USB 状态灯(USB CDC,即插即用,自动识别为 COM 口) |
驱动为纯 Python 脚本:无需编译、无需 .NET 或其它运行时;代码未使用平台专属 API,理论上 Linux/macOS 亦可运行(pyserial 跨平台),但仅在 Windows 上验证过。
python -m pip install pyserial# 绿灯常亮(驱动默认行为,表示在线)
python -m mlight.cli on green on
# 黄灯呼吸 60 秒后自动熄灭
python -m mlight.cli on yellow breath 60
# 软件呼吸(推荐):三角波+gamma 感知校正,匀速渐变、峰值瞬间折返
python -m mlight.cli on green breath --soft --period 4.0 --gamma 2.2 --curve triangle
# 红灯闪烁 30 秒后恢复绿灯
python -m mlight.cli on red blink 30 --restore GREEN_ON
# 熄灭
python -m mlight.cli off
# 亮度 50%
python -m mlight.cli brightness 50
# 自检(红→绿→黄各 1 秒)
python -m mlight.cli selftest
# 查看状态 / 端口
python -m mlight.cli status
python -m mlight.cli port
python -m mlight.cli ports指定端口(默认自动发现):python -m mlight.cli --port COM12 on green on
或设置环境变量 MLIGHT_SERIAL_PORT=COM12。
python -m mlight.server --port 6802GET /api/health— 健康检查GET /api/status— 驱动状态(端口、当前命令、是否在线)POST /api/command— 控制灯光,三种请求体任选:
{"command": "YELLOW_BREATH"}
{"color": "yellow", "mode": "breath", "timeout": 60, "restore": "OFF"}
{"color": "green", "mode": "breath", "soft": true, "period": 4.0, "gamma": 2.2, "curve": "triangle"}
{"brightness": 50}
{"action": "selftest"}POST /api/off— 熄灭
- 超时控制:任何命令可带超时,到期自动熄灭或恢复指定状态
- 软件呼吸:固件自带呼吸动画在亮度降到低端时会突然熄灭(渐变末端骤断,
属固件行为无法修改)。驱动可用
BRIGHTNESS_0..100命令做平滑渐变 (on <color> breath --soft):20ms 步进(50fps)+ gamma 感知编码 (人眼感知 ≈ 光强^0.45,编码 PWM = s^γ,γ≈2.2 时感知线性)+ 三角波曲线 (感知亮度匀速升降、峰值瞬间折返,不会像正弦那样"粘"在最亮状态 被误看成常亮;--curve sine可换回正弦),节拍误差 <1ms (实测 20.0±0.5ms),视觉接近固件连续 PWM 的丝滑效果。帧值不变时 跳过写入(暗端多帧同值零 I/O),实测呼吸态 CPU ~1% 单核、串口带宽 ~4%。 参数:--period(默认 4 秒)、--gamma(默认 2.2,1.0=线性)、--curve(默认 triangle);HTTP 服务用{"soft": true, "period": 4.0, "gamma": 2.2, "curve": "triangle"} - 动画维持:实测设备固件的闪烁/呼吸动画仅持续约 1-2 秒即自动熄灭, 驱动内置维持线程每 2 秒重申动画命令(卖家 sidecar 同样每 10 秒重申)
- 端口自动发现:pyserial 枚举 → 排除蓝牙(BTHENUM)→ 优先
VID_2E8A(Pico) → 环境变量MLIGHT_SERIAL_PORT/ 参数指定 - 断线自动重连:发送失败自动重试,重连成功后重放当前灯光状态 (重连直接复用上次成功的端口名,不重复枚举)
- 启动自检:打开串口后默认点亮绿灯常亮(驱动在线指示),可用
selftest命令做三色测试 - 无需 exe:纯 Python 脚本,符合"驱动程序无需以 exe 形式产出"的要求
目标:驱动作为常驻后台的提示灯服务,必须长时间运行且低开销。 以下是真机实测成果(服务常驻运行时的真实资源占用)。
| 项目 | 说明 |
|---|---|
| 硬件 | 1003 MLight 单灯珠版(COM12,USB CDC) |
| 系统 | Windows 11 + Python 3.14(pyserial 3.5) |
| 服务形态 | python -m mlight.server --port 6802 常驻 |
| 测量方法 | PowerShell Get-Process 采样进程累计 CPU 时间与工作集,10 秒窗口做差分;线程数取 Threads.Count |
| 测量点 | 空闲(常亮/熄灭)、软件呼吸(4s 周期、50fps)各测 10s+,30s 持续观察泄漏 |
| 状态 | CPU(单核) | 内存 | 线程 | 串口带宽 |
|---|---|---|---|---|
| 空闲(常亮/熄灭) | 0.000% | ~29 MB | 1 | 0 |
| 软件呼吸(4s 周期,50fps) | ~1.0% | ~29 MB | 2 | ~4% |
| 固件动画(闪烁/呼吸 + 2s 重申) | ~0.05%(每 2s 一帧) | ~29 MB | 2 | 极低 |
- 空闲态调度线程自动退出(无事可做即退出,绝不空转),常驻后台 CPU 为 0
- 30 秒持续观察:线程数与内存无增长(无泄漏、无僵尸线程)
- 呼吸节拍精度:20.0 ± 0.5ms(50fps 无抖动,人眼不可感知的恒定节拍)
50 帧/秒节拍 × 平均写入 36 帧/秒(4s 周期 200 个 tick 中实际写入约 145 帧,帧值不变即跳过),每帧约 0.25ms:
| 组成 | 耗时/帧 | 说明 |
|---|---|---|
| 串口写(WriteFile) | ~0.1 ms | 15 字节 @ 115200,纯系统调用 |
| 精确睡眠(time.sleep) | ~0.03 ms | 10ms 切片 1-2 次,本机精度 <1ms |
| 亮度计算(曲线 + pow) | ~1 μs | 纯 Python 计算 |
| 锁 / 循环 / 时间戳 | ~5 μs | RLock 短临界区 |
关键设计把开销压到平台下限:
- 帧值不变跳过写入:暗端 gamma 编码下多帧同值(如 0%),直接跳过, 不产生任何 I/O(模拟验证:4s 周期 200 帧中 55 帧零写入,占 28%)
- BRIGHTNESS 命令幂等:重复设置相同亮度零 I/O
- 调度线程按需存活:无超时/无动画/无呼吸时线程退出,连线程调度 开销都省掉
- 睡眠不进锁:状态变化最多延迟一个切片(10ms),无感知影响
- 空闲态:0% CPU + 单线程,适合开机自启长期常驻
- 呼吸态:~1% 单核 ≈ 8 核机器总 CPU 的 0.12%,属 Windows 系统调用开销下限,Python 层无进一步压缩空间
- 无内存/线程泄漏,可 7×24 小时运行
cd 驱动程序工程
python -m unittest discover -s tests -v全部测试可离线运行(不依赖硬件),覆盖:协议命令校验/组帧、端口发现 评分、软件呼吸曲线(三角波/正弦形状、gamma 编码方向、峰值停留对比)、 步进节拍、亮度幂等、超时恢复、停止行为。
详见 skill/SKILL.md。Agent 通过 skill/scripts/light.py
(内部调用 HTTP 服务或直接驱动)把自身状态映射为灯光:
| Agent 状态 | 灯光 |
|---|---|
| 空闲 / 已连接 | 绿灯常亮 |
| 正在执行任务 | 黄灯呼吸 |
| 等待用户确认 / 需要授权 | 红灯闪烁 |
| 任务结束 | 熄灭或恢复绿灯 |
- 串口被其它程序占用时无法打开(Windows 串口独占,需先退出占用程序)。
- 单灯珠只能同时显示一种颜色,复合命令(如
GREEN_ON+RED_BLINK)仅适用于三灯珠版。 - 设备无应答协议(纯下行命令),驱动无法读取灯珠当前状态,只能记录本地状态。
- 设备固件的闪烁/呼吸动画约 1-2 秒后自动熄灭,须周期性重申(驱动已内置,见"驱动特性"); 常亮与熄灭无此限制。
