AGENTS.md 9.3 KB

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.servicemysql.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 配置摘要:

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 才能连:

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 子域名:

[[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.4API 与系统 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。

验证

/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)。
  • 常用操作:

    # 通过 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 端口。