PIC AVR 工作室別館 arduinoの館->TopPage->接続くん->温度センサーとの接続

温度センサーとの接続

arduinoに温度センサーを繋いで、arduinoのプログラム(スケッチ)上で温度を測定できるようにしてみたいと思います。

半導体温度センサーについて

一般に半導体は光の当たり方や温度の高い低いといった「条件の変化」によって特性も変化します。 この特性の変化を積極的に利用し、温度と言う物理的変化を電気的な信号に変換してくれるのが温度センサーです。

実際に温度センサーに関することを本やネットで色々探してみると、ごく普通のシリコンダイオードを温度センサーとして使用している例も見られます。 このシリコンダイオードは一般に単価が10円以下で済んでしまう部品なのでとても安上がりなのですが、 温度と電気信号の対応関係が非線形となっているため換算が面倒だったり、測定の精度が低かったり…と、あまり簡単に扱うことができないのが難点です。

で、一般には温度による物理変化をマイコン側のAD変換器で簡単に取り込めるようにセンサーや変換回路を一体化し、 容易に扱える「電圧情報」に変換してくれるICが各社から販売されています。電圧ならADコンバーターで読み取れますからね。

このページでは、その中でも比較的手に入り安そうな3つの温度センサーICをarduinoに接続してみます。 秋月電子で買ってきた「S8100B」「LM35」「LM60」を材料にしてみました。一般的な気温測定程度ならこの程度で用が足りるでしょう。

温度センサー…熱電対について

これらの他に温度センサーというと、熱電対のように「広い温度範囲」を扱うことが出来るものもあります。 熱電対なら1000度を超える温度も計測が可能です。

が、熱電対が出力する情報は「極めて微弱な電圧変化」なので、熱電対をarduinoやマイコン類に直接接続することは出来ません。 また、熱電対の出力する電圧と温度の関係は完全な直線ではない(電圧と温度が完全な比例関係ではない)ので、 補正計算(リニアライズ)が必要となりちょっと面倒です。

なので一般に熱電対を使用する場合は各社から出ている熱伝対用のICを使うことが多いと思います。

(それらのICはADコンバーターで読める電圧域に増幅したり、リニアライズ処理を掛けたりしてくれます。 さらにはICによっては直接デジタル信号(I2Cなど)を出力するICもあります。ただし、いずれもこの手のICは1個数千円と結構高いです)

一方、微弱とはいえ電圧変化ですから、オペアンプで増幅してしまえばADコンバーターで取り込むことも理論上は可能です。 ただし極めて微弱な電圧のためかなりかなり大きな増幅を掛ける必要があります。そのため「オフセット」が極めて小さいオペアンプでなければ、 信号を増幅しているのか誤差を増幅しているのかがわからない状態になってしまいます。OPA277のような超々低オフセットな オペアンプが必要不可欠です。しかも出力電圧と温度が完全な比例関係になっていないので、補正計算が必要となってしまい面倒です。

比較的ポピュラーで扱いも簡単な例として、K型熱電対用のICで有名な「AD597」(アナデバ製) の名前だけ挙げて置くこととし、熱電対についてはこのページでは対象外とします。

arduinoと温度センサーの接続

というわけで、今回使用する温度センサーは扱いが容易な「IC化された温度センサー」を使うこととし、 arduinoに登載されたADコンバーター(アナログ入力機能)をつかって温度情報を電圧値として読み込む方針で行きたいと思います。 さっきと同じ図。この接続方法です。

LM35、LM60、S8100Bといった温度センサーは共に、3つの端子のうち2つが電源で、残り一つが電圧出力になっています。 この電圧出力の端子をarduinoのアナログ入力端子(ADC入力端子)に繋げば、温度に見合う「電圧値」として読み込むことが出来ます。

arduinoにしてもその他のマイコンにしても、大抵のADC入力回路の内部はサンプルアンドホールド回路(一種のコンデンサ)と、 基準となる電圧の生成回路、および電圧比較のためのコンパレータで構成されています。 コンパレータの入力部分は大抵FETトランジスタのゲート端子なので、これも一種のコンデンサ的な容量を持っています。

ADC内部に有る「比較電圧」の生成は、順次電圧を高低させながらコンパレータで比較することで入力電圧を調べるという動作をします。 ある意味、検索処理(バイナリサーチ)と同じことをしています。その際に電圧を高低させながら比較していくのに時間を要するのですが、 その時間の算出方法についてはデータシートに詳しく書かれているので省略します。

一方、上にあげたコンデンサの成分が高速度でサンプリングを行う際に精度に影響を及ぼすことがあります。 コンデンサへのチャージには微小ですが時間が必要です。特にADCに繋ぐ機器の出力インピーダンスが大きい場合、 チャージされるまでに時間を要します。一般には、ADCで精度を確保するためには一定の変換時間を確保しなければなりません。

とはいえ、今回のような温度センサーの場合はせいぜい1秒間に数回~数十回程度ですから、チャージには充分過ぎる時間が確保できるでしょう。 ADCに接続する機器の出力インピーダンスと変換時間の関係については各データシートに記載されていると思うので、 今回のS8100Bのような出力インピーダンスの大きい機器の場合、1回の変換にどの程度の時間を掛ける必要があるか調べることができます。

このことを踏まえ、ある程度の遅さでサンプリングを行えばインピーダンスの高い機器でも接続は可能ということになります。 S8100Bの出力インピーダンスは1MΩ前後と大きいのですが、それでも1秒に数回程度のサンプリングなら気にする必要は無いでしょう。 LM35やLM60は、arduinoなどの内蔵ADCなら考慮不要な程度の出力インピーダンスなので、 arduinoの最大サンプリング周波数でも気にする必要はないでしょう。

LM35を繋いで見る

今回挙げた3つの温度センサーICのうち、一番計算式がシンプルで済むのはこのLM35でしょう。 0℃の時に0Vが出力され、その後1℃上昇するごとに10mVずつ上昇するというICです。 ADCから入力した値に適当な値を掛け算するだけ…つまり単純な比例計算で換算が可能というわけです。

LM35のピン配置はこれ。

V+はarduinoのVccと共用、GNDも同様。VoutをADCに繋ぎます。ノイズ対策の為にVoutとGNDの間にパスコンが必要です。 0.1uFのセラコンなどでよいでしょう。

arduinoのADCは10ビット精度(0~1023の1024段階)で取り込みが可能ですが、 その時の基準電圧は幾つかの中から選択することができます。arduinoの場合は一般に5Vか1.1Vのどちらかを使用することになります。 (3.3V動作のarduinoでは、適宜読み替えて読み進めてください)

5V基準ならおよそ0~5Vの間で、1.1V基準ならおよそ0~1.1Vの間の信号を扱うことが可能です。 (なぜ「およそ」と断っているのかはデータシートをご覧ください)

また5Vを使用する場合は5V÷1024≒4.88mV単位で取り込むことが出来、1.1Vを使用する場合は 1.1V÷1024≒1.07mV単位で取り込むことが可能です。

それぞれ入力する信号の電圧域や、どの程度の精度が必要かという要件によって選択すればokです。 LM35を使って室温を計る範囲であれば、1.1Vを基準にしたほうがよさそうです。なので1.1Vを用いることにします。

ではスケッチを。reduino-nano用に組んだので、LEDが3番に繋がっている前提です…適宜13番などに変更してください。 あと、1箇所不等号を全角に変更してありますので半角に戻して使ってください。

int ledPin = 3;
int analogpin0 = 0;

int data = 0;
int summary = 0;
int i;

double t_value;
double input_value;

void setup() {
  analogReference(INTERNAL);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);

  Serial.println("start");
}


void loop() {

  digitalWrite(ledPin, HIGH);
  delay(1800);

  summary = 0;
  for(i=0;i<50;i++){
    data = analogRead(analogpin0);
    summary = summary + data;
    delay(2);
  }

  input_value = (double)summary/50;
  Serial.println(input_value);
  
  t_value = input_value * 1.1 / 1024.0 * 100.0;
  Serial.println(t_value);

  digitalWrite(ledPin, LOW);

  delay(100);
}

そういえば、arduino0013からprintクラスでfloatも扱えるようになったので、小数点以下の数値も直接printクラスで表示するように一部変えました。

実行すると、「start」の文字を表示した後に2秒毎にADCの取り込んだ数値(50回取り込みの平均値)と、温度換算の値を表示します。

変換式は…説明するほどのもんじゃないですね。0℃の時に0Vで1℃あたり10mVとシンプルですから。 で、実際に動かしてみると…ウチの環境では他の温度計と比べると1℃ほど高めに表示されるみたい。誤差についてはあとで考えることにします。

LM60を繋いでみる

次はLM60です。氷点下も計れますが、計算式の数値がちょっと細かくなります。ICのピン配置もLM35と一緒なので、戸惑うことはないでしょう。 出力が線形となるように補正されているので一次式で表せます。扱いはやっぱり難しくはありません。

LM60はデータシートに因ると、-40℃から125℃の範囲で温度を検出できるセンサと書かれています。ノーマルで氷点下も計測できます。

LM35と同じように温度の変化が電圧の変化となって出力されるセンサーICですが、温度と電圧の関係は少々ことなります。

1℃上昇するごとに6.25mV出力電圧が上昇し、0℃の時に424mVが出力されることになっています。 このあたりはデータシートに細かく書かれています。グラフにするとこんな感じ。(X軸Y軸を逆に取った方が解りやすかったかな?)

-40℃で174mV、125℃で1205mVが出力されるみたいです。微妙に直線に載ってない感じもありつつ、 小数点以下の誤差を除けば一応直線と考えて問題ない程度だと思うのでこのまま強行します。

このグラフを元にスケッチを書いてみます。例によって不等号を全角に替えてあるので戻してから使ってください。 あと、今回もADCの参照電圧は内部1.1V(=1100mV)を使うことにしました。これだと125℃まで計れないんですが、 計る人間が先にくたばってしまうので、125℃は諦めることにします。

int ledPin = 3;
int analogpin0 = 0;

double min = - 424.0 / 6.25;

int data = 0;
int summary = 0;int i;

double t_value;

void setup() {
  analogReference(INTERNAL);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);

  Serial.println("start");
  Serial.println(min);
}

void loop() {

  digitalWrite(ledPin, HIGH);
  delay(1800);

  summary = 0;
  for(i=0;i<50;i++){
    data = analogRead(analogpin0);
    summary = summary + data;
    delay(2);
  }

  Serial.println((double)summary/50.0);
  t_value = ((double)summary/50.0 /1024.0 * 1100.0 / 6.25) + min;
  
  Serial.println(t_value);

  digitalWrite(ledPin, LOW);

  delay(100);
}

実行すると、「start」の文字とADC=0の時の仮想温度を表示し、その後は2秒毎にADCの取り込んだ数値(50回取り込みの平均値) と温度換算の値を表示します。

実際に動かしてみたら部屋にある温度計とほぼ同じ値を指しました。いずれにしてもICには製品誤差は付き物の様です。

…  続きます  …

その他はひとまずブログの記事をご覧ください。