はじめに
Arduinoでプログラムを書くための基本的なことを説明します。Arduinoでは、プログラムのことを「スケッチ(sketch)」と呼びます。スケッチは、Arduinoソフトウェアを用いて作成・コンパイル・Arduinoへのアップロードなどを行います。
- プログラミング言語
- Arduinoで利用するプログラミング言語の説明です。
- プログラムの基本構造
- Arduinoを利用するためのプログラムの構造の説明です。
- Blink(Arduinoに付属のサンプルプログラム)
- サンプルプログラムによる説明です。
- コンパイル
- プログラムのコンパイル方法です。
プログラミング言語
Arduinoソフトウェアで利用するプログラミング言語はC++/Cです。Arduinoで利用するプログラミング言語のことを「Arduino言語」や「Arduino programming language」、「Arduino Language」という表現をされることもありますが、「Arduino言語」の実体は、Arduino用のAPI(C++のクラス、Cの関数)を追加したC++/C言語です。
Arduino用のAPIは、AVRマイコン等の操作に必要な、レジスタ操作などを隠蔽し、簡単にプログラムを書くための機能を提供しています。それ以外にも、文字列操作や数学ライブラリなども提供しています。詳細は、言語リファレンスを参照してください。また、公式に配布されているライブラリ以外にも、Web上にはさまざまな機器向けのライブラリや使い方についての情報が公開されています。
C++/Cコンパイラには、AVRアーキテクチャのチップ(例:Arduino Uno)の場合はavr-g++/avr-gccを利用します。ARMアーキテクチャのチップ(例:Arduino Due)の場合は、arm-none-eabi-g++/arm-none-eabi-gccを利用しています。ESP-WROOM-32の場合は、xtensa-esp32-elf-g++/xtensa-esp32-elf-c++を利用しています。このレイヤでは、AVR等のマイコンのレジスタなどが直接見えています。
C++/Cコンパイラがインストールされるため、avr-g++/avr-gccやarm-none-eabi-g++/arm-none-eabi-gccが提供するC++/Cの機能を利用することができます。残念ながらavr-g++にはlibstdc++が付属しないようです。arm-none-eabi-g++のほうには付属しています。
スケッチでは、g++/gcc等の言語としての機能、avr-libcなど言語系が提供するライブラリ、Arduinoが提供するライブラリを利用することができます。もちろん、必要に応じて自分でライブラリを作成することもできます。
簡単に図示すると以下のようになります。
avr-g++/avr-gccが提供している、「直接プロセッサを操作するインターフェイス」も操作しようと思えば操作することもできます。ただし、Arduinoを使う意味がなくなってしまうので、どうしても必要な場合のときだけの利用に留めるのがいいと思います。
ファイルと利用される言語の関係
Arduinoのスケッチは、.ino という拡張子を持つファイルに記述します。
また、メインとなるスケッチは、スケッチの拡張子を除いた部分と同名のディレクトリ(フォルダ)に配置する必要があります。この点については、Arduino IDEが自動で調整してくれます。
それ以外にも、.cや.cppといったファイルを扱うこともできます。これらは、ライブラリを記述する際に利用します。
Arduinoで利用される言語はファイルの拡張子により異なるようです。特にライブラリや複数ファイルを取り扱う際には注意が必要です。
ファイルの拡張子 | 言語 |
---|---|
.ino | C++ |
.c | C |
.cpp | C++ |
C++のクラスとして実装されているライブラリ(例えば、HardwareSerialクラス)は、C言語から呼び出すのは、少し面倒なので注意が必要です。
プログラムの基本構造
Arduinoのプログラム(スケッチ)では、2つの関数をユーザが定義(記述)する必要があります。一つはsetup()
で、もう一つはloop()
です。どちらの関数も、戻り値も引数も持ちません。
関数 | 実行契機 | 主な用途 |
---|---|---|
void setup() |
プログラムの開始後に一度だけ実行される。 | デバイスの初期化などを行う。 |
void loop() |
setup() の実行後に、無限に実行され続ける。 |
デバイスの制御などを行う。 |
フローチャートで書くと、以下の通りです。
C++/C言語では、通常、main()
という関数を定義する必要があります(Arduinoはフリースタンディング環境なのでmain()
が存在する必要はありませんが)。Arduinoでは、Arduinoソフトウェア自身がmain()
関数を定義しているため、利用者はmain()
関数を定義する必要はありません。
main()
関数を定義してはいけません」ではなく、「main()
関数を定義する必要はありません」と書いたのは、実際にはmain()
関数を定義することができるためです。Arduinoソフトウェアが定義しているmain()
関数は、ライブラリ(core.a)内の関数としてアーカイブされます。このため、ユーザ定義ファイル内にmain()
関数がある場合は、ユーザ定義ファイルのmain()
関数が利用されることになります。当然、ユーザが複数のmain()
関数を定義すると、エラーとなります。また、Arduinoソフトウェアが定義するmain()
関数には、以下で示すように、初期化のコードなどが入っているため、自分でmain()
関数を定義する場合は注意が必要です。AVRアーキテクチャの場合、Arduinoソフトウェアによりmain()
関数は以下のように定義されています。
|
|
setup()
では、プログラムの最初で一度だけ実行する必要のある初期設定などを行います。たとえば、ピンの入出力モードを設定したり、外部に接続した機器の初期化などを行うプログラムを記述します。
loop()
では、外部の状況把握、処理決定、機器操作等を行うプログラムを記述していきます。例えば、センサの値を読み取り、センサの値に応じて、他の機器を操作するプログラムを記述します。
setup()
やloop()
で実際に行う処理がない場合でも、これらの関数を定義する必要があります。定義しない場合は、リンク時にエラーが発生します。
init()
は、Arduino環境が定義している関数で、マイクロコントローラの初期設定を行なっています。この関数を利用者は定義してはいけません。正確には、定義してもエラーにはなりませんでしたが、タイマ関連の関数などが正しく動かなくなると思います。また、この関数はmain()
を定義できる理由と同じ理由で定義は可能ですが、トラブルの元なので、やめておいた方がいいと思います。自分で関数を命名する際には注意が必要です。
setup()
やloop()
に対して引数を渡すことはできません。これらの関数はそれぞれ、void setup(void)
、void loop(void)
と宣言されています。
また、loop()
を実行後、serialEventRun()
という関数が呼び出されます。この関数は、シリアルバッファにデータがあるときに、serialEvent()
という関数を呼び出す関数です。このため、serialEvent()
という関数を定義しておけば、シリアル通信の処理を行うことができます。ただし、割り込みベースではなく、ループの中での検査なので、あまり有用ではないような気がします(必要ならば、自分でloop()
の中に処理を書けばいいので)。
Arduino-1.5.2からは、新規ファイルを作成するときに、以下のコードが自動でエディタに挿入されるようになりました。
|
|
setup()
の中で初期化を行い、loop()
の中で実際の処理を記述するという性質上、グローバル変数を多用することが多くなると思います。
拡張子が “.ino"のファイルでは、Arduinoソフトウェアが自動でプロトタイプ宣言を生成するため、ユーザ自身がプロトタイプ宣言を行う必要がありません。このため、C++/C言語によるプログラミングの習得を目的としてArduinoを利用することは、私はお勧めしません。あくまで、Arduinoを使って何かをするために利用するのがいいと思います。
Blink(Arduinoに付属のサンプルプログラム)
ここでは、インストールのページで出てきた、Arduinoソフトウェアにサンプルとしてついてきた、Blinkについて、簡単に解説します。
プログラムの仕様
Arduino Unoに搭載されているLEDを1秒周期で点滅させます。
プログラムの実装
初期化
LEDが接続されているピンを、出力モードに設定します。
Arduino Unoに搭載されているLEDは、デジタルの13番ピンに接続されています。13番ピンをデジタルの出力で利用するようArduinoに指示します。
この処理は最初に1回だけ行えばいいので、setup()
の中で実行します。28行目のpinMode(LED_BUILTIN, OUTPUT);が該当する命令です。LED_BUILTINというマクロは、Arduinoの内蔵LEDが接続されているピンを示します。Arduino Unoの場合は、13と定義されています。
LEDの点滅
13番ピンをHIGHにすればLEDは点灯し、LOWにすれば消灯します。
1秒周期でLEDを点滅させるためには、以下を繰り返します。この処理は無限に実行するため、loop()
の中に記述します。
- 13番ピンをHIGHにする。
- 1秒待つ。
- 13番ピンをLOWにする。
- 1秒待つ。
33行目のdigitalWrite(LED_BUILTIN, HIGH);で、13番ピンをHIGHにし、LEDを点灯します。34行目のdelay(1000);では、1000ミリ秒(=1秒)待ちます。35行目のdigitalWrite(LED_BUILTIN, LOW);では、13番ピンをLOWにし、LEDを消灯します。36行目ではまた1000ミリ秒待ちます。loop()
自身は、Arduino自身が無限に実行するため、結果として、1秒周期でLEDが点滅します。
34行目と36行目の待ち時間を変更することで、LEDの点滅周期を自由に設定することができます。
|
|
コンパイル
作成したスケッチは、Arduinoソフトウェアを用いてコンパイルします(Visual Studio Codeを利用することも可能です)。
Arduinoソフトウェアは、簡易なプリプロセッサの役割も果たします。詳細は、ビルドプロセスにまとめています。初期のC++言語は、C言語へのコンバータとして実装されていましたが、そこまで大げさな変換ではなく、ヘッダファイル宣言や関数プロトタイプ宣言を追加する程度の変換です。
必要なファイルのコンパイルやライブラリのリンクは、 、Arduinoソフトウェアが面倒を見てくれるため、基本的には、「検証」や「マイコンボードに書き込む」ボタンを押せば、プログラムはコンパイルされます。「マイコンボードに書き込む」の場合は、コンパイルに成功すれば、スケッチはArduinoに書き込まれます。
バージョン
Hardware: | Arduino Uno/Arduino Due/ESP-WROOM-32 |
Software: | Arduino 1.8.19 |
最終更新日
March 20, 2024