599 lines
11 KiB
Markdown
599 lines
11 KiB
Markdown
# STM8 Mesh 继电器主机通信协议
|
||
|
||
本文档供主机、网关和上位机开发人员使用。通信分为两层:JDY-28M Mesh外层和设备业务内层。
|
||
|
||
## 1. Mesh外层帧
|
||
|
||
### 1.1 主机发送给设备
|
||
|
||
```text
|
||
41 54 2B AA A1 | DST_H DST_L | A0 | BUSINESS | 0D 0A
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 长度 | 说明 |
|
||
|---|---:|---|
|
||
| `41 54 2B AA A1` | 5 | JDY-28M Mesh发送固定帧头 |
|
||
| `DST_H DST_L` | 2 | 目标短地址,高字节在前 |
|
||
| `A0` | 1 | Mesh数据通信标志,固定值 |
|
||
| `BUSINESS` | 变长 | 本文档第2节定义的业务帧 |
|
||
| `0D 0A` | 2 | Mesh帧尾,固定值 |
|
||
|
||
广播地址:
|
||
|
||
```text
|
||
FFFF
|
||
```
|
||
|
||
单播到短地址`000C`:
|
||
|
||
```text
|
||
DST_H = 00
|
||
DST_L = 0C
|
||
```
|
||
|
||
### 1.2 接收端串口收到的 Mesh 数据格式
|
||
|
||
```text
|
||
F1 DD 08/09/0A | SRC_H SRC_L | DST_H DST_L | BUSINESS | 0A
|
||
```
|
||
|
||
这是接收方 UART 侧看到的接收格式,不是设备或主机发送时使用的格式。
|
||
其中前两字节是发送方短地址,后两字节是接收方短地址;业务帧中不再重复
|
||
嵌入发送方或接收方地址。根据当前实测,第三字节可能显示为`08`、`09`或`0A`,
|
||
设备和上位机解析器应兼容这三种表现。帧尾通常为`0A`,部分模块或串口工具可能
|
||
显示为`0D 0A`;其中`0D`属于外层结束符,不能计入业务数据。
|
||
|
||
无论发送方是主机还是设备,发送时都统一使用第1.1节的格式:
|
||
|
||
```text
|
||
41 54 2B AA A1 | DST_H DST_L | A0 | BUSINESS | 0D 0A
|
||
```
|
||
|
||
### 1.3 长度限制
|
||
|
||
JDY-28M完整Mesh帧最大20字节。当前业务帧最大DATA长度为4字节:
|
||
|
||
```text
|
||
BUSINESS = CMD LEN DATA CRC16_H CRC16_L
|
||
```
|
||
|
||
业务帧长度为`4 + LEN`,完整外层发送帧长度为`14 + LEN`,按20字节限制计算,
|
||
`LEN`最大为6;当前继电器控制命令使用`LEN=1`。
|
||
|
||
## 2. 业务帧格式
|
||
|
||
```text
|
||
CMD | LEN | DATA[0..LEN-1] | CRC16_H CRC16_L
|
||
```
|
||
|
||
### 2.1 CRC16规则
|
||
|
||
```text
|
||
算法:CRC-16/MODBUS
|
||
多项式:0xA001
|
||
初始值:0xFFFF
|
||
输入反射:是
|
||
输出反射:是
|
||
计算范围:CMD、LEN和DATA
|
||
发送顺序:CRC高字节在前,低字节在后
|
||
```
|
||
|
||
CRC错误时设备直接丢弃整帧,不执行命令,也不返回错误应答。
|
||
|
||
## 3. 通用应答规则
|
||
|
||
普通请求的应答命令通常为:
|
||
|
||
```text
|
||
应答CMD = 请求CMD | 0x80
|
||
```
|
||
|
||
当请求通过 Mesh 广播地址 `FFFF` 发送时,设备不会立即同时回复。设备会
|
||
根据本机短地址和系统时间生成约20~170ms、按10ms分档的随机延时,然后向外层
|
||
接收帧中的发送方地址单播回复,以降低多个设备同时应答造成的网络拥塞。
|
||
单播请求仍然直接回复。
|
||
|
||
错误应答:
|
||
|
||
```text
|
||
CMD = E0
|
||
LEN = 01
|
||
DATA[0] = 错误码
|
||
```
|
||
|
||
当前常用错误码:
|
||
|
||
```text
|
||
0x02:DATA长度错误
|
||
0x03:DATA参数错误
|
||
0x05:EEPROM或配置写入失败
|
||
0x06:未知命令
|
||
```
|
||
|
||
## 3.1 `0x07` 设备上线广播
|
||
|
||
设备在本次上电完成基础初始化、蓝牙进入可通信状态后,向 Mesh 广播一次:
|
||
|
||
```text
|
||
CMD = 07
|
||
LEN = 00
|
||
```
|
||
|
||
发送方短地址由JDY-28M外层提供,因此该命令不需要 DATA,
|
||
其他设备收到后不回复。主机或其他设备可据此更新在线状态;如果后续长期
|
||
未收到该设备的上线或业务消息,可以结合应用层超时判断设备是否离线。
|
||
|
||
## 3.2 `0x10` 固件版本发现广播
|
||
|
||
主机发现服务器存在新固件后,向Mesh广播最新版本和固件长度。该命令只用于
|
||
设备发现,不会直接启动OTA。
|
||
|
||
```text
|
||
CMD = 10
|
||
LEN = 04
|
||
DATA[0] = 最新版本高字节
|
||
DATA[1] = 最新版本低字节
|
||
DATA[2] = 固件长度高字节
|
||
DATA[3] = 固件长度低字节
|
||
```
|
||
|
||
设备收到后比较:
|
||
|
||
```text
|
||
最新版本 > 当前版本:随机延时后回复
|
||
最新版本 <= 当前版本:静默丢弃,不回复
|
||
```
|
||
|
||
需要升级的设备返回:
|
||
|
||
```text
|
||
CMD = 90
|
||
LEN = 04
|
||
DATA[0] = 当前版本高字节
|
||
DATA[1] = 当前版本低字节
|
||
DATA[2] = 需要升级的版本高字节
|
||
DATA[3] = 需要升级的版本低字节
|
||
```
|
||
|
||
设备短地址由JDY-28M外层提供。主机收到多个`0x90`后,
|
||
生成待升级设备列表,由人工选择设备,再对选中的短地址执行后续OTA流程。
|
||
|
||
## 4. 主机下发的普通命令
|
||
|
||
### 4.1 `0x01` 继电器控制
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 01
|
||
LEN = 01
|
||
DATA[0] = 00:继电器断开
|
||
DATA[0] = 01:继电器吸合
|
||
```
|
||
|
||
业务帧结构示例:
|
||
|
||
```text
|
||
01 01 01 CRC16_H CRC16_L
|
||
```
|
||
|
||
设备执行后返回:
|
||
|
||
```text
|
||
CMD = 81
|
||
LEN = 01
|
||
DATA[0] = 当前继电器控制状态
|
||
```
|
||
|
||
```text
|
||
00:断开
|
||
01:吸合
|
||
```
|
||
|
||
注意:当前没有继电器触点反馈,设备只能确认PA1控制输出已经设置,不能确认触点机械动作是否真实成功。电流异常由故障检测功能另行上报。
|
||
|
||
以主机地址`34FA`向设备地址`35FA`发送吸合命令为例,主机串口发送:
|
||
|
||
```text
|
||
41 54 2B AA A1 35 FA A0 01 01 01 50 E0 0D 0A
|
||
```
|
||
|
||
设备接收侧看到:
|
||
|
||
```text
|
||
F1 DD 0A 34 FA 35 FA 01 01 01 50 E0 0A
|
||
```
|
||
|
||
设备向主机地址`34FA`回复:
|
||
|
||
```text
|
||
41 54 2B AA A1 34 FA A0 81 01 01 B8 E1 0D 0A
|
||
```
|
||
|
||
广播吸合时,将目标地址改为`FFFF`:
|
||
|
||
```text
|
||
41 54 2B AA A1 FF FF A0 01 01 01 50 E0 0D 0A
|
||
```
|
||
|
||
设备收到广播后执行控制,并向发送方地址回复`0x81`。广播应答增加随机错峰
|
||
延时,避免多个设备同时发送造成网络拥塞。
|
||
|
||
### 4.2 `0x02` 查询继电器状态
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 02
|
||
LEN = 00
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 82
|
||
LEN = 01
|
||
DATA[0] = 00:断开
|
||
DATA[0] = 01:吸合
|
||
```
|
||
|
||
### 4.3 `0x03` 查询实际电流
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 03
|
||
LEN = 00
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 83
|
||
LEN = 02
|
||
DATA[0] = 电流高字节
|
||
DATA[1] = 电流低字节
|
||
```
|
||
|
||
单位为mA,无符号整数,高字节在前。例如实际电流为`1000mA = 0x03E8`:
|
||
|
||
```text
|
||
DATA = 03 E8
|
||
```
|
||
|
||
### 4.4 `0x05` 查询设备短地址
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 05
|
||
LEN = 00
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 85
|
||
LEN = 02
|
||
DATA[0] = 短地址高字节
|
||
DATA[1] = 短地址低字节
|
||
```
|
||
|
||
例如设备短地址为`000C`:
|
||
|
||
```text
|
||
DATA = 00 0C
|
||
```
|
||
|
||
### 4.5 `0x06` 配置电流阈值
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 06
|
||
LEN = 02
|
||
DATA[0] = 阈值高字节
|
||
DATA[1] = 阈值低字节
|
||
```
|
||
|
||
单位为mA,默认值为100mA,设备保存到内部EEPROM。例如设置100mA:
|
||
|
||
```text
|
||
DATA = 00 64
|
||
```
|
||
|
||
设备成功返回:
|
||
|
||
```text
|
||
CMD = 86
|
||
LEN = 02
|
||
DATA = 原样返回设置的阈值
|
||
```
|
||
|
||
## 5. 固件版本查询
|
||
|
||
### 5.1 `0x0E` 查询当前固件版本
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 0E
|
||
LEN = 00
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 8E
|
||
LEN = 02
|
||
DATA[0] = 版本高字节
|
||
DATA[1] = 版本低字节
|
||
```
|
||
|
||
当前版本在主程序`include/app_config.h`中定义:
|
||
|
||
```c
|
||
#define FIRMWARE_VERSION 0x0001u
|
||
```
|
||
|
||
例如版本`0x0001`返回:
|
||
|
||
```text
|
||
DATA = 01 00
|
||
```
|
||
|
||
## 6. 故障与恢复上报
|
||
|
||
故障上报由设备主动发送:
|
||
|
||
```text
|
||
CMD = 04
|
||
LEN = 01
|
||
DATA[0] = 故障码或恢复码
|
||
```
|
||
|
||
故障码:
|
||
|
||
```text
|
||
0x00:继电器吸合但持续无有效电流
|
||
0x01:继电器断开但仍存在持续电流
|
||
0x02:ACS712采样异常
|
||
0x03:蓝牙初始化异常,仅LED指示,不通过Mesh上报
|
||
0x04:电流故障恢复
|
||
0x05:蓝牙故障恢复
|
||
0x06:采样异常恢复
|
||
0x07:继电器控制失败预留码
|
||
```
|
||
|
||
主机确认故障或恢复上报:
|
||
|
||
```text
|
||
CMD = 84
|
||
LEN = 01
|
||
DATA[0] = 被确认的故障码或恢复码
|
||
```
|
||
|
||
故障产生和恢复都需要持续至少10秒确认。故障恢复上报未收到主机确认时,设备每10秒重复上报,直到收到确认。
|
||
|
||
## 7. OTA命令
|
||
|
||
主机发送的OTA固件是`mesh_relay.bin`裸二进制文件,不是HEX文本文件,也不包含Bootloader。
|
||
|
||
### 7.1 `0x08` 进入OTA
|
||
|
||
```text
|
||
CMD = 08
|
||
LEN = 04
|
||
DATA[0] = 固件长度高字节
|
||
DATA[1] = 固件长度低字节
|
||
DATA[2] = 版本号高字节
|
||
DATA[3] = 版本号低字节
|
||
```
|
||
|
||
设备收到后擦除:
|
||
|
||
```text
|
||
OTA固件区:0x003000~0x004FFF
|
||
OTA元数据:0x005000~0x005FFF
|
||
```
|
||
|
||
成功应答:
|
||
|
||
```text
|
||
CMD = 88
|
||
LEN = 01
|
||
DATA[0] = 00
|
||
```
|
||
|
||
### 7.2 `0x09` 固件分包
|
||
|
||
正常数据包:
|
||
|
||
```text
|
||
CMD = 09
|
||
LEN = 04
|
||
DATA[0] = OFFSET高字节
|
||
DATA[1] = OFFSET低字节
|
||
DATA[2] = 固件数据1
|
||
DATA[3] = 固件数据2
|
||
```
|
||
|
||
最后只有1字节固件数据时:
|
||
|
||
```text
|
||
CMD = 09
|
||
LEN = 03
|
||
DATA[0] = OFFSET高字节
|
||
DATA[1] = OFFSET低字节
|
||
DATA[2] = 固件数据1
|
||
```
|
||
|
||
设备只接受等于当前期待偏移的分包。成功后返回:
|
||
|
||
```text
|
||
CMD = 89
|
||
LEN = 03
|
||
DATA[0] = 00:写入成功
|
||
DATA[1] = 下一期待偏移高字节
|
||
DATA[2] = 下一期待偏移低字节
|
||
```
|
||
|
||
如果偏移错误、Flash写入失败或OTA状态错误,返回非0状态码。主机应根据返回的期待偏移重传。
|
||
|
||
### 7.3 `0x0A` 发送完整固件CRC16
|
||
|
||
```text
|
||
CMD = 0A
|
||
LEN = 02
|
||
DATA[0] = 完整固件CRC16高字节
|
||
DATA[1] = 完整固件CRC16低字节
|
||
```
|
||
|
||
CRC16使用CRC-16/MODBUS,计算范围是整个裸二进制固件文件。
|
||
|
||
设备接收后返回:
|
||
|
||
```text
|
||
CMD = 8A
|
||
LEN = 01
|
||
DATA[0] = 00:接收成功
|
||
```
|
||
|
||
### 7.4 `0x0B` 请求校验并提交升级
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 0B
|
||
LEN = 00
|
||
```
|
||
|
||
设备从W25Q64重新读取完整固件并计算CRC16。校验成功后写入OTA元数据:
|
||
|
||
```text
|
||
0x005000:魔数
|
||
0x005004:固件长度
|
||
0x005008:固件版本
|
||
0x00500A:CRC16
|
||
0x00500E:镜像有效标志0xA5
|
||
0x00500F:升级请求0xA5
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 8B
|
||
LEN = 01
|
||
DATA[0] = 00:校验成功,已提交升级
|
||
DATA[0] = 01:校验失败
|
||
```
|
||
|
||
当`DATA[0]=00`时,设备已经完成外部Flash固件校验并写入升级元数据。
|
||
设备会先发送`0x8B`应答,随后自动触发MCU复位,由Bootloader把OTA固件
|
||
搬运到内部Flash的`0x8500`区域。`0x8B`不代表Bootloader已经搬运完成,
|
||
主机必须继续等待设备重新启动后主动上报的`0x0D`。
|
||
|
||
### 7.5 `0x0C` 取消OTA
|
||
|
||
主机发送:
|
||
|
||
```text
|
||
CMD = 0C
|
||
LEN = 00
|
||
```
|
||
|
||
设备返回:
|
||
|
||
```text
|
||
CMD = 8C
|
||
LEN = 01
|
||
DATA[0] = 00:取消成功
|
||
```
|
||
|
||
### 7.6 OTA传输顺序
|
||
|
||
```text
|
||
查询版本:0E
|
||
进入OTA:08
|
||
等待应答:88
|
||
发送分包:09
|
||
每包等待应答:89
|
||
发送完整CRC16:0A
|
||
请求校验提交:0B
|
||
等待校验结果:8B
|
||
设备发送8B后自动复位并升级
|
||
主程序启动后主动上报升级结果:0D
|
||
主机确认升级结果:8D
|
||
```
|
||
|
||
主机等待最终升级结果的建议规则:
|
||
|
||
```text
|
||
收到 0x8B=00 后,继续等待 0x0D,默认超时时间为20秒;
|
||
收到 0x0D 且 DATA[0]=0x01:升级成功;
|
||
收到 0x0D 且 DATA[0]不是0x01:升级失败;
|
||
超过20秒没有收到0x0D:升级失败;
|
||
收到升级结果后,主机向该设备短地址发送0x8D确认。
|
||
```
|
||
|
||
## 8. OTA升级结果上报
|
||
|
||
设备启动后从`0x005010`读取升级结果,蓝牙初始化成功后主动广播:
|
||
|
||
```text
|
||
CMD = 0D
|
||
LEN = 01
|
||
DATA[0] = 升级结果码
|
||
```
|
||
|
||
结果码:
|
||
|
||
```text
|
||
0x00:无结果
|
||
0x01:升级成功
|
||
0x02:固件CRC错误
|
||
0x03:内部Flash写入失败
|
||
0x04:写入后校验失败
|
||
0x05:固件长度非法
|
||
0x06:W25Q64读取失败
|
||
```
|
||
|
||
主机确认:
|
||
|
||
```text
|
||
CMD = 8D
|
||
LEN = 01
|
||
DATA[0] = 对应的升级结果码
|
||
```
|
||
|
||
设备收到确认后清除`0x005010`。
|
||
如果主机没有发送`0x8D`,设备不会清除结果记录,后续启动时可能再次上报,
|
||
便于主机发现升级结果确认丢失。
|
||
|
||
## 9. 整片初次烧录
|
||
|
||
整片初次烧录使用合并固件,不用于OTA:
|
||
|
||
```bash
|
||
make full
|
||
```
|
||
|
||
生成文件:
|
||
|
||
```text
|
||
build/stm8_mesh_full.hex:带绝对地址的完整HEX
|
||
build/stm8_mesh_full.bin:从0x8000开始的连续BIN
|
||
```
|
||
|
||
合并地址范围为:
|
||
|
||
```text
|
||
0x8000~0x84FF:Bootloader
|
||
0x8500~0x9FFF:主程序
|
||
```
|
||
|
||
使用ST-LINK V2/SWIM烧录时优先使用HEX文件;OTA升级仍只发送
|
||
`build/mesh_relay.bin`,不能发送合并固件。
|