keer_zu 发表于 2025-5-20 13:03

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]
查看完整版本: ESP32通过蓝牙(BLE或经典蓝牙)接收JSON数据的完整协议设计方案