Appearance
GATT配网协议
本文档定义基于GATT的设备配网协议规范,适用于APP端开发者、设备端固件开发者和服务端开发者。通过标准BLE GATT服务实现设备的WiFi和MQTT配置。
协议流程
核心优势
相比传统的BluFi等配网协议,本协议具有以下优势:
- ✅ 简洁性:无BluFi等协议的复杂帧格式和序列号管理,直接GATT读写JSON数据
- ✅ 无需特殊SDK:APP端使用标准GATT API即可,避免依赖特定厂商SDK
- ✅ 调试友好:明文JSON数据传输,易于调试和问题排查
- ✅ 兼容性好:基于标准BLE GATT协议,跨平台一致性强
- ✅ 可靠性高:避免BluFi序列号错误等协议层问题
设备发现与连接
设备广播格式及APP扫描过滤规则请参考设备广播规范。
设备作为GATT Server,手机作为GATT Client进行连接。
GATT服务定义
服务UUID
0000FFFF-0000-1000-8000-00805F9B34FB
特征定义
| 特征用途 | UUID | 属性 | 描述 |
|---|---|---|---|
| 配置写入 | 0000FF01-... | Write/WriteNR | APP写入JSON配置或命令 |
| 状态通知 | 0000FF02-... | Read/Notify | 设备通知状态更新 |
完整UUID格式:0000FF0x-0000-1000-8000-00805F9B34FB
连接步骤
- 连接GATT设备
- 发现Service
0xFFFF - 发现Characteristic
0xFF01和0xFF02 - 订阅
0xFF02通知(写入CCC描述符0x0001) - 可选:请求更大MTU(建议247字节)
数据协议
数据格式规范
所有数据传输采用魔数+长度前缀格式:
┌──────────────────┬──────────────────────┬─────────────────────┐
│ 魔数 (2字节) │ 长度 (2字节, 小端序) │ JSON数据 (UTF-8) │
│ 0x46 0x44 │ │ │
└──────────────────┴──────────────────────┴─────────────────────┘| 字段 | 长度 | 类型 | 说明 |
|---|---|---|---|
| 魔数 | 2字节 | 固定值 | 固定为 0x46 0x44("FD"的ASCII码),用于识别协议 |
| JSON长度 | 2字节 | uint16_t | 小端序,表示后续JSON数据的字节长度(最大65535字节) |
| JSON数据 | 可变 | UTF-8字符串 | JSON格式的配置或状态数据,不包含null终止符 |
分包处理
当"魔数+长度+JSON数据"总长度超过MTU时,需要应用层实现分包发送和接收重组。由于BLE GATT保证数据有序传输,应用层只需:
- 发送端:将数据按MTU大小切分,依次发送
- 接收端:
- 先读取2字节魔数,验证是否为
0x46 0x44,不匹配则拒绝接收 - 再读取2字节长度,验证长度是否合理(建议最大10KB)
- 根据长度持续接收后续数据包,直到接收完整长度后再进行JSON数据解析
- 先读取2字节魔数,验证是否为
配网状态检查
在开始配网前,APP应先查询设备当前状态,避免重复配网或配网冲突。
超时时间:5秒(设备应在5秒内响应,否则APP视为设备异常)
查询请求(APP → 设备)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为check_status |
JSON示例:
json
{
"action": "check_status"
}查询响应(设备 → APP)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为check_status_result |
| configured | 布尔值 | 是 | 设备是否已配网 |
| network_connected | 布尔值 | 是 | 网络是否连接 |
| device_info | 对象 | 是 | 设备信息 |
| device_info.bluetooth_mac | 字符串 | 是 | 蓝牙MAC地址 |
| device_info.firmware_version | 字符串 | 是 | 固件版本号 |
| device_info.model | 字符串 | 是 | 设备型号 |
| device_info.serial_number | 字符串 | 是 | 厂商序列号 |
| device_info.network_mac | 字符串 | 是 | 网络MAC地址 |
JSON示例:
json
{
"action": "check_status_result",
"configured": false,
"network_connected": true,
"device_info": {
"bluetooth_mac": "3c:84:27:c1:5d:00",
"firmware_version": "1.0.0",
"model": "XHJ-Gateway",
"serial_number": "GW01_c39eae",
"network_mac": "aa:bb:cc:dd:ee:ff"
}
}扩展说明
device_info 说明: 对象必须包含 bluetooth_mac、firmware_version、model、serial_number、network_mac 五个基础字段,业务可根据需求扩展其他字段。
bluetooth_mac 说明: 蓝牙接口的物理地址,用于蓝牙通信时的设备标识。
network_mac 说明: 联网接口的物理地址。指局域网通信中与业务 IP 绑定的真实 MAC 地址(即通过 ARP 协议获取的地址)。
serial_number 说明: 每台设备的序列号唯一且固定不变,设备上电、重置等操作不会改变序列号。
WiFi扫描(可选)
此功能为可选实现,允许APP先获取设备周围的WiFi列表,然后让用户选择要连接的WiFi网络。
超时时间:15秒(设备扫描WiFi需要4-6秒,加上处理和传输时间)
设计原因
手机扫描需要授权相关复杂操作对用户不友好,且手机可扫描到的WiFi网络,设备不一定支持。例如:
- 5GHz WiFi网络(部分设备仅支持2.4GHz)
- 特定加密方式(如WPA3,部分单片机不支持)
- 企业级认证(如802.1X)
通过设备端扫描,仅展示设备实际可连接的WiFi网络,可显著提升配网成功率和稳定性。
扫描请求(APP → 设备)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为scan_wifi |
JSON示例:
json
{
"action": "scan_wifi"
}扫描结果(设备 → APP)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为scan_wifi_result |
| status | 整型 | 是 | 0=成功,0xFF=失败 |
| networks | 数组 | 是 | WiFi网络列表 |
| networks[].ssid | 字符串 | 是 | WiFi名称 |
| networks[].rssi | 整型 | 是 | 信号强度(dBm) |
JSON示例:
json
{
"action": "scan_wifi_result",
"status": 0,
"networks": [
{
"ssid": "MyWiFi-5G",
"rssi": -40
},
{
"ssid": "Office-Network",
"rssi": -65
}
]
}实现建议
- 设备收到扫描请求后,建议扫描时长为4-6秒
- RSSI值为负数,数值越大信号越强(如-40优于-65)
- 建议按信号强度降序排列网络列表
- 重复的SSID应去重,保留信号最强的
配网数据传输
配网请求(APP → 设备)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为provision |
| timestamp | 整型 | 是 | 当前时间戳(秒),用于设备端设置时间 |
| wifi | 对象 | 否 | WiFi配置,若设备已配网则无需传输 |
| wifi.ssid | 字符串 | 否 | WiFi网络名称 |
| wifi.password | 字符串 | 否 | WiFi密码,开放网络传空字符串 |
| mqtt | 对象 | 是 | MQTT配置 |
| mqtt.broker | 字符串 | 是 | MQTT服务器地址,格式:mqtt://host:port |
| mqtt.client_id | 字符串 | 是 | MQTT客户端ID |
| mqtt.username | 字符串 | 是 | MQTT用户名 |
| mqtt.password | 字符串 | 是 | MQTT密码 |
| mqtt.topic_prefix | 字符串 | 否 | 主题前缀,默认nodes |
| config | 对象 | 是 | 业务配置 |
JSON示例:
json
{
"action": "provision",
"timestamp": 1710000000,
"wifi": {
"ssid": "YourWiFiName",
"password": "YourWiFiPassword"
},
"mqtt": {
"broker": "mqtt://192.168.1.100:1883",
"client_id": "esp32-gateway-001",
"username": "mqtt_user",
"password": "mqtt_pass",
"topic_prefix": "nodes"
},
"config": {}
}config 扩展说明
config 对象用于传递业务相关配置,设备端需持久化存储。字段内容由业务层自定义,协议不做强制要求,如无业务需求config需传空对象({})不可省略。
配网响应(设备 → APP)
设备会在配网过程中多次发送状态更新:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | 字符串 | 是 | 固定为provision_status |
| status | 字符串 | 是 | 状态码,见状态码表 |
| error | 字符串 | 否 | 错误描述(status为error时必填) |
| credential | 对象 | 是 | 设备凭证,拥有该凭证即拥有设备的完全控制权(配网完成时返回) |
| credential.secret | 字符串 | 是 | 设备凭证密钥,由设备端生成的UUID v4格式 |
JSON示例(配置接收):
json
{
"action": "provision_status",
"status": "config_received"
}JSON示例(配网完成):
json
{
"action": "provision_status",
"status": "completed",
"credential": {
"secret": "550e8400-e29b-41d4-a716-446655440000"
}
}credential 扩展说明
secret由设备端随机生成,格式为标准UUID v4- 业务可根据需求扩展其他凭证相关字段
JSON示例(配网失败):
json
{
"action": "provision_status",
"status": "error",
"error": "WiFi连接超时"
}error 说明
APP收到status: "error",应将error 字符串显示出来,例如“初始化设备失败: WiFi连接超时”
状态码说明
配网状态码
协议定义以下基础状态码,业务可根据需求扩展更多中间状态:
| 状态码 | 说明 | 超时时间 |
|---|---|---|
config_received | 配置已接收 | 5秒(收到配网请求后) |
wifi_connected | WiFi已连接 | 10秒(从config_received起) |
mqtt_connected | MQTT已连接 | 5秒(从wifi_connected起) |
completed | 配网完成 | 5秒(从mqtt_connected起) |
error | 配网失败 | - |
超时处理
- APP端应按上述超时时间监控状态转换,超时未收到下一状态则视为配网失败
- 设备端应在超时前完成状态转换或返回
error状态 - 整个配网流程总超时建议为30秒
状态扩展示例
业务可根据需要添加更多中间状态,例如:
device_authenticated- 设备认证成功server_registering- 服务器注册中firmware_checking- 固件版本检查中
设备行为规范
配网成功
配网成功后,设备应:
- 持久化存储以下数据:
- WiFi配置(ssid、password)
- MQTT配置(broker、client_id、username、password、topic_prefix)
- 业务配置(config对象)
- 设备凭证(credential.secret)
- 停止BLE广播
- 断开GATT连接
- 通过MQTT开始正常业务运行
配网失败
配网失败后,设备应:
- 清除本次接收的所有配置数据,恢复到待配网状态
- 继续BLE广播,等待下一次配网请求
- 不保留任何残留状态
连接断开处理
正常断开(配网完成后):
- 设备发送
completed状态后,双方均可主动断开连接 - BLE传输层保证数据送达,无需等待APP确认
异常断开(配网过程中):
- 如果APP在收到
completed前断开连接,设备发送响应会失败 - 设备应检测到发送失败后,清除配置并恢复到待配网状态,而不应该认为配网成功
- 继续BLE广播,等待重新配网
