時間を計測するスケッチを検討します。
メモ用紙の書き換え方法
前回の記事では、C/C++言語に変数が用意されていることを説明しました。変数はメモ用紙のようなもので、ちょっと変わった感じのメモ用紙でした。特徴は覚えていますか?
今回の記事では、この変数を活用して時間を計測するスケッチを作成していきますが、最初に変数の扱い方を習得します。メモ用紙、つまり変数に数字を書くには、C/C++言語では「=」記号を使用して以下のように書きます。
「=」記号の左側には変数の名前を書きます。「=」記号の右側には書き込む数字を書きます。
メモ用紙に数字を書く、つまり変数に数字を書くことを「変数に代入する」と呼んでいます。それではこの変数の代入について、詳しくみていきます。
例えば、以下のように変数を宣言したとします。
uint8_t count;
これはuint8_t型(0〜255)で、変数名が「count」という変数の宣言ですよね。ようするに、0〜255の数字を書ける「count」というタイトルのメモ用紙を用意しています。
このメモ用紙に0を書きたい場合、つまりcountという変数に0を代入したい場合は以下のように書きます。
count = 0;
代入する数字は計算式でも構いません。
count = 12 + 34;
「=」の使い方はちょっとわかってきましたか? ここからだんだん複雑になってきます。
先ほどの説明で、「=」記号の右側は数値、と説明しましたが、変数でも問題ありません。ここで、名前が「a」と「b」の2つの変数を宣言したとします。
uint8_t a;
uint8_t b;
最初に変数「a」に123を代入します。
a = 123;
この命令を実行すると、変数「a」に「123」という数値が代入されます。次に以下のように書くと、変数「a」の内容を、変数「b」に代入することもできます。
b = a;
この命令を実行すると、変数「b」に変数「a」の内容、つまり「123」という数値が代入されます。
それでは、以下の命令を実行するとcとdは何が代入されるか考えてみてください。
uint8_t c;
uint8_t d;
c = 10 + 20;
d = c;
最初にuint8_t型の変数「c」と「d」を宣言しています。次に、cには10+20、つまり30が代入されます。そのあと、cの内容をdに代入しますので、dにも30が代入されます。答えは「cは30、dも30」です。
それでは、次の命令を実行すると、eとfには何が代入されるか考えてみてください。
uint8_t e;
uint8_t f;
e = 5 + 5;
f = e + 5;
eには5+5、つまり10が代入されます。次の「f = e + 5;」では、変数fには変数eの内容に5を加えたものを代入しますので、fには10+5で15が代入されます。答えは「eは10、fは15」です。
ちょっと慣れてきましたか? では最後に以下の命令を実行するとどうなるか考えてみましょう。
uint8_t g;
g = 3;
g = g + 5;
最初の「g = 3;」では、変数「g」に「3」が代入されます。これは問題ないですよね。
さて、次の「g = g + 5;」はどうなるのでしょうか。「=」記号は、右側の内容を左側に代入する命令ですので、最初に「g + 5」が計算されて、その結果が「g」に代入されます。つまり、8が「g」に代入されます。
ちょっとわかりづらいですよね。メモ用紙で考えてみましょう。「g = 3;」では「g」というタイトルのメモ用紙に「3」という数字が書かれます。
次の「g = g + 5;」は「『=』記号の、(1)右側の内容を、(2)左側に代入する」という処理をします。最初の「(1)右側の内容」という処理は、メモ帳gに書かれている数字は「3」ですので、「3 + 5」で「8」になります。
次にその結果を「(2)左側に代入」しますので、gには「8」が代入されることになります。
結局、「g = g + 5;」という処理は、現在のgの値に5を足す、という動作になります。このように、変数の値に数字を足したり引いたりする処理は多く出てきますのでこの書き方に慣れてください。
ところで、数学で出てくる「=」記号は「『=』記号の左側と右側が等しい」という意味でした。
C/C++言語ではその意味が変わって、「『=』記号の右側の内容を左側の変数に代入する」という意味になることに注意してください。C/C++言語の「=」記号は決して「左側と右側の内容が等しい」という意味ではありません。
ということで、「=」は正確には以下のような動作になります。
一定回数処理をするスケッチ
電子工作のプログラミングでは、決まった回数、何かの処理をすることがよくあります。
ジオラマ模型を作った場合は「LEDを〇〇回点滅する」、ロボットの場合は「〇〇歩あるく」など、決まった回数の処理をする、ということが多くあります。
このような処理は、今まで習得した知識を活用すると実現できるんです。
とはいっても、ゼロから考えてみましょう、というとかなり大変ですので、最初に一定回数処理をする典型的なスケッチを示して、そのスケッチでなぜ一定回数処理ができるのか、説明することにします。
以下のスケッチは「処理」を3回実行するスケッチです。uint8_t型の「count」という名前の変数と、while構文を使用して実現しています。
このスケッチは「処理」を3回繰り返して動作を停止します。初めて見ると何をしているのかよくわからないと思いますので、Arduinoボードがどのように処理を進めていくのか、ひとつひとつ順を追って確認しましょう。
最初に回数を数えるための変数を用意します。uint8_t型の変数名が「count」という変数です。変数名はなんでも構いませんが、数えるので「count」としました。変数countを宣言すると、Arduinoボードは適当な数字が書かれた変数を用意してくれます。
宣言したばかりの変数は適当な数字が書かれていますので、回数を数えるために1を代入します。
いよいよwhile構文の処理に入ります。whileの式の判定は「count <= 3」、つまり変数countの値が3以下、という条件になります。countの現在の値は1ですので、式が成立します。while構文の{と}で囲んだ処理が実行されます。
最初に「処理」が実行されます。この処理の実行は1回目になります。
次に変数countの値に1足します。結果として変数countの値は2になります。
{と}で囲んだ処理が終わりましたので、再度whileの式の判定に戻ります。現在の変数countの値は2、条件は「count <= 3」ですので、条件成立です。再度{と}の処理を実行します。
「処理」を実行します。この「処理」の実行は2回目です。
次に変数countに1を足します。変数countの値は3になります。
{と}で囲んだ処理が終わりましたので、再度whileの式の判定に戻ります。現在の変数countの値は3、条件は「count <= 3」ですので、条件成立です。再度{と}の処理を実行します。
「処理」を実行します。この「処理」の実行は3回目です。
次に変数countに1を足します。変数countの値は4になります。
{と}で囲んだ処理が終わりましたので、再度whileの式の判定に戻ります。現在の変数countの値は4、条件は「count <= 3」ですので、条件が成立しませんので、while構文は処理を終えて次の命令に進みます。
最後は何もしないをずっと繰り返すスケッチにしていますので、Arduinoボードはこの処理をずっと続けます。外から見ると何もしていないように見えます。
処理を一つ一つ追ってきましたが、処理が一通り終わると「処理」の部分は3回繰り返されていますよね。このように一定回数繰り返すには変数とwhile構文を使用すれば実現できるんです。
今回は、電子工作などでよく使われる「一定回数処理を繰り返す」というプログラミングをしましたが、今後自分で考えた動作をさせたい場合、このようにC/C++言語の命令を組み合わせて実現することになります。どのように組み合わせれば実現できるか、考える力を身につけていくようにしてください。
ところで、一定回数繰り返すスケッチのサンプルはできましたが、この書き方は一般的ではないんです。次に問題点と一般的な書き方を習得します。
プログラミングパターンとベストプラクティス
先ほどのスケッチでは一定回数処理をすることができましたが、ちょっとだけ問題があるんです。そのちょっとした問題を説明します。
変数を使って回数を数えましたが、uint8_t型の変数に代入できる数字の範囲は0〜255です。先ほどのスケッチでは最初に「count = 1;」として、数を1から数えていましたよね。
ちょっとした問題、というのは、uint8_t型の変数は0〜255を代入できるのに0を使っていない、ということなんです。0〜255まで使えば最大256回の回数を数えることができますが、先ほどのスケッチでは0を使っていないので、最大1〜255の255回になってしまいます。
日常生活で数を数える場合、1から始めますよね。プログラミングの世界では0から数えるのが一般的ですので是非0から数えることに慣れましょう。
ということで、一定回数を数えるスケッチの一般的な書き方は以下になります。
このスケッチでは、最初のcountの値は0にして、処理をするたびに1を足していきます。0から開始をしたので、whileの条件判定式は「3未満」としています。
このスケッチの一つ一つの動作は説明しませんが、ぜひ先ほどと同じようにきちんと「処理」が3回実行されるか、ご自分で確認してみてください。
ところで注意深い方は気づいたかもしれませんが、whileの条件式として「count <= 2」という書き方もありますよね。このように書いても3回実行されますが、この書き方は一般的ではありません。「count <= 2」と書くと、条件式は「2」という数字が使用されていまいます。「3」回処理をするのに「2」という数字が出てきて紛らわしいので、「count = 3」というように「<」記号と「3」という数字を使用して、「3」回ということを表現する慣習になっています。
このように「一定回数数える」などの典型的な処理をするスケッチ(プログラム)には決まった形があります。柔道などのスポーツなどでも形がありますよね。このように決まった形のプログラムのことを「プログラミングパターン」と呼んでいます。また、プログラミングパターンの中でも一番良い形を「ベストプラクティス」と呼ばれています。「ベスト」は日本語で「一番」、「プラクティス」は「慣習」という意味です。
今回の記事では、一定回数処理を行う方法としてwhile構文で書く方法(プログラミングパターン)を説明しましたが、実はこの形はベストプラクティスではありません。次回、一定回数処理を行うベストプラクティスを習得します。
代入演算子とインクリメント演算子
先ほど説明したように、プログラミングでは変数に何かを足す、または変数から何かを引く、という処理が多く出てきます。一定回数処理をする場合、countという変数を用意してcountに1を足して回数を数えました。
変数countに1を足す場合、以下のように書くことは説明しました。
count = count + 1;
この処理は「count」という変数を2回書く必要があるので、入力に手間がかかりますよね。そこで、C/C++言語ではこのように変数に何か足したり引いたりする場合のために「代入演算子」というものが用意されています。
count = count + 1;
と書く代わりにcount += 1;
と書けるわけです。入力文字数も減りますし、スケッチもちょっとだけ短くなりますので、変数に何か数字を足したりする場合はこれらの記号を使いましょう。
また、ネットでサンプルスケッチを見ると、このような書き方が多用されていますので、ぜひこの書き方に慣れてください。
ところで、先ほどの数を数えるケースでは変数に1を足しましたよね。このように1を足すという場面はとても多いので、「1を足す演算子」が特別に用意されています。「インクリメント演算子」と呼ばれています。「インクリメント」は日本語で「増やす」という意味です。
インクリメント演算子には1を引く「--」という記号があります。インクリメントは「増やす」ですが、逆に「減らす」は「デクリメント」という英語があるのですが、「++」と「--」を合わせてインクリメント演算子と呼んでいるようです。
ところで、このシリーズで習得しているプログラミング言語は「C/C++言語」なのですが、「C++言語」の意味はなんとなく想像つくのではないでしょうか。「C++言語」は「C言語」を元に色々な拡張をした言語です。「C言語」を一歩進めた、ということで「C++」というようにインクリメント演算子が使われています。
一定回数LEDを点滅するスケッチ
これで一定回数LEDを点滅するスケッチを作る知識がそろいました。以下はスイッチを押すとLEDが3回点滅するスケッチです。それでは、今まで習得した知識をフル活用して、このスケッチが3回点滅する処理になっているか、処理を追ってみてください。
LEDは1秒に1回つけていますので、LEDの点滅回数を数えると時間が計測できるようになります。キッチンタイマーに近づいてきましたね。
/*
* キッチンタイマー
*
* 内容: スイッチ、LED、スピーカーを使ったキッチンタイマー
* 変更履歴:
* 2019. 8.11: 新規作成
* 2019. 8.15: スタートスイッチ処理を追加
* 2019. 8.17: スイッチ関連の#define追加
* 2019. 9. 8: 点滅回数カウント追加
*/
// 秒を表現するLED関連(青色LED)
#define BYOU_LED 12 // 秒を表現する青色LEDの端子番号
#define BYOU_ON 50 // 秒を表現するLEDをつけている時間 (単位:ミリ秒)
#define BYOU_OFF 1000 - BYOU_ON // 秒を表現するLEDを消している時間 (単位:ミリ秒)
// スタートスイッチ関連
#define SWITCH 23 // スイッチを接続している端子番号
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN 3
void setup() {
// 端子の設定
pinMode(BYOU_LED, OUTPUT); // 青色LED接続端子設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子の設定
// スイッチが押されるまで待つ
while(digitalRead(SWITCH) == SWITCH_OFF) {
}
}
void loop() {
// LEDの点滅回数を数えるための変数宣言
uint8_t count;
// countに0を代入
count = 0;
// TIMER_JIKAN分の回数を数える
while( count < TIMER_JIKAN ) {
// 1秒に1回青色LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
// countを1プラス
count++;
}
// 何もしないで待つ
while( true ) {
}
}
「count++」と「++count」
この知識は頭の片隅に置いておいていただければと思います。
というのは、他の人が書いたスケッチを見ていると、たまにインクリメント演算子が変数の「後」ではなく「前」に書かれていることがあります。
uint8_t count;
count++; // 後に書くケース
++count; // 前に書くケース
今回の記事で、while文の処理の最後に count++
と書きました。このように単独で書く場合は「count++」「++count」どちらも結果は同じになります。
ところが、状況によってはこれらの意味が異なるケースが出てきます。それは、変数に代入するケースです。
具体例として以下の2つの式を考えてみます。
uint8_t count; // インクリメントする変数
uint8_t mae, ato; // 結果を代入する変数
// count++のケース
count = 1;
ato = count++;
// ++countのケース
count = 1;
mae = ++count;
ちょっとわかりづらいですが、最初にこれらの違いをまとめます。
書き方 | 意味 | |
---|---|---|
後に書く(count++) | 代入した後にcountをプラス1する | |
前に書く(++count) | 代入する前にcountをプラス1する |
先ほどの例で、6行目の「ato = count++;」のケースを見てみます。
5行目でcountの値は1になっています。6行目で「count++」を「ato」に代入しますが、この時「++」は変数の後に書かれていますので、インクリメント処理(プラス1の処理)は代入処理の「後」に行なわれます。
つまり、6行目では、最初にcountの値をatoに代入してからcountをプラス1します。6行目の処理が終わると、atoは1、countは2になります。
一方、10行目では最初にcountをプラス1してから変数maeに代入します。10行目の処理が終わると、maeは2、countは2になります。
両方とも、変数「count」はインクリメント演算子によりプラス1されますので、結果は2になります。一方、変数「ato」と「mae」はcountがプラス1処理されるタイミングが異なるので結果が異なります。
かなりややこしいですよね。
私は勘違いしやすいタイプなので、自分でスケッチを作成する場合は、なるべく勘違いしないように書いています。
例えばcountを代入の後でインクリメントしたい場合は以下のように書いています。
uint8_t count = 1;
uint8_t ato;
// 後にインクリメント
ato = count;
count++;
先にインクリメントしたい場合は以下のように書いています。
uint8_t count = 1;
uint8_t mae;
// 前にインクリメント
count++;
mae = count;
このように書くと、変数代入の前にインクリメントするのか、後にするのか明確になると思います。
ただ、人によっては「count++」と「++count」を使い分けてスケッチを作成しているケースもあります。その場合はスケッチが読めるように、頭の片隅に置いておいていただければと思います。
ミニチャレンジ課題
変数とwhile文を使うと、いろいろな計算ができるようになります。
以下のスケッチは実行するとどのような結果になるのか考えてみてください。考えがまとまったら、新規スケッチを作成、以下のスケッチをコピペして動作させてみてください。
※なお、以下のスケッチのシリアルモニタ設定の部分に、今まで説明していない命令が書かれています。この意味は第47回の補足記事で説明しています。課題の本質ではありませんので無視していただいて問題ありません。気になるようでしたら第47回の補足記事をお読みいただければと思います(現時点の知識で理解できます)。
/*
* Arduinoプログラミング入門 第25回
* ミニチャレンジ課題1
*/
void setup() {
// シリアルモニタ設定
// ここのwhileの意味はまだ説明していないので
// 無視していただくか、気になるようでしたら
// 第47回の記事をお読みください。
Serial.begin(9600);
while(!Serial){
}
// 変数の宣言
uint16_t count;
// 変数に0を代入
count = 0;
// while構文で計算
while( count < 5 ){
count++;
}
// 結果をシリアルモニタに表示
Serial.print("結果: ");
Serial.println(count);
}
void loop() {
}
今度は、while構文中の変数countの値を表示してみたいと思います。
以下のスケッチを実行するとどのような結果が表示されるか考えてみてください。
/*
* Arduinoプログラミング入門 第25回
* ミニチャレンジ課題2
*/
void setup() {
// シリアルモニタ設定
Serial.begin(9600);
while(!Serial){
}
// 変数の宣言
uint16_t count; // カウント用の変数
// 変数に0を代入
count = 0;
// while構文で計算
while( count < 5 ){
Serial.println(count);
count++;
}
// while構文後のcountの値を表示
Serial.println("---");
Serial.println(count);
}
void loop() {
}
先ほどのスケッチで、while構文の中の「Serial.println(count);」と「count++」位置を逆にしました。
以下のスケッチを実行するとどのような結果が表示されるか考えてみてください。
/*
* Arduinoプログラミング入門 第25回
* ミニチャレンジ課題3
*/
void setup() {
// シリアルモニタ設定
Serial.begin(9600);
while(!Serial){
}
// 変数の宣言
uint16_t count; // カウント用の変数
// 変数に0を代入
count = 0;
// while構文で計算
while( count < 5 ){
count++;
Serial.println(count);
}
// while構文後のcountの値を表示
Serial.println("---");
Serial.println(count);
}
void loop() {
}
次はもう少し複雑になります。変数が2個出てきますのでじっくり考えてみてください。
ポイントは、while構文が何回繰り返されるのか明確にして、その間に変数「goukei」(合計)がどのようになるのか考えてみるようにしてみてください。また、考えるときも頭の中だけで考えると混乱してきますので、ノートに変数の値がどのように変化していくか考えるようにしてみましょう。
/*
* Arduinoプログラミング入門 第25回
* ミニチャレンジ課題4
*/
void setup() {
// シリアルモニタ設定
Serial.begin(9600);
while(!Serial){
}
// 変数の宣言
uint16_t count; // カウント用の変数
uint16_t goukei; // 計算用の変数
// 変数に0を代入
count = 0;
goukei = 0;
// while構文で計算
while( count < 5 ){
count++;
goukei += count;
}
// 結果をシリアルモニタに表示
Serial.print("変数goukeiの結果: ");
Serial.println(goukei);
}
void loop() {
}
1から10までの数字の合計をシリアルモニタに表示するスケッチを作成してみてください。
1+2+3+ … +10を計算してシリアルモニタに表示するスケッチです。
更新履歴
日付 | 内容 |
---|---|
2019.9.8 | 新規投稿 |
2021.8.24 | 新サイトデザイン対応 |
2022.2.13 | ミニチャレンジ課題追加 |
2022.2.15 | インクリメント演算子説明追加 |