2021年12月13日月曜日

Arduino(ESP32)でサーミスタを使い温度を取得


背景

サーミスタとは、温度に応じて抵抗が変わる電子部品です。
その特性を利用して抵抗から温度を算出できます。

サーミスタを使う機会があったので、備忘録として具体的な部品と共に利用例を共有します。

使ったもの

  • Arduinoとして動くボードとPCに接続するケーブル
    今回はESP32-devkit-DとmicroUSBを使いました。
  • 上記ボードにプログラムを書き込めるPC
    Arduino IDE + arduino-esp32かVSCode + PlatformIOで開発と書き込みが可能です。
  • サーミスタ
  • 抵抗10kΩ1%
  • ブレッドボード
  • ジャンパワイヤ

回路作成

ESP32の開発ボードの15番ピンをアナログ読取りピンとして、サーミスタの抵抗を読み取る回路を作ります。
サーミスタを3.3Vに、10kΩ抵抗をGNDに接続した上で、サーミスタと抵抗を接続する部分の電位をESP32で読取ります。


組み上げるとこうなりました。


ESP32の開発ボードの3.3Vは穴が隠れてしまうので、裏側から配線しています。
茶色のジャンパワイヤがそれです。


サーミスタから温度を算出する関数

秋月電子QC出版のサーミスタの解説ページで、単位が摂氏(℃)の場合のサーミスタの特性値B、T0、R0とそれに応じた温度Tと抵抗Rの関係は下記の式で表せると解説されています。


この式をTが左辺に来るよう変換します。


ということで、サーミスタの抵抗がRの時のTは下記の式で求めれます。


上記の式をArduinoの関数で記述するとこうなります。
float calcTempratureByResistor(float R, int B, int R0, float T0) {
return 1 / (log(R / R0) / B + (1.0 / (T0 + 273))) - 273;
}

今回利用するサーミスタの必要な定数は、25~85℃におけるB値はデータシートより3431、R0は10kΩなので10000、T0は25℃なので25です。
マクロとして定義して下記のように使います。

#define THERMISTOR_B 3431
#define THERMISTOR_R0 10000
#define THERMISTOR_T0 25

float resistor; // calc from analog read
float temperature = calcTempratureByResistor(resistor, THERMISTOR_B,
THERMISTOR_R0, THERMISTOR_T0);

上記の記述でサーミスタの抵抗値が500から100000(100k)Ωの時の温度と抵抗の関係をESP32で計算してみると、下記の関係になりました。


温度が高くなるほど抵抗が低くなり、温度が低くなると抵抗が高くなります。
今回利用したサーミスタは25℃時に10kΩとしか書かれていませんが、低温や高温時の抵抗値を示しているサーミスタを利用すれば、分圧抵抗(今回は10kΩ)を測定したい温度の抵抗に合わせたものにして抵抗を測定することで、精度良く(1%前後)温度を測定できると思います。
(コメントを頂いたので、説明を変更しました。)

上記の関数を利用して温度を求めます。

プログラム

ESP32開発ボードの15番ピンで電圧を読み取り、それを元に温度を計算してシリアル通信で送信するプログラムです。
#include <Arduino.h>

#define PIN_THERMISTOR 15 // analog read pin

#define THERMISTOR_B 3431
#define THERMISTOR_R0 10000
#define THERMISTOR_T0 25

#define RESISTOR_PULL_DOWN 10000

#ifdef ESP32
#define ANALOG_MAX 4095
#else
#define ANALOG_MAX 1023
#endif

float calcTempratureByResistor(float R, int B, int R0, float T0) {
return 1 / (log(R / R0) / B + (1.0 / (T0 + 273))) - 273;
}

float calcResistorByAnalogValue(uint16_t analog, uint16_t analogMax,
float resistorPullDown) {
return (double)(analogMax - analog) / analog * resistorPullDown;
}

void setup() { Serial.begin(115200); }

void loop() {
uint16_t valAnalog = analogRead(PIN_THERMISTOR);
float resistor =
calcResistorByAnalogValue(valAnalog, ANALOG_MAX, RESISTOR_PULL_DOWN);
float temperature = calcTempratureByResistor(resistor, THERMISTOR_B,
THERMISTOR_R0, THERMISTOR_T0);
Serial.println("Temperature: " + String(temperature) + "C");
Serial.println("at " + String(millis()));
delay(1000);
}

要所を解説します。

ESP32のanalogReadは最大値が4095ですが、その他のArduinoは1023が最大値であることが多いので、マクロで切り替える記述にしています。
#ifdef ESP32
#define ANALOG_MAX 4095
#else
#define ANALOG_MAX 1023
#endif

10kΩのプルダウン抵抗を繋いでいるサーミスタの抵抗値を算出する関数です。
先ほどマクロで定義したANALOG_MAXを利用します。
#define RESISTOR_PULL_DOWN 10000

float calcResistorByAnalogValue(uint16_t analog, uint16_t analogMax,
float resistorPullDown) {
return (double)(analogMax - analog) / analog * resistorPullDown;
}

void loop() {
uint16_t valAnalog = analogRead(PIN_THERMISTOR);
float resistor =
calcResistorByAnalogValue(valAnalog, ANALOG_MAX, RESISTOR_PULL_DOWN);
}

温度を算出する関数を利用して温度を求め、シリアル通信で送信しています。
同じ温度が連続して出力されると表示が切り替わっているかわかりにくいので、駆動時間のmillisも一緒に出力しています。
void loop() {
float temperature = calcTempratureByResistor(resistor, THERMISTOR_B,
THERMISTOR_R0, THERMISTOR_T0);
Serial.println("Temperature: " + String(temperature) + "C");
Serial.println("at " + String(millis()));
}

プログラムができたらESP32に書き込んでください。

動作確認

115200bpsでシリアルモニタを開いて出力を見ます。
成功すればこのように温度が表示されます。
Temperature: 18.48C
at 1367017
Temperature: 18.50C
at 1368017
Temperature: 18.55C
at 1369017

サーミスタが通電していないと最低温度が表示されます。
Temperature: -273.00C
at 1417017
上記のように最低温度が表示される場合は接触不良か回路間違いの可能性があります。
サーミスタを少し抜いて接点を調整したり、回路が間違っていないか確認してください。

自分の部屋で動かしたときは、ブレッドボードのため回路の抵抗が高いのか、1%の抵抗の振れが効いているのか、タニタの温度計より約1℃低い値が算出されました。


終わり

Arduinoでサーミスタを利用して温度を取得できました。

web検索するとTから求めたRの値を配列として持っておき、それを元に抵抗値から温度を推定する解説が多かったのですが、RからTを求める計算でも問題なく(今回は誤差1℃で)求められました。
logやexpの計算を行えないマイコンでサーミスタを利用する場合は上記のように抵抗と温度の配列を事前に持ち、値を推定するのが良いのかもしれません。

参考

サーミスタのある温度における抵抗値の計算方法 秋月電子
サーミスタを使用した温度測定回路 QC出版
サーミスタ(NTC)の使い方 [Arduino]

変更履歴

2021.12.19
「サーミスタは高温時に精度が出ない」と書いていましたが、「分圧抵抗を相応のものに変更すれば高温でも精度を出せる」とコメントをいただいたので、サーミスタの抵抗値と温度のグラフに関する説明をそのように修正しました。

2 件のコメント :

辻田 さんのコメント...

>温度が高くなるほど抵抗が低くなりますが、温度が高くなるほど少ない抵抗値の違いで測定温度が異なるため、80℃を超えるような高温の温度測定にはこのサーミスタは向かないと分かります。
>(そういう場合は熱電対を使うのが良いと思います。)

ちょっとサーミスタの使い方に勘違いがあるかもと思いました。
分圧抵抗R0の値が10kΩ、サーミスタの抵抗RTHも10kΩ(だいだい30℃程度)で等しいすると、測定される電圧は、
VDD * R0 /(R0+RTH) = VDD/2
となります。温度が変化してRTH=1.1R0となった(10%の抵抗変化)とすると、測定される電圧は
VDD/2.1で、だいたい5%ぐらいの変化になります。

温度が高くなってRTH = 0.1R0の条件で同じ計算をすると、測定される電圧は
VDD/1.1がVDD/1.11になるので、約1%の電圧変化になります。

逆に温度が低くなってRTH=10R0の条件で同じ計算をすると、測定される電圧は
VDD/11がVDD/12になるので、やはり約1%の電圧変化になり、測定感度は下がります。

サーミスタで幅広い温度範囲を測るのは厳しいので、測定したい温度範囲のサーミスタの抵抗値と同じ程度の分圧抵抗を選択してあげれば、温度が低い場合も、高い場合も、同程度の電圧感度で測れるかと思います。

Asuki Kono さんのコメント...

助言ありがとうございます。
サーミスタはT0として示されている温度の周辺しか精度が出ないかと思っていましたが、高温と低音も測定器としての特性を維持するのですね。
今回利用したサーミスタは大まかな測定値しかデータシートに示されていませんが、高温や低温の温度と抵抗の関係が示されたサーミスタなら、その関係を元に分圧抵抗を測定したい抵抗値に合わせることで精度良く温度を取得できると認識しました。