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
- 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番ピンSDA、5番ピン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バイトだけが送られてきました。