Files
DTU-RemtoeLCD/README.md
T

231 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DTU-RemoteLCD - 远程显示通信工具
与 **RemoDispBus** 协议兼容的远程 LCD 显示与按键模拟工具。通过网络连接 DTU 装置,在 Web 浏览器中实时查看 LCD 画面并模拟按键操作,适用于调试、监控等场景。
## 功能特性
- **实时屏幕显示**:将装置 LCD 显存以 1bpp 位图形式拉取并渲染为 PNG
- **按键模拟**:支持方向键、确认、返回、复归等按键
- **WebSocket 传输**:采用 Socket.IO,服务端主动推送,约 100ms 刷新
- **跨平台**:浏览器访问,无需安装客户端
## 环境要求
- Python 3.7+
- 与 DTU 装置网络互通(默认端口 7003)
## 快速开始
### 1. 创建虚拟环境
在项目根目录下创建名为 `.venv` 的虚拟环境(需 Python 3.7+,已在 Python 3.14.7 验证):
```powershell
# Windows (PowerShell)
python -m venv .venv
```
```bash
# Linux / macOS
python3 -m venv .venv
```
### 2. 激活虚拟环境
```powershell
# Windows (PowerShell)
.\.venv\Scripts\Activate.ps1
```
```powershell
# Windows (CMD)
.venv\Scripts\activate.bat
```
```bash
# Linux / macOS
source .venv/bin/activate
```
> 激活后命令行前缀会出现 `(.venv)`;后续步骤也可直接使用 `.venv` 内的解释器而无需激活(见下方替代写法)。
### 3. 安装依赖
```bash
pip install -r requirements.txt
```
已验证的依赖版本:
| 依赖 | 版本 |
|------|------|
| Flask | 3.1.3 |
| Flask-SocketIO | 5.6.1 |
| python-socketio | 5.17.0 |
| Pillow | 12.3.0 |
| loguru | 0.7.3 |
### 4. 启动服务
```bash
python remo_disp_server.py [装置IP]
```
未激活虚拟环境时,可直接指定 venv 内解释器:
```powershell
# Windows (PowerShell)
.\.venv\Scripts\python.exe remo_disp_server.py
.\.venv\Scripts\python.exe remo_disp_server.py 192.168.253.3 # 预连接装置
```
```bash
# Linux / macOS
.venv/bin/python remo_disp_server.py [装置IP]
```
启动后访问 **http://localhost:8181**。
### 5. 退出虚拟环境
```bash
deactivate
```
### 使用说明
- 点击「连接」→ 输入装置 IP 和端口(默认 7003)→ 连接
- 左侧显示装置 LCD 画面(约 100ms 刷新)
- 右侧使用方向键、确认、复归、返回进行按键操作
- 点击「断开」断开与装置的连接
## 打包为 exe(Windows)
在项目目录下执行(建议先激活虚拟环境,或使用 `.venv` 内解释器):
```bash
# 安装 PyInstaller 后打包
pip install pyinstaller
python -m PyInstaller remo_disp_server.spec --noconfirm
```
或直接双击运行 `build_exe.bat`(其内部使用当前激活环境的 python,因此请先激活 `.venv` 再运行)。
打包完成后可执行文件为 **dist\\remo_disp_server.exe**。双击运行,或在命令行:
```bash
dist\remo_disp_server.exe
dist\remo_disp_server.exe 192.168.253.3
```
运行后浏览器访问 http://localhost:8181。日志会输出到控制台,同目录下会生成 `remo_disp.log`。
## 项目结构
```
DTU-RemoteLCD/
├── remo_disp_server.py # Web 服务(Flask + Socket.IO)
├── remo_disp_server.spec # PyInstaller 打包配置
├── build_exe.bat # 一键打包脚本(Windows)
├── remo_disp_ui.html # Web 前端页面
├── requirements.txt # Python 依赖
├── .venv/ # Python 虚拟环境(本地创建,不入库)
├── static/ # 静态资源(可选)
│ └── favicon.ico # 网站图标
└── README.md
```
## 协议说明
| 项目 | 说明 |
|------|------|
| **端口** | 7003 |
| **工具 → 装置** | `[0xAA][功能码][长度高][长度低][数据...][CRC]` |
| **装置 → 工具** | `[0xBB][功能码][长度高][长度低][数据...][CRC]` |
| **CRC** | 数据区逐字节异或,取低 8 位 |
```
刷新屏幕请求
AA 03 00 04 00 00 00 00 00
```
**客户端解析**(`remo_disp_server.py`):与 MCU 端(`ui_net_sink.c` 的 `rm_parser_feed`)
同款逐字节状态机解析:TAG(0xBB) 定界 → CMD → LEN(16 位大端) → DATA → CRC(数据区异或);
一帧可跨多个 TCP 包;长度字段设上限(`RM_MAX_RX_PAYLOAD`)防损坏长度锁死状态机;
失步后在下一个帧头自动重同步。两端解析策略一致,便于联调对拍。
**功能码**:
| 码 | 名称 | 说明 |
|----|------|------|
| 0 | KEEPLIVE | 保活 |
| 1 | INIT | 初始化,获取屏幕宽高、显存大小 |
| 2 | KEY | 按键 |
| 3 | LCDMEM | 读取显存(屏幕画面) |
### 延迟确认抑制(性能关键,2026-09-22 实测引入)
**现象**:拉取一帧 LCDMEM(3209B)原本中位 **68.8ms**,其中约 52ms 卡在帧尾那 137B 小段上。
**根因**(已在 DTU 侧台架确证,见 STM32F4-Base 仓库 `docs/测试说明/链路延迟测试说明.md` §7.3):
DTU 侧 CH395F 的 MSS=1024、发送 FIFO=1024B,因此每次 1024B 写恰好是**满 MSS 段**(立即发送);
而 3209B = 3×1024 + **137B**,最后这段是**次 MSS 段** → 芯片按 Nagle 规则扣住它,等前一数据被 ACK。
Windows 默认**延迟确认**把该 ACK 推迟 40~200ms,整帧因此被拖住。
**本工具的处理**(`remo_disp_server.py`,两条路径自动择一):
| 路径 | 条件 | 做法 | 实测帧耗时 |
|------|------|------|-----------|
| **A. 关闭延迟确认** | Windows(默认) | `WSAIoctl(SIO_TCP_SET_ACK_FREQUENCY=1)`(ctypes 直调;Python `socket.ioctl` 白名单不接受该码) | **20.7ms** |
| **B. 入站"轻推"兜底** | 非 Windows / A 失败 | 收帧途中若帧已开始却停顿 ≥5ms,向装置补发 1 个 `0x00`(装置只认帧头 0xBB,会丢弃)→ 该段捎带 ACK | **29.4ms** |
连接时会打印 `[connect] 延迟确认抑制: 已关闭(ACK frequency=1)` 或 `未生效(改用入站轻推兜底)`。
两条路径均**不需要改协议、不需要改装置固件**;日志级别调至 DEBUG 可看到 `[_recv] 轻推 1 字节捎带 ACK`。
## 按键映射
| 键名 | 协议码 | 说明 |
|------|--------|------|
| U | 0x02 | 上 |
| D | 0x40 | 下 |
| L | 0x10 | 左 |
| R | 0x08 | 右 |
| ENT | 0x20 | 确认 |
| ESC | 0x01 | 返回/取消 |
| RESET | 0x04 | 复归 |
| F2 | 0x80 | F2 |
## 依赖说明
| 依赖 | 用途 |
|------|------|
| Flask | Web 框架 |
| flask-socketio | WebSocket 支持 |
| python-socketio | Socket.IO 服务端 |
| Pillow | 1bpp 位图转 PNG |
| loguru | 结构化日志、错误追踪 |
## 日志与调试
- 后端使用 `loguru` 输出日志,默认会打印到控制台,并写入当前目录下的 `remo_disp.log`。
- 在 `remo_disp_server.py` 顶部有 `LOG_LEVEL` 常量:
- `LOG_LEVEL = "INFO"`:只输出 INFO/WARNING/ERROR 级别日志(默认)。
- `LOG_LEVEL = "DEBUG"`:输出包括 DEBUG 在内的详细调试信息。
- exe 版本(`remo_disp_server.exe`)同样会在运行目录生成 `remo_disp.log`,方便线下排查问题。
## 配置说明
- **Web 服务端口**:默认 8181,在 `remo_disp_server.py` 的 `main()` 中修改
- **装置端口**:默认 7003,连接时可输入自定义端口
- **Favicon**:将 `favicon.ico` 放入 `static/` 目录即可显示网站图标
- **接收耐心参数**(`remo_disp_server.py` 顶部,装置响应慢时可调节):
- `RECV_CHUNK_TIMEOUT = 3.0` — 单次 recv 最长等待秒数,超时仅表示"暂无数据",不算失败
- `RECV_FIRST_BYTE_TIMEOUT = 3.0` — 发出请求后等待响应首字节的秒数,全程无字节判为对端无应答,快速失败
- `RECV_TOTAL_BUDGET = 10.0` — 收齐一整帧的总时间预算(秒)。MCU 经 CH395F 按 1KB 块握手发送,整帧(如 3209 字节的 LCDMEM)可能耗时 0.5~3 秒以上;若装置更慢可适当调大
## 许可证
本项目仅供内部调试使用。