rs485-lpc/rs485_verification_plan.md

191 lines
12 KiB
Markdown
Raw Normal View History

# Pharos LPC RS485 串口链路验证 —— 设计与实施计划
> 目标:PC(USB-485)与 Pharos LPC 控制器共用一条 RS485 总线,实测验证
> **PC 发命令 → LPC Serial Input 触发器 → Lua 脚本 → 时间线(场景)整体亮度调节** 全链路,
> 以及 LPC→PC 反向应答。判据 = LPC 实际行为(灯光变化 / 日志 / 回包),非文档推断。
---
## 0. 前置条件
| 项 | 动作 | 状态 |
|----|------|------|
| P1 | USB-485 插在 **Ubuntu(192.168.58.7)**:`/dev/ttyUSB0` = FTDI FT232R(S/N B002S2QN),用户在 `dialout` 组,pyserial 可用 | ✅ 已通 |
| P2 | Windows 本机只做离线验证与远程驱动(Posh-SSH/SSH 免密已打通);Windows 板载 COM1~6 与 USB-485 无关(已实测:全为 ACPI\PNP0501) | ✅ 已确认 |
| P3 | LPC 上电联网,Designer 2 项目可烧录;串口模式选 **RS-485**,波特率两端一致(先 9600) | 待执行 |
| P4 | 项目内已建测试时间线 1~3(场景素材),可通过 HTTP API 或触发器手动启动 | 待执行 |
| P5 | 接线:USB-485 A/B/G ↔ LPC 端子 A(−)/B(+)/G,共地 | 待执行 |
| P6 | 参考依据:Serial_Integration.pdf(触发器/缓冲匹配机制)、Pharos Scripting Guide(`variable[1]`、`get_timeline()`) | 已读 |
---
## 1. 系统架构与接口定义
```
PC (Test-Rs485.ps1) ──USB── USB-485 ──A/B/G── RS485 总线 ──A/B/G── LPC 串口
主站,只发命令+收应答 Serial Input 触发器 → Lua → set_intensity
```
### 1.1 命令协议(ASCII,行尾 `\n`=0x0A 作帧界)
| 命令 | 方向 | 含义 | LPC 侧响应 |
|------|------|------|-----------|
| `BRT=NNN\n` | PC→LPC | 组 0 整体亮度 = NNN%(3 位补零) | 触发器 #2 挂 `lpc_serial_brt.lua` |
| `LUX=NNNNNN\n` | →LPC | 实测照度 0~200000 Lux(6 位补零)→ 闭环自动调光 | 触发器 #8 挂 `lpc_serial_lux.lua` |
| `LXT=NNNNNN\n` | →LPC | 设定目标照度(全局变量 `lux_target`,默认 500) | 触发器 #9 挂 `lpc_serial_lxt.lua` |
> 注:早期设想的固定五档 `BRT_x`、`OK\n` 应答、备选时间线方案均未采用;实测语法以 §6.4 为准。
### 1.2 LPC 触发器配置(实际部署)
| # | 类型 | Match string | 动作 |
|---|------|--------------|------|
| 2 | Serial Input | `BRT=<3d>` | Run Script(Quick Script = `lpc_serial_brt.lua`) |
| 8 | Serial Input | `LUX=<6d>` | Run Script(`lpc_serial_lux.lua`) |
| 9 | Serial Input | `LXT=<6d>` | Run Script(`lpc_serial_lxt.lua`) |
`lpc_serial_brt.lua` 实际内容见本目录文件(Designer 2 用 `get_trigger_variable(1)`,
`variable[1]` 为 v1 旧语法已废弃)。
---
## 2. 验证项分解(判据=实测)
| ID | 类型 | 验证内容 | 方法 | PASS 判据 |
|----|------|----------|------|-----------|
| S1 | 离线 | 测试工具帧编码正确 | `Verify-Rs485.ps1`:`BRT=80\n` → 7 字节含 0x0A,转义替换逻辑与工具一致 | 字节序列 42 52 54 3D 38 30 0A ✓ |
| S2 | 离线 | 行帧解析正确 | 多行混入 `\r\n` 混合行尾的缓冲拆分 | 每行完整、无串包 ✓ |
| S3 | 离线 | Lua 脚本结构完备 | 静态检查:引用 `variable[1]`、边界钳位、对每条时间线调用 `set_intensity` | 检查项全过 ✓ |
| S4 | 离线 | 工具参数模式完备 | `-ListPorts/-Send/-Listen/-Interactive` 帮助与执行路径 | 各模式可运行 ✓ |
| H1 | 硬件·单机 | USB-485 发送路径 | 已执行:端口打开/写入无错,**无自回显**(FT232+自动收发适配器不发回显属正常,判 TX 通需上真机) | ✅ TX 路径 OK |
| H1b | 硬件·总线 | 被动监听总线流量 | 已执行:9600 波特率监听 5s | 总线静默(LPC 未发包/未接入) |
| H2 | 硬件·总线 | 链路通 + 固定档触发 | 发 `BRT_50\n` | LPC 触发器日志出现对应触发;灯光变化 |
| H3 | 硬件·总线 | A/B 极性 | H2 无反应时对调 A/B 重测 | 对调后 H2 通过 |
| H4 | 硬件·总线 | 连续调光 + 通配符捕获 | 循环发 `BRT=0,25,50,75,100` | 灯光台阶变化;`variable[1]` 数值正确 |
| H5 | 硬件·总线 | 连续扫描鲁棒性 | 0→100 步进 5 循环 3 遍,行间隔 100ms | 全部命中无丢帧(串行缓冲匹配→清空机制下无粘包) |
| H6 | 硬件·总线 | 反向应答 | 触发器挂 Output Serial `OK\n`,PC `-Listen 3` | PC 收到 `OK` |
| N1 | 负例 | 波特率不一致 | 双端 9600/115200 错配 | 收乱码或无触发(预期现象,作对照) |
| N2 | 负例 | 串口模式误设 RS232 | 模式错配 | 无触发(预期现象) |
---
## 3. 执行阶段
- **阶段0 离线**(本机即可):运行 `Verify-Rs485.ps1` → S1~S4 全 PASS。
- **阶段1 单机**:USB-485 单独接总线 → H1(确认 PC 发送路径)。
- **阶段2 打通**:接入 LPC → P3 配置 → H2/H3(固定档,先证明物理链路与触发机制)。
- **阶段3 连续调光**:H4 → H5(通配符 + Lua 连续值)。
- **阶段4 反向与负例**:H6 应答;N1/N2 记录对照现象。
- **阶段5 收尾**:实测结论回填本表状态列,修正文档中"以版本说明为准"的待确认项
(通配符语法、`set_intensity` 可用性、老 DE-9 机型跳线)。
## 4. 风险与对策
| 风险 | 对策 |
|------|------|
| A/B 标各厂家不统一,接反无响应 | H3 对调实验,1 分钟排除 |
| 通配符语法随版本差异 | 首测用固定档触发器(零语法依赖),再看 Log Console 中实际收到的字节核对 Match string |
| 串行缓冲粘包/半包(连续发送过快) | 帧尾 `\n` + 行间隔 ≥100ms;H5 专门验证 |
| RS485 半双工冲突 | 约定 PC 为主站,LPC 仅应答,不同时发送 |
| `set_intensity` 不可用 | 备选:多条固定亮度时间线副本切换(§1.2) |
| 老 LPC(DE-9)RS485 为跳线配置 | 查对应安装指南;验证前确认跳线 |
| USB-485 被其他程序占用 | 探测工具报告"端口被占",先关 SymphoLight 等软件 |
## 6. 实测发现与自动闭环验证(2026-10-09 更新)
### 6.1 已确认的环境事实
| 项 | 结果 |
|----|------|
| LPC 位置 | **192.168.58.109**(Web 标题 `Pharos LPC 0140650`,MAC `00:13:B2:01:39:1A`) |
| HTTP API | 免认证可用;`GET /api/timeline`、`GET /api/group`、`GET /api/trigger`、`POST /api/timeline`(HTTP 204)实测通过 |
| 项目内容 | 1 条时间线(Timeline 1,未运行);**1 个组:num=0 "All Fixtures"(level:100)**;触发器仅 1 个 Startup(eDMX 直通)——**无串行触发器** |
| 亮度 API 修正 | Lua `Timeline` 对象无 `set_intensity`;正确做法 = `get_group(n):set_master_intensity(level[,fade[,delay]])`(0.0~1.0),`/api/group` 的 `level` 字段可回读 → **闭环判据** |
| 灯具 | `/api/fixtures` 返回空 —— 调组亮度可能无可见光效,但 level 回读仍可全自动判定 |
### 6.2 闭环自动验证(Ubuntu 上 `loop_test.sh`)
```
发送 BRT=<v> (RS485) → sleep 1 → GET /api/group 读 level → 与 v 比对 → PASS/FAIL
用法: bash loop_test.sh 80 (单点)
bash loop_test.sh loop (扫 100/0/50/80/25 汇总 PASS/FAIL)
```
### 6.3 最终验证结果(2026-10-09 全部通过)
| 验证项 | 结果 |
|--------|------|
| 离线 S1~S4 | ✅ 16/16 PASS(Verify-Rs485.ps1) |
| H1 RS485 发送路径 | ✅(FT232R 无自回显属正常,LPC 日志证实收包) |
| H2/H3 链路+触发 | ✅ LPC 日志 "RS485 input (N bytes)" + "Trigger 2 (Serial Input)" |
| H4/H5 连续调光+鲁棒性 | ✅ loop_test.sh:PASS=5 FAIL=0(100/0/50/80/25) |
| H6/N1/N2 | 按需(协议已闭环,负例未单测) |
### 6.4 实测确认的关键语法(文档未载,逆向自 pharos_designer.exe)
| 项 | 结论 |
|----|------|
| 串行通配符 | 尖括号语法 `<...>`:`<3d>` = 恰好 3 位数字;类型 `[cCdDxXsS]`;可带 `<n,type>` 变体;`<d>` 仅匹配 1 个字符;`?` `*` `#` `%` 均为普通字符 |
| 触发变量 | Designer 2 用 `get_trigger_variable(1)`(v1 的 `variable[1]` 已废弃) |
| 亮度 API | Timeline 无 set_intensity;用 `get_group(n):set_master_intensity(level)` |
| 最终协议 | PC 发 `BRT=NNN\n`(3 位补零,0~100)→ 组 0 整体亮度 = NNN% |
| 照度协议 | 发 `LUX=NNNNNN\n`(6 位补零,0~200000 Lux,仁科大量程)→ 闭环自动调光(lpc_serial_lux.lua);`LXT=NNNNNN\n` 设定目标照度(默认 500 Lux,全局变量 lux_target) |
| 照度实测 | 2026-10-09 闭环验证通过:BRT=050→level 50;LXT=000300 设目标 300 Lux;LUX=001000(超目标)→ 亮度 50→30(降 20% 步进);LUX=000100(低于目标)→ 30→50(升 20%),方向与步进均正确。触发器 #8 Lux Auto Dim / #9 Lux Target Set 已上传 LPC |
| LX2 指示模式 | 触发器 #10 Lux Display Map `LX2=<6d>`:照度→亮度对数映射 `level=log(lux+1)/log(200001)`(0.5s 渐变),灯即照度计;lx2_test.sh 8/8 PASS(0/10/100/500/1k/10k/100k/200k Lux → 0/20/38/51/57/75/94/100%) |
| 仁科寄存器 | 大量程 0~200000 Lux 型用 32 位寄存器对 0x0002(hi)/0x0003(lo),单位 1 Lux(esp32-voice src/config.rs MODBUS_ILLUMINANCE_REG=0x0002, COUNT=2;poll_illuminance() in src/modbus/master.rs);0~65535 型 reg 0x0006 单位 1 Lux |
| 工具链 | Designer 2 GUI 全程自动化操作(截图视觉闭环 + SendKeys/剪贴板);Download/Upload 同步项目 |
API 无法增改项目触发器,须在 Designer 2 中完成并上传:
1. LPC 设置:Serial Port → 模式 **RS-485**,波特率 **9600**(已完成)
2. 触发器:#2 `BRT=<3d>`、#8 `LUX=<6d>`、#9 `LXT=<6d>`,各挂对应 Quick Script(已完成并上传)
3. 回归:`bash loop_test.sh loop`(BRT 5 项)+ `bash lux_test.sh`(照度闭环 7 项)
## 7. 附录
### 7.1 工程文件
| 文件 | 用途 |
|------|------|
| `test_rs485.py` | **Ubuntu 侧主工具**(pyserial):`-l` 枚举 / `-s` 发送 / `-w` 监听 / `-i` 交互 |
| `Test-Rs485.ps1` | Windows 备用工具(当前无 USB-485,仅保留) |
| `lpc_serial_brt.lua` / `lpc_serial_lux.lua` / `lpc_serial_lxt.lua` | LPC 触发器 #2/#8/#9 Quick Script |
| `Verify-Rs485.ps1` | Windows 离线验证 S1~S4(已通过 16/16) |
| `loop_test.sh` / `lux_test.sh` | Ubuntu 闭环回归:BRT 5 项 / 照度 7 项(均已 PASS) |
| `api_smoke.sh` | LPC HTTP API 冒烟测试 |
| `0140650.pd2` | Designer 2 项目文件(已上传 LPC) |
| `ui.ps1` / `Run-Remote.ps1` / `Get-Remote.ps1` | GUI 自动化 / SSH 远程执行 / SFTP 下载 |
| `docs/` | 仁科 20w 说明书 PDF + Modbus 帧格式实测笔记 |
### 7.2 常用命令(Ubuntu 192.168.58.7)
```bash
# 枚举串口,确认 /dev/ttyUSB0 存在且权限可读(不在 dialout 组时用 sudo)
python3 test_rs485.py -l
# H1 单机自发自收(确认适配器 TX 路径)
python3 test_rs485.py -p /dev/ttyUSB0 -b 9600 -s 'BRT=80\n' -w 2
# 手动亮度 / 交互
python3 test_rs485.py -p /dev/ttyUSB0 -b 9600 -s 'BRT=080\n' -w 3
python3 test_rs485.py -p /dev/ttyUSB0 -b 9600 -i
# 照度闭环:设目标 500 Lux / 上报实测照度
python3 test_rs485.py -p /dev/ttyUSB0 -b 9600 -s 'LXT=000500\n'
python3 test_rs485.py -p /dev/ttyUSB0 -b 9600 -s 'LUX=001000\n'
# 回归
bash loop_test.sh loop # BRT 5 项
bash lux_test.sh # 照度闭环 7 项
# 无 pyserial 时
sudo apt install python3-serial
```
### 7.3 参考资料
- Serial Integration 应用说明:https://dl.pharoscontrols.com/documentation/application_notes/Serial_Integration.pdf
- 针脚定义(Pinouts):https://dl.pharoscontrols.com/documentation/single_line_diagrams/9401%20Detailed%20wiring%20examples_pinouts.pdf
- Designer 2 在线帮助:https://dl.pharoscontrols.com/software_help/designer2/Default.htm
- Designer 2 用户手册(A4 PDF):https://dl.pharoscontrols.com/documentation/manuals/Pharos%20Designer%202%20User%20Manual%20-%20A4.pdf