温度を測る その4 アナログ温度センサMCP9700、LM335Zを利用
■氷点下も測りたい
定番のLM35DZは5Vの電源をつなぐと、0℃からの温度しか測れませんでした。0℃以下の温度はマイナス電源を用意しないと測れないので面倒です。最初に、アナログ出力で、0℃のときに500mV、1℃につき10mVというLM35DZと同じように使える温度センサMCP9700を利用します。
LM35DZ | MCP9700 | LM335Z | |
---|---|---|---|
測定温度範囲[℃] | -55~150 | -40~125 | -40~100 |
動作電圧[V] | 4~30 | 2.3~5.5 | - |
温度係数 | +10.0mV/℃ | +10.0mV/℃ | +10.0mV/°K |
確度[℃] | ±0.6 | ±4(室温付近はもっと小さい) | 2(室温25℃) |
●Arduino UNOにはアナログ入力は六つもある
LM35DZはアナログ入力のA0端子につなぎました。A1という端子もあります。これもアナログ入力端子です。Arduino UNOのマイコンにはA-Dコンバータ機能は一つしかないのですが、入力を8個もっていて、高速に切り替えるので、あたかも8個の入力があるように見えます。
8回路を切り替える部分はマルチプレクサと呼ばれ、入力の電圧はサンプル&ホールド回路で短い時間保持され、ディジタルに変換されます。入力電圧は常に変化しているので、サンプル&ホールド回路がないと、変換中に入力電圧が変化するので、不都合です。
マイコンは8個の入力がありますが、Arduino UNOは6個だけ端子が用意されています。
●MCP9700をA1端子につなぐ
MCP9700の電源を5Vにつなぐと、温度と出力電圧の関係は次のようになります。
温度[℃] | -20 | 0 | 25 | 50 |
---|---|---|---|---|
電圧[mV] | 300 | 500 | 750 | 1000 |
前回までのLM35DZ(A0端子)をそのまま残し、横にMCP9700を挿してA1端子へ配線します。
●プログラム
前回までのプログラムをほとんどそのままコピーして利用します。MCP9700は500mVが0℃なので、計算した電圧から0.5Vを引きます。二つの温度センサはほぼ同じ温度を表示しています。遠くからヘアー・ドライアで熱い風を当てると、両方とも、高い温度を表示します。
氷点下は、実験道具がないので測定していません。氷に塩を混ぜた溶液に浸してもなかなか零度になりません。
#include <i2clcd.h>
i2clcd lcd(0x3e, 5);
void setup() {
Serial.begin(9600);
lcd.init_lcd();
}
void loop() {
lcd.lcdclear();
int sensorValueLM35DZ = analogRead(A0);
float voltageLM35DZ = sensorValueLM35DZ * (5.09 / 1023.0);
Serial.println( voltageLM35DZ*100);
lcd.i2cprint( "TLM35="+String(round(voltageLM35DZ*100)) +"'C" );
int sensorValueMCP9700 = analogRead(A1);
float voltageMCP9700 = sensorValueMCP9700 * (5.09 / 1023.0)-0.5;
Serial.println( voltageMCP9700*100);
lcd.lcdcu_set(0, 1);
lcd.i2cprint( "T9700="+String(round(voltageMCP9700*100)) +"'C" );
delay(1000);
}
●ほんとうにこれでよいか
二つの温度を表示しているときに、A0端子からLM35DZのVoutへつないでいるジャンパ線を5Vへつなぎます。特に問題なくMCP9700は温度を表示しています。
A0はLM35DZ出力へ戻して、今度はA1端子からMCP9700のVoutへつないでいるジャンパ線を5Vへつなぎます。LM35DZの温度表示が、低くなったり高くなるという正常でない表示に変わります。
こちらにこの状況を解説されている方がおられます。
一般的なサンプル&ホールド回路では、入力電圧を保持するコンデンサの前にボルテージ・フォロワが入っています。これによって、入力電圧を瞬間充電したコンデンサを使用後に放電します。Arduino UNOで使われているマイコンATMega328Pなどのデータシートによると、等価回路にボルテージ・フォロワが入っていません。したがって、入力側でなんとかして放電しないと、電荷が残ったままになります。たった14pFと小容量ですが、A1で5Vをサンプリングしたときは電荷がたくさん蓄積されます。その次のA0に切り替えたときに、LM35DZが電流を吸い込んで電荷を放電すれば問題はないのですが、そうでないようなのです。
MCP9700の内部等価回路は公開されていません。でも、電流の吸い込みがLM35DZより大きいようです。
対処には、電荷を逃がすためにVoutを数kΩでグラウンドにつなぐ方法があります。MCP9700は不要のようですが、2(2.2)kΩを出力-GND間に入れても表示温度は変わらなかったので、どちらの出力にも入れました。
●まだこれでは解決にならない
ヘアー・ドライアで温風を当てると、LM35DZの温度上昇がのろいです。LM35DZとMCP9700はリード線の並びが同じなので、両方MCP9700に変更し、2kΩは取り去りました。しかし、5Vへどちらかの入力端子をつなぐと、温度が低めになりました。
●LM335Zを使う
LM335Zは、これまでの温度センサと同じく温度係数が直線で扱いやすく、10mV/°Kです。ケルビンの単位なので、摂氏に直すには電圧を求めた後273.15を引きます。LM335Zには1mAを流します。5Vのときは2.2kΩがよいようです。
二つの温度を表示しているときに、A0端子からLM335ZのVoutへつないでいるジャンパ線を5Vへつなぎます。A0はLM335Z出力へ戻して、今度はA1端子からLM335ZのVoutへつないでいるジャンパ線を5Vへつなぎます。どちらも温度表示は変化しません。これは、LM335Zの吸い込み可能電流が大きいためかもしれません。
#include <i2clcd.h>
i2clcd lcd(0x3e, 5);
void setup() {
Serial.begin(9600);
lcd.init_lcd();
}
void loop() {
lcd.lcdclear();
int sensorValueA0 = analogRead(A0);
int sensorValueA1 = analogRead(A1);
float voltageA0 = sensorValueA0 * (5.11 / 1023.0);
float voltageA1 = sensorValueA1 * (5.11 / 1023.0);
float tempA0 = voltageA0 * 100 -273.15;
float tempA1 = voltageA1 * 100 -273.15;
Serial.println( String(tempA0)+" "+String(tempA1));
lcd.lcdcu_set(0, 0);lcd.i2cprint( "A0="+String(round(tempA0)) +"'C" );
lcd.lcdcu_set(0, 1);lcd.i2cprint( "A1="+String(round(tempA1)) +"'C" );
delay(1000);
}
●基準電圧を外部に変更
ここで、もう一つ問題があります。USBの5Vが5.00Vでないことです。おなじPCにつないでも日によって電圧は変化します。A-D変換の基準電圧源がいつも変化するのでは、温度も正しく測れません。いつも変わらない電圧を得るには二つの方法があります。
- DCジャックに7~9Vの電源をつなぐ(※1)。プログラムは変更なし
- 3.3VをA-DコンバータのEXTERNAL入力にする。プログラムに変更あり
2番目の方法を使います。ただし、LM335Zの測定上限は約56℃までに制限されます(※2)。
AREF端子と3.3V端子をジャンパ線で結びます。物理的な変更はこれだけです。スケッチでは、EXTERNAL入力を使うという記述をsetup()に追加し、電圧変換では3.3V端子の電圧をDMMで測って記入します。このボードでは3.30Vでした。
#include <i2clcd.h>
i2clcd lcd(0x3e, 5);
void setup() {
Serial.begin(9600);
lcd.init_lcd();
analogReference(EXTERNAL);
}
void loop() {
lcd.lcdclear();
int sensorValueA0 = analogRead(A0);
int sensorValueA1 = analogRead(A1);
float voltageA0 = sensorValueA0 * (3.3 / 1023.0);
float voltageA1 = sensorValueA1 * (3.3 / 1023.0);
float tempA0 = voltageA0 * 100 -273.15;
float tempA1 = voltageA1 * 100 -273.15;
Serial.println( String(tempA0)+" "+String(tempA1));
lcd.lcdcu_set(0, 0);lcd.i2cprint( "A0="+String(round(tempA0)) +"'C" );
lcd.lcdcu_set(0, 1);lcd.i2cprint( "A1="+String(round(tempA1)) +"'C" );
delay(1000);
}
(※1)Arduino単独で動かすときは当初DCジャックにAC-DCアダプタをつないで使っていました。今は、USB経由で5Vが供給できるので、モバイル・バッテリを使うほうが便利になりました。DCジャックから中にはプラスとマイナスの逆接続時の保護用にダイオードが入っていて、Vin端子に出力されます。その後ろに5V出力の3端子レギュレータがあり、安定な5V出力がマイコンへ供給され、この電圧は5V端子に出ています。
(※2)3.3-2.73=0.57[V]。百倍して57℃。