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

ESP32 WebSocket OTA更新字节丢失问题求助

问题:ESP32 OTA更新出现premature end错误(丢失1456字节)

场景描述

我使用Flutter应用连接ESP32上基于links2004/websockets库搭建的WebSocket服务器,通过二进制Socket传输固件。每帧数据会添加:

  • 1个更新状态标识字节
  • 1个结束标识字节(0xFF标记最后一帧)
  • 帧末尾1个校验和字节(基于帧数据计算)

两端已校验校验和与数据量,但更新时报错:

premature end: res:0, pos:864256/1966080

Linux系统显示固件文件firmware.bin大小为865712字节,但更新时丢失了1456字节(865712-864256=1456)。

相关代码与调试日志

ESP32端处理代码

esp_err_t Updater::handleUpdateData(const uint8_t *data, size_t length)
{
    //debug vars.
    static int k;
    static int j;
    if (!isUpdateInProgress)
    {
        logger().log("Fatal error, the update isn't started", debug_info_t::WARNING_D);
        return ESP_FAIL;
    }
    if (length < 3)
    {   
        //As I said, I add 3 bytes, therefore at least the frame must contain 3 bytes
        logger().log("Frame length is incosistent, frame is broken", debug_info_t::WARNING_D);
        return ESP_FAIL;
    }
    //To check that it is the last frame, I mark it with the 0xff flag
    bool isLastFragment = (data[1] == 0xFF);

    //the last byte of the frame contains the checksum
    uint8_t receivedChecksum = data[length - 1];
    uint8_t calculatedChecksum = getChecksum(data + 2, length - 3);

    if (calculatedChecksum != receivedChecksum)
    {
        //At this point, if the checksum does not match it would throw an error
        logger().log("Checksum mismatch.", debug_info_t::WARNING_D);
        return ESP_FAIL;
    }

    k+=length-3;
    logger().log(std::to_string(k).c_str());    
    //We write the data to the OTA partition and store its result, to verify that it is exactly           
    //the length received minus the 3 bytes that are not from the firmware
    size_t result=Update.write(const_cast<uint8_t *>(data) + 2, length - 3);
    if ( result!= length - 3) 
    {
      
        logger().log("Failed to write OTA update data.", debug_info_t::WARNING_D);
        return ESP_FAIL;
    }else{
        //debug to check how many bytes are being written to each packet
        std::string s="Packet"+std::to_string(j)+" writted"+ std::to_string(result);
        j++;
        logger().log(s.c_str());
    }

    if (isLastFragment)
    {
        
        std::string lastPacket="Last packet length: "+std::to_string(length-3);
        logger().log(lastPacket.c_str());
        if (!Update.end())
        {
            logger().log("Failed to finalize OTA update.", debug_info_t::WARNING_D);
            return ESP_FAIL;
        }
        else if (Update.isFinished())
        {
            logger().log("OTA update completed successfully.", debug_info_t::DEBUG_D);
           
            ESP.restart(); 
        }
        else
        {
            logger().log("OTA update not finished.", debug_info_t::WARNING_D);
            return ESP_FAIL;
        }
    }

    return ESP_OK;
}

调试日志

Flutter端

I/flutter ( 5132):packet 0=> 10048

.....
I/flutter ( 5132):packet 85=> 864128
I/flutter ( 5132):packet 86=> 865712

ESP32端

[INFO]: Packet0 writted=>10048
[INFO]: total=>10048
....
[INFO]: Packet85 writted=>10048
[INFO]: total=> 864128
[INFO]: Packet86 writted=>1584
[INFO]: total=> 865712

问题排查与解决建议

1. 核心问题:OTA初始化时未设置正确的固件大小

错误中的1966080是2MB,这很可能是你调用Update.begin()时传入的固定值,而实际固件大小只有865712字节。ESP32的OTA库会校验写入总字节数是否等于初始化时设置的大小,如果不匹配就会抛出premature end错误。

解决方法:
在启动OTA前,获取固件的实际大小并传入Update.begin():

// 假设firmwareSize是从Flutter端获取的固件总大小(865712)
if (!Update.begin(firmwareSize)) {
    logger().log("Failed to start OTA update.", debug_info_t::ERROR_D);
    return ESP_FAIL;
}

2. 验证OTA内部统计的写入量

在调用Update.end()之前,打印OTA库内部统计的写入进度,对比你自己统计的k值:

if (isLastFragment) {
    std::string totalWritten = "OTA internal progress: " + std::to_string(Update.getProgress());
    logger().log(totalWritten.c_str());
    std::string yourTotal = "Your calculated total: " + std::to_string(k);
    logger().log(yourTotal.c_str());
    
    // 后续的Update.end()逻辑...
}

如果两者数值不一致,说明Update.write()的实际写入量和你计算的length-3存在差异,需要排查WebSocket帧的完整性。

3. 检查WebSocket帧传输完整性

虽然两端日志显示总字节数一致,但可能存在WebSocket帧被分片传输,而ESP32的websockets库未正确拼接的情况。可以:

  • 在Flutter端,确保每帧发送的固件数据长度正确(前85包10048字节,最后一包1584字节),且数据是从文件中连续读取的。
  • 在ESP32端,打印每帧的length值,确认帧总长度等于固件数据长度+3(标识+结束+校验和)。

4. 重置静态统计变量

代码中k和j是静态变量,如果更新失败后未重置,会导致下一次更新时统计错误。建议在启动更新时重置这两个变量:

// 在启动OTA的代码中添加
isUpdateInProgress = true;
k = 0;
j = 0;

5. 验证分区表配置

确认ESP32的分区表中,OTA分区的大小至少大于固件实际大小(比如设置为1MB或2MB)。可以通过idf.py partition_table命令查看和修改分区表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:30:56