CRC32 エンドポイントを使って HTTP 経由の ESP32 LittleFS ファイルアップロードを検証する方法

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}
0x14323782

2つの値が一致すればアップロードはバイト単位で同一。これにより完全な同期ワークフローが簡単になる:リモートファイルを一覧し、各リモート CRC をローカルファイルと比較し、異なるものをアップロードし、ローカルに存在しなくなったものを削除する。

関連記事


同じカテゴリの類似記事をチェック: ESP32 C/C++