# Distributed Collector Gateway - 使用说明

## 功能概述

本设备是一个分布式采集网关，支持通过 MQTT 远程控制 MODBUS RTU 轮询参数，并定期上报设备状态信息。

## SNTP 时间同步

设备内置 SNTP（Simple Network Time Protocol）时间同步功能，在网络连接成功后会自动同步系统时间。

### 时间同步特性

- **自动同步**：获取 IP 地址后自动启动 SNTP 客户端
- **多服务器支持**：配置了中国地区多个 NTP 服务器
  - `cn.pool.ntp.org` - 中国 NTP 服务器
  - `ntp1.aliyun.com` - 阿里云 NTP 服务器
  - `ntp.tencent.com` - 腾讯云 NTP 服务器
- **时区设置**：自动设置为北京时间（CST-8）
- **超时保护**：等待时间同步最长 10 秒，超时后使用本地时间继续运行

### 时间同步状态

设备会在启动日志中显示时间同步状态：

```
I (xxxx) SNTP_ESP: 初始化SNTP服务
I (xxxx) SNTP_ESP: 时区设置为北京时间 (CST-8)
I (xxxx) SNTP_ESP: 当前时间: 1970-01-01 08:00:00 Thursday  (同步前)
I (xxxx) SNTP_ESP: 时间同步成功: 2026-02-01 15:30:45  (同步后)
I (xxxx) SNTP_ESP: 时间同步完成
```

### 时间在系统中的应用

同步后的时间会在以下场景中使用：

1. **设备状态上报** - `update_time` 字段显示精确的同步时间
2. **数据采集记录** - 可扩展用于记录数据采集时间戳
3. **日志时间戳** - 方便调试和问题追踪

### 注意事项

- 需要网络连接正常才能进行时间同步
- 首次启动时，时间同步可能需要几秒钟
- 如果网络连接异常，设备会使用本地时间继续工作
- 时间同步完成后，系统时间会持续由 SNTP 守护进程维护

## 设备状态上报

### 上报主题

`CONFIG_MQTT_PUB_TOPIC` (在 `sdkconfig` 中配置)

### 上报类型

设备会定期上报以下类型的数据：

1. **设备状态**（`message_type: "device_status"`）
2. **MODBUS 采集数据**（`function_code: 3`）

### 设备状态上报格式

```json
{
  "message_type": "device_status",
  "mac_address": "D0:CF:13:1B:C3:94",
  "ip_address": "192.168.1.100",
  "chip_model": "ESP32-S3",
  "idf_version": "v5.5.2-dirty",
  "uptime": 16,
  "uptime_desc": "16秒",
  "free_heap": 312996,
  "status": "online",
  "status_desc": "在线",
  "update_time": "2026-02-01 15:30:45",
  "led1_state": 1,
  "led1_desc": "常亮",
  "led1_function": "网络状态灯",
  "led2_state": 4,
  "led2_desc": "心跳",
  "led2_function": "通信状态灯",
  "modbus_enabled": 1,
  "modbus_enabled_desc": "启用",
  "modbus_channel": 0,
  "modbus_channel_desc": "通道0 (UART0)",
  "modbus_slave_addr": 1,
  "modbus_interval": 1000,
  "heap_status": "充足"
}
```

### 字段说明

#### 基本信息

| 字段 | 类型 | 说明 |
|------|------|------|
| `message_type` | string | 消息类型：`"device_status"` |
| `mac_address` | string | 设备 WiFi MAC 地址（唯一标识） |
| `ip_address` | string | 设备 IP 地址 |
| `chip_model` | string | 芯片型号 |
| `idf_version` | string | ESP-IDF 版本 |

#### 运行状态

| 字段 | 类型 | 说明 |
|------|------|------|
| `uptime` | number | 设备运行时间（秒） |
| `uptime_desc` | string | 运行时间中文描述（如："1天5小时30分"） |
| `free_heap` | number | 剩余堆内存（字节） |
| `heap_status` | string | 内存状态：`"充足"` / `"一般"` / `"紧张"` |
| `status` | string | 设备状态：`"online"` |
| `status_desc` | string | 设备状态中文描述：`"在线"` |
| `update_time` | string | 状态更新时间（YYYY-MM-DD HH:MM:SS，通过 SNTP 同步） |

#### LED 状态

| 字段 | 类型 | 说明 |
|------|------|------|
| `led1_state` | number | LED1 状态：0=关闭, 1=常亮, 2=慢闪, 3=快闪, 4=心跳 |
| `led1_desc` | string | LED1 状态中文描述 |
| `led1_function` | string | LED1 功能：`"网络状态灯"` |
| `led2_state` | number | LED2 状态：同 LED1 |
| `led2_desc` | string | LED2 状态中文描述 |
| `led2_function` | string | LED2 功能：`"通信状态灯"` |

#### MODBUS 轮询状态

| 字段 | 类型 | 说明 |
|------|------|------|
| `modbus_enabled` | number | MODBUS 轮询是否启用（0=禁用, 1=启用） |
| `modbus_enabled_desc` | string | 轮询状态中文描述：`"启用"` / `"禁用"` / `"未配置"` |
| `modbus_channel` | number | 当前使用的 RS485 通道（0 或 1） |
| `modbus_channel_desc` | string | 通道中文描述 |
| `modbus_slave_addr` | number | 当前轮询的从机地址 |
| `modbus_interval` | number | 当前轮询间隔（毫秒） |

### 中文提示字段说明

带有 `_desc` 后缀的字段是中文提示字段，设计用于：
- **直接显示在 Web 页面上**，无需额外转换
- **不会被程序解析**，仅用于展示
- **提升用户体验**，让状态更直观

Web 页面可以选择显示：
- 程序解析字段（如 `led1_state`）用于逻辑判断
- 中文描述字段（如 `led1_desc`）用于界面显示

### 上报时机

- MQTT 订阅成功后立即上报一次
- 之后每隔 10 秒上报一次（可在 main.c 中修改 `mqtt_start_device_status_task(10000)` 的参数）

---

## MODBUS 控制说明

本设备是一个分布式采集网关，支持通过 MQTT 远程控制 MODBUS RTU 轮询参数。

## MQTT 控制指令

### 控制主题

发布控制指令到订阅主题：`CONFIG_MQTT_SUB_TOPIC` (在 `sdkconfig` 中配置)

### 指令格式 (JSON)

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000,
  "enabled": true
}
```

### 参数说明

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `command` | string | 是 | 固定为 `"modbus_poll"` |
| `channel` | number | 是 | RS485 通道号 (0 或 1) |
| `slave_addr` | number | 是 | 从机地址 (1-247) |
| `start_addr` | number | 是 | 起始寄存器地址 (0-65535) |
| `reg_count` | number | 是 | 读取寄存器数量 (1-125) |
| `interval` | number | 是 | 轮询间隔（毫秒），最小 100ms |
| `enabled` | boolean | 否 | 是否启用轮询，默认 `true` |

## 使用示例

### 示例 1：读取设备地址 1 的寄存器

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000
}
```

设备会每 1 秒读取一次从机地址 1，从寄存器 0 开始的 2 个寄存器。

### 示例 2：读取设备地址 10 的寄存器

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 10,
  "start_addr": 0,
  "reg_count": 5,
  "interval": 2000
}
```

设备会每 2 秒读取一次从机地址 10，从寄存器 0 开始的 5 个寄存器。

### 示例 3：读取指定范围的寄存器

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 10,
  "reg_count": 4,
  "interval": 500
}
```

设备会每 500ms 读取一次从机地址 1，从寄存器 10 开始的 4 个寄存器（地址 10, 11, 12, 13）。

### 示例 4：停止轮询

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000,
  "enabled": false
}
```

设置 `"enabled": false` 可以暂停轮询任务。

### 示例 5：使用 RS485 通道 1

```json
{
  "command": "modbus_poll",
  "channel": 1,
  "slave_addr": 5,
  "start_addr": 0,
  "reg_count": 10,
  "interval": 1000
}
```

使用 RS485 通道 1 进行轮询。

## 数据上报

### 上报主题

`CONFIG_MQTT_PUB_TOPIC` (在 `sdkconfig` 中配置)

### 上报数据格式 (JSON)

```json
{
  "channel": "RS485-1",
  "slave_addr": 1,
  "function_code": 3,
  "status": "success",
  "byte_count": 4,
  "register_count": 2,
  "registers": [
    556,
    998
  ]
}
```

### 字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `channel` | string | RS485 通道名称 |
| `slave_addr` | number | 从机地址 |
| `function_code` | number | MODBUS 功能码（始终为 3） |
| `status` | string | 状态：`"success"` 或 `"exception"` |
| `byte_count` | number | 数据字节数 |
| `register_count` | number | 寄存器数量 |
| `registers` | array | 寄存器值数组 |

### 异常响应格式

```json
{
  "channel": "RS485-1",
  "slave_addr": 1,
  "function_code": 131,
  "status": "exception",
  "exception_code": 2
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| `exception_code` | number | 异常码：<br>1 - 非法功能<br>2 - 非法数据地址<br>3 - 非法数据值<br>4 - 服务器设备故障 |

## 动态更新

每次发送 MQTT 控制指令都会立即更新轮询参数，无需重启设备。

### 更新流程

1. 发送新的控制指令
2. 设备接收并解析 JSON
3. 立即更新轮询配置
4. 下一次轮询使用新参数

## MQTT 控制指令

### 控制主题

发布控制指令到订阅主题：`CONFIG_MQTT_SUB_TOPIC` (在 `sdkconfig` 中配置)

### 指令格式 (JSON)

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000,
  "enabled": true
}
```

### 参数说明

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `command` | string | 是 | 固定为 `"modbus_poll"` |
| `channel` | number | 是 | RS485 通道号 (0 或 1) |
| `slave_addr` | number | 是 | 从机地址 (1-247) |
| `start_addr` | number | 是 | 起始寄存器地址 (0-65535) |
| `reg_count` | number | 是 | 读取寄存器数量 (1-125) |
| `interval` | number | 是 | 轮询间隔（毫秒），最小 100ms |
| `enabled` | boolean | 否 | 是否启用轮询，默认 `true` |

### 支持的指令类型

当前支持以下控制指令：

#### 1. MODBUS 轮询控制

```json
{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000,
  "enabled": true
}
```

#### 2. 设备状态上报间隔控制（可选扩展）

可通过修改 `main.c` 中的参数来调整状态上报间隔：

```c
mqtt_start_device_status_task(10000);  // 10000ms = 10秒
```

或运行时调用 API：

```c
mqtt_update_report_interval(5000);  // 5秒
```

## 注意事项

1. **轮询间隔最小为 100ms**，设置更小的值会被拒绝
2. **寄存器数量最大为 125**，MODBUS RTU 限制
3. **从机地址范围 1-247**，0 为广播地址
4. **通道号只能是 0 或 1**
5. 首次发送指令后会自动启动轮询任务

## 使用 mosquitto_cli 测试

### 发送控制指令

```bash
mosquitto_pub -h <broker_ip> -p 1883 -t <sub_topic> -m '{
  "command": "modbus_poll",
  "channel": 0,
  "slave_addr": 1,
  "start_addr": 0,
  "reg_count": 2,
  "interval": 1000
}'
```

### 监听数据上报

```bash
mosquitto_sub -h <broker_ip> -p 1883 -t <pub_topic>
```

## 硬件连接

### RS485 通道 0 (UART0)

| 引脚 | GPIO | 功能 |
|------|------|------|
| RO (RX) | GPIO 41 | RS485 接收器输出 |
| DE/RE | GPIO 42 | 数据使能/接收器使能 |
| DI (TX) | GPIO 44 | RS485 驱动器输入 |

### RS485 通道 1 (UART2)

| 引脚 | GPIO | 功能 |
|------|------|------|
| RO (RX) | GPIO 43 | RS485 接收器输出 |
| DE/RE | GPIO 2 | 数据使能/接收器使能 |
| DI (TX) | GPIO 1 | RS485 驱动器输入 |
