Photo by Darryl Brian on Unsplash
前段:タイマーはプログラム作成初心者にぴったりの題材
「M5Stackを購入したはいいけど、何をしたらいいかわからない」
そんなあなた。タイマーをつくってみてはどうか。タイマーは実生活に使えるため、つくっていてやる気が出てくる。完成したら楽しい。「おぉ!」となる。
幸いなことにM5Stackはすでにディスプレイとボタンがついているため、プログラムさえ組めばすぐに動く。やってみよう。
1. 環境構築
環境構築は以前説明した記事の通りである。
2. 配線
配線はまったく不要。
3. サンプルプログラムによる動作確認
3-1.時計のサンプルプログラムを流す
はじめにArduino IDEの画面から
ファイル> スケッチ例 > M5Stack > Advance > Display > TFT_CLock_Digital
を選択。
コードが表示されたらM5StackをPCに接続して書き込みを行う。
動く様子
3-2.コードの解説
M5StackはArduinoのプログラムで動いているが、そのプログラムは三つのまとまりで表現されている。
setup()とloop()とその他である。
- setup()関数 : プログラムが動いた時に一度だけ呼ばれる
- loop()関数: 繰り返し実行される
- その他:他の関数だったり宣言文だったりする
今回はsetup()の前で必要な変数の宣言がされている。そしてsetup()で文字の色や大きさを指定している。loop()で時刻を表示させている。
サンプルプログラムはコンパイルされた時刻をもとに時計を表示させる。そのため、一旦電源をOFFにしてから再度ONにすると時刻がずれる。また、正確に時刻の処理をしていないので長時間放置していると実際の時間とずれが生じる。
文字の色の変更
1 2 |
M5.Lcd.setTextColor(0x39C4, TFT_BLACK); |
文字の色を変更する。上の場合、0x39CAが文字色、TFT_BLACKが背景色。
文字の大きさの変更
1 2 |
M5.Lcd.setTextSize(1); |
デフォルトの文字の大きさは1だが、小さすぎる。2とか3にするといい。
文字の描画
1 2 |
M5.Lcd.drawChar(':', xpos, ypos - 8, 8); |
指定した座標に文字を書ける。文字の大きさも指定できる。この場合は(xpox,ypos-8)という場所に文字サイズ8で 「 : 」 を書いている。
4. サンプルプログラムを改造してタイマーにする
4-1. 分と秒だけ表示させる
サンプルプログラムを書き換えていき、タイマーをつくってみよう。まずは分と秒だけ表示されるようにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
if (omm != mm) { // Redraw hours and minutes time every minute omm = mm; // Draw hours and minutes // if (hh < 10) xpos += M5.Lcd.drawChar('0', xpos, ypos, 8); // Add hours leading zero for 24 hr clock // xpos += M5.Lcd.drawNumber(hh, xpos, ypos, 8); // Draw hours // xcolon = xpos; // Save colon coord for later to flash on/off later // xpos += M5.Lcd.drawChar(':', xpos, ypos - 8, 8); if (mm < 10) xpos += M5.Lcd.drawChar('0', xpos, ypos, 8); // Add minutes leading zero xpos += M5.Lcd.drawNumber(mm, xpos, ypos, 8); // Draw minutes xsecs = xpos; // Sae seconds 'x' position for later display updates } if (oss != ss) { // Redraw seconds time every second oss = ss; xpos = xsecs; if (ss % 2) { // Flash the colons on/off M5.Lcd.setTextColor(0x39C4, TFT_BLACK); // Set colour to grey to dim colon // M5.Lcd.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon xpos += M5.Lcd.drawChar(':', xsecs, ysecs, 6); // Seconds colon M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK); // Set colour back to yellow } else { // M5.Lcd.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon xpos += M5.Lcd.drawChar(':', xsecs, ysecs, 6); // Seconds colon } |
プログラムの文章のはじめに「//」を挿入すると、その行はコメントとして認識される。その行にかいた処理は無視される。今回は時間が表示されるプログムがあるところを、上記の要領でコメントアウトさせる。
秒数のところが小さく表示されてしまうので修正する。M5.Lcd.drawCharのところの「6」を「8」に変更する。
あと、
1 2 |
int ysecs = ypos + 24; |
を、
1 2 |
int ysecs = ypos; |
にする。そうすると、ちょうどよくなる。
4-2. 3分から1秒ずつ減るようにする
タイマーなのだからカウントアップではなくカウントダウンさせたい。3分から1秒ずつ減るようにして、最後は0で停止するようなプログラムをつくってみる。
はじめに
1 2 |
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time |
を以下ように書き換える。
1 2 3 |
uint8_t hh = conv2d(__TIME__); uint8_t mm = 3, ss = 0; |
uint8_tというのは変数の型である。conv2d() というのはコンパイルされた時刻を取得するための関数であり、サンプルプログラムの一番下に書かれている。今回は無視していい。mm,ssがそれぞれ分と秒の変数である。
次にカウントアップさせているところを修正する。修正前は以下のようになっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Adjust the time values by adding 1 second ss++ ; // Advance second if (ss == 60) { // Check for roll-over ss = 0; // Reset seconds to zero omm = mm; // Save last minute time for display update mm++; // Advance minute if (mm > 59) { // Check for roll-over mm = 0; hh++; // Advance hour if (hh > 23) { // Check for 24hr roll-over (could roll-over on 13) hh = 0; // 0 for 24 hour clock, set to 1 for 12 hour clock } } |
一行ずつコメントが書かれているので解説はいらないだろう。「変数++」というのはインクリメント演算子で、++
が変数の前にあるか後ろにあるかで意味合いが変わってくる。
変数++ :xを返し、1を加える。
++変数 :1を加えたxを返す
1ずつ減らすにはx--
とすればいい。
しかしそこだけを書き換えてもうまくいかない。数字が0からいきなり255となってしまう。それは変数の型のせいである。uint8_t型は1バイトの符号なし整数である。0~255の整数をとる。この型は0から1をマイナスすると-1とはならず255となる。
そこで以下のように書き換えてみよう。
1 2 3 4 5 6 7 8 9 10 |
// Adjust the time values by adding 1 second ss = ss -1 ; // Advance second if (ss == 255) { // Check for roll-over ss = 59; // Reset seconds to zero mm = mm -1; // Advance minute if (mm == 255) { // Check for roll-over mm = 0; } |
これで3分からカウントダウンできるようになった。
ついでに0秒になったらカウントがストップするプログラムにしてみよう。新しく変数を宣言する。uint_8
の直後あたりに以下の変数を加える。
1 2 |
boolean isMeasuring = true; |
boolean というのは変数の型であり、trueかfalseのどちらかの値を持つことができる。今回は「測定中かどうか」という意味合いの変数をつくった。カウントが0になったらisMeasuringをfalse
とする。
loop()のはじめを以下のように書き換える。
変更前
1 2 3 4 5 6 7 8 9 |
void loop() { if (targetTime < millis()){ ~~~ // Adjust the time values by adding 1 second ss = ss -1 ; ~~~ } |
変更後
1 2 3 4 5 6 7 8 9 10 11 12 |
void loop() { if (targetTime < millis() && isMeasuring ){ ~~~ // Adjust the time values by adding 1 second if( ss == 0 && mm == 0){ isMeasuring = false; //stop }else{ ss = ss -1 ; ~~~ } } |
これで0のときはカウントが止まるようになった。
4-3. ボタンを押してスタート/ストップを切り替える
このままでは味気ないのでAボタン(M5Stackの三つのボタンのうち、左側のボタン)を押したらタイマーの状態が切り替わるようにしてみよう。loop()のはじめを以下のように書き換える。
1 2 3 4 5 6 7 8 9 10 11 |
void loop() { //------書き換え部分 M5.update(); if (M5.BtnA.wasReleased()) { isMeasuring =! isMeasuring; delay(5); } //------ if (targetTime < millis() && isMeasuring ) { ~~~ |
M5.BtnA.wasReleased()
はボタンが離されたかどうかを判別する条件である。ここの条件を「押されたかどうか」に変更してしまうと不具合が起きる。ボタンを押し続けている間も条件を満たしてしまうからである。
ボタンの状態を判別する際は直前に必ずM5.update()
を加えよう。これをしないとボタンが押されても反応しない。
5. おわりに
これでタイマーが完成した! 次回はいよいよIoT的なことをする。タイマーが0になったらLINEに通知が出るようにするぞ!