5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (19) ディジタル温度センサ4 I2C ADT7410

分解能が0.0078℃と高い温度センサ

 アナログデバイセズのADT7410は、I2Cインターフェースの温度センサです。同じスペックでSPIインターフェースをもつADT7310があります。I2Cなので、ラズパイとの配線は20cm程度が限界ですが、スレーブ・アドレスを複数設定できるので、複数の温度を同時に測定するのに適しています。

 いずれも、秋月電子通商でブレッドボードで利用できる小型の基板に実装したモジュールが入手できます。

 読み出した温度は、同じくI2CインターフェースのキャラクタLCD表示器AQM0802に表示します。

ADT7410のスペック

  • 電源電圧;2.7~5.5V
  • 消費電流;210uA
  • 温度の確度;±0.4℃(-40~105℃、3.0V時)
  • 測定温度;-55~150℃
  • 温度の分解能;0.0078℃
  • 温度変換時間;240ms
  • I2Cデータ転送速度;0~400kHz

ADT7410モジュールの構成

 基板上にA0とA1のスレーブ・アドレスを設定するジャンパ・パターンが二つ、SDAとSCL信号を10kΩでプルアップするジャンパ・パターンが二つあります。ラズパイは本体側にプルアップ抵抗が入っているので、ジャンパ・パターンはそのままで使います。

 スレーブ・アドレスを設定するジャンパ・パターンは下記の接続です。もともと10kΩでプルダウンされているので、オープンであればスレーブ・アドレスは0x48です。4通り設定できるので、複数のセンサを同時に利用できます。

 J3(A0)をショートしたので、スレーブ・アドレスは0x49になります。


スレーブ・アドレス ジャンパ・パターン
内部固定 A1 A0 16進 J4設定 J3設定
10010    0 0 0x48  オープン  オープン
0 1 0x49  オープン  はんだでショート
1 0 0x4A    はんだでショート  オープン
1 1 0x4B    はんだでショート   はんだでショート

  1;High=3.3V

コンフィギュレーション・レジスタ

 コンフィギュレーション・レジスタのアドレスは0x03です。電源が入った直後のレジスタの内容は0x00が設定されています。7ビット目がビット数の設定です。読み出すデータは、13ビットと16ビットの2通りが設定できます。デフォルトは13ビットです。16ビットで読み出したいときは、プログラムの最初でデフォルトの13ビットから16ビットに変更するため、0x80を書き込みます。

bit7 6 5 4 3 2 1 0

0=13ビット

1=16ビット

オペレーション・モード INT/CTモード INT極性 CT極性 フォルト・キュー

読み出した温度データの形式

 最上位ビットが符号の2の補数形式です。温度の入っているレジスタは0x00と0x01です。上位バイトの0x00を読み出すと、自動的にアドレスはインクリメントされ、下位バイトの0x01を読み出します。

 13ビットのときは、左詰めでデータが入っている(ビット15からビット3)ので、2バイトで読み出した後に右に3ビット・シフトします。

上位バイト 下位バイト
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 x x x

接続

 AQM0802のソケットのピン配置は、I2Cの信号に合わせています。しかし、GPIOピンに挿すと、ADT7410が接続できません。回路図ではAQM0802をGPIOに挿すように書きましたが、実際は、ラズパイのGPIOにあるI2C信号の3番ピンSDA5番ピンSCL、1番ピンの3.3V、6番ピンのGNDをブレッドボードにもってきて配線しました。

 配線図には書いていませんが、ブレッドボード上の電源ラインには0.1uFの積層セラミック・コンデンサ、22uFの電解コンデンサを入れています。なくても動作すると思いますが、入っていると安定な動作が望めます。

 配線が終わってからラズパイの電源を入れます。

(※)I2Cの有効化は、1-Wireの説明を参照ください。1-Wireと同じく、I2CもEnableにチェックを入れています。

 ターミナルからi2cdetect -y 1 で配線が正しいかを確認します。ADT7410が49、LCDが3eです。

プログラム

 2017年9月6日現在のOSのバージョンです。

uname -a
4.9.41
python -V
2.7.13
i2cdetect -V
3.1.2

 最初に、13ビットで読み出しを行うプログラムを作ります。

 コンフィギュレーション・レジスタには何も書きこまず、デフォルトのままで、読み出しだけを行います。2バイト読み出したら、13ビット分が左詰めになっているので、3ビット右にシフトします。この値に0.0625を乗じると、温度になります。

 read_i2c_block_data()の2番目の引数はコマンドです。ADT7410ではアドレス・ポインタ・レジスタの値を入れます。温度が入っているレジスタは0x00ですから'0'を渡します。その後に、何バイト読み込むという引数を指定しますが、ここでは省略しました。詳細はコラムを参照してください。

 読み出したデータはリスト形式で得られます。

#!/usr/bin/env python
import smbus
i2c = smbus.SMBus(1)
addr=0x49 #ADT7401
def sign13(x):
       return ( -(x & 0b1000000000000) |
               (x & 0b0111111111111) )
#main
data = i2c.read_i2c_block_data(addr, 0)
raw = (((data[0]) << 8) | (data[1]) ) >> 3
#print (hex(data[0]));print(hex(data[1]));print(hex(raw));print(bin(raw))
raw_s = sign13(int(hex(raw),16))
temp = raw_s * 0.0625
print (str(temp) +"C")

 次に、16ビットに設定して読み出すプログラムを作ります。

 コンフィギュレーション・レジスタ(0x03)を設定します。書き込むデータは最上位ビットが1なので、0x80です。

#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x49 #ADT7401
config = 0x03
def sign16(x):
       return ( -(x & 0b1000000000000000) |
                (x & 0b0111111111111111) )
#main
i2c.write_byte_data(addr,config,0x80)
data = i2c.read_i2c_block_data(addr, 0,2)
raw = ((data[0]) << 8) | (data[1])
#print (hex(data[0]));print(hex(data[1]));print(hex(raw));print(bin(raw))
raw_s = sign16(int(hex(raw),16))
temp = raw_s * 0.0078
print (str(temp) +"C")

 読み出した温度をLCD AQM0802に表示します。

 LCDcommand()とwriteLCD()はLCD用の関数です。LCDcommand()は、LCDへ直接命令を送信します。writeLCD()は、引数に入っている文字列を表示します。initLCD()はLCDの初期化コードを送信します。

 プログラム・リストは、表示の関係でTabキーが無視されます。スペース8個に代えてあります。また、リスト中を2回クリックすると、全選択になるので、CTRL-Cでコピーし、テキスト・エディタにCTRL-Vで貼り付けて利用してください。ラズパイに持っていくと、リターン・コードなどが化けていることがあるので、一度消して、ラズパイのテキスト・エディタで改行してください。

 プログラムを仮にadt7410.pyとすると、sudo chmod 755 adt7410.py で実行権を付け、ターミナルから、python adt7410.pyで実行します。i2cやSPIのグループにpiユーザが属しているので、sudoは不要です。

#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x49 #ADT7401
addr02=0x3e #lcd
configR = 0x03
_command=0x00;_data=0x40;_clear=0x01;_home=0x02;display_On=0x0f;LCD_2ndline=0x40+0x80

#LCD AQM0802
def LCDcommand( code ):
i2c.write_byte_data(addr02, _command, code)
time.sleep(0.2)

def writeLCD( message ):
mojilist=[]
for moji in message:
mojilist.append(ord(moji))
i2c.write_i2c_block_data(addr02, _data, mojilist)
time.sleep(0.2)

def initLCD():
LCDcommand(0x38);LCDcommand(0x39);LCDcommand(0x14);LCDcommand(0x73);LCDcommand(0x56);LCDcommand(0x6c);LCDcommand(0x38);LCDcommand(_clear);LCDcommand(display_On)

def sign16(x):
return ( -(x & 0b1000000000000000) |
(x & 0b0111111111111111) )

#main
initLCD()
i2c.write_byte_data(addr,configR,0x80)

while 1:
data = i2c.read_i2c_block_data(addr,0,2)
raw = ((data[0]) << 8) | (data[1])
# print (hex(data[0]));print(hex(data[1]));print(hex(raw));print(bin(raw))
raw_s = sign16(int(hex(raw),16))
temp = raw_s * 0.0078
print (str(temp) +"C")
LCDcommand(_clear)
writeLCD("T" + str(temp) )
time.sleep(1)

 実行例です。読み出した小数点第4位まで表示しました。確度は±0.4℃ですが、データシートのグラフを見ると、もっと良いようです。

コラム read_i2c_block_data(int addr,char cmd,byte)

 I2Cのスレーブ・デバイスが送り返してくるデータを読み取るのが、read_i2c_block_data()です。

引数は スレーブ・デバイスのアドレスコマンド受け取るバイト数です。

ここで、受け取るバイト数を指定していないとどうなるでしょうか。

read_i2c_block_data(0x49, 0)

 読み出しデータは、レジスタ0x00、0x01の温度データに続き、0x02のステータス、0x03のConfigurationなどを読み出して送ってきます。

 次に、2バイトを読み出すことを明記します。

read_i2c_block_data(0x49, 0,2)

 2バイトだけが送られてきました。