# DZXJ DTU 项目 AGENTS.md 本文件记录本项目开发/维护时需要注意的关键信息,方便下次快速上手。 注意每次改动需要更新本文件,这非常重要!!!!!!!! ## 项目概览 - **设备**:Rockchip RK3588 嵌入式开发板(`hostname=ido`,aarch64),作为 DTU(数据传输单元)运行。 - **应用**:`dzxj_dtu` —— 串口(MODBUS-RTU/RS485) ↔ MQTT 网关,带 Web 管理界面(Flask + Socket.IO + Vue 前端)。 - **代码部署位置(设备上)**:`/root/dzxj_dtu/`,代码库在本仓库。 - **设备 IP**:eth1 = `192.168.199.149`(远程管理用),wlan0 = `192.168.10.3`,实际 IP 见串口屏显示。 - **系统服务**: - `dzxj_dtu.service` → `/root/dzxj_dtu/backend/app.py`(venv Python) - `frpc.service` → FRP 客户端(stcp 映射 SSH + http 域名映射 80 端口) - `easytier.service` → EasyTier VPN(组网) - `mosquitto.service` → 本地 MQTT Broker(:1883) - `nginx.service`、`mysql.service` 等 ## 远程访问方式 ### 方式 1:FRP stcp 映射 SSH(推荐) 149 上跑 **frpc**(仅客户端,不跑 frps),通过 stcp 模式把本地 22 端口映射到公网 frps 服务器。 - frps 服务器:`device.wenhq.top:8580`,token = `gx@12345678`(frps 部署在外部,不在本设备)。 - 配置:`/opt/frp/frpc.toml` - 服务:`systemctl status frpc` / `systemctl restart frpc` frpc 配置摘要: ```toml serverAddr = "device.wenhq.top" serverPort = 8580 auth.method = "token" auth.token = "gx@12345678" [[proxies]] name = "149-ssh" type = "stcp" localIP = "127.0.0.1" localPort = 22 secretKey = "149-ssh-sk" ``` **访问端(你的电脑)需要配置 visitor** 才能连: ```toml serverAddr = "device.wenhq.top" serverPort = 8580 auth.method = "token" auth.token = "gx@12345678" [[visitors]] name = "149-ssh" type = "stcp" serverName = "149-ssh" sk = "149-ssh-sk" bindPort = 6000 ``` 然后 `ssh root@127.0.0.1 -p 6000`(密码 `123456`)即可连到 149 的 22 端口。 ### 方式 2:FRP http 域名映射 80 端口 frpc 里已有 http 代理,把本地 80 端口映射成 `pxj` 子域名: ```toml [[proxies]] name = "149-http" type = "http" localIP = "127.0.0.1" localPort = 80 subDomain = "pxj" ``` 通过 `http://pxj.` 访问(具体子域解析规则由 frps 端配置决定)。 ### 方式 3:EasyTier VPN 组网 - 配置:`/etc/systemd/system/easytier.service` - 网络名 `pxjdtu` / secret `123456`,peer 服务器 `remote-mange.dnnbuild.com:25700` - **必须 `--no-tun` 模式**(本设备内核无 tun 模块,`/dev/net/tun` 不可用,`modprobe tun` 失败)。 - 用 `--port-forward tcp://0.0.0.0:2222/10.11.21.0:22` 做端口转发,访问 `127.0.0.1:2222` 可到 MacBook 的 SSH(仅演示,10.11.21.0 是当时 peer 的虚拟 IP,会变)。 - 状态查询:`/home/admin/easytier-cli node` / `peer` / `peer-center`。 - **已知限制**:`--no-tun` 模式下虚拟 IP(10.11.21.0/24)不会加到系统路由表,`ping`/直接 `ssh 10.11.21.x` 不通;要访问 peer 服务只能走 port-forward。想要完整虚拟网需在有 TUN 支持的设备上跑 easytier。 ## DHT11 温湿度读取 ### 工作方式(重点!) 读取优先走 **C 语言读取器**,Python gpiod 只作回退。 1. **C 二进制读取器**:`/root/dzxj_dtu/backend/scripts/dht11_reader` - 源码:`backend/scripts/dht11_reader.c`(用 libgpiod C API,open-drain + 高速采样) - 编译命令:`gcc -O2 -o dht11_reader dht11_reader.c -lgpiod`(设备已装 `libgpiod-dev`) - 用法:`dht11_reader [chip] [line]`,默认 `gpiochip0 19` - 输出 JSON:`{"valid":true,"humidity":43,"temperature":31,"raw":[...],"checksum":76}`(温湿度为整数,无小数) - **二进制必须存在**,否则回退到 Python gpiod,而 venv 里的 gpiod API 不兼容,会读取失败(症状:日志报 `module 'gpiod' has no attribute 'Chip'` 或读到 0.0) 2. **Python gpiod 回退**:`backend/modules/dht11_sensor.py` - 设备 venv 装的是 pip `gpiod 1.5.4`,**API 与系统 apt 的 python3-libgpiod 1.4.x 完全不同**: - `gpiod.Chip(...)`(1.4.x) vs `gpiod.chip(...)`(1.5.x) - `gpiod.LINE_REQ_DIR_OUT` vs `gpiod.line_request.DIRECTION_OUTPUT` - `line.request(consumer=..., type=...)` vs `line.request(type=...)`(1.5.x 无 consumer 参数) - 1.5.x chip 对象无 `close()` 方法 - 代码已加了兼容层(`_gpiod_chip_class`、常量映射),但**优先靠 C 二进制**,尽量别依赖这个回退。 ### 配置(环境变量,无默认值则使用 config.py 默认) | 变量 | 默认 | 说明 | | ----------------------- | ------------- | ------------------------------ | | `DHT11_ENABLED` | `true` | 是否启用 | | `DHT11_GPIO_PIN` | 无 | GPIO line 号(设备上为`19`) | | `DHT11_GPIO_CHIP` | `gpiochip0` | GPIO chip | | `DHT11_POLL_INTERVAL` | `5` | 读取间隔秒 | | `DHT11_SIMULATE` | `false` | 是否模拟数据 | - GPIO 19(RK 平台 GPIO0_C3 系)接 DHT11 data 脚,DHT11 需上拉电阻(模块自带)。 - 若 `DHT11_ENABLED=true` 但未配 GPIO_PIN 且非模拟,会跳过传感器启动并打 warning。 ### 验证 ```bash /root/dzxj_dtu/backend/scripts/dht11_reader gpiochip0 19 # 期望输出: {"valid":true,"humidity":xx,"temperature":xx,...} journalctl -u dzxj_dtu | grep -E 'DHT11|环境传感器' # 正常: "DHT11 本地传感器数据已处理: 温度=31.0°C, 湿度=43.0%" ``` ## MQTT 相关(重点) - 本地 mosquitto broker 跑在 `:1883`。 - 应用连接的是**外部 MQTT**(经网页配置):当前 `xt.wenhq.top:8581`。 - **MQTT 配置会持久化**到 `/root/dzxj_dtu/backend/mqtt_config.json`: - 通过 Web `/api/mqtt/connect` 连接成功后自动 `save_mqtt_config()`。 - 服务重启后 `__main__` 里会 `auto_connect_mqtt()` 读取该文件自动重连。 - 若没有该文件或连接失败,MQTT 不会自动连接,需在网页手动连一次。 - 关键函数(`backend/app.py`):`save_mqtt_config()` / `load_mqtt_config()` / `auto_connect_mqtt()` / `mqtt_connect()`(API `/api/mqtt/connect`)。 - MQTT 客户端封装:`backend/modules/mqtt_client.py`(paho-mqtt==1.6.1,重连无限尝试,`max_reconnect_attempts=0`)。 - 主题前缀默认 `线架系统`,DTU 协议主题格式:`{topic_prefix}/{customer_id}/dtu/{dtu_id}/{topic}`。 ## 串口 / RS485 - 设备串口:`/dev/ttyS8 @ 9600`,连接配置持久化在 `/root/dzxj_dtu/backend/serial_config.json`。 - 服务启动时 `auto_connect_serial()` 自动重连。 - MODBUS-RTU 协议实现在 `backend/modules/modbus_rtu.py`;串口封装在 `backend/modules/serial_port.py`。 - 注意:硬件通信固定 6 字节(12位) 卡号,MQTT 边界拼成 8 字节(16位) `jumper_uid`(前 4 位 DTU 号 + 后 12 位卡号),适配逻辑在 `app.py` 的 `_to_mqtt_jumper_uid` / `_from_mqtt_jumper_uid`。 ## 部署与更新 - 代码在 `/root/dzxj_dtu/`,后端在 `backend/`,前端构建产物在 `backend/static/`。 - 服务使用 venv:`/root/dzxj_dtu/venv/bin/python`(依赖见 `backend/requirements.txt`,注意 gpiod 是 pip 1.5.4)。 - 常用操作: ```bash # 通过 frp 连上后 systemctl restart dzxj_dtu journalctl -u dzxj_dtu -f systemctl status frpc easytier mosquitto ``` - 更新代码后用 scp 覆盖对应文件再 `systemctl restart dzxj_dtu`。 - 本地工作目录 = 本仓库;改代码后注意同步到设备。 ## 排障速查 | 症状 | 排查 | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | SSH 连不上 149 | 先`ping 192.168.199.149`;再试 `ssh -p 6000 root@127.0.0.1`(frp stcp);若直接 IP 通但 6000 不通,查 `systemctl status frpc` | | frpc 起不来 | `journalctl -u frpc`;注意 stcp 配置字段是 `secretKey`(不是 `sk`),老版本报 `unknown field "sk"` | | 温湿度读到 0.0 或报 gpiod 错 | 确认`dht11_reader` 二进制存在且可执行;`dht11_reader gpiochip0 19` 手测 | | easytier 组网后虚拟 IP 不通 | `--no-tun` 模式限制,虚拟 IP 不进路由表,只能走 port-forward | | MQTT 重启后没自动连 | 确认`/root/dzxj_dtu/backend/mqtt_config.json` 存在;存在则看 `auto_connect_mqtt` 日志 | ## 注意事项(重要) - **不要在本设备上装/启用 tun**:内核无 tun 模块,easytier 必须 `--no-tun`。 - **frps 不在本设备**:只跑 frpc,别在本机部署 frps 或删除 frpc。 - **stcp 配置字段是 `secretKey`**,不是 `sk`(frp 0.70 报错)。 - 设备的 root 密码是 `123456`(本机/测试环境)。 - 改 `app.py` 后必须重启 `dzxj_dtu` 才生效。 - Web 管理页面端口 `5001`(Flask),前端由 nginx 提供 80 端口。