センサ・シリーズ 温湿度①SHT31 その1 測定準備
湿度は相対湿度(Relative Humidity: RH)のことをいい、空気が水蒸気の形で包含できる水分量(飽和水蒸気量)は、温度の関数になっています。したがって、湿度センサは温度も同時に測定します。
使用環境
|
●使用するセンサSHT31のおもなスペック
- 電源電圧;2.4~5.5V
- インターフェース;I2C(最大データ転送速度1MHz)
- 温度;-40~+125℃(確度±0.2℃)、分解能0.015℃
- 相対湿度;0~100%(0~90℃時)、分解能0.01%
- データ・ビット長;16ビット
- スレーブ・アドレス;ADDRピンをプルアップで0x45、GNDへつなぐと0x44
●インターフェースはI2C
I2Cバスでは、データSDAとクロックSCLの二つの信号で、ラズパイ(マスタ)とセンサ(スレーブ)がデータのやり取りをします。バスには、複数のスレーブがつながっていることが前提です。したがって、スレーブを認識するためにスレーブ・アドレスがあります。工場出荷時に固定されているセンサもありますが、多くは、わずかに変更できます。
スレーブ・アドレスは7ビットです。10ビットも規格上ありますが、通常使われません。データのやり取りが8ビット単位なのに7ビットである理由は、LSBがRead/Writeの判別に使われるからです。マスタが何かのコマンドを発するとき、最初はスレーブ・アドレスをバス上に出します。LSBが'0'であれば書き込み、'1'であれば読み出しであることをスレーブに対して知らせます。
すべてのスレーブ・デバイスはバスを常に監視していて、スレーブ・アドレスが自分であれば、8ビットを受け取った直後にACK(Acknowledge)を'1'で返します。これらのデータはSDA信号です。データがある限りクロックSCLも存在します。
ただし、次の三つの状態では、特別な'1'/'0'の組み合わせがおこります。
- これから一連のやり取りが始めるという「スタート・コンディション」
- これでやり取りは終了するという「ストップ・コンディション」
- ちょっと特別な状態で、ストップ・コンディションを作らずに通信を続ける「リピーテッド・スタート・コンディション」
特別な状態がもう一つあります。マスタがスレーブにデータをよこせといってきたので、センサ内部でデータを用意しようとしたけど測定自体に時間がかかるのでちょっと待っていてというのを「クロック・ストレッチ」機能で対応します。SHT31ではこのモードが使われることがあります。
このやり取りを汎用的な書式で表すと次の形になります。
7ビットのスレーブ・アドレス+LSB(Readは'1'、Writeは'0')、8ビット値、8ビット値、... |
pythonのプログラムでは、I2Cのやり取りにはsmbusライブラリを使うのが一般的です。現在のOSには、最初から入っています。smbusライブラリの書式では、上記の書式を記述しやすいような関数名にしています。
READでよく使う関数
1バイトだけ;read_byte_data(アドレス, コマンド) 2バイト以上;read_i2c_block_data(アドレス, コマンド, バイト数) Writeでよく使う関数 1バイトだけ;write_byte_data(アドレス, コマンド、1バイトのデータ) 2バイト以上;write_i2c_block_data(アドレス, コマンド, 複数バイトのデータのlist) |
●市販品
I2Cバスは内部バスなので、マスタとスレーブの距離は長くても10~20cmで使うのが前提です。ブレークアウト・ボードはGPIOピンからの配線に適しています。下記のボードは秋月電子通商で入手しました。
長いケーブルがつながった製品もあります。アマゾンで入手しました。
長いケーブルをつなぐと、外来ノイズの影響を受けやすくなります。キャパシタンスが増加してバスをドライブしきれなくなって波形がなまることもあるでしょう。そのために?、SHT31は、2バイトのデータが健全であるかを判断するためにCRCデータが一緒に送られてきます。
CRCは8ビットのダラス・マキシム(x8 + x5 + x4 + 1)という形式です。
●接続
ケーブルの先はオスだったので、切り取り、メスのジャンパ・ケーブルを取り付けました。
SHT31ケーブル | ラズパイ |
---|---|
赤色 | 3.3V 1番ピン |
黒色 | GND 9番ピン |
青色 | SDA 3番ピン |
黄色 | SCL 5番ピン |
●スレーブ・アドレスを確認
ブレークアウト・ボードならば、ADDRピンをVddもしくはGNDにつなぐことでスレーブ・アドレスを決められる製品があります。そうでない製品もあるので、上記のようにラズパイとつなぎ、ターミナルで確認します。
smbusライブラリもi2ctoolsパッケージに入っています。i2ctoolsのi2cdetectコマンドでバス1を探ります。0x44に見つかりました。
●プログラムの最初に設定する事柄
センサによっては、最初からデータの入ったレジスタを読むだけという使いやすいデバイスもあります。SHT31では、電源が入ったら、パワーオン・リセットがかかり、待機状態になります。待機状態では、測定コマンドを待ちます。
測定コマンドは、単発測定コマンドと周期的連続測定コマンドの2種類があります。単発測定コマンドでは、消費電力を下げる用途に向いています。どちらのコマンドも、
- 繰返し精度設定レベル:低 0.25%RH、0.24℃
- 繰返し精度設定レベル:中 0.15%RH、0.12℃
- 繰返し精度設定レベル:高 0.1%RH、0.06℃
の選択ができます。周期的連続測定コマンドはさらに、測定頻度を0.5、1、2、4 および10(回/秒)から選べます。頻度が高いと発熱するとデータシートに書かれています。
単発測定コマンドでは、クロック・ストレッチの有無が選べます。
設定できるコードをまとめます。
◆単発測定コマンド
設定条件 | コマンド・コード | ||
---|---|---|---|
繰返し精度 レベル |
クロック・ストレッチ 設定 |
上位バイト | 下位バイト |
高 | 有効 | 0x2c | 0x06 |
中 | 0x0d | ||
低 | 0x10 | ||
高 | 無効 | 0x24 | 0x00 |
中 | 0x08 | ||
低 | 0x16 |
◆周期的連続測定コマンド
設定条件 | コマンド・コード | ||
---|---|---|---|
繰返し精度 レベル |
測定頻度[mps] (測定回数/秒) |
上位バイト | 下位バイト |
高 | 0.5 | 0x20 | 0x32 |
中 | 0x24 | ||
低 | 0x2f | ||
高 | 1 | 0x21 | 0x30 |
中 | 0x26 | ||
低 | 0x2d | ||
高 | 2 | 0x22 | 0x36 |
中 | 0x20 | ||
低 | 0x2b | ||
高 | 4 | 0x23 | 0x34 |
中 | 0x22 | ||
低 | 0x29 | ||
高 | 10 | 0x27 | 0x37 |
中 | 0x21 | ||
低 | 0x2a |
念のため、最初にソフト・リセット(0x30、0xA2)を送ります。データシートには、リセットの完了は最大1msかかるとなっているので、その時間待ってから次のコマンドを送ります。
動作確認のためにステータス・レジスタを読み出します。レジスタの内容です。
ビット | 内容 | デフォルト |
---|---|---|
15 | 発信中のアラートの有無 '0':発信中アラートなし '1':ひとつ以上の発信中アラートあり |
1 |
14 | リザーブド | 0 |
13 | ヒータの動作状態 '0':ヒータ停止中 '1':ヒータ稼働中 |
0 |
12 | リザーブド | 0 |
11 | 温度のアラート発信状態 '0':アラート発信なし '1':アラート発信中 |
0 |
10 | 相対湿度のアラート発信状態 '0':アラート発信なし '1':アラート発信中 |
0 |
9~5 | リザーブド | 00000 |
4 | リセット履歴 '0':リセット履歴なし(ステータス・レジスタのデータ消去コマンドをその時点で最後に受信した以降) '1':リセット履歴あり(ハード・リセット、ソフト・リセット・コマンドまたは電源電圧低下によるリセット) |
1 |
3、2 | リザーブド | 00 |
1 | その時点で最後に受取ったコマンドの実行状態 '0':当該コマンドを正常に実行 '1':当該コマンドは未実行(コマンド自体が無効またはコマンドのチェック・サムが異常なため) |
0 |
0 | その時点で最後に受け取った書き込み データのチェック・サム照合結果 '0':当該データのチェック・サムは正常 '1':当該データのチェック・サムは異常 |
0 |
●メインのプログラム
周期的連続測定コマンドを送ります。測定頻度は4回/秒、繰返し精度設定レベル:高の0x23、0x34を書き込みます。過去、エラーなくデータが取得できた実績があります。
このコマンドを送ると、読み出しの要求をしなくとも連続でデータを送ってきます。そしてこのコマンドは任意に変更できるので、次のコマンドは測定完了時間分待ってから送ります。データシートから最大15msです。
周期的連続測定モードでの測定値の読み出しコマンドは0xe0、0x00です。最初に、このコマンドを送らずにデータを読み取ります。
smbusライブラリには2バイトのコマンドを書き込む関数はありません。「アドレス、コマンド、データ」を書き込む関数write_byte_data()を流用します。
読み取りは「アドレス、コマンド、読み取り数」です。しかし、このときコマンド1バイトは何を送ればいいのか不明です。下記のプログラムでは0xffを送りましたが、0x00でも結果は変わりませんでした。
読み出した16ビットのデータは、測定データを、温度(摂氏)を求める換算式は次のとおりです。rawT は測定値です。
temp = -45 + 175 * rawT / 2^16 - 1 |
相対湿度の換算式は次のとおりです。rawR は測定値です。
RH = 100 * rawR / 2^16 - 1 |
import smbus import time i2c = smbus.SMBus(1) addr = 0x44 #main i2c.write_byte_data(addr, 0x30, 0xa2) #i2c.write_byte_data(addr, 0x30, 0x41) time.sleep(0.1) i2c.write_byte_data(addr, 0xf3, 0x2d) statusMsb,statusLsb,statuscrc = i2c.read_i2c_block_data(addr,0xff,3) print(' status= {0:0>8b} {0:0>8b} {0:0>8b}'.format(statusMsb, statusLsb, statuscrc) ) i2c.write_byte_data(addr, 0x23, 0x34) time.sleep(0.015) while 1: #i2c.write_byte_data(addr,0xe0,0x0) dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(addr,0xff,6) rawT = dataTmsb << 8 | dataTlsb rawR = dataHmsb << 8 | dataHlsb print (' tempData= {0:0>16b} 0x{1:x} HumidityData= {2:0>16b} 0x{3:x}'.format(rawT,rawT,rawR,rawR) ) temp = -45 + rawT * 175 / 65535 RH = 100 * rawR / 65535 print(' temp= {0:.1f} `C, humi= {1:.1f} RH%'.format(temp, RH)) time.sleep(3) print ("---") |
実行結果です。ステータスはうまく読み出せていないようですが、温度と湿度は正常に読めているようです。ソフト・リセットは何度も繰り返すとI/Oエラーになりました。