前回の記事が長くなってしまったので、引き続きVSCODE + PlatformIOでM5Stackで電子工作するときにはまったポイントを備忘録としてメモ。
この記事では、PlatformIOの売りであるライブラリの依存性管理機能LDF(Library Dependency Finder)の使い方を適宜調べて記載更新していく予定
本当は、M5Stackで株式会社アクエストの音声合成エンジンAquesTalkのESP32版である、AquesTalk pico for ESP32を使いたくて、はまったからLDFを調べていたのだけど、それはまた次の記事にでも。なかなか本命まではたどり着けないなあ。
ざっくり理解したこと
- 使用するBoardのPlatformとFrameworkがもつtool chainとライブラリがどこに保存されるのか、また、自作ライブラリ・git hubなどからのライブラリを直接ライブラリを落とす場合は、どこに保存して利用するのかを意識する。(この場合はlibrary.jsonの記載が大事)
- configuration fileであるplatfomrio.iniの設定項目、lib_depsを使って、利用するライブラリをProject毎に独立してインストールする。 lib_extra_dirsにlib_depsではインストールできないライブラリなどで利用するものを指定して格納する。
- 利用するライブラリの依存やリンク関係を考えながら、lib_ldf_modeでどのStorage(フォルダ)までビルド時に参照しにいくかを考える。
前提として理解しておくべきフォルダ、ファイル類の作られる場所など
LDFの使い方に飛びつく前に、まずPlatformIOを利用する際に作成されるファイル、フォルダ類の場所を中心に確認しよう。
用語
- Board(Name)
- M5 Stack Core ESP32とかM5Stick-CとかArduino Unoとかを指すもの
- Platform
- アプリケーションの実行環境。でもMCU(Micro Control Unit)の名前そのもののときもあれば、OSを指すときもありそうな。PlatformIOでいうと、Espressif 32とかAtmel AVRとか
- Framework
- 設計から実装にまたがって再利用される一連の仕組み。なんのことかわからん。 Arduino(IDE?)とかESP-IDFとかSimbaとか。エディタ + ビルド一式(コンパイラ>アセンブラ>リンカ)とTool chainとの違いなどよくわからん。IDEだととりあえず思っておく
まとめるとこんな感じ
Name | Platform | Framework | MCU |
---|---|---|---|
Arduino Uno | Atmel AVR | Arduino, Simba | Atmeg328p |
M5 Stack Core ESP32 | Espressif 32 | Arduino, ESP-IDF | ESP32 |
M5Stick-C | Espressif 32 | Arduino, ESP-IDF | ESP32 |
ライブラリファイル等の保存場所の確認
私の環境(Windows)だとこんな感じ。Project毎につくられる、extensions.jsonをみるのが一番手っ取り早いと思う。
"C:\Users\xxxxx\Documents\PlatformIO\Projects\Project名.vscode\extensions.json"
PlatformIOそのもの
"C:\Users\xxxxx.platformio"に作成されている。
この配下にこういったフォルダ類が有る。
- lib
- packages
- platforms
Global ライブラリについて
このlibはPlatformIO HOMEのライブラリ管理から単純にインストールしたらここに入る(後述)。
Globalに利用するライブラリを入れる場所だが、PlatformIOではライブラリをGlobalで利用することは推奨されていない。
platformsとframeworkについて
まずPIOHOMEで以下のようなProjectを作る
platform = atmelavr
board = uno
framework = arduino
"C:\Users\xxxxx.platformio\packages"フォルダを覗いてみると、どうやらここに指定したframeworkがデフォルトでもつ関数やスケッチ例がおいてあるらしい。
|_ framework-arduinoavr
|_framework-arduinoavr\libraries\__cores__
|_arduino\Wire
|_src\Wire.h
|_framework-arduinoavr\libraries\__cores__
|_arduino\Wire\examples\master_reader\master_reader.ino
|_toolchain-atmelavr
こんなのができる
また、M5stack coreをBoardとして使うProjectをつくると
platform = espressif32
board = m5stack-core-esp32
framework = arduino
C:\Users\xxxxx\.platformio\packages\
|_framework-arduinoespressif32
といったフォルダができる。
つまり、選択したboardとplatformとframeworkに合わせてtoolchainやデフォルトで使える関数やそのExampleがpackages配下にできるのだろう。
わからない platformsフォルダの役割
"C:\Users\xxxxx.platformio"配下のplatformsフォルダの役割がわからない。
PIOHOMEで以下のようなProjectを作ると
platform = atmelavr
board = uno
framework = arduino
"C:\Users\xxxxx.platformio\platforms\atmelavr"
こういったplatformsフォルダができる。
このplatformsフォルダの中には、
"C:\Users\xxxxx.platformio\platforms\atmelavr\examples\arduino-blink"
こういうExampleスケッチもある。
ここのExamplesはpackagesi\framework-arduinoavr\libraries\配下にあるExamplesとどういう役割の違いがあるのだろう。うーむ
PlatformIOで作成したProjectの保存場所
さて、PlatformIO HOME画面からTutorial ArduinoというProject名を新規作成してみる。
私の環境はPjojectをつくると
"C:\Users\xxxxx\Documents\PlatformIO\Projects\Tutorial Arduino"
というフォルダが作られ、このフォルダには以下のようなフォルダやファイルが出来上がる
|_.pioenvs\
|_.piolibdeps\
|_.vscode\
|_.include\
|_lib\
|_src\
|_main.cpp ←これがソース・ファイルになる
|_test\
|_.gitignore
|_.trabis.yml
|_platromio.ini
## ライブラリのインストール方法
【非推奨】グローバルにインストールする
PIOHOMEからライブラリマネージャーに行ってRegistryからライブラリ検索して選択してインストールする。
こうすると、前述したGlobalライブラリフォルダにライブラリが保存される。
例:ArduinoJsonをグローバルライブラリにインストールした場合は
"C:\Users\xxxxx.platformio\lib\ArduinoJson_ID64
ここに保存される。
PlatformIO上の全てのプロジェクトから参照可能にしたいならば、Globalライブラリもありだが、わざわざPlatformIO選んだのは、ライブラリの競合・依存性を管理したいためなので、この選択はしない方が吉。
【推奨】lib_depsを指定してProject毎にインストールする
まず、Project毎に独立したライブラリ管理をしたいならば、
1.Installの横の...にカーソルをあてて、Install toをクリック
2.インストール先をGlobal storageではなくて、個別のProjectを選んであげればよい。
でも、この方法よりも、該当するProjectのplatformio.iniを編集してlib_deps optionを指定して、インストールするやり方のほうが、platform.iniを見ればどのライブラリを活用しているのかひと目でわかるので良いと思う。
[env:m5stack-core-esp32]
platform = espressif32
##これを追記 2つのライブラリ使うなら2つ書く(名前、ID、バージョン指定など)
lib_deps =
# Using a library name
ArduinoJson
M5Stack
board = m5stack-core-esp32
framework = arduino
どちらも結果はProject毎の.piolibdepsにライブラリが保存される。
"C:\Users\xxxxx\Documents\PlatformIO\Projects\Tutorial Arduino.piolibdeps\ArduinoJson_ID64"
"C:\Users\xxxx\Documents\PlatformIO\Projects\Tutorial Arduino.piolibdeps\M5Stack_ID1851"
自作ライブラリなど
もちろん、自作ライブラリやgithubからダウンロードしたライブラリを、Project専用のライブラリフォルダに明示的にいれる使い方もできる。
"C:\Users\wataru\Documents\PlatformIO\Projects\Tutorial Arduino\lib"
ライブラリの依存性の管理方法
いよいよこれからLDF(Library Dependency Finder)の使い方を書いていこう。
PlatformIOの依存性管理の基本は、6つのLibrary optionsの中から特に、lib_deps, lib_extra_dirs, lib_ldf_modeを上手に使うことだと思う。
まずGlobalライブラリの使用を避けて、前述したplatformio.iniにlib_depsを指定してライブラリをProjectの.piolibdepsフォルダへインストールするのが基本。
プライベートなライブラリを使いたい場合は、Project毎のlibフォルダに入れるか、lib_extra_dirsを作成しplatformio.iniに記載してこの中に入れる。
そして、ソース・ファイルと各ライブラリの依存性を考慮しながら、LDFのmodeをなるべく手間なく、かつ、思わぬライブラリ競合の可能性が少ないように (off) > chain(default) > chain + > deep > deep + の順番で考慮して、lib_ldf_modeに記載して、うまいこと動くように設定する。
これがLDFをうまく使う基本になる。
さらにうまくいかないときは、各libraryのlibrary.jsonをいじったりするんだろうけど、それは今回の記事の対象外。
LDFってそもそもなに?
C++をビルドするときにどのLibraryをソース・ファイルと一緒にコンパイルするかを管理する仕組みと理解した。
宣言で指定されたフォルダ内だけ探すのか、includeでネストされているファイルを探すのか、フォルダ全体を探すのか、includeだけでなく、C/C++ Preprocessor conditional syntax (#ifdef, if, defined, else, and elif) でつながっているファイルも探すのかなどをmodeを変更して管理する。
6つのLibrary options
platfomio.iniに指定できるLibrary optionsはこの6つ。この記事では、lib_deps(前述), lib_extra_dirs, lib_ldf_modeについて扱う。
- lib_deps
- lib_ignore
- lib_extra_dirs
- lib_ldf_mode
- lib_compat_mode
- lib_archive
lib_extra_dirsの使い所とLDFがライブラリを探す順位
LDFがビルドする時に利用するライブラリをどの保存場所Storage(=folder)からもってくるか。それには優先順位がある。
build環境毎に設定できる、lib_extra_dirsが一番優先順位が高く、その次が、Project毎のlibフォルダ。3番目が、同じくproject毎に設定できるlibdeps_dir(.piolibdepsフォルダ)となる。
正確にはこの順番。ふむふむ
- lib_extra_dirs - extra storages per build environment
- lib_dir - own/private library storage per project
- libdeps_dir - project dependency storage used by Library Manager
- "home_dir/lib" - global storage per all projects
- Library storages built into frameworks, SDKs.
Dependency Finder Mode (lib_ldf_mode)の使い方
Dependency Finder Mode (lib_ldf_mode) は、なるべくdeep,deep+は使わないように済むようにするのが推奨らしい。
- off
- Builds only the libraries that are specified in manifests (library.json, module.json) or using lib_deps option.
- chain(default)
- Parses ALL C/C++ source files of the project and follows only by nested includes (#include ..., chain…) from the libraries. It also parses C, CC, CPP files from libraries which have the same name as included header file. Does not evaluates C/C++ Preprocessor conditional syntax.
- deep
- Parses ALL C/C++ source files of the project and parses ALL C/C++ source files of the each found dependency (recursively). Does not evaluates C/C++ Preprocessor conditional syntax
- chain+
- The same behavior as for the chain but evaluates C/C++ Preprocessor conditional syntax
- deep+
- The same behavior as for the deep but evaluates C/C++ Preprocessor conditional syntax
いよいよAquesTalkに再チャレンジ!
長くなったので、また次の記事にまとめよう。