piotrgolawski/glow-linux
GitHub: piotrgolawski/glow-linux
逆向工程实现的 Spacetronik Glow Three LED 环境灯原生 Linux 控制器,支持实时屏幕同步 ambilight 效果。
Stars: 0 | Forks: 0
# glow-linux
**Spacetronik Glow Three 27V LED 环境光系统的原生 Linux 控制器。**
无需 Windows,无需 Wine,无需虚拟机 —— 完全原生控制,支持系统托盘集成、分区颜色控制、白光色温调节以及实时屏幕同步 (ambilight)。
## 目录
- [功能特性](#features)
- [工作原理](#how-it-works)
- [逆向工程故事](#the-reverse-engineering-story)
- [阶段 1:识别设备](#phase-1-identifying-the-device)
- [阶段 2:USB 流量捕获](#phase-2-usb-traffic-capture)
- [阶段 3:破解 CH340 初始化](#phase-3-cracking-the-ch340-initialization)
- [阶段 4:解码 LED 协议](#phase-4-decoding-the-led-protocol)
- [阶段 5:Wayland 下的屏幕捕获](#phase-5-screen-capture-on-wayland)
- [协议规范](#protocol-specification)
- [安装说明](#installation)
- [使用指南](#usage)
- [架构设计](#architecture)
- [故障排除](#troubleshooting)
- [法律声明](#legal-notice)
- [许可证](#license)
## 功能特性
- **电源开/关**,带有系统托盘指示器
- **纯色模式** —— 为所有 57 个 LED 选择任意颜色
- **分区颜色控制** —— 5 个区域(左下、左、上、右、右下)
- **快速颜色预设** —— 12 种一键颜色预设
- **白光模式** —— 平滑色温滑块,范围从 1800K(暖烛光)到 7500K(冷日光)
- **屏幕同步模式** —— 使用 PipeWire ScreenCast(Wayland 原生)实现实时 ambilight 效果
- **亮度控制** —— 5–100% 亮度滑块
- **系统托盘** —— AppIndicator3 托盘图标,带有快速访问菜单
- **自动启动** —— 可选登录时启动
- **配置持久化** —— 记住您上次的设置
- **最小化到托盘** —— 窗口隐藏到系统托盘,应用程序保持运行
## 工作原理
Spacetronik Glow Three 27V 是一款专为显示器设计的氛围 LED 灯光系统。它由 57 个可独立寻址的 RGB LED 组成,排列在显示器背面周围(左下 5 个,左侧 14 个,顶部 19 个,右侧 14 个,右下 5 个)。
该设备通过 USB 连接,使用 **QinHeng CH340** USB 转串口芯片(USB ID `1a86:7523`)。官方仅限 Windows 的 "GLOW" 应用程序通过专有的串行协议以 **500,000 波特率** 与其通信。制造商未提供 Linux 支持。
本项目对该协议进行了逆向工程,并在 Linux 上原生实现,从而完全摆脱了对 Windows 应用程序的依赖。
## 逆向工程故事
### 阶段 1:识别设备
该设备显示为标准的 CH340 USB 转串口转换器:
```
Bus 001 Device 005: ID 1a86:7523 QinHeng Electronics CH340 serial converter
```
Linux 加载 `ch341` 内核驱动程序并创建 `/dev/ttyUSB0`。但是,简单地打开此串口并发送命令是行不通的 —— 设备依然保持熄灭状态。官方 Windows 应用程序是一个 Flutter 应用程序,没有公开的 API 或协议规范。
最初的尝试包括:
- **Hyperion** —— 不支持此设备
- **OpenRGB** —— 不支持此设备
- **Wine** —— Flutter 应用程序在 Wine 下因 GDI 错误而崩溃
- **通过 pyserial 进行串行嗅探** —— 设备忽略所有以此方式发送的命令
突破来自于意识到 Linux `ch341` 内核驱动程序并没有复制 Windows CH340 驱动程序所执行的确切初始化序列。
### 阶段 2:USB 流量捕获
为了理解 Windows 应用程序实际发送的内容,捕获了原始 USB 流量:
1. 使用 libvirt/QEMU USB 直通将 USB 设备直通给 Windows 11 虚拟机
2. 安装官方 Windows 应用程序
3. 在 Linux 主机上,使用 `usbmon` 捕获所有 USB 流量:
```
sudo modprobe usbmon
sudo tcpdump -i usbmon1 -w /tmp/usb_init.pcap -s 0
```
4. 启动 Windows 应用程序,打开 LED,更改颜色
5. 分析 pcap 文件以解码每一个 USB 事务
这揭示了两项关键发现:
1. CH340 芯片在能以 500,000 波特率正确通信之前,需要特定的**厂商特定 USB 控制传输**序列
2. LED 协议使用带有 `AA 55` 头部的自定义二进制帧格式
### 阶段 3:破解 CH340 初始化
Windows CH340 驱动程序在初始化期间发送 14 个厂商特定 USB 控制传输。标准的 Linux `ch341` 驱动程序并没有完全复制这些传输,这就是为什么用 pyserial 打开 `/dev/ttyUSB0` 无法与设备通信的原因。
捕获的初始化序列:
```
Transfer 1: bmRequestType=0x40 bRequest=0xA1 wValue=0xC39C wIndex=0xD98A (Modem ctrl)
Transfer 2: bmRequestType=0xC0 bRequest=0x95 wValue=0x2C2C wIndex=0x0000 (Read reg)
Transfer 3: bmRequestType=0x40 bRequest=0x9A wValue=0x0F2C wIndex=0x0007 (Write reg)
Transfer 4: bmRequestType=0x40 bRequest=0xA4 wValue=0x00DF wIndex=0x0000 (Serial init)
Transfer 5: bmRequestType=0x40 bRequest=0xA4 wValue=0x009F wIndex=0x0000 (Serial init)
Transfer 6: bmRequestType=0xC0 bRequest=0x95 wValue=0x0706 wIndex=0x0000 (Read reg)
Transfer 7: bmRequestType=0x40 bRequest=0x9A wValue=0x2727 wIndex=0x0000 (Write reg)
Transfer 8: bmRequestType=0x40 bRequest=0x9A wValue=0x1312 wIndex=0xF383 (Baud 500000)
Transfer 9: bmRequestType=0xC0 bRequest=0x95 wValue=0x2C2C wIndex=0x0000 (Read reg)
Transfer 10: bmRequestType=0x40 bRequest=0x9A wValue=0x0F2C wIndex=0x0007 (Write reg)
Transfer 11: bmRequestType=0x40 bRequest=0xA4 wValue=0x00DF wIndex=0x0000 (Serial init)
Transfer 12: bmRequestType=0x40 bRequest=0xA4 wValue=0x00FF wIndex=0x0000 (Final init)
Transfer 13: bmRequestType=0xC0 bRequest=0x95 wValue=0x0706 wIndex=0x0000 (Read reg)
Transfer 14: bmRequestType=0x40 bRequest=0x9A wValue=0x2727 wIndex=0x0000 (Write reg)
```
关键见解:传输 #8 使用 CH340 寄存器值 `0x1312` 和 `0xF383` 将波特率设置为 500,000。传输 #12 使用 `0x00FF` 而不是 `0x009F`(传输 #5),表明这是一个两阶段的串口初始化。
解决方案使用 **pyusb** 来:
1. 分离 Linux `ch341` 内核驱动程序
2. 直接声明 USB 接口
3. 完全像 Windows 驱动程序那样发送所有 14 个控制传输
4. 通过批量端点直接通信(EP OUT: `0x02`, EP IN: `0x82`)
**这是让 LED 在 Linux 上亮起的关键突破。**
### 阶段 4:解码 LED 协议
在 CH340 正确初始化后,从捕获的 USB 流量中解码了 LED 协议:
**帧结构:**
```
AA 55 [CMD] [LEN_HI] [LEN_LO] [PAYLOAD...] 0D 0A
```
识别出三种命令类型:
1. **握手 (CMD=0x00)**:启动时发送一次
- PC 发送:`AA 55 00 00 00 0D 0A`(7 字节,无负载)
- 设备响应 20 字节的设备信息帧
2. **设置颜色 (CMD=0x01)**:以 ~30 FPS 持续发送
- 头部:`AA 55 01`
- 长度:`00 AC`(大端序 uint16,值为 172)
- 负载:`00` 字节 + 57 × RGB 三元组(171 字节颜色数据)
- 终止符:`0D 0A`
- 总计:每帧 179 字节
3. **心跳 (CMD=0x02)**:由设备定期发送
- 8 字节状态帧
- 包含电源状态和数据接收状态的标志
LED 布局(57 个 LED,从显示器背面看,从左下角顺时针方向):
```
Zone 0: Bottom-Left (LED 0–4) — 5 LEDs
Zone 1: Left (LED 5–18) — 14 LEDs
Zone 2: Top (LED 19–37) — 19 LEDs
Zone 3: Right (LED 38–51) — 14 LEDs
Zone 4: Bottom-Right (LED 52–56) — 5 LEDs
```
### 阶段 5:Wayland 下的屏幕捕获
对于 ambilight/屏幕同步功能,在 Wayland 上捕获屏幕内容具有挑战性:
1. **`mss` 库** —— 使用 X11 的 `XGetImage()`,在 Wayland 上失败并提示 "XGetImage() failed"
2. **GNOME Shell D-Bus 截图** —— 以 root 身份运行时被安全策略阻止(在 udev 规则修复之前,USB 访问最初需要 root 权限)
3. **`xdg-desktop-portal` ScreenCast** —— 正确的 Wayland 原生解决方案
最终实现使用:
- 通过 D-Bus 的 **`xdg-desktop-portal`** 请求屏幕共享权限(用户会看到标准的系统“共享屏幕”对话框)
- **PipeWire** 作为媒体传输层
- **GStreamer** 配合 `pipewiresrc` 构建实时视频捕获流水线
- 帧被缩小到 320×180 以提高性能
- 每一帧被划分为与物理 LED 区域匹配的区域
- 计算每个区域的平均颜色,并支持可配置的饱和度提升
- 颜色通过时间平均(75% 新 / 25% 旧)进行平滑处理,以减少闪烁
GStreamer 流水线:
```
pipewiresrc path={node_id} ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=180 ! appsink
```
这以 ~30 FPS 运行,CPU 占用极低,并且原生支持 Wayland 和 X11(通过 XWayland 门户支持)。
## 协议规范
### 帧格式
| 偏移量 | 大小 | 值 | 描述 |
| -------- | ------ | ------- | ------------- |
| 0 | 1 | `0xAA` | 头部字节 1 |
| 1 | 1 | `0x55` | 头部字节 2 |
| 2 | 1 | CMD | 命令 (0x00=握手, 0x01=颜色, 0x02=心跳) |
| 3–4 | 2 | LEN | 负载长度 (大端序 uint16) |
| 5 | LEN | ... | 负载数据 |
| 5+LEN | 1 | `0x0D` | 终止符字节 1 (CR) |
| 6+LEN | 1 | `0x0A` | 终止符字节 2 (LF) |
### 颜色命令 (CMD=0x01)
负载结构(172 字节):
| 偏移量 | 大小 | 描述 |
| -------- | ------ | ------------- |
| 0 | 1 | `0x00` (填充字节) |
| 1–3 | 3 | LED 0: R, G, B |
| 4–6 | 3 | LED 1: R, G, B |
| ... | ... | ... |
| 169–171 | 3 | LED 56: R, G, B |
### 连接参数
| 参数 | 值 |
| ----------- | ------- |
| USB VID | `0x1A86` |
| USB PID | `0x7523` |
| 芯片 | QinHeng CH340 |
| 波特率 | 500,000 |
| 数据位 | 8 |
| 停止位 | 1 |
| 校验位 | 无 |
| 批量 OUT 端点 | `0x02` |
| 批量 IN 端点 | `0x82` |
## 安装说明
### 前置条件
- 带有 GTK4、PipeWire 和 Python 3.10+ 的 Linux(在 Ubuntu 24.04 上测试)
- PipeWire(Ubuntu 22.10+、Fedora 34+ 默认包含)
- 连接了兼容的基于 CH340 的 LED 环境光设备
### 快速安装
```
git clone https://github.com/YOUR_USER/glow-linux.git
cd glow-linux
./install.sh
```
安装程序将:
1. 安装 Python 依赖项(`pyusb`、`numpy`、`Pillow`、`dbus-python`)
2. 安装系统软件包(`gir1.2-appindicator3-0.1`、GStreamer 绑定、GTK4/Adwaita)
3. 设置 udev 规则以允许无需 root 即可访问 USB(`/etc/udev/rules.d/99-ch340-glow.rules`)
4. 在应用程序菜单中安装 `.desktop` 文件
5. 启用登录时自动启动
### 手动安装
```
# Python 依赖
pip3 install --user pyusb numpy Pillow dbus-python
# 系统依赖 (Ubuntu/Debian)
sudo apt install gir1.2-appindicator3-0.1 gir1.2-gstreamer-1.0 \
gir1.2-gst-plugins-base-1.0 gstreamer1.0-pipewire python3-gi gir1.2-adw-1
# 无 root USB 访问
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="7523", MODE="0666"' | \
sudo tee /etc/udev/rules.d/99-ch340-glow.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
# 运行
python3 glow-control.py
```
## 使用指南
### 启动
```
python3 glow-control.py
```
或者在安装后从应用程序菜单中找到 **"Glow Control"**。
### 工作流程
1. **连接** —— 点击 "Connect" 以检测并初始化设备
2. **开机** —— 切换电源开关
3. **选择模式**(非活动模式会变灰以清晰显示):
- **颜色** 选项卡:选择纯色、选择分区颜色或使用快速预设
- **白光** 选项卡:拖动色温滑块,从暖色 (1800K) 到冷色 (7500K)
- **屏幕同步** 选项卡:点击 Start,在门户对话框中选择您的屏幕,享受 ambilight 效果
4. **亮度** —— 使用滑块调整(5–100%)
5. **最小化** —— 点击最小化按钮隐藏到系统托盘
6. **托盘菜单** —— 右键点击托盘图标以快速切换电源和选择颜色预设
### 托盘图标
该应用程序在系统托盘中运行,右键菜单提供:
- 显示/隐藏窗口
- 电源开关
- 快速颜色选择(红、绿、蓝、白、暖色等)
- 亮度预设
- 退出
## 架构设计
```
glow-control.py (main GTK4 process)
├── DeviceController # USB/CH340 communication
│ ├── connect() # Detach kernel driver + CH340 init + handshake
│ ├── _ch340_init() # 14 vendor-specific USB control transfers
│ ├── _build_frame() # AA 55 01 ... protocol frame builder
│ └── _send_loop() # 30 FPS color send thread
│
├── ScreenCaptureManager # Wayland screen capture
│ ├── start() # xdg-desktop-portal ScreenCast session
│ ├── _on_sample() # GStreamer appsink frame callback
│ ├── _process_loop() # Frame → LED color mapping thread
│ └── _map() # Screen region → LED zone mapper
│
├── GlowApp (Adw.Application) # GTK4/Adwaita GUI
│ ├── Color mode # Solid + per-zone + presets
│ ├── White light mode # Temperature slider 1800K–7500K
│ ├── Screen sync mode # PipeWire ScreenCast ambilight
│ └── Config persistence # ~/.config/glow-control.json
│
└── glow-tray.py (separate GTK3 process, IPC via Unix socket)
└── AppIndicator3 tray icon # System tray with quick-access menu
```
### 线程模型
- **主线程**:GTK4 事件循环 + 用于托盘 IPC 的 Unix socket 监听器
- **发送线程**:以 30 FPS 向 USB 写入颜色帧
- **捕获线程**:PipeWire/GStreamer 流水线 + 帧处理(仅屏幕同步)
- **D-Bus 循环**:通过 `dbus.mainloop.glib` 集成,用于门户通信
- **托盘进程**:独立的 GTK3 进程(GTK3 和 GTK4 无法在同一个进程存)
## 故障排除
### 找不到设备
- 检查 USB 连接:`lsusb | grep 1a86:7523`
- 检查内核驱动程序:`ls -la /dev/ttyUSB0`
- 确保 udev 规则已到位:`cat /etc/udev/rules.d/99-ch340-glow.rules`
- 安装 udev 规则后重新插拔 USB 线缆
### 权限被拒绝
- 运行安装程序以设置 udev 规则:`./install.sh`
- 或手动执行:`sudo chmod 666 /dev/ttyUSB0`
### 屏幕同步无法启动
- 确保 PipeWire 正在运行:`systemctl --user status pipewire`
- 检查门户:`systemctl --user status xdg-desktop-portal`
- 在 GNOME 上:确保已安装 `xdg-desktop-portal-gnome`
### LED 闪烁或颜色错误
- 检查亮度是否过低
- 在屏幕同步模式下,尝试调整饱和度滑块
- 确保没有其他应用程序正在访问 `/dev/ttyUSB0`
## 文件
```
glow-linux/
├── glow-control.py # Main application (GTK4/Adwaita)
├── glow-tray.py # System tray helper (GTK3/AppIndicator3)
├── glow-control.desktop # Desktop entry for app menu + autostart
├── install.sh # One-click installer
├── README.md # This file
├── LICENSE # MIT License
└── icons/
├── glow-256.png # App icon (256×256)
├── glow-128.png # App icon (128×128)
├── glow-64.png # App icon (64×64)
├── glow-48.png # App icon (48×48)
└── glow-32.png # Tray icon (32×32)
```
## 法律声明
本项目是通过 USB 设备通信协议的**净室逆向工程**创建的。该过程仅涉及对作者自有设备的 USB 流量进行观察和分析。没有对任何专有软件进行反编译、反汇编或复制。
- 用于互操作性的逆向工程受 **欧盟指令 2009/24/EC 第 6 条** 保护
- 通信协议属于功能规范,不受版权限制
- 未规避任何 DRM、加密或技术保护措施
- 本仓库中的所有代码均为原创作品
**Spacetronik** 和 **Glow** 是其各自所有者的商标。本项目不附属于、不由其认可或以任何方式与 Spacetronik 相连。这些名称仅用于识别兼容的硬件设备。
## 许可证
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
标签:Ambilight, CH340, GTK4, LED控制器, PipeWire, RGB灯光, Spacetronik, USB协议逆向, Wayland, 协议分析, 外设控制, 实时捕获, 屏幕同步, 开源硬件, 显示器氛围灯, 智能灯光, 权限提升, 桌面应用, 硬件黑客, 系统托盘, 逆向工具