小休止を取りまして、マニュアルモードだけではなくて、プログラムモードを追加してほしいという声がありましたので、第2部を本日よりスタート致します。
アナマル.1から10までで理解できていない部分があれば、復習をおすすめします。
全体の構造
プログラムモードを追加するということは、プログラムが出来るモードも追加するということになるのでイメージとしてはこんな感じです。
構造を事前に考えて各所を作っていくやり方が望ましいので、流れとしては、前回までのスケッチを改造しますので、
マニュアルモードとプログラムモードを切り替える部分を作る。
↓
プログラムモードを作る。
↓
モードを切り替えるのをスイッチで行う。
↓
記憶させる機能を作る。
↓
プログラムセットモードを作る。
ここまでやって、LEDだけの表示では足りないようならば先日ご紹介した1602というディスプレイを追加します。
モードを移行する方法を検討する。
モードとは言え、いろんなやり方があるのですが、例えば、
○基本的にはメインループしながら、そのモードならではの命令が来たときに一瞬そのモードに飛んで、すぐメインループに戻る。
○一旦メインループからそのモードのループに飛んだら、戻れと命令するまでメインループに戻らずにモードの中でループする。
この2つがわかりやすいと思います。
今回の場合、マニュアルモードとプログラムモードは、例えばフットスイッチの入力処理などは共通化出来ます。 それに対して、プログラムセットモードでは、前回決めたように、
Bボタン→平時は長押しでセットモードに。
セットモード
Aボタン→セットするchを選択
Bボタン→短押しでセット。長押しで通常モードに。
このように、ボタン(スイッチ)の使い方が共通ではないのもありますので、プログラムセットモードの中でループする方法を採りたいと思います。
マニュアルモードとプログラムモードを切り替える部分を作る。
//SW4 const int SW[4] = {A0, A1, A2, A3};//chのスイッチ const int SW4 = A4;//man/prg切り替え const int SW5 = A5; const int LED[4] = {7, 6, 5, 4}; int state[4] = {0}; //ONOFFの状態を保存 int val[4] = {0}; //スイッチの状態を保存 int old_val[4] = {0}; //前回の状態を保存 int a; int count = 0; // 1周ループしたら+1 int mode = 1; // 0でmanual,1でPRG void setup() { Serial.begin(9600); pinMode(LED[0], OUTPUT); pinMode(LED[1], OUTPUT); pinMode(LED[2], OUTPUT); pinMode(LED[3], OUTPUT); pinMode(SW[0], INPUT_PULLUP); pinMode(SW[1], INPUT_PULLUP); pinMode(SW[2], INPUT_PULLUP); pinMode(SW[3], INPUT_PULLUP); } void loop() { a = count % 4; sw(a); count = count + 1; } void sw(int num) { val[num] = digitalRead(SW[num]); //スイッチの状態をval[num]に入れる if ((val[num] == 1) && (old_val[num] == 0)) { delay(10);// 10ms待つ。 if (mode == 1) { prg_process(num); } state[num] = 1 - state[num]; // state[num]をひっくり返す。 } if (mode == 0) { if (state[num] == 1) { digitalWrite(LED[num], HIGH); // LEDへの信号をHIGH(点灯) } else { digitalWrite(LED[num], LOW); // LEDへの信号をLOW(消灯) } } old_val[num] = val[num]; // 処理が済んだので前回の状態にval[num]をコピーする。 } void prg_process(int num)// PGMモード時動作 { }
ここで新たに、プリプロセッサとして、
int mode = 1; // 0でmanual,1でPRG
という変数を作りました。
この変数を後々スイッチで切り替えることで、マニュアルモードと、プログラムモードの切り替えに利用できます。
次に、
if (mode == 1) { prg_process(num); }
をどこかに挟み、それとは別に
void prg_process(int num)// PGMモード時動作 { 中身は後ほど作成 }
という関数を作ることで、モード変更の仕組みが完成です。
ではどこに挟むか、ということですが、前回のスケッチの命令の順番を少し変えて、
「スイッチから手を離した瞬間に判定」することにします。
if ((val[num] == 1) && (old_val[num] == 0)) { delay(10);// 10ms待つ。 if (mode == 1) { prg_process(num); } state[num] = 1 - state[num]; // state[num]をひっくり返す。 }
state
の情報は必要ないので、この位置が良さそうです。
現在mode = 0;
にしていますので、この状態で前回と同じようにマニュアルモードで動作するはずです。
ここで、数分間チェックをしていたら有ることに気づきました。
数分後に正常に動作しなくなります。
先にこの部分をデバッグしましょう。
問題はcountにあった。
時間が立つと条件が変わると変数はこのスケッチではcount
ですので、これをシリアルモニタで見ていきます。
void loop() { a = count % 4; sw(a); count = count + 1; Serial.println(count, DEC); }
そうすると、オーバーフロー(数値が満タンになりあふれること)するときにこうなりました。
考えてみれば当然です。intは0を中心としてマイナス領域もカバーするからです。
ちなみにこのときのa
の数値はこうなります。
対策としては、count
のintを、符号なしunsigned intに変更します。
この後数十分チェックしましたが問題は解決したようです。
modeについて
今回作成したスケッチでは、mode == 1
の時と、mode == 0
の時をそれぞれ記述しています。
本来でしたら片方は省略しても構いませんが、デバッグの際にわかりやすいので、このようにどちらの場合も記述したほうが良いかと思います。