ESP32通过蓝牙(BLE或经典蓝牙)接收JSON数据的完整协议设计方案
以下是针对ESP32通过蓝牙(BLE或经典蓝牙)接收JSON数据的完整协议设计方案和代码实现,重点解决**数据分包、粘包处理**和**JSON解析**问题。---
## **协议设计方案**
### **1. 数据帧格式**
为可靠接收不定长的JSON数据,设计基于**长度前缀 + 数据内容**的协议:
```
[起始标志][数据长度][结束标志]
```
* **起始标志**(1字节):固定为 `0xAA`,标识数据帧开始。
* **数据长度**(2字节):JSON数据的实际长度(大端序)。
* **JSON数据**(N字节):UTF-8编码的JSON字符串。
* **结束标志**(1字节):固定为 `0x55`,标识数据帧结束。
### **2. 数据传输流程**
```
发送端:
1. 计算JSON数据长度(N)。
2. 构造帧:[高8位长度][低8位长度]
3. 通过蓝牙发送完整帧。
接收端:
1. 检测起始标志0xAA。
2. 读取长度字段,计算需接收的JSON数据长度。
3. 读取指定长度的JSON数据和结束标志0x55。
4. 校验结束标志,若正确则解析JSON。
```
### **3. 优点**
* 明确标识数据边界,解决粘包问题。
* 长度校验避免缓冲区溢出。
* 支持动态长度JSON数据。
---
## **代码实现**
### **1. 依赖库**
* **ArduinoJson**:处理JSON解析。
* **BluetoothSerial**:ESP32经典蓝牙库。
### **2. 完整代码**
```
#include <BluetoothSerial.h>
#include <ArduinoJson.h>
BluetoothSerial SerialBT;
#define BUFFER_SIZE 512// 根据实际JSON长度调整
// 协议常量
#define START_MARKER 0xAA
#define END_MARKER 0x55
// 接收状态机
enum RecvState {
WAIT_START,
READ_LENGTH_HIGH,
READ_LENGTH_LOW,
READ_DATA,
CHECK_END
};
// 全局变量
uint8_t buffer;
uint16_t data_length = 0;
uint16_t data_index = 0;
RecvState state = WAIT_START;
void setup() {
Serial.begin(115200);
SerialBT.begin("ESP32_JSON"); // 蓝牙设备名称
Serial.println("Bluetooth Started!");
}
void loop() {
while (SerialBT.available() > 0) {
uint8_t byte = SerialBT.read();
switch (state) {
case WAIT_START:
if (byte == START_MARKER) {
state = READ_LENGTH_HIGH;
}
break;
case READ_LENGTH_HIGH:
data_length = byte << 8;
state = READ_LENGTH_LOW;
break;
case READ_LENGTH_LOW:
data_length |= byte;
if (data_length > BUFFER_SIZE) {
// 数据过长,重置状态
resetState();
Serial.println("Error: Data too long!");
} else {
data_index = 0;
state = READ_DATA;
}
break;
case READ_DATA:
buffer = byte;
if (data_index >= data_length) {
state = CHECK_END;
}
break;
case CHECK_END:
if (byte == END_MARKER) {
// 完整帧接收完成,解析JSON
processJSON(buffer, data_length);
} else {
Serial.println("Error: End marker mismatch!");
}
resetState();
break;
}
}
}
// 解析JSON数据
void processJSON(uint8_t* data, uint16_t len) {
// 转换为字符串并添加终止符
char json_str;
memcpy(json_str, data, len);
json_str = '\0';
// 解析JSON
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, json_str);
if (error) {
Serial.print("JSON解析失败: ");
Serial.println(error.c_str());
return;
}
// 提取数据示例
if (doc.containsKey("sensor")) {
const char* sensor = doc["sensor"];
float value = doc["value"];
Serial.printf("传感器: %s, 值: %.2f\n", sensor, value);
}
}
// 重置接收状态
void resetState() {
state = WAIT_START;
data_length = 0;
data_index = 0;
}
```
---
## **关键点说明**
### **1. 状态机设计**
* 使用状态机(`WAIT_START` → `READ_LENGTH` → `READ_DATA` → `CHECK_END`)确保按协议顺序处理数据。
* 解决蓝牙数据分包和粘包问题。
### **2. 缓冲区安全**
* 检查 `data_length` 是否超过 `BUFFER_SIZE`,防止溢出。
* 动态分配缓冲区(可根据项目需求改用静态分配)。
### **3. 错误处理**
* 结束标志校验:检测帧是否完整。
* JSON解析失败时输出错误信息。
---
## **发送端示例(Python模拟)**
```
# 安装依赖: pip install pybluez
import bluetooth
import struct
def send_json_over_bt(address, json_str):
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
sock.connect((address, 1))
# 构造协议帧
start_marker = b'\xAA'
end_marker = b'\x55'
data = json_str.encode('utf-8')
length = struct.pack('>H', len(data))# 大端序2字节
frame = start_marker + length + data + end_marker
sock.send(frame)
sock.close()
# 使用示例
send_json_over_bt("ESP32_JSON_MAC", '{"sensor":"temperature","value":25.5}')
```
---
## **优化建议**
1. **增加CRC校验**:在帧尾添加2字节CRC16校验,提高数据可靠性。
2. **超时机制**:若长时间未收到结束标志,自动重置接收状态。
3. **PSRAM支持**:如果JSON数据较大,启用ESP32的PSRAM:
```
// 替换DynamicJsonDocument为PSRAM版本
DynamicJsonDocument doc(8192); // 8KB PSRAM
```
通过以上方案,可稳定实现ESP32通过蓝牙接收并解析JSON数据。实际项目中需根据数据量调整缓冲区大小和JSON文档容量。
页:
[1]