当社製プログラミング対応製品で採用されているArduinoを使ったプログラミングの基礎について説明します。
電気ポットを思い出してみてください。
水を入れて【沸騰】ボタンを押すと、赤いLEDが点灯すると同時に湧き上がりまでの残り時間が液晶パネルに表示されて、ヒーターのスイッチがONになります。
水温を監視して、98度を一定時間キープしたら湧き上がりと見做してヒーターの電源を切り、沸騰を示す赤いLEDをオフにして、替わりに保温を示す黄色のLEDを点灯させます。
再び水温を監視して、95度程度まで下がったら、ヒーターの電源を入れて98度に達するのを待ちます。
ほとんどの製品では、このような動きをするでしょう。
沸騰ボタンは、スイッチですので、押せば接点がつながってONになることは、誰でもわかるでしょう。
でも、指を離したら接点が開くためOFFになってしまい、お湯を沸かすことはできません。
スイッチを押しっぱなしにしていないとお湯が沸かないポットを見たことがありますか?
湧き上がりはどのように判断しますか?
沸騰・保温のLEDは、どうやって切り替えますか?
お湯が覚めたことをどのように判断しますか?
単純な機械に見えても、仕組みを簡単に説明できないほど謎に包まれています。
この仕組みを理解するところから、紐解いていきたいと思います。
「マイコン」という言葉を誰もが一度は耳にしたことがあると思います。
コンピュータや科学とは全く無縁の方でも、「何の略?」と聞けば、「マイクロ・コンピュータ」と答えるほど、社会に定着しています。
マイコン・・・ここでは「マイクロ・コントローラ」と理解してください。
しばしばMCU(Micro Controller Unit)と呼ばれます。
コントローラと呼ばれるくらいですので、その本分は「制御」です。
つまり、「制御」するための電子部品となります。
先ほど触れた電気ポットの例では、【沸騰】ボタンの接点状態の検出やヒーター、LEDの入り切りなどを制御します。

電気ポットの中は、スイッチやヒーターなどがマイコンを中心に、このように接続されています。
スイッチの検出やヒーターのON/OFF制御だけでなく、液晶表示器に表示データを送るなど、比較的高度な処理を行うことも可能です。
マイコンがとても便利な電子部品であることが理解できたと思います。
でも、ただつないだだけでは動きません。
マイコンは、電気ポットでお湯を沸かすためだけにあるのではなく、様々な用途で使われることを想定しているからです。
そこで、プログラミングが必要となります。
電気ポットの例では、
・沸騰ボタンが押されたら、ヒーターと沸騰LEDをONにし、
沸騰までの残り時間として15.00(分)と表示するように液晶表示器にデータとコマンドを送信する。
・水温センサーを監視し、95度程度まで冷めたことが検出できたら、沸騰ボタンが押されたときと同じ処理をする。
・水温センサーを監視し、98度に達したら、沸かしはじめの水温が85度以上であった場合は、直ちにヒーターと沸騰を示す赤のLEDをOFFにして、保温を示す黄色のLEDをONにし、液晶表示器に表示させる内容を現在の湯温に切り替える。
沸かしはじめの水温が84度以下であった場合は、沸騰した状態を30秒間キープした後、ヒーターと沸騰を示す赤のLEDをOFFにして、保温を示す黄色のLEDをONにし、液晶表示器に表示させる内容を現在の湯温に切り替える。
・沸騰中は、現在の水温を元に、湧き上がり時間を加減表示させる。
こうした動きをマイコンにさせる必要があります。

これは、一部の当社製品で採用されているESP-WROOM32というMCUです。
Arduinoソフトウェアを使用した書き込みにも対応しています。
このMCUを使って、電気ポットを再現していきます。

LEDやヒーター、スイッチなどを上の図のようにマイコンにつなぎます。
オレンジ色の線が向かう先に、数字や記号のようなものが書いてあります。
保温を示すLEDがIO25(アイ・オー25)に、
沸騰を示すLEDがIO33に、
沸騰ボタンがIO32に、
保温/取消ボタンがIO35に、
温度センサーがIO34に接続されていることがわかります。
IO(アイ・オー)の後に続く数字が、プログラム側からマイコンの端子にアクセスする時に使用するナンバーです。
これを、プログラムで操作することで、接続した装置を操ったり、スイッチの入力を検出したりします。
マイコンにつないだヒーターやLEDをONにするには、マイコンの端子から電気を送らなければなりません。
ここでは、その方法を説明します。
マイコンの端子から電気を出力させるときは、以下の命令を実行します。
| digitalWrite(ピン番号, HIGH) |
電気を切るときは、以下の命令を実行します。
| digitalWrite(ピン番号, LOW) |
【ピン番号】の部分に、IOに続く数字を入れます。
例えばヒーターをONにしてお湯を沸かしたい場合は、以下のように書きます。
| digitalWrite(26, HIGH) |
接続図を見ると、ヒーターはIO26につながっていることが確認できます。
そのため、26をピン番号に指定します。
ここまでで、マイコンから電気を送る方法が理解できたので、ヒーターやLEDに通電できるようになりました。
しかし、【沸騰】ボタンが押されたことをプログラムが認識できなければ、ヒーターやLEDに通電を行うきっかけがありません。
【沸騰】ボタンが押されたことを検出することでヒーターに通電を行い、間違って押したのであれば【保温/取消】ボタンを押すことでキャンセルできなければなりません。
ここでは、その方法を説明します。
【沸騰】【保温/取消】いずれのボタンも、マイコンに接続されています。
そして、ボタンを押すことでスイッチがつながり、マイコンに電気が送られる仕組みになっています。
マイコンの端子に電気が来ているかどうかをプログラムから確認するには、以下の命令を実行します。
| digitalRead(ピン番号) |
沸騰ボタンがIO32に、保温/取消ボタンがIO35につながっているので、以下のようにプログラムします。
| digitalRead(32) digitalRead(35) |
これで、各々のスイッチが今、どの状態であるかが確認できるようになりました。
しかし、あくまで確認しただけです。
人に例えるなら、「洗面所の電灯が灯いてるかどうか見てきて」と頼まれて、一応見てきたものの、本当に見てきただけなので居間に戻った時に憶えていないのです。
これでは役に立ちません。
しっかりと状態を憶えておくためには、以下のように書かなくてはなりません。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) |
こうすることで、【沸騰】ボタンが押されることで32番ピンに電気が流れると、RevoilKEY_STATUSが1になり、指を離すなどして接点が離れて電気が途絶えると0になります。
(【保温/取消】ボタンが押されることで35番ピンに電気が流れると、CancelKEY_STATUSが1になり、指を離すなどして接点が離れて電気が途絶えると0になります。)
「RevoilKEY_STATUS」や「CancelKEY_STATUS」は、プログラムを実行した結果を保存するために使うメモリの区画の名称で、この通りでなくても自由に名前を付けることができます。
そのため、
| NaganomeiLOVE = digitalRead(32) Come_in_join_us = digitalRead(35) |
これでも正しく動作します。(digitalReadの部分は変えられません。)
この「メモリの区画」のことを正しくは「変数(へんすう)」と呼びます。
変数は、プログラムが大きくなるほどその数は多く使われることになるので、あまりにわかりにくい名称であったり、打ちにくいもの、他の変数と紛らわしいものを使用すると、プログラミング作業の妨げになります。
また、計算値を保存する変数を取り違えることで、不具合(バグ)の原因になることもあります。
自分の中で命名ルールを作って単純明快なものにすることがポイントです。
【沸騰】ボタンを押してお湯を沸かすとき、お湯が沸いたらヒーターを止めないと、やがてポットの中のお湯は沸騰して全てなくなり、過熱事故を起こしてしまいます。
それを防ぐためには、お湯の温度をマイコンが認識しなければなりません。
右の図は、LM35DZという温度センサーの仕様書(データシート)に書かれているグラフです。
横軸が温度、縦軸が電圧という関係で、温度に比例して出力電圧が上がっていくことがわかります。
冒頭の図では、IO34に温度センサーがつながっています。
マイコンに接続されたスイッチの状態を検出する処理では、電気がきているときは1が、来ていないときは0を変数に入れる処理を説明しました。

ここでは、0か1かではなく連続的に変化する電圧を取得する方法について説明することで、プログラムから温度を取得できるようにします。
「連続的に変化する電圧」のことを「アナログ」といいます。
アナログ値を取得するには、以下のようにプログラミングします。
| analogRead(ピン番号) |
スイッチ状態の検出と同様、アナログ値を取得しただけで憶えていなかったら意味がないので、読み取ったアナログ値を変数に代入します。
| Temp = analogRead(ピン番号) |
この処理を実行すると、変数【Temp】には、マイコン電圧を基準に4096文率の値がTempに代入されます。
例えば、マイコンに3ボルトの電源をつなげた状態で1.5Vの電圧が掛かっている電線をつなぎ、この処理を実行すると、
入力に加えた電圧1.5V / マイコンの電圧 = 0.5 なので、
4096 × 0.5 = 2048が、Tempに代入されます。
3V / 4096 = 0.000732421875 なので、非常に細かい区切りで電圧を取得することができます。
そのため、1.5Vの電源をつないでこの処理を実行しても、わずかな誤差が大きく扱われることで2048になることは非常に珍しく、これに加えて気温等を含めた周辺のコンディションなどの影響を受けて±50〜75の触れ幅で常に値が変動します。
ここまでの説明で、
・マイコンの端子(ピン)から電気のON/OFFをする
・スイッチが押されたことの検出
・温度に比例して電圧値が上がるタイプ(アナログ)の温度計の温度読み取り
・・・ができるようになりました。
しかし、これだけでは電気ポットとしては機能しません。
これらを1つの流れに組み立てることで電気ポットとして使えるようにします。
まず、【沸騰】ボタンが押されたら、ヒーターをONにします。
32番ピンに接続された【沸騰】ボタンが押されたことを検出するには、以下のようにプログラムします。
| digitalRead(32) |
【沸騰】ボタンが押されたことをプログラムで憶えていなければならないので、次のように変数に記憶します。
| RevoilKEY_STATUS = digitalRead(32) |
「ボタンが押されたことを検出したら、ヒーターの電源を入れる」などといった、「もし○○ならば、△△を行う。(そうでなければ××を行う)」といったプログラム処理を、条件分岐といいます。
以下のようにプログラムします。
| if(成立条件){ 条件が成立している時の処理 }else{ 条件が成立していない時の処理 } |
成立条件は、以下のように書きます。

電気ポットの【沸騰】ボタンが押されたら・・・の処理を作るため、この書き方に倣って以下のようにプログラムします。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ }else{ } |
【沸騰】キーが押された時の処理は、ヒーターの電源を入れることと、沸騰を示す赤いLEDに通電することなので、その処理をifの中に追加します。
赤いLEDは33番ピン、ヒーターは26番ピンにつながっていますので、以下のように書きます。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) }else{ } |
これで、【沸騰】ボタンが押されたら、ヒーターと沸騰中を示す赤いLEDへの通電が行われるようになりました。
次に、お湯が沸いた時にヒーターを切る処理を追加します。
湯温は、温度センサーで検知します。
ここで、改めて温度センサーの性質について振り返ります。
センサーとマイコン、両方とも電源電圧5ボルトで駆動させた時、温度と温度センサーの出力電圧の関係は右のグラフの通りとなります。

100度に達した時の温度センサーの出力電圧は、3.8ボルトを若干上回った値となります。
検出電圧は、やや低めに設定することをおすすめします。
例えば、センサーに値が低めに出る誤差があったり、高所で使用するときなど、沸点が低くなっている場合、いつまで経っても湧き上がりません。
判定温度を97度か98度程度にしておき、沸騰してから若干長めにヒーターへの通電を継続させることで100度にするのが良い設計と言えます。
このMCUは、電源電圧を基準に、4096分割した値で電圧を検出します。
このため、3.8ボルトを検出するためには、以下の計算値に読み替えます。

では、ここから実装していきます。
まず、温度センサーがつながっているマイコン端子からアナログ値の読み込みを行います。

温度センサーは、IO34につながっているので、
| analogRead(34) |
となるでしょうか。
違います。
このマイコンに限らず、同じ端子を使っていても、多くのマイコンは、デジタルとアナログでピン番号が違います。
上記の図を見ると、温度センサーがつながっているIO34の左に「A6」が併記されています。
これがアナログのピン(端子)番号です。
そのため、正しくは以下の通りとなります。
| anarogRead(6) |
anarogReadだけでは、読み込んだだけになりますので、プログラムの中で比較処理で使えるように記憶しておかなければなりません。
Tempreture(温度)という変数(自分自身で命名した記憶領域を示すシンボルの名称)に保存するようにプログラムすると、以下の通りとなります。
| Tempreture = anarogRead(6) |
これを、ここまでで作成したプログラムに追記すると以下の通りとなります。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) }else{ } Tempreture=analogRead(6) // 湯温を測ります。 |
沸騰した時にヒーターと【沸騰】を示すLEDを消灯させ、【保温】LEDを点灯させる時の処理を追加すると、以下の通りとなります。
次に、変数「Tempreture」を見ることで沸騰したかどうか判定する処理を行います。
温度センサーの出力電圧の説明の際、概ね97〜98度の時のセンサー出力電圧が3.8ボルトで、その時の電源電圧5ボルトに対する4096分率の値が3113であることを説明しました。
この値とTempretureを比較し、その結果、Tempretureの方が高い場合沸騰したと判定する処理をプログラムします。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ }else{ } |
Tempretureが3113を超えた時に行う処理をプログラムします。
33番ピンに接続されている【沸騰】LEDと、26番ピンに接続されたヒーターをOFFにして、25番ピンに接続された【保温】LEDを点灯させる必要があるため、以下のようにプログラムします。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
プログラムを改めて読み返すと、お湯が沸いた時に【保温】を示すLED(25番ピン)を点灯させる処理はありますが、【沸騰】ボタンを押した時に消灯させる処理がありません。
このまま実行すると、再沸騰させる時に【沸騰】と【保温】の2つのLEDが同時点灯してしまいます。
そのため、【沸騰】ボタンが押された時の処理に、【保温】LEDを消灯させる処理を追加します。
| RevoilKEY_STATUS = digitalRead(32) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
保温動作は、温度センサーによる沸騰検出時だけでなく、【保温/取消】ボタンが押された時も同じ動作をします。
間違えて【沸騰】ボタンが押された時、取り消すための操作が必要だからです。
これから、その処理を追加します。
【保温/取消】ボタンは、IO35に接続されているので、
| digitalRead(35) |
この処理を行うことで、【保温/取消】ボタンの状態を取得することができます。
【沸騰】ボタン同様、取得した状態をプログラムで活用できるようにするため、覚えておかなければならないため、以下のように変数に代入します。
| CancelKEY_STATUS = digitalRead(35) |
これをプログラムに追加します。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
これで、プログラムの中で【保温/取消】ボタンが認識できる状態になりました。
次に、このボタンが押された時の処理をプログラムの中に追加していきます。
【保温/取消】が押された時、ヒーターをOFFにして【沸騰】LEDを消灯し、【保温】LEDをONにします。
この処理、見覚えないでしょうか。
【沸騰】ボタンを押して水を加熱し、97〜98度に達した時の処理と同じですね。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } if(CancelKEY_STATUS==1){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
このように、中身が同じプログラムを2つ重複して書くのも一つの方法ですが、スマートとは言えません。
本書は、初心者向けの記事なので、特に問題がないならこのまま進めたいのが筆者の本意です。
ただ、もしプログラムに修正が発生した場合、片方の修正を忘れる可能性は排除できません。
そのため、あるべき方法で、【保温/取消】ボタンが押された時の処理を追加していきます。
インターネット検索などで、より的確な検索結果を得るために、絞り込みを行った経験がある人は、多いと思います。
ワード「A」とワード「B」、両方が含まれる検索結果を得るため、AとBの間にスペース記号を入れた場合もこれに含まれます。
スペース記号は、自動的に読み替えられていますが、正しくは「A AND B」です。
一方、ワードA、ワードBいずれかを含めば良い場合は、「A OR B」と検索します。
ANDを論理積、ORを論理和といいます。
この手法は、プログラミングの「if」でも使用することができます。
プログラミングで論理積(AND=いずれも満たす)を扱う場合、以下のように書きます。
| if((A==1)&&(B==1)){ } |
論理和(OR=いずれか満たす)を扱う場合、以下のようにプログラムします。
| if((A==1)||(B==1)){ } |
書き方(プログラムのしかた)の特徴としては、各々の条件を括弧()で囲い、
| (A==1)、(B==1) |
この2つを && または||で連結させます。
論理積(AND=いずれも満たす)の場合は&&で、
論理和(OR=いずれか満たす)の場合は || を使います。
| (A==1)&&(B==1) または (A==1)||(B==1) |
これをさらに、「 if() 」で囲います。
| if((A==1)&&(B==1)) または if((A==1)||(B==1)) |
この方法を使って、【沸騰】ボタンを押して加熱を始めて湯温が97〜98度付近に達したら保温に切り替わる処理に、【保温/取消】ボタンが押された時の処理を追加します。
追加前のプログラムは、以下の通りです。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if(Tempreture>=3113){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
朱書している行が保温状態に移行する処理なので、ここを次のように変更します。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) if(RevoilKEYSTATUS==1){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
これで、【保温/取消】ボタンが動作するようになりました。
誤って【沸騰】ボタンを押しても、【保温/取消】ボタンを押すことでヒーター動作をキャンセルすることができます。
現在、使用者(生活者)自らが【沸騰】ボタンを押した時はヒーターを動かすことはできますが、温度が下がったことを検出してヒーターをONにする処理が入っていないので、このままでは冷めてしまいます。
【保温/取消】と同じ方法で、【沸騰】ボタンが押された時の処理の中に、水温の低下を条件とする論理和を使って、85度以下になったらヒーターをONにする処理を追加していきます。
ここで再び、温度センサーの仕様書(データシート)を振り返ります。
85度程度になったことを検出する必要があるので、ここから判定電圧を読み取ります。
中央のグラフを読むと、だいたい3.75ボルト程度であることがわかります。
沸騰したことを検知する処理を作り込む時に使用した公式を使って、判定値を計算します。


これにより、電源電圧5ボルトに対する4096分率の値が3072まで低下したら、ヒーターの電源を入れれば良いことが判断できます。
この条件を、プログラムに追加していきます。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } Tempreture=analogRead(6) if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
Tempreture=analogRead(6) に注目してください。
この行は、湯温を測定するための処理ですが、湯温が一定以下になったら再沸騰させる処理よりも後で温度測定を行っているため、これでは湯温の測定ができません。
ただ、マイコンはプログラムの最後まで処理を終えたら、再び1行目に戻って順に処理を行い、延々とループを繰り返します。
このため、最初の一回だけ温度を取り損ねても、後で測定した値を保持したまま1行目に戻ります。
さらには、マイコンは1秒間に2億回ものプログラムが実行できるほど高速で動いているため、最初の一度だけ測定し損ねたことを人が見分けることは不可能です。
そのため、このままでも問題ありませんが、念のため心理的抵抗を感じるのであれば、本来あるべき位置に処理を移動させます。
移動させると、次の通りとなります。
| RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) Tempreture=analogRead(6) if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } // この行削除 if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
これで、電気ポットのプログラムの主要処理部が完成しました。
このプログラムは、処理の流れや構文としては合っているのですが、マイコンに転送するためには様々なルールや下処理が必要となります。
次の項目で、マイコンで扱えるように修正・追記していきます。
プログラムは、主要な処理の流れだけ書いても動作しません。
例えば、既に出来上がったプログラムの中に出てきたanalogReadやdigitalRead、自分自身で名付けたRevoilKEY_STATUSなどの文字の並びは何者なのかマイコンは全くわかりません。
1つ1つの意味をマイコンに理解させる処理が必要になるのです。
Arduinoは、C言語を使用しています。
C言語という言葉がプログラミング言語であることは、21世気になった今はもちろん、90年代でも老若男女・業種問わずなんとなく理解できている方は多かったと思います。
C言語のルールも含めて、ここまでで作ったプログラムが動くように修正をしていきます。
プログラムの中に出てきたanalogReadやdigitalRead、さらにはifなどの単語は、「命令」といいます。
マイコンがプログラムを実行する時は、チップ内にある複雑な電子回路を使って計算します。
電子回路単体では、プログラムを理解することも翻訳することもできません。
例えば、掛け算の処理を行う電気回路を作るため、2つのスイッチを直列につないだものを用意したとしましょう。
スイッチが直列につながっているので、いずれか片方だけONになっていても、電球は光りません。
電球を光らせるためには、両方のスイッチがONになっていなければなりません。
このようなものでも、1×0=0、0×1=0、1×1=1の計算を行う立派な計算回路となります。

1+1=1になる点が若干気持ち悪いだろう。
ONかOFFだけで正しく表現すると、正しくはこうなります。
1+1=0(繰り上がり1)
こうなるようにするには、足し算回路と掛け算回路を複数組み合わせて、このように作ります。

これで正確に計算できるようになったものの、1桁の計算しかできない。
0+0から1+1の4つの組み合わせの計算など、わざわざ計算機など使わなくても誰でも計算できる。
では、137+96などといった実用ベースの計算をするにはどうしたら良いか。
先ほどの回路を複数組み合わせてこのようにします。

ここでは、2ごとに繰り上がる性質の算数(2進数)ができる回路が8個組み合わされています。
2の8乗=255までの計算ができるようになりました。
これを8ビットの計算機といいます。
255でも電卓としては若干心許ないだろう。
例えば16ビットにすれば65535まで扱えます。
32ビットであれば約1677万まで扱えるようになります。
このようにして、実用ベースの計算機は構成されています。
※電卓の場合、実際は4〜8ビットの計算回路を使用して計算結果を一時的にメモリに保存しながら数桁ごとに計算を繰り返して大きな数字を扱えるようにしています。
こうしないと、8桁電卓(上限99,999,999)の場合でも35ビット必要になり、高価なものになってしまうからです。
では、この計算機で73+18を計算してみましょう。
A7からA0の端子にスイッチを付けて、2進数で73を入力すると、順に「OFF,ON,OFF,OFF,ON,OFF,OFF,ON」となる。
次にB7からB0の端子にスイッチを付けて2進数で18を入力すると、順に「OFF,OFF,OFF,ON,OFF,OFF,ON,OFF」となる。
S7からS0の電気出力の端子にLEDがつながっていたとしたら、
S7からS0にかけて順に「消灯,点灯,消灯,点灯,点灯,消灯,点灯,点灯」となるはずだ。
1つずつ(2ごとに)繰り上がるため、S7=128 , S6=64 , S5=32 , S4=16 , S3=8 ,S2=4 , S1=2 , S0=1を意味する。
これをあてはめると、64+16+8+2+1=91となるはず。
コンピュータはこのような仕組みで計算をしています。
では本題に戻します。
キーボードもディスプレイもない電球とスイッチと電池だけの回路にここにどうやって
GOUKEI=GOUKEI+4;
このようなプログラムを流し込むのか、全くイメージができないと思います。
そこで、コンピュータがプログラムを実行できる仕組みについて簡単に触れておきます。
この図は、マイコン(MCU)のチップの1区画に実装されている中央演算処理装置(CPU)を、さらに細かくした算術処理装置(ALU)の概略図です。
どのような複雑な計算も、電気仕掛けで成し遂げる計算回路です。

ALUには、計算を行うために必要な様々な回路が組み込まれています。
一例ですが、
1番目に値を読み込むための回路(入力されたデータをメモリに保存する回路)を、
2番目に計算回路を、
3番目に比較回路を、
4番目にたしざん回路・・・のように、図の通り各々の回路をON/OFFするスイッチが順に並んでいたとします。
そこに、「10000000」という電気信号が送られた場合、1番目の「値を読み込む回路」だけがONとなり、その他の回路はOFFになります。
「10000000」というデータは、画像左下にあるクロック信号(信号の送受信のタイミングを司る信号で、メトロノームのような役割を果たしています。)に合わせて電気を「ON OFF OFF OFF OFF OFF OFF OFF」のように送ることで、「10000000」として扱われます。
モールス信号と非常によく似た方法がとられています。
計算回路への入口となるMPX(マルチプレクサ)は、最初にONまたはOFFを受け取った場合、最初の8つの信号は「経路1」に、その後に続く8個の信号は「経路2」、その次の8個は「経路3」に、クロック信号に合わせて自動的に交通整理を行う回路です。
経路1の内容は、どのように処理するかといった処理内容を意味しており、これが「命令」の本分となります。
これをオペコードといいます。
続いて入ってくる経路2と経路3の内容は、処理対象となる要素が2進数で送られてきます。
先ほど紹介した計算回路の「入力A」と「入力B」に相当するものです。
これをオペランドと言います。
オペランドの送信が終わったら、再度経路1に切り替わって、オペコードの処理が行われます。
コンピュータは、8個1組のオペコードと、8個2組のオペランドを延々と繰り返し送信することで、計算をすることができるのです。
近年発売されるコンピュータの中には、演算負荷が最高に達した時7ギガヘルツまでクロック周波数を上げるものがあります。
「ギガ」は10億を意味するので、この例では0と1の信号が1秒間に70億回もマルチプレクサに流し込まれることになります。
赤い旗と白い旗を用意して、「赤あげて、白下げない」を1秒間に70億回やってみてください。
とてつもない情報量であることが想像できると思います。
これをあらかじめ決められたビット数ごとにオペコードとオペランドに振り分けるわけですが、そんなに高速で流し込んでズレないか疑問に感じるでしょう。
その心配はありません。
時計の秒針が60回回転したら短針が1時間分(1/12回転)進み、720回転したら短針が1回転し、それが何日、何ヶ月、何年経っても絶対に比率がズレることがないのと同じことが電気仕掛けで実現されています。
コンピュータの近くで雷が落ちたり、おかしなタイミングでハードウェアを取り外すなどを行った場合はズレることがありますが、これは時計の歯車に例えるなら、針に無理な力を加えてギアが欠けたり、軸が折れたりすることと同じことです。
もしズレた場合は、異常を検知する仕掛けが動くことで、強制的に再起動がかかります。
この再起動は、オペコードとオペランドの整合性の不一致を検出したら直ちに行われます。
設計上は可及的速やかに行わなければならないことになっています。
この際、復旧に必要なダンプデータ(直前のメモリやレジスタの内容)を出力することがあります。
例えば、0+128の計算結果を比較処理で使用する・・・という処理を行っている途中に近所で落雷などがあって意図しない電圧変動が発生したり、急に電源を切る(電源を切っても、基板上のすべてのパーツの電圧が0ボルトになるわけではなく、0.05秒程度は極めて不健全な状態でコンピュータは動き続けます。)などしてオペランドとオペコードがズレたとします。
本来、計算式で使用するデータ(足される数)であるはずの128が次のステップのオペコードに流れ込みます。
128は2進数にすると10000000です。
こちらで紹介した例だと10000000は読み込み回路となりますが、コンピュータによって仕様は異なります。
仮に書き込み回路だとしたら、次のステップ以降に続くデータでメモリに保存してある保存データが上書きされます。
この状態が長引くと、データ破壊の範囲が次第に広がり、取り返しがつかなくなります。

オペコード(命令)は、流し込まれたものをそのまま実行するほどコンピュータは単純にできておらず、実際には(例えるなら)0から27ビット目までの値を16で割り、その余り(剰余)を28〜31ビット目に記録し、オペコードがデコーダーに流れ込んできた段階で計算結果が合うかどうかのチェックをするなどの処理をしています。
もしここで不整合が発覚したら直ちにリセットピンの電圧を(電源が切られるまで)恒久的にLoに落とし、その状態を維持し続ける(=リセットキーを押し続ける)ことで、命令実行のトリガーとなるクロック源を止めたまま電源を切るなどのプロテクト処理が施されています。

この図は、パソコン用のCPUでよく耳にするIntel Coreプロセッサの祖先となる約40年前のインテル製プロセッサ「i8080」という製品のデータシート(仕様書)の抜粋です。
電卓用に開発されたCPUではあるものの、ゲームセンターや喫茶店で大ヒットしたインベーダーゲームのプログラムを走らせるために必要な演算を行うといった、インテル社の想像を大きく超える形で広く普及しました。
この方法は今も昔も変わらず採用されている計算方法です。
余程大きな技術革新でもない限りは、今後も大きく変わることはありません。
インテル製のマイクロプロセッサだけでなく、アップル社製のA1も、IBMのPowerPCもこの部分はメーカーを問わず全く同じです。
例えば、
GOUKEIという変数に7という数が入っていて、
GOUKEI=GOUKEI+4;という計算をコンピュータにさせるとしましょう。
言うまでもなく答えは11ですが、この計算もコンピュータは先の説明の通り、0と1しかないスイッチのON/OFFだけで計算しています。
このプログラムが、どうしたら0と1だけのデータに化けて、電気仕掛けの計算回路に計算をさせているか、その仕組みを説明していきます。
その中で、「ライブラリ」がどのように使われているかを理解すれば、その重要性と役割が理解できるはずです。
1.GOUKEIの初期値は7としてGOUKEI=GOUKEI+4を実行すると、
ALUの中にあるスイッチのON/OFFに読み替えがしやすい「機械語」という言語に変換されます。
この変換処理に「ライブラリ1」が使われます。

2.さらに、「mov」や「add」でもALUは理解することはできないので、スイッチの入りきりに対応したON/OFFに対応した0と1だけの命令に変換されます。
この処理で「ライブラリ2」が使われます。

ここでの処理は、完全に0と1になっていないとコンピュータで扱えないため、データ部分(実数部分)は、2進数に変換されます。
2進数とは、いかなる数字も0と1だけで表現したものです。
コンピュータの計算回路では、電気が流れている/流れていないという2種類の情報しか扱えません。
そのため、私たちが普段用いている10ごとに繰り上がる10進数という概念に対して、2ごとに繰り上がる2進数という概念を用います。

桁数は大幅に増えますが、ON/OFFの2値しか扱えない電気回路でも数が扱えるようになりました。
プログラム内容が全て0と1だけになったら、モールス信号のようなONとOFF(電気信号)で表現できるため、計算回路に流し込むことができるようになります。
このように、ライブラリのおかげでコンピュータは計算や情報処理ができるようになるのです。
もしライブラリがなければ、文章のようにも見えるプログラムは、全く意味をもたない文字の並びになるのです。
そもそも、文字の並びという概念さえも理解することはできません。
これで、ライブラリの重要性が理解できたと思うので次に進めたいところですが、知識を定着させるため、参考までにコンピュータが計算できる仕組みを最後まで説明します。
モールス信号のようなON/OFFのみの電気信号に変換されたプログラムは、図の左下にあるクロック(メトロノームのようなものです)のタイミングにあわせて、順に送信していきます。
MPX(マルチプレクサ)は、ON/OFFのような断続的な電気信号が送られてきた時、最初の8個は経路1に、後に続く16個は経路2に、経路2への送信が終われば再び経路1に・・・と経路1に8個→経路2に16個を延々と繰り返し交互に橋渡しを行う役割を担います。
モールス信号のような信号が送信されると、最初の8個の信号が経路1を経由して計算回路の機能選択スイッチに対して順に電気が送られます。
プログラムの1行目のオペコード(命令部)は10000000なので、MPX経由で「ON OFF OFF OFF OFF OFF OFF OFF」が計算回路の機能選択スイッチに送られるため、図のいちばん上にある「読み込み回路」のみが有効になります。

先頭の8個分の信号が送られたら、以降の16個は自動的にMPXによって経路2に送られます。
経路2の橋渡し先は計算に使うメモリーです。
最初の8個がメモリアドレスで、後半の8個は処理に使用される数(データ)です。

読み込み回路がONの状態でデータがメモリに送り込まれたら、直ちに主記憶メモリにデータが移されます。

この段階で既に16個の信号を送っているため、MPXの振り先は再び「経路1」のALUに戻ります。
次も「10000000」なので、「読み込み回路」だけがONになり、他の回路はOFFになります。

続けて、16個の信号が読み込まれて、計算対象の値がメモリ2に読み込まれます。

次に、「01010000」という信号がALUに渡されます。
2番目と4番目の計算回路がONになります。
2番目と4番目は、計算を行う回路と、足し算を専門に行う回路です。
足し算だけONになっていても、計算回路がONになっていないと計算結果として扱われないため、2つの回路がONになっています。
この状態でクロック(メトロノームのようなもの)がALUに対して送られると、

直ちに計算か実行され、計算結果で上書きされます。
2進数の「00001011」は、(2の3乗)+(2の1乗)+1=11ですので、7+4=11という計算の実行が終了しました。
このように、人が理解しやすいプログラミング言語を、計算で使用される電子回路の性質に合ったものに置き換えるためにライブラリが必要不可欠なのです。
早速、プログラムにライブラリを読み込む命令を追記します。
ライブラリの読み込みは、以下のようにプログラムします。
| #include<ライブラリ名.h> |
ライブラリ名称は、使用する開発ソフトやマイコンによって異なります。
Arduinoの場合は、Arduino.hを使用します。
Arduino.hは、パソコンのファイル名に似た書き方に見えますが、実際にパソコンの中に入っているファイルの名前です。
開発ソフトは、Arduino.hというファイルに書かれている内容を全てメモリに取り込むことで、本来であれば理解できないはずのifやdigitalReadがどういう意味を持つか、調べることができるのです。
テキストエディタで開いてみると、
・Aという命令が入ってきたら、Bのように読み替えなさい。
・Bは、Cというファイルに書かれた規則にしたがって機械語にしなさい。
・Dという命令が入ってきたら、メモリのE番地の内容を確認し、FならばGというルールに従って機械後にしなさい
などといった処理や、関連ファイルとのリンクなどが記述されています。
※実は、近年のArduinoはより初心者に優しくするため、ライブラリを読み込まなくても使えるようになってきています。
ただ、正統派のやり方ではないことと、それを受けて将来仕様変更される可能性もありますので、ライブラリを読み込む習慣はつけておくことをお勧めします。
ライブラリの読み込みを追加したプログラムは、以下の通りです。
| #include<Arduino.h> RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) Tempreture=analogRead(6) if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
ここでは必要ないので読み込みしていませんが、複雑な計算を行う時は化学計算に対応した命令を使えるようにするためのライブラリとしてmath.hを、WiFiでインターネット接続する時はethernet.h や wifi.h などを読み込みます。
全部読み込めば良いのでは?という思いが脳裏をよぎるかもしれませんが、いたずらに多く読み込んでも、マイコンのプログラムメモリを圧迫しますので、使うものだけ読み込むようにします。
ライブラリは、ベンダーやサードパーティーから提供されたものだけでなく、自分自身で作ることも可能です。
ライブラリを読み込むことで、プログラムに書かれた命令がマイコンで認識することが可能になりました。
以下のプログラムは、現時点でマイコンが認識可能な部分を赤で記したものです。
| #include<Arduino.h> RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) Tempreture=analogRead(6) if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
大半が理解可能になったとはいえ、1つでもマイコンが理解できない文字があれば、マイコンで扱うことはできません。
マイコンで理解できない文字に共通している要素は、データを扱うために自分自身で付けた変数の名称であることがわかります。
この文字が変数であることをマイコンに教えなくてはなりません。
プログラムでデータを扱う時、メモリにデータを保存しなければなりません。
しかし、このような単純なプログラムでさえ、3つも変数が出てきていますので、名前を付けないと混乱してしまいます。
また、メモリの中の1区画をユーザーデータの保存用に割り当てなければ、そもそもデータを扱うことすらできません。
このような時、メモリ領域を確保すると同時に、プログラム内で扱いやすいように一意の名称とリンクさせる処理を行います。
以下のようにプログラムします。
| int 名称; |
電気ポットのプログラムでは、
【沸騰】ボタンの状態を保存する変数「RevoilKEY_STATUS」
【保温/取消】ボタンの状態を保存する変数「CancelKEY_STATUS」
温度センサーから取得した水温に比例した値を保存する変数「Tempreture」
の3つの変数を扱います。
先ほどの書式に倣って、この3つの変数を使うためのメモリ領域の確保と命名を行う命令をプログラミングします。
| int RevoilKEY_STATUS int CancelKEY_STATUS int Tempreture |
マイコンを含め、コンピュータのメモリは電気的ノイズやプログラムのバグ等の影響で、意図しないデータが入っていることがあります。
このデータは、時として思いもよらない動作を引き起こす原因にもなるため、初期値はゼロとして初期化します。
この場合、以下のようにプログラムします。
| int RevoilKEY_STATUS = 0 int CancelKEY_STATUS = 0 int Tempreture = 0 |
プログラムに追加すると、以下の通りとなります。
| #include<Arduino.h> int RevoilKEY_STATUS = 0 int CancelKEY_STATUS = 0 int Tempreture = 0 RevoilKEY_STATUS = digitalRead(32) CancelKEY_STATUS = digitalRead(35) Tempreture=analogRead(6) if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH) digitalWrite(26, HIGH) digitalWrite(25,LOW) }else{ } if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW) digitalWrite(26, LOW) digitalWrite(25, HIGH) }else{ } |
追加する場所には注意が必要です。
#include<Arduino.h>よりも前の行に追記すると、ライブラリが読み込まれていないため、「int」の意味が理解できず、エラーとなります。
そのため、#include<Arduino.h>の後に書きます。
但し、変数を使用する前に「int」を実行していないと、プログラムが変数を理解できないため、エラーとなります。
このように、きちんとプログラミングしているつもりなのにエラーになることは、特に初心者では多く見受けられますので、流れを読みながら作ることが肝要です。
C言語は、命令の1つ1つを「;」セミコロン(半角)で区切らなければなりません。
ここでの説明では、全体の流れを掴んでいただくことに集中していただくため省略しましたが、セミコロンがなければエラーになります。
プログラムを書いていると、処理の意味を記したメモやタイムスタンプなどのちょっとしたメモを書きたくなることが出てきます。
そのような時、「//」(半角スラッシュ記号2つ)を入れることで、以降ぼ部分をメモとして使用することができるようになります。
※この記号を入れずにメモ書きなどを行うと、プログラムとして取り扱われるため、エラーになります。
以上2つを考慮して修正すると、以下の通りとなります。
| #include<Arduino.h> // includeはカンマ不要 int RevoilKEY_STATUS = 0; int CancelKEY_STATUS = 0; int Tempreture = 0; RevoilKEY_STATUS = digitalRead(32); CancelKEY_STATUS = digitalRead(35); Tempreture=analogRead(6); //沸騰の判定処理 if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ //ifやelseなどのカギ括弧の行もカンマ不要 digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); }else{ } //保温への切り替え判定処理 if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ //ifやelseなどのカギ括弧の行もカンマ不要 digitalWrite(33, LOW); digitalWrite(26, LOW); digitalWrite(25, HIGH); }else{ } |
マイコンの端子は、これまでの説明の通り、様々な使われ方をします。
センサーやスイッチをつなぐことで電気が流れ込んでくることもあれば、ヒーターやLEDの駆動のために電気が出力されることもあります。
特に、電気が入る/出るの違いは、マイコン側で構成すべき電気回路も全く異なります。
ALUの説明で触れたように、コンピュータは0と1で構成された命令(オペコード)を使って、経路上の回路を入り切りすることで選択することができるため、入力/出力の切り替えも同様、プログラミングをすることで行う必要があります。
そのため、端子の使われ方をプログラミングします。
電気を入力させる場合は、以下のプログラムを書きます。
| pinMode(ピン番号,INPUT); |
電気を出力する場合は、以下のプログラムを書きます。
| pinMode(ピン番号,OUTPUT); |
IO25とIO33にはLEDが、IO26にはヒーターが接続されているので、出力に設定する必要があります。
これをプログラムすると以下の通りとなります。
| pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); |
IO32とIO35にはスイッチが、IO34にはセンサーが接続されているので、入力に設定する必要があります。
この処理を追加すると、以下の通りとなります。
| pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); pinMode(33,INPUT); pinMode(35,INPUT); pinMode(34,INPUT); |
この処理をここまでで作ったプログラムに追加すると、以下の通りとなります。
| #include<Arduino.h> // includeはカンマ不要 pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); pinMode(33,INPUT); pinMode(35,INPUT); pinMode(34,INPUT); int RevoilKEY_STATUS = 0; int CancelKEY_STATUS = 0; int Tempreture = 0; RevoilKEY_STATUS = digitalRead(32); CancelKEY_STATUS = digitalRead(35); Tempreture=analogRead(6); //沸騰の判定処理 if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ //ifやelseなどのカギ括弧の行もカンマ不要 digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); }else{ } //保温への切り替え判定処理 if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ //ifやelseなどのカギ括弧の行もカンマ不要 digitalWrite(33, LOW); digitalWrite(26, LOW); digitalWrite(25, HIGH); }else{ } |
Arduinoに限らず、多くのマイコンでは起動時に一度だけ実行する処理と、無限に繰り返す処理(またはイベント待ち)の2つに分かれています。
マイコンの初期化や変数の領域確保などは、一回行えば十分なので、起動時に一度だけ実行します。
一方、湯温やボタンの状態監視は、継続的に行われるものなので、無限に繰り返す処理としてプログラミングします。
Arduinoは、新規作成すると以下の状態が初期状態として表示されます。
| void setup(){ } void loop(){ } |
一度だけ行う処理をvoid setup()の括弧 {} 内に、
無限に繰り返す処理をvoid loop()の括弧 {} 内にプログラムします。
| #include<Arduino.h> void setup(){ pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); pinMode(33,INPUT); pinMode(35,INPUT); pinMode(34,INPUT); int RevoilKEY_STATUS = 0; int CancelKEY_STATUS = 0; int Tempreture = 0; } void loop(){ RevoilKEY_STATUS = digitalRead(32); CancelKEY_STATUS = digitalRead(35); Tempreture=analogRead(6); if((RevoilKEYSTATUS==1)||(Tempreture<=3072)){ digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); }else{ } if((Tempreture>=3113)||(CancelKEY_STATUS==1)){ digitalWrite(33, LOW); digitalWrite(26, LOW); digitalWrite(25, HIGH); }else{ } } |
ここで一つ注意が必要なことがあります。
ライブラリの読み込みは一度限りなのでvoid setup()の括弧内に書くように思えますが、先にライブラリ(Arduino.h)が読み込まれていないと、void setup()さえ理解することができません。
そのため、先頭で処理します。
マイコンは、とても高速で動作しています。
ここで例に挙げたマイコンは、数百円で手に入る廉価なものではあるものの、1秒間に2億回以上の計算を行うことができます。
スイッチを押す時、普通は人差し指で押し込んで「カチッ」と音がしたら指を離す動作をします。
「カチッ」という瞬間は、人の意識の中では瞬間的なものであっても、1秒間に2億回もの処理ができるマイコンにとっては非常に長いものであり、人の目ではスイッチ接点が押し込まれる一方のように見えても、数億分の1秒という時間の中ではスイッチスプリングの振動で接点が何度もついたり離れたりします。
この現象をチャタリングといいます。
どれだけ素早くスイッチを押しても、マイコンからは連打として判断されるこの現象が、時として思わぬ動作をすることがあります。
ここでは、それを避ける処理を行います。
【沸騰】ボタンが押されたことを検知する時、ここまでの説明では以下のようにプログラムすると説明しました。
| RevoilKEY_STATUS = digitalRead(32); |
チャタリング対策のため、別の方法で検知を行います。
繰り返し処理とは、ある条件が成立している限り、特定の処理を繰り返す処理をいいます。
| while(○○○){ △△△; } |
この場合、「条件○○○が成立している限り△△△を実行しなさい」という意味になります。
この処理を使って、チャタリング対策を行います。
| while(digitalRead(32)==1){ } |
このように書くことで、【沸騰】ボタンが押されている限りは・・・という処理を作ることができます。
この処理の括弧内に書く処理は、【沸騰】LEDをONにしたり、ヒーターに通電する処理ではありません。
※もしそのようにプログラムした場合、ボタンを押している間しかヒーターは動きません。
このように書きます。
| while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; } |
RevoilKEY_PushCOUNTERは、ボタンがどれくらい押し続けられたかを数えるための変数です。
digitalRead(32)が1である限り・・・つまり【沸騰】ボタンが押されている限り括弧内の処理を繰り返します。
括弧内には、RevoilKEY_PushCOUNTERに1を足すプログラムが書かれています。
つまり、【沸騰】キーが押されている間、RevoilKEY_PushCOUNTERの値が増え続けていくのです。
しかし、先程の説明の通りマイコンの計算速度は非常に高速であるため、「カチッ」という瞬間のことであっても、マイコンで扱える数の上限を振り切ってしまいます。
これを防ぐため、ディレイ(時間潰し)を入れます。
| while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } |
delay は、カッコ内の数に1000分の1秒を掛けた数(1ミリ秒)だけ時間潰しをするプログラムです。
繰り返す度に1ミリ秒の時間潰しをするため、仮に5秒間【沸騰】ボタンを押しっぱなしにしても RevoilKEY_PushCOUNTER は5000程度にとどまるため、マイコンで扱うことができる数の上限を振り切る心配はありません。
人が指でボタンを押し込んだ時、スイッチ接点が接続されている時間は10〜30ミリ秒程度といわれています。
つまり、RevoilKEY_PushCOUNTER が概ね20程度以上であれば人が押したもので、それに満たない場合はチャタリングか混入したノイズである疑いが強く、スイッチが押されたと判断するには大いに疑問がある状態となります。
既に説明した「if(もし○○ならば)」を使って、人がボタンを押したことを判定する処理を追加します。
| while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } if(RevoilKEY_PushCOUNTER>=30){ } |
これで、確実に人が【沸騰】ボタンを押したとみなすことができるので、この括弧の中にヒーターをONにしたり、【沸騰】LEDを点灯させる処理を追記します。
| while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } if(RevoilKEY_PushCOUNTER>=30){ digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); } |
これで正確に人が押したと判断できるようになりましたが、一度でも【沸騰】ボタンを押すと、RevoilKEY_PushCOUNTERが30を超えたままであるため、【沸騰】ボタンを押していなくても押し続けられているものとして扱われてしまい、非常に危険です。
そのため、以下のようにRevoilKEY_PushCOUNTERをリセットします。
| while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } if(RevoilKEY_PushCOUNTER>=30){ digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); } RevoilKEY_PushCOUNTER=0; |
これで、偶発的な電気的ノイズやチャタリングなどのような30ミリ秒に満たない入力があっても、
if(RevoilKEY_PushCOUNTER>=30)
の条件を満たさないため、ヒーターの電源はONになりません。
そして、その直後にある
RevoilKEY_PushCOUNTER=0;
この処理で、連続押下秒数がリセットされるため、また次押された時は0から数え直しになります。
ここまでに作ったプログラムにマージすると以下の通りとなります。
| #include<Arduino.h> void setup(){ pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); pinMode(33,INPUT); pinMode(35,INPUT); pinMode(34,INPUT); int RevoilKEYPush_COUNTER = 0; int CancelKEYPush_COUNTER = 0; int Tempreture = 0; } void loop(){ while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } while(digitalRead(35)==1){ CancelKEY_PushCOUNTER = CancelKEY_PushCOUNTER + 1; delay(1); } Tempreture=analogRead(6); if((RevoilKEYPushCOUNTER>=30)||(Tempreture<=3072)){ digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); }else{ } if((Tempreture>=3113)||(CancelKEYPush_COUNTER>=30)){ digitalWrite(33, LOW); digitalWrite(26, LOW); digitalWrite(25, HIGH); }else{ } RevoilKEY_PushCOUNTER=0; CancelKEY_PushCOUNTER=0; } |
ここまでの説明で、最低限使える状態の電気ポットのプログラムが完成しました。
仮にこのプログラムが書かれたマイコンが実装された電気ポットを買って箱から出し、水を入れて電源プラグをコンセントに差し込めば、お湯が沸き、沸騰後は90度前後を常に維持するようになります。
しかし、これだけでは商品としてはもう一つです。
お湯は、沸騰していなくても蒸発しますので、放置すればそのうち空焚き状態になります。
放置しなくても、大きな地震などで電気ポットが転倒してもお湯がなくなり空焚き状態になります。
空焚きは火災の原因になるため、非常に危険です。
明らかな異常を検知したら、ポットを停止しなければなりません。
新たに、この処理を追加しますので、なぜこのような処理を行うのか、考えてみてください。
| #include<Arduino.h> void setup(){ pinMode(25,OUTPUT); pinMode(33,OUTPUT); pinMode(26,OUTPUT); pinMode(33,INPUT); pinMode(35,INPUT); pinMode(34,INPUT); int RevoilKEYPush_COUNTER = 0; int CancelKEYPush_COUNTER = 0; int Tempreture = 0; int OPERATION_MODE=0; //0=NORMAL 1=WARNING int REASON_CODE=0; //0=OVERHEAT 1=NOHEAT int loopCounter=0; int BLINK_CONTROL_FLAG=0; } void loop(){ if(OPERATION_MODE==0){ while(digitalRead(32)==1){ RevoilKEY_PushCOUNTER = RevoilKEY_PushCOUNTER + 1; delay(1); } while(digitalRead(35)==1){ CancelKEY_PushCOUNTER = CancelKEY_PushCOUNTER + 1; delay(1); } Tempreture=analogRead(6); if((RevoilKEYPushCOUNTER>=30)||(Tempreture<=3072)){ digitalWrite(33, HIGH); digitalWrite(26, HIGH); digitalWrite(25,LOW); if(loopCounter<=1800){ delay(100); loopCounter=loopCounter+1; }else{ OPERATION_MODE=1; REASON_CODE=2; } }else{ } if((Tempreture>=3113)||(CancelKEYPush_COUNTER>=30)){ digitalWrite(33, LOW); digitalWrite(26, LOW); digitalWrite(25, HIGH); loopCounter=0; }else{ } if(Tempreture>=3200){ OPERATION_MODE=1; REASON_CODE=1; }else{ } RevoilKEY_PushCOUNTER=0; CancelKEY_PushCOUNTER=0; }else{ if(REASON_CODE==1){ digitalWrite(25, HIGH); }else{ if(BLINK_CONTROL_FLAG==1){ digitalWrite(33, HIGH); }else{ digitalWrite(33, LOW); } } if(REASON_CODE==2){ digitalWrite(33, HIGH); }else{ if(BLINK_CONTROL_FLAG==1){ digitalWrite(25, HIGH); }else{ digitalWrite(25, LOW); } }else{ } delay(500); if(BLINK_CONTROL_FLAG==1){ BLINK_CONTROL_FLAG=0; }else{ BLINK_CONTROL_FLAG=1; } } } |
* インテル、Intel、Intel Inside、Intel Inside ロゴ、インテル Coreはアメリカ合衆国およびその他の国におけるインテルコーポレーションまたはその子会社の商標または登録商標です。
* AMD、AMD Arrowロゴ、ATI、およびそれらの組み合わせ、RadeonはAdvanced Micro Devices, Inc.の商標です。