e-DIY
センサー

Raspberry Pi用温度センサーをサーミスタとADCを使って自作

サーミスタとADC

サーミスタとADCを使って温度センサーを自作してみたいと思います。

「温度センサーなんて安く買えるやん!」なんて言わないで…

やってみるとセンサーの仕組みが分かって面白いですよ!

わざわざセンサーを自作するなんて面倒!という方はこちらの記事をどうぞ。

DHT11はライブラリがGitHub上で公開されているので簡単にできてしまいます。

サーミスタとは

サーミスタとは、温度が変わると抵抗値が変わる電子部品です。

サーミスタ

このように温度が上がると抵抗値は下がります。

サーミスタの温度特性

ですので、サーミスタの抵抗値が分かれば温度が分かります。

ADCとは

ADCとはAnalog Digital Converterの略で、アナログ電圧をデジタルデータに変換する回路です。
ADコンバータともいいます。

今回使用したADS7830は8bitのADCなので、255分割してデジタル信号に変換します。
基準電圧を3.3Vとすると、3.3V / 255 = 12.94mVのステップで電圧を測定することになります。

ADコンバータの分解能

この最小ステップを分解能といい、1LSB(Least Significant Bit)で表します。

ADS7830の概要

ADS7830は12Cインターフェースを備えた8bitのADコンバータです。
CH0~CH7までの8端子の測定端子を持っています。

ADS7830の端子配置図

内部基準電圧は2.5Vで、外部の基準電圧に切り替えることもできます。

ADS7830の使い方

はじめにマスター側(今回はラズパイ)からADS7830のアドレスを送信します。
アドレス・バイトの構成はこのようになっています。

ADS7830のアドレスバイト

引用:ADS7830データシートより

最後のR/Wは省略できるので、7bitでアドレスを指定します。

最初の5bitの初期値は10010です。
次の2bitはA0、A1端子をGNDに接続するか、VDDに接続するかで決まります。

A1 A0 アドレス
GND GND 00
GND VDD 01
VDD GND 10
VDD VDD 11

この4通りがあるので、ADS7830は同じマスターに4台までスレーブ接続できます。

よって、A1、A0共にGNDに接続した場合のアドレスは、1001000になります。
アドレスが一致していればスレーブ側からACK(受信確認)が送信されます。

次にコマンド・バイトを送って動作モードを決めます。
コマンド・バイトの構成はこのようになっています。

ADS7830のコマンドバイト

引用:ADS7830データシートより

SDは差動入力かシングルエンド入力かを決めます。
0が差動入力、1がシングルエンド入力です。

C2~C0で入力チャンネルを選択します。
3bitでCH0~CH7の8端子に対応しています。

PD1、PD0でパワーダウンを選択します。

PD1 PD0 説明
0 0 各AD変換の間のパワーダウン
0 1 内部リファレンス・オフ、およびADコンバータ・オン
1 0 内部リファレンス・オン、およびADコンバータ・オフ
1 1 内部リファレンス・オン、およびADコンバータ・オン

Xは未使用です。

シングルエンド入力、CH0を使用、内部リファレンス・オフ、ADコンバータ・オンとすると、コマンド・バイトは10000100となります。

温度測定回路

サーミスタとADCを使った温度センサー回路です。

温度測定回路
ラズパイ温度センサー回路

3.3Vを10kΩの抵抗とサーミスタで抵抗分割して、その中点の電圧をADCで読みます。
中点の電圧:\(V_{CH0}\)は、

\[
V_{CH0} = \frac{R_{t}}{R_{1}+R_{t}}V_{DD}
\]

です。
サーミスタの抵抗値:\(R_{t}\)は温度によって変化します。

サーミスタはスターターキットに入っていたものを使っているので型番は分かりませんが、計算式は次のように記載されていました。
\[
R_{t} = R_{0} \times \exp(B(\frac{1}{T_{2}}-\frac{1}{T_{1}}))
\]

\(R_{0}\)は25℃におけるサーミスタの抵抗値で、使用したサーミスタでは10kΩ。
\(B\)はサーミスタの温度係数で3950。
\(T_{2}\)は現在のケルビン温度。セルシウス温度に275.15℃を足した値です。

ADCで\(V_{CH0}\)をモニタすることで\(R_{t}\)を計算することができます。

\[
R_{t} = \frac{V_{CH0}}{V_{DD}-V_{CH0}}R_{1}
\]

\(R_{t}\)が分かれば現在の温度\(T_{2}\)を求めることができます。

\[
T_{2} = \frac{1}{\frac{1}{T_{1}}+\frac{\ln \frac{R_{t}}{R_{0}}}{B}}
\]

この一連の計算をPythonでプログラムしていきます。

Pythonプログラム

こちらが今回作成したPythonのプログラムです。

import RPi.GPIO as GPIO
import time
import math # Mathモジュール
import smbus # SMBus用モジュール

class ADCDevice: 
    def __init__(self):           # コンストラクタ。初期化用の関数。
        self.cmd = 0
        self.address = 0
        self.bus=smbus.SMBus(1)   # SMBusの引数に1を指定する。ラズパイのI2Cバスの番号

class ADS7830(ADCDevice):                   # ADCDeviceクラスを継承
    def __init__(self):
        super(ADS7830, self).__init__()
        self.address = 0x48                 # ADS7830の初期アドレス(A1,A0端子を共にGND接続)  
        self.cmd = 0x84                     # コマンドバイト

        
    def analogRead(self):
        value = self.bus.read_byte_data(self.address, self.cmd)
        return value

    def close(self):
        self.bus.close()

adc = ADS7830()

def setup():
    global adc
    
def loop():
    while True:
        value = adc.analogRead()                              # 指定したピンのAD値を読む
        voltage = value / 255.0 * 3.3                         # ADC値を電圧に変換
        Rt = 10 * voltage / (3.3 - voltage)                   # サーミスタの抵抗値を計算
        tempK = 1/(1/(273.15 + 25) + math.log(Rt/10)/3950.0)  # ケルビン温度を計算
        tempC = tempK -273.15                                 # セルシウス温度に変換
        print ('ADC Value : %d, Voltage : %.2f, Temperature : %.2f'%(value,voltage,tempC))
        time.sleep(0.1)

def destroy():
    adc.close()
    GPIO.cleanup()
    
if __name__ == '__main__':
    print ('Program is starting ... ')
    setup()
    try:
        loop()
    except KeyboardInterrupt:
        destroy()

初期アドレスとコマンド・バイト

class ADS7830(ADCDevice):                   # ADCDeviceクラスを継承
    def __init__(self):
        super(ADS7830, self).__init__()
        self.address = 0x48                 # ADS7830の初期アドレス(A1,A0端子を共にGND接続)  
        self.cmd = 0x84                     # コマンドバイト

今回はA1ピンとA0ピンをGNDに接続しているので、初期アドレスは1001000です。
16進数で書くと0x48です。
アドレスは7bitなので、0x48(01001000)の2bit目からがアドレスとなります。

コマンド・バイトは先述の通り、シングルエンド入力、CH0を使用、内部リファレンス・オフ、ADコンバータ・オンとすると、10000100となります。
16進数で書くと0x84です。

温度の計算

def loop():
    while True:
        value = adc.analogRead()                              # 指定したピンのAD値を読む
        voltage = value / 255.0 * 3.3                         # ADC値を電圧に変換
        Rt = 10 * voltage / (3.3 - voltage)                   # サーミスタの抵抗値を計算
        tempK = 1/(1/(273.15 + 25) + math.log(Rt/10)/3950.0)  # ケルビン温度を計算
        tempC = tempK -273.15                                 # セルシウス温度に変換
        print ('ADC Value : %d, Voltage : %.2f, Temperature : %.2f'%(value,voltage,tempC))
        time.sleep(0.1)

先述の、サーミスタの抵抗値から温度を算出する計算式を使って温度を計算させています。

面倒ですが、アナログ値を計算するために取得したADC値を一旦アナログ値に再変換する必要があります。
そこからサーミスタの抵抗値を計算し、温度を算出するという流れです。

温度測定結果

プログラムを走らせるとこのように、ADC値、電圧、温度が表示されました。

センサー取得温度表示

サーミスタを指などで温めると温度が変化していきます。
グラフに時間経過で温度がどのように変化するかを表示されることもできます。

グラフに温度をリアルタイム表示

やり方はこちらの記事で詳しく説明していますので参考にしてみてください。

関連キーワード

フリーワード検索