Abigor-DIY/Open-Trofeo-LCD
GitHub: Abigor-DIY/Open-Trofeo-LCD
为利民Thermalright Trofeo LCD散热器显示屏逆向开发的开源Linux驱动程序,集成主题管理、系统监控和Qt桌面GUI。
Stars: 1 | Forks: 0
# Thermalright Trofeo LCD — Linux 开源驱动
为 Thermalright Trofeo LCD 散热器显示屏逆向工程开发的驱动程序。
## 项目状态
当前重点:
- 为 Thermalright Trofeo LCD USB 显示屏提供稳定的本地后端。
- 包含主题库、主题设计器和动画工作室的 Qt 桌面 GUI。
- 实时系统状态、MPRIS/正在播放组件、音量/EQ 组件、仪表盘和动画背景。
- 更安全的单后端运行时,以避免 USB `Resource busy` 冲突。
- 用于公开测试的文档和打包。
已知开发领域:
- 主题设计器的布局和编辑器工具仍在完善中。
- 动画工作室已具备功能,但时间轴 UX、稳定性和导出工作流仍在持续演进。
- 部分高级组件可能需要针对 LCD 刷新性能进行调优。
- 实验性 Flatpak 打包已可用于本地测试,但目前仍推荐通过 source/venv 方式启动。
- 计划在 Flatpak 运行时布局稳定后提供 DEB/RPM 包。
## 发布计划
短期发布计划:
1. 将当前的开发预览版及此 README 警告推送到 GitHub。
2. 保持源码安装为早期测试人员的主要支持方法。
3. 添加包含明确 USB 设备访问说明的 Flatpak manifest。
4. 构建测试包:先提供 Flatpak,如果运行时布局稳定,再提供 DEB/RPM。
5. 在本地安装、udev 和后端启动验证通过后,在 GitHub Releases 上发布可下载的构件。
Flatpak 打包清单:
- 打包 Python 依赖项或使用 `requirements.txt` 中锁定的模块。
- 包含 PySide6/Qt 运行时依赖项。
- 记录 `0416:5408` 的 USB 访问要求。
- 测试直接 USB 访问是需要 `--device=all` 还是需要更严格的 udev/portal 设置。
- 在 XDG 路径下持久化应用状态,尤其是 `~/.local/state/open-trofeo-lcd`。
- 提供 `.desktop` 入口和图标。
- 确保只有一个后端实例可以拥有 LCD 设备。
实验性 Flatpak 构建说明可在 `docs/flatpak.md` 中找到。
## 发现的协议
| 参数 | 值 |
|-----------------|------------------------------------------|
| VID:PID | `0416:5408` (Winbond Electronics Corp.) |
| USB Class | Vendor Specific (0xFF) |
| EP OUT | 0x09 (Bulk, 512B max packet) |
| EP IN | 0x81 (Bulk, 512B max packet) |
| 分辨率 | 1920×462 像素 |
| 图像格式 | JPEG (baseline, 4:2:0) |
| 帧率 | ~6.3 FPS (TRCC 默认) |
| 帧大小 | 每张 JPEG 约 310–320 KB |
### 数据包结构
每张 JPEG 都被分割成块,作为 USB 批量传输发送:
```
Standard chunk: 4096 bytes total
[0] 0x01 Command (send image)
[1] 0xFF Marker
[2..5] uint32 LE JPEG total size / frame metadata
[6] 0x00 Fixed
[7] 0xF0 Fixed
[8] 0x01 Fixed
[9] 0x01 Fixed
[10] N Frame counter (low byte, wraps)
[11] seq Sequence within frame (0x00, 0x08, 0x10, ...)
[12..15] 0x00 Padding
[16..4095] JPEG data (4080 bytes)
Final chunk:
In captures this often appeared as a 2048-byte transfer, but that detail is still
the least certain part of the protocol. The driver therefore supports multiple
final-packet strategies for live testing:
- `auto`: final packet size = 16-byte header + remaining JPEG data
- `pad-2048`: zero-pad final packet to 2048 bytes
- `pad-4096`: zero-pad final packet to 4096 bytes
```
在每个数据包之后,主机会从 EP1 IN 读取一个 512 字节的 ACK。
## 安装
### 依赖项
```
# Kubuntu / Ubuntu / Debian
sudo apt install python3-venv python3-pip python3-usb python3-pil playerctl
# 后端、渲染器和 GUI 使用的 Python 依赖项
python3 -m venv .venv-gui
.venv-gui/bin/pip install -r requirements.txt
# Qt GUI venv 的辅助脚本
scripts/setup_gui_venv.sh
# 可选但推荐用于 "Now Playing" 小部件(MPRIS 元数据)
# (Spotify / Chromium / YT Music web / 等)
playerctl -v
```
Python 包记录在 `requirements.txt` 中:
- `PySide6` 用于 Qt GUI
- `pyusb` 用于 USB 通信
- `Pillow` 用于图像渲染
- `opencv-python-headless` 用于动画稳定工具
- `trcc-linux` 用于兼容 TRCC 的 LCD 传输辅助工具
### USB 权限
LCD 设备标识为 `0416:5408`。在以普通用户身份运行 GUI 之前,请安装 udev 规则。
如果没有此规则,应用程序可能会要求 root 权限或因
权限错误而失败。
### Udev 规则
```
sudo cp 99-trofeo-lcd.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
```
执行此操作后,请拔下并重新插入 LCD。
### 验证设备
```
lsusb | grep 0416
# 应显示:Bus XXX Device XXX: ID 0416:5408 Winbond Electronics Corp.
```
## 用法
```
# 测试图(彩条)
python3 trofeo_lcd.py --test
# 打印了 packet plan 的测试图
python3 trofeo_lcd.py --test --packet-debug
# 尝试 2048 字节填充的 final packet 变体
python3 trofeo_lcd.py --test --final-packet-mode pad-2048
# 发送自定义图像
python3 trofeo_lcd.py my_image.png
# 发送现有的 JPEG(逐字节不重新编码)
python3 trofeo_lcd.py --raw-jpeg-passthrough reference_frame_trcc.jpg
# 系统监控模式(自动刷新)
python3 trofeo_lcd.py --monitor
# 每 2 秒循环一次图像
python3 trofeo_lcd.py --loop --interval 2.0 wallpaper.jpg
# 降低 JPEG 质量以加快传输速度
python3 trofeo_lcd.py --monitor --quality 70
```
在 `--monitor` 模式下,驱动程序会显示实时的 Linux 数据:
- CPU 使用率(基于 `/proc/stat` 的差值,无阻塞休眠)
- 每核摘要(平均值/最大值)
- CPU 频率 (`/proc/cpuinfo`)
- CPU 温度(通过 `thermal`/`hwmon` 尽力获取)
- 负载平均值 (`/proc/loadavg`)
## 阶段 2.1:服务运行时 (systemd user)
此阶段在后台运行稳定的重播,具有自动重启和文件日志记录功能,无需手动运行长命令。
### 1) 安装用户服务
```
scripts/trofeo_service.sh install
```
这将创建:
- `~/.config/systemd/user/trofeo-lcd.service`
- 本地配置:基于 `.trofeo-service.env.example` 的 `.trofeo-service.env`
### 2) 启动、状态和日志
```
scripts/trofeo_service.sh start
scripts/trofeo_service.sh status
scripts/trofeo_service.sh logs
```
注意:
- 请勿将 `sudo` 与 `systemd --user` 一起使用。
- 如果 `start` 失败,脚本会自动打印 `status` 和最新的 `journalctl` 日志行。
文件日志:
- `~/.local/state/open-trofeo-lcd/service.log`
### 3) 登录后自启动
```
scripts/trofeo_service.sh enable
```
### 4) 停止 / 重启
```
scripts/trofeo_service.sh stop
scripts/trofeo_service.sh restart
```
### 配置
编辑 `.trofeo-service.env`:
- `PCAP_FILE` (默认:`dzis.pcapng`)
- `FRAME_INDEX`
- 计时:`ACK_TIMEOUT_MS`、`INTER_PACKET_DELAY`、`FRAME_DELAY`
- 连接重试:`CONNECT_RETRIES`、`CONNECT_RETRY_DELAY`
## 阶段 2.2:后端 API(用于 Qt GUI)
后端暴露一个本地 HTTP API 并自行管理重播工作器。
### 1) 安装并启动后端
```
scripts/trofeo_backend_service.sh install
scripts/trofeo_backend_service.sh start
scripts/trofeo_backend_service.sh status
scripts/trofeo_backend_service.sh logs
```
注意:
- `trofeo_backend_service.sh start` 会停止旧的 `trofeo-lcd.service`,以避免 `Resource busy` 冲突。
- 请勿将 `sudo` 与 `systemd --user` 一起使用。
### 2) 关键端点
```
# backend 和 worker 状态
curl -s http://127.0.0.1:18777/v1/status
# 启动 replay loop
curl -s -X POST http://127.0.0.1:18777/v1/start
# 停止 replay loop
curl -s -X POST http://127.0.0.1:18777/v1/stop
# 更改 frame index(如果正在运行则重启 worker)
curl -s -X POST http://127.0.0.1:18777/v1/set-frame \
-H 'Content-Type: application/json' \
-d '{"frame_index": 10}'
# 发送一张图像并返回 loop
curl -s -X POST http://127.0.0.1:18777/v1/send-image \
-H 'Content-Type: application/json' \
-d '{"path":"reference_frame_trcc.jpg","raw_jpeg_passthrough":false,"resume_loop":false}'
```
### 3) 后端配置
文件:`.trofeo-backend.env`
- `HOST`、`PORT`
- `PCAP_FILE`、`FRAME_INDEX`
- 计时/重试:`ACK_TIMEOUT_MS`、`INTER_PACKET_DELAY`、`FRAME_DELAY`、`CONNECT_RETRIES`、`CONNECT_RETRY_DELAY`
- `THEMES_FILE` (默认:`.trofeo-themes.json`)
- `PLAYLIST_FILE` (默认:`.trofeo-playlist.json`)
- `AUTOSTART=1` 在后端启动时立即开始重播
## 阶段 2.3:Qt GUI 客户端
用于控制后端 API 的最小化 Qt 面板:
- 状态运行时 (`mode/running/pid/uptime/error`)
- `start/stop/restart/scan`
- 更改 `frame_index`
- `send-image`
- 编辑基本后端配置 (`pcap`、计时)
### 启动 GUI
```
scripts/run_trofeo_gui.sh
```
可选择使用不同的后端 URL:
```
scripts/run_trofeo_gui.sh http://127.0.0.1:18777
```
文件:
- `trofeo_gui.py`
- `scripts/run_trofeo_gui.sh`
- `scripts/setup_gui_venv.sh`
## 阶段 2.4:主题管理器
后端和 GUI 包含一个主题预设管理器:
- 将预设保存到 `.trofeo-themes.json`
- `add/update/remove`
- 对选定的主题一键 `apply`
API 端点:
```
# 列出 theme
curl -s http://127.0.0.1:18777/v1/themes
# 添加或更新 theme
curl -s -X POST http://127.0.0.1:18777/v1/themes/add \
-H 'Content-Type: application/json' \
-d '{"name":"dark_ref","path":"reference_frame_trcc.jpg","raw_jpeg_passthrough":false}'
# 移除 theme
curl -s -X POST http://127.0.0.1:18777/v1/themes/remove \
-H 'Content-Type: application/json' \
-d '{"name":"dark_ref"}'
# 应用 theme(可选恢复 loop)
curl -s -X POST http://127.0.0.1:18777/v1/themes/apply \
-H 'Content-Type: application/json' \
-d '{"name":"dark_ref","resume_loop":false}'
# 应用时使用更长的 API 请求超时(可选)
curl -s -X POST http://127.0.0.1:18777/v1/themes/apply \
-H 'Content-Type: application/json' \
-d '{"name":"dark_ref","resume_loop":false,"timeout_s":90}'
```
## 阶段 2.5:播放列表 / 调度器
后端和 GUI 支持主题播放列表,通过按顺序切换预设来实现动画效果:
- 条目列表 `{name, duration_s}`
- `start/stop` 播放列表
- 保存至 `.trofeo-playlist.json`
API 端点:
```
# playlist 预览
curl -s http://127.0.0.1:18777/v1/playlist
# 添加条目(theme 必须存在)
curl -s -X POST http://127.0.0.1:18777/v1/playlist/add \
-H 'Content-Type: application/json' \
-d '{"name":"dark_ref","duration_s":3.5}'
# 按索引移除条目
curl -s -X POST http://127.0.0.1:18777/v1/playlist/remove \
-H 'Content-Type: application/json' \
-d '{"index":0}'
# 启动/停止 scheduler
curl -s -X POST http://127.0.0.1:18777/v1/playlist/start
curl -s -X POST http://127.0.0.1:18777/v1/playlist/stop
```
## 阶段 2.6:捆绑包导入 / 导出
捆绑包是一个存储在单个 JSON 文件中的 `themes + playlist` 配置快照。
API 端点:
```
# 在响应中将 bundle 导出为 JSON
curl -s http://127.0.0.1:18777/v1/bundle/export
# 将 bundle 保存到文件
curl -s -X POST http://127.0.0.1:18777/v1/bundle/save \
-H 'Content-Type: application/json' \
-d '{"path":".trofeo-bundle.json"}'
# 加载 bundle(替换)
curl -s -X POST http://127.0.0.1:18777/v1/bundle/load \
-H 'Content-Type: application/json' \
-d '{"path":".trofeo-bundle.json","merge":false}'
# 加载 bundle(合并)
curl -s -X POST http://127.0.0.1:18777/v1/bundle/load \
-H 'Content-Type: application/json' \
-d '{"path":".trofeo-bundle.json","merge":true}'
```
## 故障排除
**找不到设备:**
- 检查 `lsusb | grep 0416`
- 确保没有其他程序(例如在 Wine 下运行的 TRCC)正在使用该设备
- 尝试 `sudo python3 trofeo_lcd.py --test`
**权限被拒绝:**
- 安装 udev 规则(见上文)
- 或使用 `sudo` 运行
**图像完全没有变化:**
- 尝试 `--final-packet-mode pad-2048`
- 如果仍然失败,请尝试 `--final-packet-mode pad-4096`
- 使用 `--packet-debug` 比较数据包计划
- 需要每个数据包的 ACK,这现在是默认行为
- 也可以测试一个较短的延迟,例如 `--inter-packet-delay 0.01`
**图像显示异常:**
- LCD 分辨率为 1920×462——属于超宽屏。图像会自动调整大小
- 尝试使用 `--quality 90` 以获得更好的图像质量
- 如果你有一个已知完好的 TRCC JPEG,请使用 `--raw-jpeg-passthrough` 来独立于 JPEG 生成单独测试 USB 协议
## 逆向工程过程
1. 在 Windows 上使用 USBPcap + Wireshark,在 TRCC 运行时进行抓包
2. 该设备出现在与物理插口 (USBPcap4) 不同的单独 USB 根集线器 (USBPcap1) 上——集线器 (Terminus Tech 1a40:0101) 将其路由到了那里
3. 对 83,512 个数据包的分析表明,发送了 524 个 JPEG 帧,帧率约为 6.3 FPS
4. 每一帧都是标准的 baseline JPEG,分辨率为 1920×462,被分割成带有 16 字节专有头部的 USB 批量传输
5. 在抓包中未发现单独的复杂初始化序列;目前主要的不确定之处在于少量头部字节的确切含义和最终数据包的结构
## 许可证
这是一项出于互操作性目的而进行的独立逆向工程工作。
使用需自担风险。
标签:DEB/RPM, Flatpak打包, LCD屏幕, Linux驱动, PySide6, Python, Python脚本, Thermalright, USB设备, 主题管理器, 主题编辑器, 主题设计, 云资产清单, 利民, 动画引擎, 外设控制, 开源驱动, 无后门, 桌面GUI, 桌面定制, 水冷散热器, 硬件交互, 硬件驱动, 系统工具, 系统状态, 逆向工具, 逆向工程