5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (18) ディジタル温度センサ3 I2C MCP9808
■確度±0.25℃が得られるI2C温度センサ
MCP9808は、データは最小0.0625℃単位で読み出せ、確度は ±0.25℃と細かな温度変化をとらえるのに便利な温度センサです。インターフェースはI2Cです。測定した温度は同じくI2CインターフェースのLCD AQM0802に表示します。
Adafruitのブレークアウト・ボードをスイッチサイエンスで入手しました。
●MC9808の主なスペック
- 電源電圧; 2.7~5.5 V
- 消費電流;200 μA
- インターフェース;I2C(データ転送速度0~400kHz)
- 測定温度範囲;-40~125℃(確度±0.25°C(typ))、-20~100℃(確度±0.5°C(max))
●MCP9808ボードのピン配置
SDAとSCLのプルアップ抵抗は、ラズパイ本体についているので取り去ります。ラズパイとの接続は下記の4本です。
ピン名称 | 備考 |
---|---|
Vdd | +3.3Vへ |
Gnd | グラウンド |
SCL | I2C。10kΩでプルアップ済み |
SDA | I2C。10kΩでプルアップ済み |
Alert | 温度の読み出しでは使わない |
アドレス A0 | 下記のアドレス選択を参照 |
アドレス A1 | 下記のアドレス選択を参照 |
アドレス A2 | 下記のアドレス選択を参照 |
●スレーブのアドレス
A2、A1、A0端子に何もつながないとスレーブ・アドレスは0x18です。変更したいときは、Vddにつなぎます。
アドレス | 値 |
---|---|
A6 | 0 固定 |
A5 | 0 固定 |
A4 | 1 固定 |
A3 | 1 固定 |
A2 | 外部ピンで設定。解放で0 |
A1 | 外部ピンで設定。解放で0 |
A0 | 外部ピンで設定。解放で0 |
変更例です。ここではデフォルトで使いました。
ピン名 | |||||||
---|---|---|---|---|---|---|---|
A2 | Vdd | Vdd | Vdd | - | - | - | Vdd |
A1 | - | Vdd | Vdd | Vdd | Vdd | - | - |
A0 | - | - | Vdd | - | Vdd | Vdd | Vdd |
アドレス | 0x1c | 0x1e | 0x1f | 0x1a | 0x1b | 0x19 | 0x1d |
●温度読み出しレジスタは0x05
最上位にある符号signは、負のとき1、0℃以上では0です。2の補数形式の13ビット(分解能+0.0625°C)でデータが格納されています。13、14、15のビットはアラート情報なので、温度を読み出すときはマスクします。
●接続
AQM0802のソケットのピン配置がI2C バス1の信号位置に合わせています。しかし、GPIOに挿すと、MCP9808が接続できません。回路図ではAQM0802をGPIOに挿すように書きましたが、実際は、ラズパイのGPIOにあるI2C信号の3番ピンSDA、5番ピンSCL、1番ピンの3.3V、6番ピンのGNDをブレッドボードに持ってきて配線しました。
配線が終わってから、ラズパイの電源を入れます。
(※)I2Cの有効化は、1-Wireの説明を参照ください。1-Wireと同じく、I2CもEnableにチェックを入れています。
ターミナルから、i2cdetect -y 1 で配線が正しいかを確認します。LCDの3eとMCP9808の18が見えているはずです。
●プログラム
MCP9808とI2C接続のキャラクタLCD表示器はsmbusライブラリを利用します。変換待ちをするsleep()用にtimeライブラリを使います。
最初に、MCP9808からデータを読み出すところまでのプログラムを作ります。
sign16()関数は2の補数のデータから、符号のついた数字に変換します。
#!/usr/bin/env python
import smbus
i2c = smbus.SMBus(1)
addr=0x18 #MCP9808
def sign16(x):
return ( -(x & 0b1000000000000000) |
(x & 0b0111111111111111) )
#main
data = i2c.read_i2c_block_data(addr, 0x05)
raw = ((data[0] & 0x1f) << 8) | (data[1])
#print (hex(data[0]));print(hex(data[1]));print(hex(raw))
raw_s = sign16(int(hex(raw),16))
temp = raw_s * 0.0625
print (str(temp) +"C")
実行結果です。
LCDに表示する部分を追加します。
LCDcommand()とwriteLCD()はLCD用の関数です。LCDcommand()は、LCDへ直接命令を送信します。writeLCD()は、引数に入っている文字列を表示します。initLCD()はLCDの初期化コードを送信します。
本ブログのプログラム・リストは、表示の関係でTabキーが無視されます。スペース8個に代えてあります。また、リスト中を2回クリックすると、全選択になるので、CTRL-Cでコピーし、テキスト・エディタにCTRL-Vで貼り付けて利用してください。ラズパイに持っていくと、リターン・コードなどが化けていることがあるので、一度消して、ラズパイのテキスト・エディタで改行してください。
プログラムを仮にMCP9808b.pyとすると、sudo chmod 755 MCP9808b.py で実行権を付け、ターミナルから、python MCP9808b.pyで実行します。i2cやSPIのグループにpiユーザが属しているので、sudoは不要です。
#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x18 #MCP9808
addr02=0x3e #lcd
_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()
while 1:
data = i2c.read_i2c_block_data(addr, 0x05)
raw = ((data[0] & 0x1f) << 8) | (data[1])
# print (hex(data[0]));print(hex(data[1]));print(hex(raw))
raw_s = sign16(int(hex(raw),16))
temp = raw_s * 0.0625
print (str(temp) +"C")
LCDcommand(_clear)
writeLCD("T=" + str(temp) )
time.sleep(1)
実行結果です。最上位ビットを1にしたテスト・データを表示しています。