# 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 秒以上;若装置更慢可适当调大 ## 许可证 本项目仅供内部调试使用。