Arduino IDE开发ESP8266 NodeMCU时OTA下载文件是否需要CRC校验?
ESP8266 OTA升级CRC校验必要性及实现方案
一、CRC校验的必要性
非常有必要,核心原因如下:
- 网络传输过程中的丢包、错包、信号干扰都可能导致下载的bin文件出现字节错误,一旦烧录错误的固件会直接导致设备变砖,只能有线重刷
- ESP8266自带的Updater类默认只做最基础的写入校验,不会校验整个固件的完整性,无法识别传输过程中产生的整体数据偏差
- CRC32校验实现简单,资源占用极低,对ESP8266这类资源有限的MCU非常友好,额外开销可以忽略
二、具体实现步骤
前置准备
首先需要在服务端提前计算好待升级bin文件的CRC32值,两种常见实现方式:
- 本地打包固件时用工具计算后和bin文件一起存到服务器,或者直接存在bin文件的末尾固定位置
- 服务器收到固件上传请求时自动计算CRC32,提供单独的接口给设备查询对应版本固件的CRC值
设备端代码实现
可以直接用ESP8266核心库自带的CRC32类,不需要额外安装第三方库,实现逻辑如下:
- 升级前先请求服务端获取对应固件的目标CRC32值
- 下载固件的同时逐块计算当前接收数据的CRC32值
- 固件下载完成后对比计算得到的CRC和服务端下发的CRC,一致才调用
Updater.end()提交烧录,不一致就丢弃本次下载的固件,触发重试逻辑
核心代码示例
#include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiClient.h> #include <Updater.h> #include <CRC32.h> const char* ssid = "你的WiFi名称"; const char* password = "你的WiFi密码"; // 固件下载地址 const char* firmwareUrl = "http://你的服务器地址/firmware.bin"; // 固件CRC32查询地址 const char* crcUrl = "http://你的服务器地址/firmware_crc"; CRC32 crc; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("WiFi连接成功"); doOTA(); } void doOTA() { WiFiClient client; HTTPClient http; // 第一步:获取目标CRC值 uint32_t targetCrc = 0; if(http.begin(client, crcUrl)) { int httpCode = http.GET(); if(httpCode == HTTP_CODE_OK) { targetCrc = strtoul(http.getString().c_str(), NULL, 16); Serial.printf("获取到目标CRC32: 0x%X\n", targetCrc); } http.end(); } if(targetCrc == 0) { Serial.println("获取CRC失败,取消升级"); return; } // 第二步:下载固件并计算CRC if(http.begin(client, firmwareUrl)) { int httpCode = http.GET(); if(httpCode == HTTP_CODE_OK) { int firmwareSize = http.getSize(); Serial.printf("固件大小: %d字节\n", firmwareSize); if(Updater.begin(firmwareSize)) { WiFiClient* stream = http.getStreamPtr(); uint8_t buf[128]; size_t bytesRead; crc.reset(); while (http.connected() && stream->available()) { bytesRead = stream->readBytes(buf, sizeof(buf)); // 计算CRC crc.update(buf, bytesRead); // 写入升级分区 Updater.write(buf, bytesRead); } uint32_t calculatedCrc = crc.finalize(); Serial.printf("计算得到的CRC32: 0x%X\n", calculatedCrc); if(calculatedCrc == targetCrc) { if(Updater.end()) { Serial.println("升级成功,即将重启"); ESP.restart(); } else { Serial.printf("升级失败: %s\n", Updater.getErrorString()); } } else { Serial.println("CRC校验不匹配,丢弃固件"); Updater.abort(); } } else { Serial.println("升级分区初始化失败"); } } http.end(); } } void loop() { }
注意事项
- 服务端计算CRC32的时候要和ESP8266的
CRC32类使用相同的多项式,默认都是0x04C11DB7,只要没有手动修改就不会有匹配问题 - 如果不想单独做CRC查询接口,也可以把固件的CRC32值放在bin文件的最后4个字节,设备下载完所有数据后,先取最后4个字节作为目标CRC,再计算前面所有数据的CRC对比即可
- 校验失败后可以做最多3次重试,避免偶发的网络波动导致升级失败
内容的提问来源于stack exchange,提问作者Tushar Yadav
相关产品推荐
相关产品推荐

