
Arduino CLI
スケッチについて
Arduinoのプロジェクトは「スケッチ(sketch)」と呼ばれます。
多くのArduinoプロジェクトは.ino
ファイルが1つだけで完結しているので、そのファイルをスケッチと呼ぶものだと思いがちですが、スケッチは他の環境で呼ぶところの「プロジェクト」で、現代では複数のファイル及びディレクトリから構成されています。
スケッチフォルダーの構成
スケッチを置くディレクトリを「スケッチフォルダー(sketch folder)」と呼びます。また、スケッチフォルダーの最上位のディレクトリを「スケッチルートフォルダ(sketch root folder)」と呼びます。
スケッチルートフォルダには少なくとも1つの.ino
ファイルが存在する必要があります。また、その1つの名前はスケッチ名.ino
である必要があります。このファイルを「プライマリスケッチファイル(primary sketch file)」と呼びます。
実は無理にそうする必要はありません。
arduino-cli compile
のオプションとして、ファイル名を指定すれば、それでコンパイルすることは可能です。ただし、それをやるメリットは何もありませんしコマンドラインが長くなるだけなので、そういったものだと思って使うのが良さそうです。
追加のコードファイル
スケッチは複数のファイルで構成可能です。
arduino-cliが対応している拡張子は以下のものです。
拡張子 | 意味 |
---|---|
.ino | Arudino言語 |
.cpp | C++ |
.c | C |
.S | アセンブラ |
.h .hpp, hh | ヘッダファイル |
.tpp .ipp | ヘッダファイル |
これらの拡張子があるファイルに関しては、arduino-cliが適当に処理してくれます。
srcディレクトリ
arduino-cliはsrc
というディレクトリは再帰的にコンパイルするようになっています。
ですからたとえば、
foo/
foo.ino
src/
bright/
bright.c
bright.h
というようなディレクトリ構成になっていれば、勝手にsrc/
を辿ってbright/
をコンパイルをして… ということをしてくれます。
ですから、モジュール構成をしたい場合は、src/
というディレクトリを作って、その中に適当に分割して置いてやれば、自動的に処理してコンパイルしてくれます。
dataディレクトリ
arduino-cliはdata
というディレクトリに関しては何もしません。
ですから、「何もして欲しくないファイル」はここに置くと良いでしょう。
実のところ、arduino-cliがコンパイルの対象にするのはスケッチルートフォルダとsrc
以下だけなので、これら以外であればarduino-cliは何もしません。
ではなぜこのような仕様が存在しているかと言えば、Arduino IDEの歴史的な事情のようです。詳しいことは公式ドキュメントに書かれているので、興味のある人はそっちを読んで下さい。
ただし、歴史的にそうであったと言うことは多くの人達(先達)がこういった流儀をとっているはずなので、「そういったものなのだ」ということは認識しておいて良いでしょう。
複数ファイルに分割する例
複数ファイルに分割した場合、ino
とそれ以外とでは扱いが異なります。
ino
の場合、特に考えることなく、必要箇所をそのまま分割します。たとえば、
#define MIX(col, val) ((uint16_t)((uint8_t)(col) * (uint8_t)(val)) >> 8)
int
bright(
int _br,
int *f,
int upper,
int lower)
{
int r = (int)( random() & 0x3);
int low = 10; //lower + (int)( random() & 0x3F );
int high = 200; //upper + (int)( random() & 0x3F );
_br = _br + (( *f > 0 ) ? r : -r);
if ( _br < low ) {
*f = 1;
_br = low;
} else
if ( _br > high ) {
*f = -1;
_br = high;
}
return (_br);
}
static int r, br[NR_LED];
static int f[NR_LED];
static int i;
static int fact;
static int msec;
static int num = 0;
このような部分があったとして、bright
だけ分割して別ファイルにすることを考えます。
この場合、拡張子をino
に分割する場合は、bright
だけのファイルにして構いません。
int
bright(
int _br,
int *f,
int upper,
int lower)
{
int r = (int)( random() & 0x3);
int low = 10; //lower + (int)( random() & 0x3F );
int high = 200; //upper + (int)( random() & 0x3F );
_br = _br + (( *f > 0 ) ? r : -r);
if ( _br < low ) {
*f = 1;
_br = low;
} else
if ( _br > high ) {
*f = -1;
_br = high;
}
return (_br);
}
元のファイルからは、この部分を除きます。本当に単純に「抜いて別ファイル」にするだけです。
これでコンパイルすれば、問題なく処理が終わります。
他方、「これはCだよな」と思って、分割した部分の拡張子をc
にすると、
$ arduino-cli compile --fqbn esp32:esp32:esp32
/home/ogochan/foo/foo.ino: In function 'void loop()':
/home/ogochan/foo/foo.ino:194:11: error: 'bright' was not declared in this scope
br[j] = bright(br[j], &f[j], 0xC0, 0);
^~~~~~
/home/ogochan/foo/foo.ino:194:11: note: suggested alternative: 'sig_t'
br[j] = bright(br[j], &f[j], 0xC0, 0);
^~~~~~
sig_t
Used library Version Path
WiFi 2.0.0 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11/libraries/WiFi
Used platform Version Path
esp32:esp32 2.0.11 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11
Error during build: exit status 1
このようにいろいろエラーとなります。
この場合は、以下のような対応が必要になります。
-
ヘッダファイルを用意してincludeする
ino
の方にもc
の方にも必要です -
c
には必要なヘッダファイルを自分でincludeするino
の場合はいろいろ「よしな」にしてくれますが、c
は自分で対応する必要があります -
includeする時は
extern "C"
でくくる必要があるino
の本質はC++です。C++からCを呼び出す時にはこうしてやる必要があります(呼び出すコードがC++であるなら当然これは不要です)。
自分の書いたコードだけであれば、ino
として分割する方が簡単ですが、他から持って来たコードだと、ino
として扱うことが出来ないこともあります。そのような場合はCやC++だったりしますから、このような使い方は把握しておく必要があります。
このような手当をしてやることにより、
$ arduino-cli compile -b esp32:esp32:esp32
Sketch uses 276153 bytes (21%) of program storage space. Maximum is 1310720 bytes.
Global variables use 23096 bytes (7%) of dynamic memory, leaving 304584 bytes for local variables. Maximum is 327680 bytes.
Used library Version Path
WiFi 2.0.0 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11/libraries/WiFi
Used platform Version Path
esp32:esp32 2.0.11 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11
というように、無事コンパイルが通るようになりました。
スケッチメタ情報
sketch.yaml
というファイル名でスケッチルートフォルダにあります。このファイルは自分で作らないと存在しないので、ないこともあります)。
これにはスケッチの「プロファイル」が定義されています。これは要するにスケッチをビルド(コンパイル)する時の情報が記録されているファイルです。
スケッチメタ情報の内容
このファイルには以下の情報が含まれています。
-
ボードFQBN
ターゲットのコア プラットフォームの名前とバージョン (必要に応じてサードパーティ プラットフォームのインデックス URL も含む)
-
ターゲット コア プラットフォームの依存関係である、考えられるコア プラットフォームの名前とバージョン (必要に応じてサードパーティ プラットフォームのインデックス URL を含む)
-
スケッチで使用されているライブラリ (バージョンを含む)
ファイルの形式としては、
profiles:
<PROFILE_NAME>:
notes: <USER_NOTES>
fqbn: <FQBN>
platforms:
- platform: <PLATFORM> (<PLATFORM_VERSION>)
platform_index_url: <3RD_PARTY_PLATFORM_URL>
- platform: <PLATFORM_DEPENDENCY> (<PLATFORM_DEPENDENCY_VERSION>)
platform_index_url: <3RD_PARTY_PLATFORM_DEPENDENCY_URL>
libraries:
- <LIB_NAME> (<LIB_VERSION>)
- <LIB_NAME> (<LIB_VERSION>)
- <LIB_NAME> (<LIB_VERSION>)
...more profiles here...
という感じです。それぞれの意味は、
-
<PROFILE_NAME>はプロファイル識別子であり、ユーザー定義フィールドであり、使用できる文字は英数字、アンダースコア_、ドット.、およびダッシュ-です。
-
は、ターゲットコアプラットフォームの識別子です。たとえば、arduino:avrやadafruit:samdのような文字列です。 -
<PLATFORM_VERSION>必要なターゲットコアプラットフォームのバージョンです。
-
<3RD_PARTY_PLATFORM_URL>ターゲットコアプラットフォームをダウンロードするためのインデックスのURL(Arduino IDEでは「追加のボードマネージャーURL」とも呼ばれます)。公式のarduino:*プラットフォームの場合はこのフィールドは省略できます。
-
<PLATFORM_DEPENDENCY>, <PLATFORM_DEPENDENCY_VERSION>および<3RD_PARTY_PLATFORM_DEPENDENCY_URL>はそれぞれ
, <PLATFORM_VERSION>および<3RD_PARTY_PLATFORM_URL>と同じ情報が含まれていますが、メインコアプラットフォームのコアプラットフォームについての依存関係の情報が含まれています。これらのフィールドはオプションです。 -
libraries:プロジェクトをビルドするために必要なライブラリが定義されるセクションです。このセクションはオプションです。
-
<LIB_VERSION>は、ライブラリに必要なバージョンです (例: 1.0.0)。
-
<USER_NOTES>開発者がコメントを追加するために使用できるフリーのテキスト文字列です。このフィールドはオプションです。
となっています。
簡単な例ではこんな感じです。
profiles:
esp32:
fqbn: esp32:esp32:esp32
platforms:
- platform: esp32:esp32 (2.0.11)
XIAO:
fqbn: esp32:esp32:XIAO_ESP32C3
platforms:
- platform: esp32:esp32 (2.0.11)
default_profle: esp32
default_fqbn: esp32:esp32:esp32
default_port: /dev/ttyACM0
このプロファイル名は、コンパイル時に使ってコマンドラインを簡単にすることに使えます。
スケッチメタ情報の雛形を得る
スケッチメタ情報は全部自分で書いても良いのですが、「今のコンパイル環境とコマンドラインに対応したもの」であれば、arduino-cliに出力させることができます。
$ arduino-cli compile --fqbn esp32:esp32:esp32 --dump-profile --no-color --only-compilation-database
のようにすると、ライブラリ情報と共にプロファイルが出力されます。
$ arduino-cli compile --fqbn esp32:esp32:esp32 --dump-profile --no-color --only-compilation-database
Used library Version Path
WiFi 2.0.0 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11/libraries/WiFi
Used platform Version Path
esp32:esp32 2.0.11 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11
profiles:
esp32:
fqbn: esp32:esp32:esp32
platforms:
- platform: esp32:esp32 (2.0.11)
このprofiles:
の部分を切り取ってsketch.yaml
の元にすれば良いです。
複数対応している場合だと、たとえば
$ arduino-cli compile --fqbn esp32:esp32:XIAO_ESP32C3 --dump-profile --no-color --only-compilation-database
Used library Version Path
WiFi 2.0.0 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11/libraries/WiFi
Used platform Version Path
esp32:esp32 2.0.11 /home/ogochan/.arduino15/packages/esp32/hardware/esp32/2.0.11
profiles:
XIAO_ESP32C3:
fqbn: esp32:esp32:XIAO_ESP32C3
platforms:
- platform: esp32:esp32 (2.0.11)
のように出力されたものをマージしてやれば良いでしょう。
デフォルトの設定
sketch.yaml
の中に記述することにより、コマンドラインのデフォルト値が設定可能です。
以下のものがデフォルトとして設定できます。
- --fbqn
- default_fqbn
- --port
- default_port
- --protocol
- default_protocol
- --profile
- default_profile
たとえば、
default_fqbn: arduino:avr:uno
default_port: /dev/ttyACM0
default_protocol: serial
default_profile: myprofile
これらを設定しておくことにより、コンパイルやアップロードの時に、これらのオプションを指定する必要がなくなります。