ESP-IDFを使ってみる

コンポーネントのバージョン


ESP-IDFもFreeRTOSとlwIPが組み込まれています。
組み込まれているコンポーネントのバージョンを他の環境と比べてみました。


esp-open-rtos

#include <stdio.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"

#include "lwip/init.h"

void user_init(void)
{
  uart_set_baud(0, 115200);
  printf("SDK version:%s\n", sdk_system_get_sdk_version());
  printf("freeRTOS version:%s\n", tskKERNEL_VERSION_NUMBER);
  printf("NEWLIB version:%s\n", _NEWLIB_VERSION);
  printf("lwIP version string:%s\n", LWIP_VERSION_STRING);
  printf("lwIP version:%d-%d-%d-%d\n",
    LWIP_VERSION_MAJOR,LWIP_VERSION_MINOR,
    LWIP_VERSION_REVISION,LWIP_VERSION_RC);
}

実行すると以下の様に表示されます。

SDK version:0.9.9
freeRTOS version:V10.0.1
NEWLIB version:3.0.0
lwIP version string:2.0.3d
lwIP version:2-0-3-0


ESP8266_RTOS_SDK V3

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/init.h"

void app_main(void)
{
    printf("SDK version:%s\n", esp_get_idf_version());
    printf("freeRTOS version:%s\n", tskKERNEL_VERSION_NUMBER);
    printf("NEWLIB version:%s\n", _NEWLIB_VERSION);
    printf("lwIP version string:%s\n", LWIP_VERSION_STRING);
    printf("lwIP version:%d-%d-%d-%d\n",
      LWIP_VERSION_MAJOR,LWIP_VERSION_MINOR,
      LWIP_VERSION_REVISION,LWIP_VERSION_RC);
}

実行すると以下の様に表示されます。

SDK version:v3.2-dev-44-g5e98e3f-dirty
freeRTOS version:V10.0.1
NEWLIB version:2.2.0
lwIP version string:2.0.3
lwIP version:2-0-3-255


ESP-IDF

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/init.h"

void app_main()
{
    printf("freeRTOS version:%s\n", tskKERNEL_VERSION_NUMBER);
    printf("NEWLIB version:%s\n", _NEWLIB_VERSION);
    printf("lwIP version:%d-%d-%d-%d\n",
      LWIP_VERSION_MAJOR,LWIP_VERSION_MINOR,
      LWIP_VERSION_REVISION,LWIP_VERSION_RC);
    printf("ESP-IDF version:%s\n", esp_get_idf_version());
}

ESP-IDF v4.0
git clone -b release/v4.0 --recursive https://github.com/espressif/esp-idf.git
v4.0のサポートは2022年1月に終了しました。
freeRTOS version:V8.2.0
NEWLIB version:3.0.0
lwIP version:2-1-3-0
ESP-IDF version:v4.0.2-120-g4f2a2a8ce

ESP-IDF v4.1
git clone -b release/v4.1 --recursive https://github.com/espressif/esp-idf.git
v4.1のサポートは2023年1月に終了しました。
freeRTOS version:V8.2.0
NEWLIB version:3.0.0
lwIP version:2-1-3-0
ESP-IDF version:v4.1-520-gc3324a82a

ESP-IDF v4.2
git clone -b release/v4.2 --recursive https://github.com/espressif/esp-idf.git
v4.2のサポートは2023年6月に終了しました。
freeRTOS version:V8.2.0
NEWLIB version:3.0.0
lwIP version:2-1-3-0
ESP-IDF version:v4.2.2-331-g5595042c16

ESP-IDF v4.3
git clone -b release/v4.3 --recursive https://github.com/espressif/esp-idf.git
V4.3の後半からfreeRTOSとNEWLIBがバージョンアップしました。
v4.3のサポートは2024年1月に終了しました。
freeRTOS version:V10.2.1
NEWLIB version:3.3.0
lwIP version:2-1-3-0
ESP-IDF version:v4.3.2-224-g5fc51252e9

ESP-IDF v4.4
git clone -b release/v4.4 --recursive https://github.com/espressif/esp-idf.git
freeRTOSがバージョンアップしました。
なぜか、lwipがバージョンダウンしています。
v4.4のサポートは2024年7月に終了しました。
freeRTOS version:V10.4.3
NEWLIB version:3.3.0
lwIP version:2-1-2-0
ESP-IDF version:v4.4.5-104-g8b94183c9c

ESP-IDF v5.0
git clone -b release/v5.0 --recursive https://github.com/espressif/esp-idf.git
lwipが再び2-1-3-0になりました。
freeRTOS version:V10.4.3
NEWLIB version:4.1.0
lwIP version:2-1-3-0
ESP-IDF version:v5.0.2-392-ga7ab77663a

ESP-IDF v5.1
git clone -b release/v5.1 --recursive https://github.com/espressif/esp-idf.git
freeRTOS version:V10.4.3
NEWLIB version:4.1.0
lwIP version:2-1-3-0
ESP-IDF version:v5.1.2-602-gdb1e54a0c5

ESP-IDF v5.2
git clone -b release/v5.2 --recursive https://github.com/espressif/esp-idf.git
FreeRTOSとNWELIBがバージョンアップしました。
FreeRTOSの変更履歴がこちらに 公開されています。
System calls restrictions: The following system calls are no longer available to unprivileged tasks.
とか書いてありますが、ESP-IDFではあまり影響が無いようです。
xQueueGetStaticBuffersなどのAPIが追加されています。
freeRTOS version:V10.5.1
NEWLIB version:4.3.0
lwIP version:2-1-3-0
ESP-IDF version:v5.2.2

ESP-IDF v5.3
git clone -b release/v5.3 --recursive https://github.com/espressif/esp-idf.git
freeRTOS version:V10.5.1
NEWLIB version:4.3.0
lwIP version:2-1-3-0
ESP-IDF version:v5.3.2-362-gd7d3eb3f0a

ESP-IDF v5.4
git clone -b release/v5.4 --recursive https://github.com/espressif/esp-idf.git
lwipが2-2-0-0になりました。
freeRTOS version:V10.5.1
NEWLIB version:4.3.0
lwIP version:2-2-0-0
ESP-IDF version:v5.4



ESP-IDFのバージョンが上がると、APIが変わることがよくあります(_ _メ)
例えばmd5を求める関数にMD5Init、MD5Update、MD5Finalが有りますが、V4.3→V4.4で以下の様に関数名が変わり ました。
MD5Init --> esp_rom_md5_init  
MD5Update --> esp_rom_md5_update  
MD5Final --> esp_rom_md5_final  

FatFSのファイルシステムをマウント、アンマウントする関数に、esp_vfs_fat_spiflash_mount、 esp_vfs_fat_spiflash_unmountが有りますが、
V4.4→V5.0で以下の様に関数名が変わりました。
esp_vfs_fat_spiflash_mount --> esp_vfs_fat_spiflash_mount_rw_wl
esp_vfs_fat_spiflash_unmount --> esp_vfs_fat_spiflash_unmount_rw_wl
_rw_wl は ReadWrite_WearLevelling の略だと思いますが、ネーミングにセンスがないです。
V5.0でも古い関数名はまだ使えますが、V5.1でおそらく完全に廃止されると思います。

また、FreeRTOSのバージョンが上がると、FreeRTOSのAPIが変わることが有ります。
いつの間にか、以下のAPIとマクロが変わりました。
pcTaskGetTaskName --> pcTaskGetName
portTICK_RATE_MS --> portTICK_PERIOD_MS

この様にESP-IDFのバージョンでAPIが変わるときは、ESP_IDF_VERSIONとESP_IDF_VERSION_VALのマクロ を使えば、
バージョンに応じてソースを変えることができます。

こちらがmd5を求めるソースですが、ESP_IDF_VERSIONとESP_IDF_VERSION_VALのマクロを使って、
関数名とヘッダーファイルを切り 替えています。
#include <string.h>

#include "freertos/FreeRTOS.h"

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#define MD5Init esp_rom_md5_init
#define MD5Update esp_rom_md5_update
#define MD5Final esp_rom_md5_final
#include "esp_rom_md5.h"
#else
#include "rom/md5_hash.h"
#endif // ESP_IDF_VERSION


void app_main()
{
#ifdef CONFIG_IDF_TARGET_ESP32
        printf("Core is ESP32@%dMhz\n", ets_get_cpu_frequency());
#elif defined CONFIG_IDF_TARGET_ESP32S2
        printf("Core is ESP32S2@%dMhz\n", ets_get_cpu_frequency());
#elif defined CONFIG_IDF_TARGET_ESP32C3
        printf("Core is ESP32C3@%dMhz\n", ets_get_cpu_frequency());
#endif
        printf("esp_get_idf_version:%s\n", esp_get_idf_version());
        printf("IDF_VER :%s\n", IDF_VER);
        printf("ESP_IDF_VERSION_MAJOR :%d\n", ESP_IDF_VERSION_MAJOR);
        printf("ESP_IDF_VERSION_MINOR :%d\n", ESP_IDF_VERSION_MINOR);
        printf("ESP_IDF_VERSION_PATCH :%d\n", ESP_IDF_VERSION_PATCH);
        printf("ESP_IDF_VERSION :%d\n", ESP_IDF_VERSION);

        struct MD5Context context;
        MD5Init(&context);

        unsigned char text[64];
        memset(text, 0, sizeof(text));
        MD5Update(&context, text, sizeof(text));

        unsigned char digest[16];
        MD5Final(digest, &context);
        for(int i=0;i<16;i++) {
                printf("%02x ", digest[i]);
        }
        printf("\n");
}

IDF_VER、ESP_IDF_VERSION_MAJOR、ESP_IDF_VERSION_MINOR、 ESP_IDF_VERSION_PATCH、ESP_IDF_VERSIONのマクロは
それぞれ以下の値が設定されています。
IDF_VER :v4.4-dev-2825-gb63ec47238-dirty
ESP_IDF_VERSION_MAJOR :4
ESP_IDF_VERSION_MINOR :4
ESP_IDF_VERSION_PATCH :0
ESP_IDF_VERSION :263168

特定のバージョンだけをサポートする場合、以下のコードでビルドを抑制することができます。
この様にすると、Ver4.xではビルドできなくなります。
#include "freertos/FreeRTOS.h"
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#error This version no more support.
#endif



ESP-IDF v5.0 になって、 int32_t と uint32_t の扱いが変わりました。
以前は、 int32_t/uint32_t は、それぞれ int/unsigned int に展開されていましたが、このバージョンからは long/unsigned long に展開されます。
理由はこ ちらに公開されていますが、Xtensa、RISC-V、およびその他のアーキテクチャの整数であるアップストリーム GCC と展開方法を一致させるための様です。
intもlongもesp32では32ビット変数ですが、printf/ESP_LOGxのフォーマット書式が変わってしまいました。
今までは、以下でコンパイルできました。
#include <stdio.h>
#include "esp_log.h"

static const char *TAG = "MAIN";

void app_main()
{
    int32_t i32; // v4.4ではint
    i32 = 1;
    uint32_t u32; // v4.4ではunsigned int
    u32 = 1;
    ESP_LOGI(TAG, "%d", i32);
    ESP_LOGI(TAG, "%u", u32);
}

v5.0 では、上記のコードはコンパイルエラーとなります。
以下の様に変更すると、v5では通りますが、v4.4ではコンパイルエラーになります。
#include <stdio.h>
#include "esp_log.h"

static const char *TAG = "MAIN";

void app_main()
{
    int32_t i32; // v5.0ではlong
    i32 = 1;
    uint32_t u32; // v5.0ではunsigned long
    u32 = 1;
    ESP_LOGI(TAG, "%ld", u32);
    ESP_LOGI(TAG, "%lu", u32);
}

以下の様に変更することで、v4.4でもv5.0でもコンパイルできるようになります。
#include <stdio.h>
#include <inttypes.h>
#include "esp_log.h"

static const char *TAG = "MAIN";

void app_main()
{
    int32_t i32; // v4,4ではint v5.0ではlong
    i32 = 1;
    uint32_t u32; // v4.4ではunsigned int v5.0ではunsigned long
    u32 = 1;
    ESP_LOGI(TAG, "%"PRIi32, i32);
    ESP_LOGI(TAG, "%"PRIu32, u32);
    ESP_LOGI(TAG, "PRIi32=[%s]", PRIi32);
    ESP_LOGI(TAG, "PRIu32=[%s]", PRIu32);
    ESP_LOGI(TAG, "PRIx32=[%s]", PRIx32);
    ESP_LOGI(TAG, "sizeof(int32_t)=%d", sizeof(int32_t));
    ESP_LOGI(TAG, "sizeof(uint32_t)=%d", sizeof(uint32_t));
}

PRIi32 PRIu32 PRIx32 は 変数型に応じて設定される文字列で、v4.4では以下の文字列が設定されます。
I (331) MAIN: PRIi32=[i]
I (341) MAIN: PRIu32=[u]
I (341) MAIN: PRIx32=[x]
I (341) MAIN: sizeof(int32_t)=4
I (341) MAIN: sizeof(uint32_t)=4

v5.0では以下の文字列が設定されます。
変数のバイト数はどちらも4バイトです。
I (331) MAIN: PRIi32=[li]
I (341) MAIN: PRIu32=[lu]
I (341) MAIN: PRIx32=[lx]
I (341) MAIN: sizeof(int32_t)=4
I (341) MAIN: sizeof(uint32_t)=4

この変更には、困っている人が沢山いるようで、こちらに沢山 のコメントが有りますが、
xtensa と RISCV で、展開が異なるのも問題なので、この変更は仕方ないですが、滅茶苦茶影響が大きいです。
関数名の変更と違って、一括置換で検出できないのが痛いです。
CMakeLists.txtに以下のコンパイルオプションを追加すれば、ソースを直さなくても、とりあえずはコンパイルが通ります。
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(version)

idf_build_set_property(COMPILE_OPTIONS "-Wno-error=format" APPEND)



ESP-IDF v5.0 になって、ビルドしたファームのサイズがずいぶんと大きくなりました。
こちらがv4.4.2でビルドした時のファームのサイズ(0xebd80)です。
esp-idf-ftp-camera.bin binary size 0xebd80 bytes. Smallest app partition is 0x110000 bytes. 0x24280 bytes (13%) free.

こちらが同じソースを、v5.0でビルドした時のファームのサイズ(0x1012c0)です。1Mでは収まらなくなりました。
バージョンが上がるに従って、だんだん大きくなっています。
esp-idf-ftp-camera.bin binary size 0x1012c0 bytes. Smallest app partition is 0x110000 bytes. 0xed40 bytes (5%) free.

factoryパーテイションのデフォルトサイズは1M(=0x100000)ですが、これを0x110000に変更する必要が有ります。
2M FlashのSoCでは、これに伴いstorageを960K(0xF0000)から、0xE0000に変更する必要が有ります。
$ cat partitions.csv
# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
#factory,  app,  factory, 0x10000, 0x100000,
factory,  app,  factory, 0x10000, 0x110000,
#storage,  data, spiffs,  ,        0xF0000,
storage,  data, spiffs,  ,        0xE0000,



共通関数はcomponentsとして定義することができます。
componentsディレクトリに共通関数のソースやヘッダーを格納しますが、ESP-IDF Ver5でCMakeLists.txtの書式が変わりました。
これがVer4までの書式です。
$ cat esp-idf/examples/ethernet/enc28j60/components/eth_enc28j60/CMakeLists.txt
idf_component_register(SRCS "esp_eth_mac_enc28j60.c"
                            "esp_eth_phy_enc28j60.c"
                       INCLUDE_DIRS ".")

これがVer5からの書式です。
PRIV_REQUIRESが増えています。
これが無いとdriver関連のincludeでエラーになります。
Ver5では、driverコンポーネントを含む幾つかのコンポーネントは、自動的にリンクされなくなりました。
この地味な変更で、driverを使っているVer4までのcomponentsは、CMakeLists.txtを全て変更する必要が有りま す(_ _メ)
ESP-IDFのIssuesに沢山、これに関する報告が上がっています。
Ver5に移行した方々は、みんな、困ったようです。
試しに、Ver4のCMakeLists.txtにPRIV_REQUIRESを追加しましたが、問題なくビルド出来ました。
$ cat esp-idf/examples/ethernet/enc28j60/components/eth_enc28j60/CMakeLists.txt
idf_component_register(SRCS "esp_eth_mac_enc28j60.c"
                            "esp_eth_phy_enc28j60.c"
                       PRIV_REQUIRES driver
                       INCLUDE_DIRS ".")



ESP-IDF v5.0 になって、こ れらのコンポーネントは、標準のROMから、IDF Component Registryに移りました。
これらのライブラリを使う場合は、Manifest File(idf_component.yml)を作成する必要が有ります。
これらを使う公式サンプルにも、idf_component.ymlが含まれていますが、そのまま使うと
ESP-IDF v4でビルドした時にも、Manifest File(idf_component.yml)に従ってコンポーネントを追加しようとします。
ESP-IDF v4では、これらのライブラリは標準ライブラリなので、コンポーネントが競合してエラーになります。
そこで、Manifest File(idf_component.yml)にルールを追加して、v5の時だけコンポーネントを追加するようにします。
これでESP-IDF v4でもv5でもビルドが通ります。
この技はEspressIFのエンジニアに教えてもらいました。
$ cat idf_component.yml
## IDF Component Manager Manifest File
dependencies:
  espressif/mdns:
    version: "^1.0.3"
    rules:
      - if: "idf_version >=5.0"

ターゲットを指定すると、ビルドに必要なファイルをダウンロードして、managed_components と dependencies.lock が作られます。
$ ls
CMakeLists.txt  README.md  main  sdkconfig  sdkconfig.old

$ idf.py set-target esp32

$ ls
CMakeLists.txt  build              main                sdkconfig
README.md       dependencies.lock  managed_components  sdkconfig.old

mdns expat tjpeg esp_tinyusb あたりがよく使われるコンポーネントです。
tcpip_adapter のコンポーネントは完全に廃止されました。



ESP-IDF v5.0 になってRMTドライバーの仕様が全く別物になりました。
今までは、デバイス毎に独立した定義や関数を使っていましたが、v5.0ではデバイスに依存しない仕組みになり、
より汎用のドライバーになりましたが、使い方も難しくなりました。
赤外線、ステップモーター、Led Strip(WS2812)、ESC(Electronic Speed Controller)の様に、
パルスで制御するデバイスは全てRMTドライバーを使うことになります。



ESP-IDFのバージョンアップは、今使っているesp-idfを避難して、最新版を再インストールするだけです。
但し、ログインスクリプトに「. export.sh」が指定されていると正しくインストールできません。
バージョンアップする際には、「. export.sh」が動いていない環境が必要です。
バージョンアップにより、今まで動いていた機能が動かなくなったり、APIが変わってしまうことが有るので、古いバージョンは避難しておいた方が いいです。
$ idf.py --version
ESP-IDF v4.4-dev-2825-gb63ec47238-dirty

$ mv esp-idf esp-idf_v4.4-dev-2825-gb63ec47238-dirty

$ vi $HOME/.profile
. export.sh をコメントアウトして再ログイン

$ git clone --recursive https://github.com/espressif/esp-idf.git

$ cd esp-idf

$ ./install.sh

$ vi $HOME/.profile
. export.sh のコメントアウトを解除して再ログイン

ESP-IDFのバージョンダウンは、今使っているesp-idfのフォルダー名を変えて、避難してある古いフォルダー名を変更するだけですが、
メージャーバージョンをバージョンダウンする場合は注意が必要です。
メージャーバージョンのバージョンアップにより、toolchainが更新されている場合があり、その場合、toolchainを再インストール する必要が有ります。
メージャーバージョンをバージョンダウンする場合は、一旦$HOME/.espressifを削除し、再度install.shを実行しないと toolchainとの互換性が損なわれます。
$ idf.py --version
ESP-IDF v5.1.2-dirty

$ mv esp-idf esp-idf_v5.1.2-dirty

$ vi $HOME/.profile
. export.sh をコメントアウトして再ログイン
 
$ mv esp-idf_v4.4-dev-2825-gb63ec47238-dirty esp-idf
$ sudo rm -r $HOME/.espressif
$ cd esp-idf

$ ./installl.sh

$ vi $HOME/.profile
. export.sh のコメントアウトを解除して再ログイン



esp-idfのリポジトリからブランチを指定せずにダウンロードすると、その時点の開発最新版をダウンロードします。
例えば安定版が5.0の時は、開発最新版は5.1となりますが、まれにこの法則通りになっていないことが有ります。
安定版が5.1の時に、ブランチを指定せずにダウンロードするとv5.3-dev-1353が落ちてきます。
release/v5.2 のブランチを指定すると v5.2-beta1-263が落ちてきます。
つまり、安定版=v5.1.2 ベータ版=v5.2-beta1-263 開発版=v5.3-dev-1353の3つのバージョンが存在している状態です。
但し、この状態は長くは続かず、数日のうちに安定版=v5.2 開発版=v5.3-dev-1353となります。
こ ちらにバージョン文字列のネーミングルールが公開されています。

続く....