🗓️ この記事の要点
  • ESP-IDF 6.0 では cJSON・esp-mqtt などが本体同梱でなくなり、公式の ESP Component Registry から取得する方式になりました
  • 使い方は idf_component.yml依存を1行書くだけ。ビルド時にコンポーネントマネージャが自動で取得し、dependencies.lock でバージョンが固定されます
  • 本記事は ESP-IDF 6.0.2ESP32-S3 実機で、Wi-Fi 接続 → cJSON で JSON 組み立て → HTTP POST 成功までを実際に通した記録です

🧭 はじめに:#include "cJSON.h" が通らない?——それ、6.0 の仕様です

ESP-IDF で JSON を扱うといえば cJSON です。v5.x までは本体に同梱されていたので、何も考えずに #include "cJSON.h" と書けば使えました。当サイトの ESP32-S3 × ESP-IDF|cJSONとHTTP ClientでJSONをPOST送信 も、その時代の書き方です。

ESP-IDF 6.0 では、これがビルドエラーになります。 cJSON が本体から外され、ESP Component Registry(コンポーネントレジストリ)から取得する方式に変わったからです。

と聞くと面倒になったように感じますが、実際にやってみると逆でした。idf_component.yml というファイルに依存を1行書くだけで、ビルド時に自動で取ってきてくれる。npm(Node.js)や cargo(Rust)を使ったことがあれば、あの感覚です。組み込み開発にも、ようやく「依存管理」の時代が来た——本記事では、それを ESP32-S3 実機で最後まで通して確かめます。

この記事は ESP-IDF 6.0 シリーズの実践編です。6.0 で何が変わったかの全体像は ESP-IDF 6.0 移行ガイド、6.0 環境の作り方(複数バージョン共存)は EIM の使い方 を先にどうぞ。

ソースコード全体は GitHub で公開しています(MIT License)。

ramtuc/esp-idf6-registry-http-post(GitHub)


💡 ESP Component Registry とは

💡 ワード解説:ESP Component Registry

ESP Component Registrycomponents.espressif.com)は、Espressif が運営する ESP-IDF 用コンポーネントの公式配布サイトです。Espressif 製・サードパーティ製のライブラリが登録されていて、プロジェクトの idf_component.yml(マニフェストファイル)に名前とバージョン制約を書くと、IDF Component Manager がビルド時に自動でダウンロード・組み込みします。

  • npm でいう package.json = idf_component.yml/node_modules = managed_components//package-lock.json = dependencies.lock
  • レジストリ自体は v5.x 時代からありましたが、6.0 で「同梱コンポーネントの移管先」となり、事実上の必須インフラになりました

6.0 で本体からレジストリへ移管された主なコンポーネントは次のとおりです(詳細は移行ガイドの該当節を参照)。

コンポーネント 6.0 での扱い
cJSON レジストリの espressif/cjson へ(本記事で実演
esp-mqtt コンポーネントマネージャ経由での取得へ
wifi_provisioning network_provisioning に名称変更のうえレジストリへ

なぜ外に出すのか。公式の説明を移行の文脈で読むと、本体のスリム化コンポーネント単位の独立した更新が軸です。本体のリリースを待たずにライブラリ側だけ更新できるのは、利用者にとっても悪い話ではありません。


🗺️ 作るもの:Wi-Fi 接続 → JSON 組み立て → HTTP POST

やることは IoT の基本形そのものです。ESP32-S3 が Wi-Fi に接続し、cJSON でペイロードを組み、公開エコーサーバ(postman-echo.com)へ HTTP POST して、返ってきたレスポンスを確認します。

flowchart TD YML["main/idf_component.yml
(espressif/cjson を1行宣言)"] --> CM["IDF Component Manager
(idf.py 実行時に自動で動く)"] CM --> REG["ESP Component Registry
components.espressif.com"] REG --> MC["managed_components/espressif__cjson
(プロジェクト内に取得)"] CM --> LOCK["dependencies.lock
(解決したバージョンを固定)"] MC --> APP["アプリ:Wi-Fi 接続 → cJSON で JSON → esp_http_client で POST"]

プロジェクト構成は最小です。

http-post-registry/
├── CMakeLists.txt
└── main/
    ├── CMakeLists.txt
    ├── idf_component.yml   ← 今回の主役
    ├── Kconfig.projbuild   ← SSID/パスワードを menuconfig 項目化
    └── main.c

📦 核心:idf_component.yml を書く——そして取得される瞬間

主役のファイルはこれだけです。全文を載せます。

## IDF Component Manager マニフェスト
## 6.0 では cJSON が本体同梱でなくなったため、レジストリから取得する
dependencies:
  idf: ">=6.0"
  espressif/cjson: "^1.7.19~2"
  • 置き場所は main/ コンポーネントの直下です(プロジェクト直下ではない点に注意)
  • espressif/cjson が「名前空間/コンポーネント名」。バージョン制約 ^1.7.19~2 は「1.7.19~2 以上・メジャー 1 のまま」の意味で、レジストリのコンポーネントページに掲載されている表記をそのまま使えます
  • 依存の追加はコマンドでもできます: idf.py add-dependency "espressif/cjson^1.7.19~2"

この状態で idf.py set-target esp32s3(または reconfigure)を実行すると、コンポーネントマネージャが動きます。実際のログがこちら。

NOTICE: Dependencies lock doesn't exist, solving dependencies.
NOTICE: Updating lock file at D:\tmp\esp-idf-6-migration\http-post-registry\dependencies.lock
NOTICE: Processing 2 dependencies:
NOTICE: [1/2] espressif/cjson (1.7.19~2)
NOTICE: [2/2] idf (6.0.2)

実行後、プロジェクトに2つのものが増えています。

  • managed_components/espressif__cjson/ — レジストリから取得された cJSON の実体。これでソースの #include "cJSON.h" がそのまま通ります
  • dependencies.lock — 解決結果のバージョンを固定するロックファイル。別のマシンでも同じバージョンが再現されます(npm の lock ファイルと同じ発想)
✅ 「esp-mqtt が本体から抜けた」こともログで確認できた

同じ configure ログに Component directory .../esp-idf/components/mqtt does not contain a CMakeLists.txt file. No component will be added という行が出ます。6.0 のソースツリーでは mqtt の置き場所がになっていて、「同梱をやめてレジストリへ」が実際のディレクトリ構造にも表れています。MQTT を使うプロジェクトを 6.0 に移すときは、cJSON と同じ要領で idf_component.yml に依存を書くことになります。

⚠️ managed_components は「触らない」ディレクトリ

managed_components/ の中身はコンポーネントマネージャの管理物です。ここを直接編集すると、次の解決時にハッシュ不一致で怒られます。生成物なので、Git 管理からも外すのが基本です(dependencies.lock は逆にコミットする——ここも npm と同じ流儀)。


🔌 コード:Wi-Fi 接続と HTTP POST(6.0 の作法で)

コードは素直な IoT の基本形です。全文は GitHub リポジトリ にあります——クローンして idf.py menuconfig で Wi-Fi の SSID/パスワードを設定すれば、README の手順どおりそのまま実機で動かせます。ここでは要点を抜粋します。

Wi-Fi STA 接続(イベントグループで完了待ち)

static void wifi_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
    if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < MAX_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
        } else {
            xEventGroupSetBits(s_wifi_events, WIFI_FAIL_BIT);
        }
    } else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_events, WIFI_CONNECTED_BIT);
    }
}

SSID とパスワードはコードに直書きせず、Kconfig.projbuild で menuconfig の項目にして sdkconfig 側へ逃がしています。認証情報をソースから分離しておくと、コードを記事や Git に出すときに事故が起きません。

config APP_WIFI_SSID
    string "WiFi SSID"
    default "changeme"

cJSON でペイロードを組んで POST

/* レジストリから取得した cJSON で JSON ペイロードを組む */
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "device", "esp32-s3");
cJSON_AddStringToObject(root, "idf", IDF_VER);
cJSON_AddNumberToObject(root, "free_heap", (double)esp_get_free_heap_size());
char *payload = cJSON_PrintUnformatted(root);

esp_http_client_config_t config = {
    .url = "http://postman-echo.com/post",
    .event_handler = http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, payload, strlen(payload));
esp_err_t err = esp_http_client_perform(client);

判定は esp_http_client_perform() の戻り値(通信としての成否)と HTTP ステータスコード(サーバの応答)を別々に見ます。公開エコーサーバは混雑で一時的に 5xx を返すことがあるため、実装では 2xx 以外に備えた軽いリトライも入れています。

cJSON の API は v5.x 時代とまったく同じです。変わったのは「どこから来るか」だけで、書き味は何も変わりません。esp_http_client は 6.0 でも本体同梱のままなので、こちらは追加の依存宣言も不要です。

なお main/CMakeLists.txtPRIV_REQUIRES に注意点がひとつ。使用する本体コンポーネント(esp_wifiesp_http_client など)は明示しますが、cJSON は書きませんidf_component.yml の依存はコンポーネントマネージャが自動で配線してくれるからです。


🧪 実機実録:ビルド → 書き込み → POST 成功まで

ここからは、この環境(EIM で導入した v6.0.2+ESP32-S3 実機)で実際に通した記録です。

ビルドと書き込み

eim run "idf.py -C <プロジェクト> build" v6.0.2
eim run "idf.py -C <プロジェクト> -p COM5 flash" v6.0.2

ビルドは約 1,070 ステップで、バイナリは 893,760 バイト(既定の 1MB アプリパーティションの 85%)。Wi-Fi・TCP/IP・HTTP クライアントが乗ると、hello_world(約142KB)の6倍のサイズ感になります。書き込みは3イメージすべて Hash of data verified で完了しました。

シリアルログ:起動から POST 成功まで約2秒

リセット後のシリアル出力の要点です(SSID・MAC アドレス・IP はマスクしています)。

I (424) post_registry: connecting to Wi-Fi...
I (734) wifi:connected with <SSIDマスク>, aid = 21, channel 5, BW20
I (734) wifi:security: WPA2-PSK, phy: bgn, rssi: -37
I (1784) esp_netif_handlers: sta ip: 192.168.0.xxx, mask: 255.255.255.0
I (1784) post_registry: Wi-Fi connected
I (1784) post_registry: POST payload: {"device":"esp32-s3","idf":"v6.0.2","free_heap":287456}
I (2094) post_registry: HTTP POST status = 200 (attempt 1)
I (2094) post_registry: response body (380 bytes):
{"args":{},"data":{"device":"esp32-s3","idf":"v6.0.2","free_heap":287456},
 "headers":{...,"content-type":"application/json","user-agent":"ESP32 HTTP Client/1.0",...},
 "json":{"device":"esp32-s3","idf":"v6.0.2","free_heap":287456},
 "url":"https://postman-echo.com/post"}
I (2124) post_registry: done

電源投入から POST 完了まで約 2.1 秒。エコーサーバのレスポンスの json フィールドに、cJSON で組んだペイロードがそっくりそのまま返ってきているのが分かります。レジストリから取得した cJSON が実機できちんと仕事をした、何よりの証拠です。


✅ まとめ

  • ESP-IDF 6.0 では cJSON・esp-mqtt などが本体同梱でなくなり、ESP Component Registry から取得する方式になりました
  • 使い方は main/idf_component.yml依存を1行。ビルド時に managed_components/ へ自動取得され、dependencies.lock で再現性が担保されます——package.json / node_modules / lock ファイルの関係そのままです
  • cJSON の API は v5.x と同一。変わるのは取得経路だけで、コードの書き味は変わりません
  • 実機(ESP32-S3+v6.0.2)で Wi-Fi 接続 → JSON 組み立て → HTTP POST → レスポンス確認まで一通り成立しました
  • 「本体のリリースを待たずにライブラリだけ更新できる」体制は、使う側にもメリットがあります。6.0 移行の"面倒な変更"に見えて、実は開発体験の底上げでした

❓ よくある質問(FAQ)

Q. v5.x のプロジェクトを 6.0 に移したら cJSON でビルドエラーになりました。最短の直し方は?

A. main/idf_component.yml を作成(既にあれば追記)して espressif/cjson: "^1.7.19~2" を依存に追加し、idf.py reconfigure を実行してください。managed_components/ に取得されれば、既存の #include "cJSON.h" はそのまま通ります。ソースコードの変更は不要です。

Q. managed_components や dependencies.lock は Git にコミットすべきですか?

A. managed_components/ は生成物なのでコミットせず(.gitignore に追加)、dependencies.lockコミットします。ロックファイルを共有すれば、チームや CI でも同じバージョンの依存が再現されます。

Q. レジストリにないライブラリを使いたいときは?

A. 従来どおり components/ ディレクトリに手動でソースを置く方法も引き続き使えます。レジストリは「同梱の置き換え+配布の標準化」であって、手動コンポーネントを禁止するものではありません。

Q. オフライン環境ではビルドできなくなりますか?

A. 一度取得すれば managed_components/ はローカルに残るため、以後の再ビルドに通信は不要です。初回解決だけネットワークが必要になります。


関連記事


参考