HTTP 経由で ESP32 の LittleFS にファイルをアップロードする際、再度ダウンロードせずにバイトが無事に届いたことを検証したい。簡単な方法は、デバイス上のファイルの CRC32 を計算する GET エンドポイントで、ローカルコピーの CRC32 と比較できる。
エンドポイント
example.txt
GET /api/file-crc?path=<relative/path>応答:
response.json
{"path":"lfs_test.txt","size":25,"crc32":"0x14323782","crc32_u32":338835330}crc32 は16進表現、crc32_u32 は同じ値の符号なし整数——クライアントで比較しやすい方を使う。
ファームウェアハンドラ(ESP-IDF)
HTTPHandlersFiles.cpp
esp_err_t handleFileCRC(httpd_req_t *req) {
QueryURLParser parser(req);
if (!parser.HasParameter("path")) {
httpd_resp_set_status(req, "400 Bad Request");
httpd_resp_sendstr(req, "{\"error\": \"Missing path parameter\"}");
return ESP_OK;
}
std::string rel = parser.GetParameter("path");
if (!sanitize_path(rel, rel) || rel.empty()) {
httpd_resp_set_status(req, "400 Bad Request");
httpd_resp_sendstr(req, "{\"error\": \"Invalid path\"}");
return ESP_OK;
}
std::string fullpath = std::string(LittleFSManager::mountPoint()) + "/" + rel;
uint32_t crc;
size_t size;
if (!LittleFSManager::computeCRC32ForFile(fullpath, crc, size)) {
httpd_resp_set_status(req, "404 Not Found");
httpd_resp_sendstr(req, "{\"error\": \"File not found\"}");
return ESP_OK;
}
cJSON *json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "path", rel.c_str());
cJSON_AddNumberToObject(json, "size", (double)size);
char crc_hex[16];
snprintf(crc_hex, sizeof(crc_hex), "0x%08lX", (unsigned long)crc);
cJSON_AddStringToObject(json, "crc32", crc_hex);
cJSON_AddNumberToObject(json, "crc32_u32", crc);
set_content_type_with_charset(req, "application/json");
if (!cjson_serialize_and_send(req, json)) {
cJSON_Delete(json);
httpd_resp_set_status(req, "500 Internal Server Error");
httpd_resp_sendstr(req, "{\"error\": \"Failed to serialize JSON\"}");
return ESP_OK;
}
cJSON_Delete(json);
return ESP_OK;
}CRC32 は esp_rom_crc.h の esp_rom_crc32_le で計算する。ROM 関数の結果には、Python の zlib.crc32 が生成する従来の CRC32 に一致させるために最後に ~crc ^ 0xFFFFFFFF の補正が必要な点に注意:
LittleFSManager.cpp
#include <esp_rom_crc.h>
bool computeCRC32ForFile(const std::string &fullpath, uint32_t &crc_out, size_t &size_out) {
FILE* f = fopen(fullpath.c_str(), "rb");
if (!f) return false;
struct stat st;
if (stat(fullpath.c_str(), &st) != 0) { fclose(f); return false; }
size_out = st.st_size;
uint8_t *buf = (uint8_t*)malloc(size_out);
if (!buf) { fclose(f); return false; }
size_t read = fread(buf, 1, size_out, f);
fclose(f);
if (read != size_out) { free(buf); return false; }
uint32_t rom = esp_rom_crc32_le(0, buf, read);
uint32_t crc = (~rom) ^ 0xFFFFFFFFu;
free(buf);
crc_out = crc;
return true;
}ハンドラを登録:
HTTPServerWrapper.cpp
static const httpd_uri_t fileCrcHandler = {
.uri = "/api/file-crc",
.method = HTTP_GET,
.handler = handleFileCRC,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &fileCrcHandler);ローカルファイルと比較
verify.sh
curl -s "http://10.56.11.162/api/file-crc?path=lfs_test.txt"
python3 -c "import zlib; print(hex(zlib.crc32(open('lfs_test.txt','rb').read())))"output.txt
{"path":"lfs_test.txt","size":25,"crc32":"0x14323782","crc32_u32":338835330}
0x143237822つの値が一致すればアップロードはバイト単位で同一。これにより完全な同期ワークフローが簡単になる:リモートファイルを一覧し、各リモート CRC をローカルファイルと比較し、異なるものをアップロードし、ローカルに存在しなくなったものを削除する。
関連記事
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください