方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【ESP32 × PlatformIO】フレームワークをArduinoとESP-IDFで併存する方法

esp32-platformio-espidf-arduino
この記事は約18分で読めます。

はじめに

方言を話す、おしゃべり猫型ロボット「ミーア」を開発中。

https://mia-cat.com

前回、こちらの記事で、AWS IoT関連の設定ファイルをLittleFS領域からNVS領域に移動するのを実装した。

ただ、このままだとセキュリティ的に脆弱なので、今回はNVS暗号化の実装を試みようとしたところ、下記記載に遭遇。

https://github.com/espressif/arduino-esp32/issues/9233

Flash EncryptionとSecure BootはArduino IDEでは提供されていないし、今後も提供する予定がないとのことorz

というわけで、まずは、今使用しているArduinoフレームワークからESP-IDFフレームワークヘ変更する必要がある。

ちなみに、現在のplatform.iniファイルでの設定は下記。

C++
; PlatformIO Project Configuration File
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif32
framework = arduino
board = esp32dev

今回は、フレームワーク変更をPlatformIOでどのように行うかに関して記載。

下記Espressif公式サイトの説明参照

https://docs.platformio.org/en/latest/frameworks/espidf.html#configuration

下記順番で進めていく

  • platform.iniのframeworkにespidfを追加
  • CmakeLists.txtファイルをルートとsrcディレクトリに追加
  • sdkconfig.defaultsファイルを追加し、arduinoでも起動できるように必要な設定を追加
  • arduinoライブラリでesp-idfでも起動するのに必要なものは追加

ESP-IDFフレームワーク概要

CmakeLists.txtファイルとは?

ESP-IDFフレームワークはCMakeというビルドシステムを用いている。

CMakeLists.txtファイルは、CMakeビルドシステムに対してプロジェクトのビルド方法を指示するファイル。ESP-IDFを使用する際、プロジェクトの構成とビルドプロセスを管理するために必要となる。

PlatformIOでESP-IDFを使用するプロジェクトは、通常、以下の構造を持つ。

Plaintext
project_dir/
├── include/
├── src/
│   ├── CMakeLists.txt
│   └── main.c
├── CMakeLists.txt
└── platformio.ini

ルートディレクトリ、srcディレクトリの2つのCmakeLists.txtファイルが存在することがわかる。それぞれの用途と記載方法を見ていく。

ルートディレクトリにあるCMakeLists.txt

プロジェクト全体の設定を行う。一般的に、このファイルには以下のような内容が含まれる。

CMake
# 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.16.0)

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

project-name は CMake の中で設定するプロジェクトの名前で、これは任意の名前を指定することができる。この名前は、CMake はこの名前を使用してプロジェクト固有の変数を生成し、ビルドプロセスを管理する。

PlatformIO のプロジェクト名は主に PlatformIO の環境内で使用され、CMake の project-name は CMake と ESP-IDF のビルドシステム内で使用されるので、PlatformIOのプロジェクト名を一致させる必要はないが、一致させた方が可読性が高まる。

srcディレクトリ内のCMakeLists.txt

特定のコンポーネント(この場合はアプリケーションのメインコンポーネント)のビルドプロセスを制御する。最小限の設定では、以下のようにコンポーネントをビルドシステムに登録する

CMake
idf_component_register(SRCS "main.c")

これらのCMakeLists.txtファイルは、プロジェクトがESP-IDFのビルドシステムを使用して正しくビルドされるために必要。また、追加のコンポーネントや特別なビルド設定が必要な場合には、これらのファイルに対応する設定を追加する。

sdkconfig.defaults

platformio.iniファイルは、PlatformIOプロジェクトの主要な設定ファイルであり、プロジェクトのビルド設定、使用するボード、フレームワーク、ライブラリ依存関係などを定義する。

一方、sdkconfig.defaultsはESP-IDF固有の設定ファイルで、ESP-IDFフレームワークの様々な設定を事前に定義できる。システムの動作、機能の有効化/無効化、パフォーマンスのチューニングなどが含まれる。プロジェクトのルートディレクトリに設置する。

ESP-IDFフレームワークの概要を把握したところで、実際に現在の設定を置き換えていく。

platformio.iniの更新:frameworkにespidfを追加

platformio.iniファイルのframeworkの部分をarduinoからespidfに変更する。

当初、framework = espidfと指定しなければいけないと思っていたし、公式サイトにもそのように記載されていたのでその手順で進めていた。

その場合、下記作業が発生して、大変になる(実際途中まで進めてしまいかなり大変だった)

Arduinoライブラリの管理:

  • Arduinoのライブラリを使用するためには、componentsフォルダ配下にArduinoライブラリを全てgit cloneする必要があり、これにより、プロジェクトのファイル容量が数GBになる可能性がある。

コードの書き換え

  • espidfフレームワークを指定してビルドすると、arduinoライブラリ関連は全てエラーが出るので、ArduinoライブラリがArduino.hでインクルードされている部分をすべて書き換える必要がある。
ShellScript
In file included from src/app_config.cpp:1:
src/app_config.h:1:10: fatal error: FS.h: No such file or directory

************************************************************
* Looking for FS.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:FS.h"
* Web  > https://registry.platformio.org/search?q=header:FS.h
*
************************************************************

    1 | #include <FS.h>
      |          ^~~~~~
compilation terminated.
Compiling .pio/build/release/src/donwload.cpp.o
In file included from src/button_manager.cpp:1:
src/button_manager.h:1:10: fatal error: Arduino.h: No such file or directory

*****************************************************************
* Looking for Arduino.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:Arduino.h"
* Web  > https://registry.platformio.org/search?q=header:Arduino.h
*
*****************************************************************

しかし下記の記述で、PlatformIOでArduinoとESP-IDFのフレームワークを併用することができるとのこと。自分の場合は、途中でこれに気づいて、かなり時間を食ってしまった。

C++
[env]
platform = espressif32 @ 6.7.0
framework = arduino, espidf

このように指定することで、既存の多くのArduinoライブラリを直接利用できるため、コードのポーティングや書き直しの手間を省くことができる。

というわけで、framework = arduino, espidf を設定する。

実際にespidf公式でも、espidfとarduinoをplatform.iniで併用しているexample(blink, wifiscan)が記載されている

https://github.com/platformio/platform-espressif32/tree/master/examples?utm_source=platformio.org&utm_medium=docs

CMakeLists.txtの作成(ルート・src)

プロジェクトのルートディレクトリにCMakeLists.txtファイルを作成する。以下の内容を記述。

CMake
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(clocky_platformio)  #プロジェクト名

srcフォルダにもう1つのCMakeLists.txtファイルを作成する。以下の内容を記述。

CMake
file(GLOB_RECURSE app_sources *.cpp)
idf_component_register(SRCS ${app_sources}
          					INCLUDE_DIRS "."
                    INCLUDE_DIRS "json"
                    )

まず、ワイルドカードを使ってsrcディレクトリ内のすべての.cppファイルをビルド対象として指定する。

  • file: CMakeの組み込みコマンドで、ファイルシステムの操作を行う。
  • GLOB_RECURSE: ファイルパターンを再帰的に検索するオプション。ディレクトリとそのサブディレクトリを再帰的に検索し、指定されたパターンに一致するファイルを見つける。
  • app_sources: 検索結果を格納する変数の名前。この変数には、見つかったファイルのパスが格納される。

というわけで、以下のコードを実行すると、srcディレクトリおよびそのサブディレクトリ内のすべての.cppファイルを検索し、それらのパスをapp_sources変数に格納する。

CMake
file(GLOB_RECURSE app_sources *.cpp)

また、INCLUDE_DIRS "."により、srcディレクトリ内のヘッダーファイルがインクルードパスに含まれる。同様に、jsonサブディレクトリをビルド対象に含めたいので、INCLUDE_DIRS “json”と追加する。

CMake
file(GLOB_RECURSE app_sources *.cpp)
idf_component_register(SRCS ${app_sources}
										INCLUDE_DIRS "."
                    INCLUDE_DIRS "json"
                    REQUIRES esp-idf)

sdkconfig.defaultsの作成

ESP-IDFでは、必要な設定をsdkconfig.defaultsに記載することで初期設定を行うことができる。

新規にsdkconfig.defaultsファイルをプロジェクトのルートディレクトリに作成し、下記を記載する

Plaintext
CONFIG_AUTOSTART_ARDUINO=y
CONFIG_FREERTOS_HZ=1000
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y

CONFIG_AUTOSTART_ARDUINO=y

  • 機能: Arduinoライブラリを自動的に起動する設定。
  • 理由: この設定を有効にすることで、ESP-IDFの環境でArduinoのスケッチ(setup()とloop()関数)を自動的に実行できるようになる。つまり、ESP-IDFのデフォルトのapp_main()関数を使用する必要がなくなる。ESP-IDFとArduinoフレームワークを併用する際に、Arduinoのライブラリや関数が適切に動作するようにするために必要。

CONFIG_FREERTOS_HZ=1000

  • 機能: FreeRTOSのシステムティックレート(Hz)を1000に設定。
  • 理由: デフォルトのシステムティックレートは100Hzだが、Arduinoのスケッチやライブラリは一般的に1msの精度(1000Hz)を前提として動作する。したがって、この設定を行うことで、Arduinoのコードが正確に動作するようにする。

CONFIG_MBEDTLS_PSK_MODES=y

  • 機能: mbedTLSでPSK(Pre-Shared Key)モードを有効にする。
  • 理由: PSKモードは、事前共有鍵を使用した暗号化通信を可能にする。セキュリティ機能を強化するために、この設定を有効にすることで、PSKを使用した暗号化通信を実装できる。

CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y

  • 機能: mbedTLSでPSKによる鍵交換を有効にする。
  • 理由: PSKモードを有効にした場合、PSKによる鍵交換も有効にする必要がある。これにより、事前共有鍵を使った安全な鍵交換が可能になり、セキュリティ通信を確立できる。

MQTT通信でTLS/SSLを使用し、PSKを用いる場合には、CONFIG_MBEDTLS_PSK_MODESCONFIG_MBEDTLS_KEY_EXCHANGE_PSKの設定が必要。

menuconfigでsdkconfigを設定することも可能

ちなみにsdkconfigの設定は、下記でplatformIOでESP-IDFのmenuconfigを開くことで設定も可能

ShellScript
pio run -t menuconfig

下記画面が開くので、設定項目にチェックをつけていく

  • CONFIG_AUTOSTART_ARDUINO:「Component config」->「Arduino Configuration」->「Autostart Arduino setup and loop on boot」
  • CONFIG_FREERTOS_HZ:「Component config」->「FreeRTOS」->「Tick rate (Hz)」

指定項目をチェック後、保存しようとすると保存先のファイルを指定できるので、選択するとファイルに設定が反映される。

もちろん、GUIでの設定が面倒な場合はsdkconfigファイルを直接修正してもよい。

LittleFSリンカーエラー対応

上記設定でビルドすると、LittleFSリンカーエラーが発生した

ShellScript
Linking .pio/build/debug/firmware_debug.elf
/Users/ky/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/debug/src/main.cpp.o:(.literal._Z11setupCommonv+0x24): undefined reference to `fs::LittleFSFS::begin(bool, char const*, unsigned char, char const*)'

ESP-IDFとArduinoの両方のフレームワークを使用している場合に、ArduinoのLittleFSライブラリが正しくリンクされていないことが考えられる。

同じような問題に直面している人を発見。

https://github.com/platformio/platform-espressif32/issues/965

Componentsフォルダにesp_littlefsライブラリを追加→失敗

littlefsライブラリのクローン

プロジェクトのcomponentsフォルダにesp_littlefsライブラリをクローンする。

https://github.com/joltwallet/esp_littlefs.git

ShellScript
mkdir -p components
cd components
git clone --branch v1.14.4 https://github.com/joltwallet/esp_littlefs.git

CMakeLists.txtの修正

プロジェクトのルートにあるCMakeLists.txtファイルを修正してesp_littlefs

ライブラリを含める。

CMake
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/components/esp_littlefs")

project(clocky_platformio)

上記でビルドしたところ、リンクエラーが解消されず

ShellScript
components/esp_littlefs/src/esp_littlefs.c:12:10: fatal error: littlefs/lfs.h: No such file or directory
 #include "littlefs/lfs.h"
          ^~~~~~~~~~~~~~~~
compilation terminated.

idf_component.ymlを使用してesp_littlefsライブラリを管理→成功

先ほどの同じ問題に遭遇した人のスレッドで、下記の記載があったのでトライ

Add managed components by addingidf_component.yml See as example  https://github.com/Jason2866/Arduino_IDF_LittleFS/blob/main/src/idf_component.yml

idf_component.ymlファイルの作成: プロジェクトのsrcフォルダにidf_component.ymlファイルを作成し、以下の内容を追加。

YAML
dependencies:
  idf: ">=4.4"
  esp_littlefs:
    git: https://github.com/joltwallet/esp_littlefs.git

CMakeLists.txtの修正: プロジェクトのルートディレクトリにあるCMakeLists.txtファイルを修正し、managed_componentsフォルダをビルドシステムに追加する。

CMake
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

list(APPEND EXTRA_COMPONENT_DIRS managed_components)

project(clocky_platformio)

managed_componentsフォルダが生成されるのは、idf_component.ymlの仕様によるもの。生成されるのはcomponentsフォルダではない。これは、ESP-IDFのコンポーネント管理システムが依存関係を自動的にダウンロードして配置するためのフォルダ。

idf_component.ymlを使用することで、ESP-IDFのビルドシステムは依存関係を自動的に管理し、必要なライブラリを正しいバージョンでダウンロードして配置したのか?明確な理由は不明だが。

祈るような気持ちでcompileが終わるのを待つ。

ようやくCompileエラーをクリア!

無事ビルド完了するも、、

無事ビルドできた!

ここまでくるのに、正味15時間以上かかったのでは?orz

ファイル数が多くなってきたので、C++のコンパイルに時間がかかるようになり、エラーを1つずつ解消すると、少しずつコンパイル進むのだが、より後のコンパイルでエラーがある場合は表示されるようになるので、待機時間が長く、「よしこの修正で次こそは!」と思っても最後の方でコンパイルエラーが発生するとガックリというのを繰り返した。。

と思ったら、Wifi接続後のMQTT通信後に下記エラーが発生。

ShellScript
***ERROR*** A stack overflow in task loopTask has been detected.

一難去って、また一難。上記エラーの解消記事はこちら

タイトルとURLをコピーしました