概要
第6回目です。前回は、Arduino(アルディーノ)の PWM出力機能 について勉強しました。今回は、**アナログ入力(=ポートに印加されているアナログ電圧値をプログラムから読み取る機能)**について、Tinkercad Circuit(Aruduino&回路シミュレータ)を利用して勉強していきます。
今回は、新しい要素として、アナログ入力 と シリアル通信(シリアルモニタ出力) が登場します。
準備:シリアル通信(シリアルモニタ出力)
前回 から、プログラムのなかで 変数 を使いはじめました。
C言語では、次のように printf(...)
を使うことで、変数に格納されている値を画面(ディスプレイ)に出力して確認することできました。これは、プログラムが意図したように動作するかを確認するデバッグ作業においても役に立っていました。
#include<stdio.h>
main(){
int i;
int sum=0;
for(i=0;i<=5;i=i+1){
sum=sum+i;
printf("sum=%d (i=%d)\n",sum,i);
}
}
Arduino には 画面(ディスプレイ、標準出力)が備わっていないので、この方法は使えないのですが、代わりに**シリアル通信という手段で Arduino と PC を接続して、PC側(シリアルモニタ)で変数の値を確認することができます**。
これは、TinkerCad Circuits によるシミュレーション環境でも利用が可能です。
実際にやってみましょう。
-
TinkerCad Circuits にアクセス&ログインして、「新しい回路」で新規作成して、Arduino UNO を配置します(ArduinoのみでOKです)。
-
次のようにブロックプログラムを作成します。(2020/5/4時点では)日本語訳がいまいちなので、英語環境のほうが分かりやすいと思います(言語環境の切り替えは、前回 を参照してください)。
-
上記のロジックの動作(変数 a の値が繰り返し毎に、どのように変化するか)を脳内でシミュレーション(トレース)してください。
はじめに 変数 a をゼロにセットします。そのあと、条件「aが10未満」を満たす間、「a←a+1(a+1をaに格納)」、「1秒待機」を繰り返します。変数 a は1秒経過ごとに1ずつ増えていきます。やがて 10 に達して条件「aが10未満」を満たさなくなると、繰り返し(ループ)から抜けます。ループから抜けた後は、再び、最初にもどります。つまり、変数 a をゼロにセットとして・・・が続きます。 -
この程度であれば、脳内でのトレースは比較的簡単です(何回か練習すれば簡単にできるようになります)。しかし、複雑なロジックになると、それも難しくなります。そのようなとき、C言語では、
printf(...)
などを使って、変数値を画面出力して動作を確認しました。Arduino では、次のようにして、同じようなことができます。 -
出力/Output のグループに移動します。「シリアルモニタ( )以下を含む に改行を出力」/「print to serial monitor( )with newline」をがあります。
-
このブロックは、( ) に設定された内容を、シリアルモニタ画面(後述)に出力するブロックです。改行(newline)付きで出力するか、付けないかをオプションで設定できます。
-
シリアルモニタ出力ブロックを、次のように組み込み、繰り返し毎に 変数 a の値が、改行付きでシリアルモニタに出力されるようにします。
ブロックのhello world
のところには 変数 a をはめ込みます。 -
シリアルモニタパネルが開いて、シリアル出力された内容を確認することができます。回路例はこちら(TinkerCadに移動します)。
- 「改行なし」でシリアル出力をすると、シリアルモニタの出力がどのようになるか確認しましょう。
- シリアルモニタの右下の「クリア」と「(グラフマーク)」を押下するとどのようになるか確認しましょう。
復習:Arduinoのデジタル入力
第4回で学習したように、Arduinoのデジタル入力は、ポート(ピン/端子)に印加されている電圧を HIGH($=1$) または LOW($=0$) の2値で読み取ることができました。例えば、$5 \mathrm{V}$ が印加されていればプログラムのなかで「$1$」という数値として、$0 \mathrm{V}$ が印加されていれば「$0$」という数値として取得することできました。
ところで、$4 \mathrm{V}$ が印加されているときはどうなのでしょうか?「$1$」が取得できます。では、$0.8 \mathrm{V}$ のときはどうなのでしょうか?「$0$」が取得できます。印加電圧を $0\mathrm{V}$ から徐々に高くしていくと、ある電圧で取得できる値が「$0$」から「$1$」に切り替わります。このときの電圧を閾値電圧(しきいちでんあつ)といいます。
次の図に示すような方法で、デジタル入力の閾値電圧を確認してみると「約 $2.3 \mathrm{V}$」でした。ただし、実機では、個体差や環境温度によって、必ずしも、この値になるわけではありません。また、ヒステリシス性もあるため、$0 \mathrm{V}$ から $5 \mathrm{V}$ に向けて電圧を上げていったときの閾値と、$5 \mathrm{V}$ から $0 \mathrm{V}$ に向けて電圧を下げていったときの閾値は違ってくる可能性があります。このようなことからも、基本的にはデジタル入力ポートには中途半端な電圧を与えような使い方はしません。
Arduinoのアナログ入力
デジタル入力では、電圧を「$1$」か「$0$」の 2値 でしか読みとることができませんでした。
これに対して、Arduino UNO のアナログ入力では、印加されている電圧 $0 \mathrm{V}$ から $5 \mathrm{V}$ を、$0$ から $1023$ までの「$1024$ 段階」で読み取ることができます( $0 \mathrm{V}$ から $5 \mathrm{V}$ の入力電圧を、$0$ から $1023$ までの整数値にマッピングした値をプログラム内から参照できます)。
例えば、$5 \mathrm{V}$ が印加されていればプログラムのなかで「$1023$」という数値が、$0 \mathrm{V}$ が印加されていれば「$0$」という数値が、$2.39 \mathrm{V}$ が印加されていれば、
$$ \frac{2.39}{5.00}\times 1023 = 489 $$
「$489$」という数値(整数値)が取得できます。なお、$1024$ 段階の分解能を持つので「分解能 $10\mathrm{bit}
$($1024=2^{10}$)の A/Dコンバータ機能 を持っている」のように表現されることもあります。
A/Dコンバータ の A は Analog、D は Digital の頭文字です。「$0$から$5 \mathrm{V}$のような連続量(アナログ量)」を「$1024$ 段階のような離散量(デジタル量)」に変換(コンバート)する機能です。
このアナログ入力機能は、ブロックの場合は「アナログピン Ax を読み取る」、コードの場合は analogRead(...)
を使用します。
また、ポートには、A0 から A5 を使用します(デジタル入出力ポート D0 から D13 は使えません)。
なお、アナログ電圧の読み取りは、$1024$ 段階になるので $5\mathrm{V} \div 1024 \simeq 4.9\mathrm{mV} $ よりも小さな電圧の変化を見ることはできません(例えば、$1\mathrm{mV}$ の入力も $4\mathrm{mV}$ の入力も、analogRead(...)
の戻り値は、同じ「0」という値になります)。もっと小刻みに値の読み取りが必要な場合は、12bit 以上の分解能を持った A/Dコンバータ を別途、外付けして利用します。
- ADS1015搭載 12bit A/Dコンバータ@スイッチサイエンス
- ADS1115搭載 16bit A/Dコンバータ@スイッチサイエンス
Arduino の AD変換についての詳しい情報は、公式のリファレンス や、Arduino UNO の CPU である「ATmega328P」のデータシート のなかの「Analog-to-Digital Converter」の項目などを参照して下さい。
注意
アナログ出力(PWM出力)で設定できる値の範囲は、0~255までの256段階、アナログ入力の戻値の範囲は、0~1023までの**1024階** です。混同しないように気を付けてください。
アナログ出力センサー
センシング結果をアナログ量で出力するタイプのセンサーを、ここでは「アナログセンサー」とよぶことにします。アナログセンサーのうち、出力する物理量が 電圧値 や 抵抗値 であるものは、Arduino のアナログ入力を使って読み込むことができます。
温度センサー TMP36
TinkerCad Circuits では「TMP36」という温度センサーが利用できます。実在するセンサーで、センシングした温度をアナログ電圧として出力する機能を持っています。つまり、Arduino のアナログ入力ポートと接続すれば、プログラムのなかで温度情報を利用することができます。
電子部品の使い方や特性は、型番をキーワードに検索すれば見つかります。このセンサーの場合であれば「TMP36 Arduino
」でググればOKです。また「TMP36 datasheet
」と検索すれば、各種特性などが詳細に記載されたデータシートという資料も見つかります。
さて、この TMP36 ですが、3つの端子があり「電源 $V_{\mathrm{S}}$」「センサ出力 $V_{\mathrm{OUT}}$」「GND」が割り当てられています。実物を使う場合、「電源 $V_{\mathrm{S}}$」と「GND」を逆接続すると、数秒もせずに壊れるので注意しましょう(Bottom View(部品の下から見たピン配置)と Top View(部品の上から見たピン配置)の勘違いがよくあります)。
センサーを機能させるためには、電源として 端子 $V_{\mathrm{S}}$ に $2.7$~$5.5 \mathrm{V}$、端子 GND に $0\mathrm{V}$ を供給します。すると、端子 $V_{\mathrm{OUT}}$ と 端子 GND の間に、温度に応じた電圧が出力されます。
(シミュレーションができる環境では)実際に使ってみたほうが理解がはやいと思うので、次のように回路を構成してシミュレーションを実行してみましょう。
シミュレーションの実行中にセンサーをクリックすると、環境温度を変化させることができます。
- 回路例はこちら(TinkerCadに移動します)。
温度を操作してみると(低温域でも高温域でも)**温度が $1^\circ\mathrm{C}$ 上がると、出力電圧が $10\mathrm{mV}$ 高くなる特性**を持っていることが分かります。また、電源電圧を、$3.3\mathrm{V}$ や $4\mathrm{V}$ に変えても、同じ温度のときは、同じ電圧を出力している点もポイントです。
ここでは実験(シミュレーション)で特性を確認しましたが、実際は先にデータシートなどで動作電圧や特性を調べておきます(先述のように「TMP36 datasheet
」と検索すればがみつかります)。データシート記載の情報をいくつかピックアップして、次に記載します。
TMP35/TMP36/TMP37 は、低電圧高精度摂氏温度センサーであり、摂氏温度に比例する電圧を出力します。(中略)+25℃で±1℃の精度を、−40℃~+125℃の温度範囲で±2℃の精度を、それぞれ提供します。
センサーの電源電圧 $V_{\mathrm{S}}$ は 2.7 V~5.5 V
、出力特性は次のようになっています。
上記の出力特性を読み解けば、温度 $t$ のときの 出力電圧 $V_{\mathrm{OUT}}$ が、次のようになることが分かります。
$$ V_{\mathrm{OUT}} = 0.01 t + 0.5\quad [\mathrm{V}]$$
式に $t=25$ を代入すると $V_{\mathrm{OUT}}=0.75 [\mathrm{V}]=750 [\mathrm{mV}]$ となり、シミュレーション結果の $749 [\mathrm{mV}]$ ともよく一致します($1\mathrm{mV}$ の誤差がありますが、+25℃で±1℃の精度
ということを考慮すれば問題ではないことが分かると思います)。
さて、このセンサー出力を Arduino の A0端子(アナログ0番端子) に接続して、温度を25℃にしたとき、analogRead(...)
で値を取得すると、その値はいくらになるでしょうか?次式より「$153$」前後になることが計算できます。
$$ x = \frac{0.75}{5.00}\times 1023 = 153.45 $$
実際に、回路とプログラムを作成して確かめてみましょう。次のように、analogRead(...)
で読み取った値を 変数 $x$ に格納し、シリアル出力を使って、その値を確認してみましょう。
- 回路例はこちら(TinkerCadに移動します)。
- 温度の微調整は、画面を拡大して([Ctrl]+マウスホイールで拡大操作)操作してください。
整理
温度センサ TMP36 と アナログ入力 を使えば、外部温度の情報をロジック内に組み込むことができます。ただし、温度 $t$ と、アナログ入力で取得される値 $x$ には、次の関係があります(ここまでに示した2つの式から導けますね)。
$$ x = \frac{0.01 t + 0.5}{5.00}\times 1023 $$
$$ t = \frac{500x}{1023} - 50 $$
ロジックのなかに温度を取り込むことができれば、それを利用して分岐処理ができます。例えば、温度 $t$ が $25$度以上のとき(つまり、アナログ入力値 $x$ が $153$ 以上のとき)に、ビルトインLEDを点灯するような機能は、次のようなロジックで構成することができます。
演習14:簡易熱中症警告灯
熱中症の危険度は、暑さ指数(気温、湿度、輻射熱、気流などから計算)で判断することができます。
ここでは、簡易的に温度のみで危険度を判断し「35℃以上は赤色LED」「31℃以上35℃未満は橙色LED」「28℃以上31℃未満は黄色LED」「24℃以上28℃未満は緑色LED」「24℃未満は青色LED」が点灯するような回路とプログラムを TinkerCad Circuit で作成してみましょう。
作成のポイントは、いきなり5色のLEDを配置・配線しないことです。
まずは、温度センサー1個とLED 1個の最小構成で回路を組み、ロジックを構築して、シミュレーションで動作確認することです。それができたら「1つだけLEDを追加して、ロジックを修正、動作確認」を繰り返して完成させましょう(面倒に思うかもしれませんが、このように1つ1つ確認しながら進めることがミスを減らし、結果的には時間短縮になります)。
また、意図する動作にならないときは、シリアルモニタ出力を利用してデバッグしてみましょう。
- 余力があれば、35℃以上のときに警告音がでるようにしてみましょう(圧電ブザーは、第4回の 課題9 で利用しました)。
- 解答例はこちら(TinkerCadに移動します)。
- ブロックプログラミングでは、見通しが悪く冗長なロジックになってしまうことを実感してください(いずれはブロックでロジックを構成することに限界がくることを体感してください)。
演習15:簡易熱中症警告灯(フルカラーLED)
フルカラーLEDは、**RGB(Red,Green,Blue)の3色を組み合わせて任意の色で発光**させることができる LED です(ひとつの素子の内部に、赤と緑と青の3つのLEDが封入されていると思ってください)。
例えば「赤」と「青」を点灯させ、「緑」を消灯させれば、光が合成されて「紫」に点灯します。また「赤」と「緑」を点灯させ、「青」を消灯させれば「黄」に点灯します。
3色の点灯の有無だけではなく、強弱も調整すれば、あらゆる色(フルカラー)が出力可能です。各色の強弱調整には、前回学習したPWM出力(analogWrite(...)
)を利用します。
上記のように 3つの analogWrite(...)
を組み合わせてロジックを構成可能ですが、ブロック環境では フルカラーLED操作専用ブロック も利用可能です。
このフルカラーLEDを使って 演習14 と同じ機能を持った簡易熱中症警告灯を作成してみましょう。
- 解答例はこちら(TinkerCadに移動します)。