You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32向AWS IoT Core发送MQTT仅首条成功,问题出在哪?

ESP32-CAM AWS IoT消息截断问题排查与修复

问题现象

ESP32-CAM捕获图像转Base64后通过Pubsubclient发送至AWS IoT Core,首次烧录后第一条消息完整发送成功,但后续所有消息均被截断(缺失闭合引号、大括号,附带多余空白),重置设备也无法解决,仅首条消息有效。设备与AWS连接状态正常,已尝试调整分片大小、设置setBufferSize,问题依旧。

原代码片段

void publishMessage() {
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  }  

  char *input = (char *)fb->buf;

//AWS Lambda Actions need base64 encoded data, jsonified
  char output[base64_enc_len(3)];
  String image;
  for (int i=0;i<fb->len;i++) {
    base64_encode(output, (input++), 3);
    if (i%3==0) image += String(output);
  }
  
  String imageFile = "{\"image\":\""+image+"\"}";
  uint32_t fbLen=imageFile.length();

  if(!client.connected()) {
    Serial.println("Connecting to AWS IOT");

    while (!client.connect(THINGNAME)) {
      Serial.print(".");
      delay(100);
    }

    if (!client.connected()) {
      Serial.println("AWS IoT Timeout!");
      return;
    }
  }

  boolean res = client.beginPublish(AWS_IOT_PUBLISH_TOPIC, fbLen, false);
  Serial.println("publish began " + String(res));

  for (uint32_t n=0;n<fbLen;n=n+2048) {
    String str = "";
    if (n+2048<fbLen) {
      str = imageFile.substring(n, n+2048);
      client.write((uint8_t*)str.c_str(), 2048);
    }
    else if (fbLen%2048>0) {
      uint32_t remainder = fbLen%2048;
      str = imageFile.substring(n, n+remainder);
      client.write((uint8_t*)str.c_str(), remainder);
    }
    Serial.println(String(n));
  }  

  Serial.println("publish written " + String(res));

  res = client.endPublish();
  Serial.println("publish ended " + String(res));

  esp_camera_fb_return(fb);
}

问题根源分析

  1. Base64编码逻辑错误
    原循环按单字节i++遍历,每次调用base64_encode处理3字节,但仅当i%3==0时才将编码结果加入image字符串。这意味着每3次循环仅保留1次编码结果,其余2次的编码数据被覆盖丢失,导致最终Base64字符串不完整。首次运行可能因内存偶然状态侥幸凑成完整数据,后续运行则必然出现截断。

  2. output数组未预留终止符空间
    base64_enc_len(3)返回4(3字节Base64编码后为4字符),但定义char output[base64_enc_len(3)]时未预留字符串终止符\0的位置,String(output)会读取内存中后续的垃圾数据,导致消息出现多余空白和乱码。

  3. 内存碎片累积
    频繁使用String拼接和substring生成新字符串,会在ESP32的堆内存中产生大量碎片,后续运行时内存分配异常,加剧消息发送的不稳定。

修复方案

1. 修正后的完整代码

void publishMessage() {
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  }  

  char *input = (char *)fb->buf;

// 修正Base64编码逻辑
  char output[base64_enc_len(3) + 1]; // 预留字符串终止符空间
  String image;
  int remaining = fb->len;

  while (remaining > 0) {
    int chunkSize = min(3, remaining);
    base64_encode(output, input, chunkSize);
    output[base64_enc_len(chunkSize)] = '\0'; // 添加终止符避免读取垃圾数据
    image += String(output);
    input += chunkSize;
    remaining -= chunkSize;
  }
  
  String imageFile = "{\"image\":\""+image+"\"}";
  uint32_t fbLen=imageFile.length();

  if(!client.connected()) {
    Serial.println("Connecting to AWS IOT");

    while (!client.connect(THINGNAME)) {
      Serial.print(".");
      delay(100);
    }

    if (!client.connected()) {
      Serial.println("AWS IoT Timeout!");
      return;
    }
  }

  boolean res = client.beginPublish(AWS_IOT_PUBLISH_TOPIC, fbLen, false);
  Serial.println("publish began " + String(res));

  // 优化分片发送:直接使用原字符串指针,避免生成新String
  const char* msgPtr = imageFile.c_str();
  uint32_t sent = 0;
  while (sent < fbLen) {
    uint32_t sendSize = min(2048, fbLen - sent);
    client.write((uint8_t*)(msgPtr + sent), sendSize);
    sent += sendSize;
    Serial.println(String(sent));
  }  

  Serial.println("publish written " + String(res));

  res = client.endPublish();
  Serial.println("publish ended " + String(res));

  esp_camera_fb_return(fb);
}

2. 额外优化建议

  • 增大Pubsubclient缓冲区:在初始化时设置client.setBufferSize(65536);(根据实际消息大小调整,确保大于Base64 JSON的总长度)。
  • 减少String依赖:对于大尺寸图像,可考虑逐段编码并直接发送,避免将完整Base64字符串存入内存,进一步降低内存碎片风险。
  • 添加内存监控:在代码中加入Serial.printf("Free heap: %d\n", esp_get_free_heap_size());,观察内存变化,排查内存泄漏问题。

内容的提问来源于stack exchange,提问作者David Buchs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 16:59:55