ほぼほぼ初心者のためのArduinoプログラミング
2019/11/04版 (Scrapbox版 2023/06/07)
宮武 昌史
※ 免責事項
この資料は,初歩的なArduinoプログラミングを行うことに特化しており,説明は大幅に省略し,また正確ではない。本格的にプログラミングをやろうとしている人にはむしろ害悪となる可能性があるので,十分注意すること。サンプルプログラムやArduinoリファレンスカードも参考にして欲しい。 1. 変数
プログラムの中で使う変数は,void setup() { } 内で「名前」と「型」を定義(プログラミングでは宣言という)する。名前は一部ダメなものもあるが,基本的には何でも良く,2文字以上でも良い(意味が分かる変数名にすることが望ましい)。型は,とりあえず整数の int だけ覚えておけば事足りる。例えば次のように定義。
code:arduino
int delta;
ここでは,deltaという名前の整数変数を定義している。なお,各命令の最後にはセミコロン “ ; ” を忘れずに。初期値を予め与える必要がある場合,次のように書けば定義と初期化が同時にできる。
code:arduino
int delta = 0;
ここでは,変数の定義と同時に初期値として0を代入している。
定義した変数は,次のように値を代入したり,代入された値を使ったりできる。
code:arduino
a = 1; // 変数 a に 値 1 を代入
b = a*(2-a); // 値の代入された a を用いて,a(1-a)を計算し,変数 b に代入
“ = ” は等しいという意味ではなく,右辺の値を左辺に代入する,という意味を持つ。
2. 関数
プログラミングにおける関数は,$ \sin x のような数学関数だけでなく,もっと広い概念を持つ。自分で関数を定義することもできるが,様々な関数は既に定義されている。入力は関数名の直後の ( ) 内で指定し,値を出力する関数の場合は,それを変数や別の関数に入れることで利用できる。例えば,次のような命令も関数である。
code:arduino
// 入力も出力もある関数
sw = digitalRead(18);
// 説明: ピン番号18を入力として関数に与え,ピンに信号が入っていればHIGHを,
// 入ってなければLOWを出力し,それを変数swに渡して保存する
// 18の所には変数を書いても動作する
// 入力しかない関数
digitalWrite(12,sw);
// 説明: ピン番号12及びHIGH/LOW信号の入った変数swを入力とし,ピンにswを出力
// (プログラム上では出力はない)
// (これによりLEDが点灯または消灯する)
// swの所には直接 HIGH または LOW と書いても動作する
$ \sin x のような数学関数の場合,$ xを入力して$ \sin xを出力すると見なせる。しかし,上の digitalWriteでは入力しかない。また,出力しかなかったり,入出力ともにない関数も有り得る。
3. 配列
1.ではスカラーとしての変数が定義されるが,ベクトルも定義できる。プログラミングでは「配列」という。例えば,次で3次のベクトル変数swが定義できる。
code:arduino
注意しなくてはならないのは,ベクトルの各成分に値を代入したり,値を読んだりする際は,sw$ [0] , sw$ [1] , sw$ [2] , sw$ [3] となる点。すなわち,sw = ( sw$ [0] , sw$ [1] , sw$ [2] , sw$ [3] ) と理解しよう。上で定義した sw に sw$ [4] は含まれないため,訳の分からない値が返ったり,訳の分からないメモリ領域に値が書き込まれ,プログラムが意図しない動作をしてしまう。これは初心者に極めて多いミス。
配列も定義と同時に初期化できる。その場合は次数を書く必要はなく,初期値の次数で自ずと定義される。次のように書くと,4つの成分が全て0の配列が定義される。
code:arduino
int sw[] = {0, 0, 0, 0};
4. 繰り返し処理
最も良く使う for 文のみを扱う。次の例で説明する。
code:arduino
for (i = 1; i < 5; i++)
{
swi-1 = digitalRead(20-i); }
ここで,for の行の ( ) 内が繰り返しを制御する部分である。ここで, i は制御のための変数であり,事前に定義が必要であるが,変数名は何でも良い。i = 1 は初期条件で,i++ はループを1回回る毎にiを1ずつ増やし,i < 5 を満たす間だけループを続けることを意味する。この場合,i = 1, 2, 3, 4 の4回ループする。この時,次の命令が連続的に実行される(つまり,上と下は同じ動作をする)。
code:arduino
sw0 = digitalRead(19); // i = 1 sw1 = digitalRead(18); // i = 2 sw2 = digitalRead(17); // i = 3 sw3 = digitalRead(16); // i = 4 ループが数回程度ならfor文を使うまでもないが,プログラムの見通しが良くなる。また,ループが多くなる場合にfor文は欠かせない。この例では,併せて配列を使っていることがプログラムの簡易化に寄与していることも見逃してはならない。
for 文は入れ子にして使うこともできる。for 文の制御変数 ( i や j など) はそれぞれ別にしなくては,意図せず制御変数の値が書き換えられ,うまく動かなくなる。
5. 条件分岐
if文で条件により処理を分ける。条件は,等号や不等号などの数式で与えられ,次のようなものがある。等号条件は間違いやすいので特に注意が必要。(既に述べたように,“ = ” 1つは,右辺の値を左辺に代入する意味である)
code:arduino
// 等号条件
a == b // a = bでないことに注意!
// 不等号条件
a >= b
a <= b
a > b
a < b
条件を満たす時だけに実行する場合は if のみ,条件を満たす時と満たさない時で処理を変えたい場合は if と else を使う。3つ以上に分岐する場合は,次のように else if を使えば良い。
code:arduino
{
digitalWrite(11, HIGH);
}
{
digitalWrite(11, HIGH);
}
else
{
digitalWrite(11, LOW);
}
この場合,最初の if でsw$ [0] の状態を確認し,LOWの場合はピン11にHIGHを出力する(LED点灯)。sw$ [0] がHIGHの場合,2番目の if でsw$ [1] の状態を確認し,LOWの場合はピン11にHIGHを出力する。そうでない場合は,else によりピン11にLOWを出力する。これは,sw$ [0] とsw$ [1] のどちらかがLOWであればHIGHを出す。
複数の条件がある場合,and を示す “ && ” や or を示す “ || ” でそれらを結合できる。例えば,上は下のように簡略化できる。
code:arduino
if ( (sw0 == LOW) || (sw1 == LOW) ) {
digitalWrite(11, HIGH);
}
else
{
digitalWrite(11, LOW);
}
if 文は入れ子にして使うことができるが,上のように and や or ,あるいは else if を使って構造を単純化できないかを考えることをお勧めする。
6. コメント,インデント,スペース
プログラムを読みやすく,特に後で意味を理解しやすくするためには,コメントを入れることが必要である。既にここまででも使っているが // から行末の後はコンパイル時に無視されるため,説明文をコメントとして入れられる。
if や for を沢山使い,プログラムが複雑になってくると,構造が分かりにくく, “ { ” と “ } ” の数や位置がおかしいなど,バグが潜みやすい。ctrl-T(コントロールキーとTキーを同時に押す)か,ツールメニューの「自動整形」を選ぶと,インデントを自動調整してくれて見やすくなる。空行も同様である。
半角スペースやタブは,プログラムの可読性を上げるために入れると良い。例えば,次のようにすれば可読性が上がることが分かるだろう。(流儀は色々有り得る)
code:arduino
// スペース・インデントあり
pinMode(7, OUTPUT);
pinMode(8, INPUT );
pinMode(9, INPUT );
pinMode(10, OUTPUT);
for (i = 1; i < 8; i++)
{
digitalWrite(i, HIGH);
if (i = 5)
{
j = 1;
}
}
// スペース・インデントなし
pinMode(7,OUTPUT);
pinMode(8,INPUT);
pinMode(9,INPUT);
pinMode(10,OUTPUT);
for(i=1;i<8;i++)
{
digitalWrite(i,HIGH);
if (i = 5)
{
j = 1;
}
}
7. プログラミングで良くあるミス
プログラム中に,コメント以外で全角文字が入っているとコンパイルできない。全角スペースは特に発見が難しいので,検索置換で削除が必要。
コマンドは void setup() または void loop() の後の { } 内に入っている必要がある。
命令の最後にセミコロンを忘れる。
for文を入れ子にした際,制御変数(i や jなど)が同じになっていると意図した動作をしない。
for文やif文の有効範囲を示す { } が意図した範囲になっていない。
if文の条件などで,等号の == ではなく代入の = を使っている。