第四课:ESP32 串口通信接收数据帧并解析 —— 实现自定义传感器协议支持

第四课:ESP32 串口通信接收数据帧并解析 —— 实现自定义传感器协议支持

在很多实际项目中,我们经常会遇到通过串口接收传感器或其他设备的数据,这些数据通常不是简单的 ASCII 字符串,而是 结构化的数据帧,包含起始位、地址、数据体、校验码、结束位等。
本课将以一个典型的二进制协议数据帧为例,带你一步步实现 ESP32 对其接收、验证与解析的完整逻辑。


📦 一、数据帧结构说明

假设传感器每次通过串口发送的数据帧如下所示:

[0x49] [ADDR] [LEN] [DATA …] [CHECKSUM] [0x4D]

  • 0x49: 固定起始码
  • ADDR: 设备地址
  • LEN: 数据体长度(不含起始/结束/校验等)
  • DATA: 数据体内容,长度由 LEN 决定
  • CHECKSUM: 从 ADDRDATA 所有字节的校验和
  • 0x4D: 固定结束码

例如,一个代表地址为 0x01、发送 2 字节数据 0x0A, 0x0B 的完整数据帧如下:

0x49 0x01 0x02 0x0A 0x0B 0x13 0x4D

其中 0x13 = 0x01 + 0x02 + 0x0A + 0x0B


🔌 二、ESP32 硬件串口接线说明

ESP32 支持多个 UART,我们使用 UART1 来连接传感器:

信号说明ESP32 引脚方向
传感器 TXGPIO18 (U1RXD)ESP32 接收
传感器 RXGPIO17 (U1TXD)ESP32 发送

⚠️ 注意:传感器与 ESP32 的串口是“交叉”连接,TX 接 RX,RX 接 TX。


🧠 三、核心功能实现概述

  1. 初始化 UART1 串口:使用 HardwareSerial 初始化,指定 GPIO 引脚;
  2. 接收字节流并存入缓冲区:判断何时收到完整的一帧;
  3. 数据帧合法性校验
    • 起始码是否为 0x49
    • 结束码是否为 0x4D
    • 校验和是否一致
  4. 解析帧内容并打印
    • 地址字段
    • 数据体长度和具体内容

💡 四、完整代码与说明

#include <HardwareSerial.h>

#define RX_PIN 18         // 传感器的 RX 引脚接 ESP32 的 GPIO18 (U1RXD)
#define TX_PIN 17         // 传感器的 TX 引脚接 ESP32 的 GPIO17 (U1TXD)
#define BAUD_RATE 115200  // 串口波特率
#define BUFFER_SIZE 256   // 接收缓冲区大小

HardwareSerial mySerial(1); // 使用 UART1
uint8_t buffer[BUFFER_SIZE]; // 定义接收缓冲区
int bufferIndex = 0;         // 缓冲区索引

// 验证数据帧是否合法
bool validateFrame(uint8_t* frame, int len) {
  if (len < 5) return false; // 数据帧太短
  if (frame[0] != 0x49 || frame[len - 1] != 0x4D) { // 检查起始码和结束码
    Serial.println("错误: 起始码或结束码不匹配!");
    return false;
  }

  // 校验和计算
  uint8_t checksum = 0;
  for (int i = 1; i < len - 2; i++) { // 地址到数据体的校验和
    checksum += frame[i];
  }

  if (checksum != frame[len - 2]) {
    Serial.println("错误: 校验码不匹配!");
    return false;
  }

  return true;
}

// 解析数据帧
void parseFrame(uint8_t* frame, int len) {
  if (!validateFrame(frame, len)) {
    Serial.println("无效数据帧,跳过解析!");
    return;
  }

  uint8_t address = frame[1];      // 地址
  uint8_t dataLength = frame[2];   // 数据体长度
  uint8_t* data = &frame[3];       // 数据体起始地址

  Serial.print("解析成功,地址 = ");
  Serial.println(address, HEX);

  Serial.print("数据体 = ");
  for (int i = 0; i < dataLength; i++) {
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void resetBuffer() {
  bufferIndex = 0;
  memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区
}

void setup() {
  Serial.begin(115200); // 初始化调试串口
  mySerial.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); // 初始化传感器串口
  Serial.println("开始与传感器通信...");
}

void loop() {
  // 检查串口是否有数据可读
  while (mySerial.available()) {
    uint8_t byteReceived = mySerial.read();

    // 防止缓冲区溢出
    if (bufferIndex >= BUFFER_SIZE) {
      Serial.println("警告: 缓冲区溢出,丢弃当前数据!");
      resetBuffer();
      continue;
    }

    // 存储接收到的字节到缓冲区
    buffer[bufferIndex++] = byteReceived;

    // 检测是否接收到结束码
    if (byteReceived == 0x4D) {
      Serial.println("接收到完整数据帧!");
      Serial.print("数据帧原始内容: ");
      for (int i = 0; i < bufferIndex; i++) {
        Serial.print(buffer[i], HEX);
        Serial.print(" ");
      }
      Serial.println();

      // 如果数据帧以起始码开头且合法,解析之
      if (buffer[0] == 0x49 && validateFrame(buffer, bufferIndex)) {
        parseFrame(buffer, bufferIndex);
      } else {
        Serial.println("无效数据帧,跳过解析!");
      }

      // 重置缓冲区,准备接收下一帧
      resetBuffer();
    }
  }

  delay(50); // 避免占用过多 CPU
}


🔍 五、关键逻辑详解

1️⃣ 校验函数 validateFrame()

用于判断当前帧是否完整、合法,是防止误解析的核心步骤。检查三点:

  • 长度是否够
  • 起始码、结束码是否正确
  • 校验和是否与接收到的匹配
if (frame[0] != 0x49 || frame[len - 1] != 0x4D) {...}

若前面字节不是 0x49 起始码,或者校验和错,则打印错误信息并重置缓冲区。

🧪 六、调试建议与常见问题
接收不到数据?

检查传感器波特率是否为 115200;

检查是否接线正确(TX 接 RX);

使用串口助手(如串口调试精灵)模拟发帧验证 ESP32 接收功能。

数据帧校验失败?

注意校验和计算范围是 [ADDR] ~ [DATA],不含起始码和校验字节本身;

数据体长度错误也会导致校验失败。

缓冲区溢出?

确保数据频率合理,或加大 BUFFER_SIZE;

加入溢出判断逻辑,避免数据覆盖或死循环。

🚀 七、应用拓展建议
将解析后的数据用 BLE、MQTT、HTTP 方式上传;

支持多种帧格式:可进一步封装解码器类;

引入状态机机制解析多段复杂帧协议;

将数据体保存为 CSV、JSON 结构并写入 SD 卡。

本课实战演示了 ESP32 如何与自定义协议的传感器进行串口通信并正确解析结构化数据帧,为后续实现复杂协议通讯或多传感器接入打下基础。下一课,我们将尝试将解析后的数据存入 SD 卡或通过 BLE 广播发送。

如果你喜欢这样的内容,记得点赞 👍 收藏 📁 留言 💬,支持原创 ESP32 教程持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值