|
|
@@ -0,0 +1,174 @@
|
|
|
+# 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主域名>` 访问(具体子域解析规则由 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 端口。
|