ラズパイにセンサーを取り付けて温度・湿度を計測し、その結果をメール送信するプログラムを作成しました。
- 準備(温湿度センサー・はんだごての購入)
- はんだづけ
- 回路を繋ぐ
- プログラム作成Ⅰ(smbus)
- プログラム作成Ⅱ(smtplib)
- cronの設定
今回は
- 前回行ったはんだ付け及び回路作成は成功したか確認
- 4. プログラム作成(主にsmbusモジュールのコーディング)
です。
はんだ付け・回路作成の確認を行いつつ、そこから得られるI²Cチャンネル及びI²Cアドレスの値を使用し、smbusモジュールでプログラム作成を進めます。
前回の投稿はこちらから
はんだ付け・回路作成が成功したか確認
確認の手順
それぞれコマンド i2cdetect で調べていきます。オプションを変更することで以下の働きをします。
- I²Cチャンネルを確認
- I²Cアドレスを確認
ラズパイで使用できるI²Cチャンネルを調べ、温湿度センサーSHT31接続時にアドレス0x44が出力されれば、はんだ付け・回路作成は成功、と言えるはずです。
また、I²Cチャンネル及びI²Cアドレスの値はsmbusモジュールのコーディングに必要となるので、プログラム作成に関わる重要な部分だと思います。
コマンド i2cdetect の扱いに関してはこちらが詳しいです。
- 『これ一冊でできる!ラズベリーパイ超入門』
- 『RasberryPi活用』様
- 『バージョン管理された人』様:『I2C Toolsの簡単な使い方について』
わたしも書籍、こちらの方々の記事を参考にして作業を進めました。ありがとうございました!
I²Cチャンネルの確認
まずはI²Cチャンネルを調べます。
コマンド i2cdetect をオプション -l(エル) で実行すると、そのデバイスで使用できるI²Cチャンネルが一覧表示されます。
ラズパイで実行してみます。
$ sudo i2cdetect -l
すると
i2c-1 i2c
と表示されました。
単純に考えてはいけないのかもしれませんが、I²Cチャンネルとは「i2c-〇」の数値〇を指すようです。この場合、「i2c-1」を意味する「1」ということですね。
わたしの環境では、チャンネルは「1」のようです。
I²Cチャンネルについては、『これ一冊でできる!ラズベリーパイ超入門』の212頁NOTEが詳しいです。設定ファイルの書き換えによって使用するチャンネルを変更できるようです。
I²Cアドレスの確認
ラズパイのI²Cチャンネルがわかりました。続いてI²Cアドレスが表示されるか試していきます。
I²Cデバイス(SHT31)接続時、同じくコマンド i2cdetect の引数にチャンネルを指定して実行すると、I²Cアドレスが表示されます。
I²Cアドレスが表示できれば、I²Cデバイスが動作している、つまり、はんだ付け・回路に不具合がない、と言えるはずです。
引数「1」でコマンドを実行します。
$ sudo i2cdetect 1
するとこんな警告が出ます。
WARNING! This program can confuse your I2C bus, cause data loss and worse!
電子パーツによっては i2cdetect を実行すると制御不能になるケースもあるんだそうです。
yキーを押して実行します。はんだブリッジが成功していればアドレス0x44が表示されるはずです。
Continue? [Y/n] y
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
表示されました!
プログラム作成Ⅰ
コーディングの手順
- SMBusインスタンスの作成
- 単発測定コマンドの送信
- 測定値の読み出し
- ビット演算
- 測定値の換算
わたしが実際にコーディングをした際には、『初心者のためのセンサと測定入門』様『高精度温湿度センサー SHT31-DISの使い方』を参考にさせていただきました。とてもわかりやすいです。ありがとうございました!
SMBusインスタンスの作成
I²Cチャンネルを指定し、SMBusインスタンスを作成します。
『i2c python documentation』では下記のサンプルコードが記載されています。
b = SMBus(0) # 0 indicates /dev/i2c-0
i2c python documentation
インスタンス作成時の引数の根拠についてのコメントですね。引数の「0」は /dev/i2c-0 の「0」、ということだと思います。
では、わたしの環境でこの dev 以下に i2c-0 は存在するのでしょうか?
ラズパイのディレクトリを確認すると、この dev 以下に「i2c」を名前に含むファイルは「i2c-1」しか存在しないようです。おそらく、わたしの環境では、使用できる引数は「1」ですね。
先程、コマンド i2cdetect で出力された内容は
i2c-1 i2c
でした。これは、I2Cチャンネルの一覧、そしておそらくは、環境に存在する「i2c-〇」ファイル名一覧、だったようです。
チャンネル「1」を引数にインスタンスを作成し、I²Cアドレスも変数に格納しておきます。
i2c = smbus.SMBus(1)
adress = 0x44
単発測定コマンドの送信
SHT31-DIS仕様書(日本語)のデータシートには
本センサーは測定コマンドを認識すると温度と相対湿度の測定を始めます
SHT31-DIS仕様書(日本語)
とあります。わたしは単発測定コマンドで実行します。
単発測定コマンドに複数の設定があり、選択が可能です。わたしの選択は
繰り返し精度レベル | クロックストレッチ設定 | MSB部 (上位8bit) | LSB部 (下位8bit) |
---|---|---|---|
高 | 有 | 0x2C | 06 |
クロックストレッチについては『デバッグ・ラボへようこそ』様の記事『温湿度センサーSHT31をクロックストレッチで使う時の備忘録』が詳しく、参考にさせていただきました。ありがとうございました!
では、どのようにして単発測定コマンドをコーディングすればよいのか?
write_byte_data(i2c_addr, register, value, force=None)
こちらの関数を使えば大丈夫そうです。では、なぜこの関数なのでしょうか?
『smbus2 Documentation』を参照します。write系の関数は
- write_byte(i2c_addr, value, force=None)
- write_byte_data(i2c_addr, register, value, force=None)
- write_block_data(i2c_addr, register, data, force=None)
- write_i2c_block_data(i2c_addr, register, data, force=None)
- write_quick(i2c_addr, force=None)
- write_word_data(i2c_addr, register, value, force=None)
5番はおそらく送信確認のための関数ですね。通信に失敗するとIOErrorを投げるらしいです。
6番は word ですし、違うでしょう。どのように使うんでしょうね?
3、4番の引数 data は list of bytes とありますから、違うでしょうね。送信するデータは2bytesのみです。
register にMSB部、 data にLSB部を格納した長さが 1 のリスト、を渡せば成立するかも…?それは置いておきます。
1番は1byteの送信しかできないようなので、これも違いますね。
消去法の結果、2番の
write_byte_data(i2c_addr, register, value, force=None)
が残りました。わたしが勝手に納得しているだけで、この関数を使うべき正当な理由は別にあるのかもしれません。
こちらの関数に引数を渡すと
i2c.write_byte_data(address, 0x2C, 0x06)
測定値の読み出し
同じくSHT31-DIS仕様書(日本語)のデータシートを見るに、計6bytesのデータがセンサーから返ってきます。
- 温度測定値(MSB部)
- 温度測定値(LSB部)
- チェックサム
- 湿度測定値(MSB部)
- 湿度測定値(LSB部)
- チェックサム
byteデータのリストが戻り値になっている関数を使えばよさそうですね。『smbus2 Documentation』を参照して、この目的に合う関数を探すと
- read_block_data(i2c_addr, register, force=None)
- read_i2c_blcok_data(i2c_addr, register, length, force=None)
このどちらかになりそうです。
前者は32bytesまで扱うことができ、後者は予め返ってくるデータの長さを指定できるようです。
記事を参考に後者の
read_i2c_blcok_data(i2c_addr, register, length, force=None)
でコーディングを進めます。
『smbus2 Documentation』によれば、引数 register は「Start register」と解説されています。
これは、「返ってくるデータを何番目から取得しますか?」ということでしょうかね?この場合、byteデータのリストのインデックス[0]番から必要なので 0x00 、ということだと推測します。
先頭のデータは必要ない、そういうケースもあるのかもしれません。
返ってくるデータは6bytesなので、 length は 0x06 、ですね。まとめると
dac = i2c.read_i2c_block_data(address, 0x00, 0x06)
ちなみに、前者の
read_block_data(i2c_addr, register, force=None)
を使い
dac = read_block_data(address, 0x00)
でプログラムを作成し実行したところ、この箇所で
OSError: [Errno 71] Protocol error
というエラーが発生しました。
エラーが起きる具体的な理由はわかりませんでした。疑問を放置するのはスッキリしませんが、はまると抜けられなくなりそうなので、ここは先に進みます。
ビット演算
本来16bitの長さであるはずのデータが8bitずつ、MSB部とLSB部に分かれて返ってきている、ということですので、元の16bit長にデータを変換する必要があります。
上の桁と下の桁の2つに分かれてしまった1つの数値を、計算して元に戻しましょう、ということだと思います。
dacという変数に関数が返すリストデータを格納すると
データの種類 | MSB部 | LSB部 |
---|---|---|
温度 | dac[0] | dac[1] |
湿度 | dac[3] | dac[4] |
8bitずつのデータを結合させるには、ビット演算でMSB部のデータを8bit前方に移動させます。温度の例で挙げれば
dac[0] << 8
ですね。シフト演算で移動させた空きは0で埋まるので、LSB部のデータと論理和を求めれば
dac[0] << 8 | dac[1]
となり、元の16bit長データに変換されます。
dac[2]とdac[5]は使用しませんが、チェックサムのデータが格納されているはずです。
Pythonではないですが、『わかりやすいC 入門編 第2版』の第7.4章からのビット演算の解説がとてもわかりやすいと思います。この投稿を書く際、改めて参考にさせていただきました。
測定値の換算
SHT31-DIS仕様書(日本語)、取扱説明書によれば、返ってきた温度・湿度のデータは物理量値への換算が必要だそうです。その計算式も記載されています。
先ほどの温度データをtmp_tに、湿度データをtmp_hという変数に格納すれば
#温度
-45 + 175 * tmp_t / (pow(2, 16) -1)
#湿度
100 * tmp_h / (pow(2, 16) -1)
まとめ
今回のコードです。
import smbus
#TODO smBusの設定
i2c = smbus.SMBus(1)
address = 0x44
#TODO 単発測定コマンド送信
i2c.write_byte_data(address, 0x2C, 0x06)
#TODO データ読み取り
dac = i2c.read_i2c_block_data(address, 0x00, 0x06)
#TODO ビット演算
tmp_t = dac[0] << 8 | dac[1]
tmp_h = dac[3] << 8 | dac[4]
#TODO 換算
temperature = -45 + 175 * tmp_t / (pow(2, 16) -1)
humidity = 100 * tmp_h / (pow(2, 16) -1)
次回へ続く
今回はここまでです。
はんだ付け・回路作成が成功しているかを確認し、そこで得られたI²Cチャンネル、アドレスをもとにsmbusのコーディングを行いました。
次は5. プログラム作成Ⅱ(smtplib)、6. cronの設定についてです。読んでくださった方、ありがとうございました。
著者名 | 書籍名 | 出版 | 発行年月日 | 参照頁 |
---|---|---|---|---|
福田和宏 | これ一冊でできる!ラズベリーパイ超入門 | ソーテック社 | 初版 :2020/2/29 第3刷:2021/4/20 | 212~ |
川場隆 | わかりやすいC 入門編 第2版 | 秀和システム | 初版:2019/7/1 | 136~ |
サイト名 | ページタイトル | URL | 参考にした日付 |
---|---|---|---|
RasberryPi活用 | http://seminarinfo.html.xdomain.jp/RaspberryPiLinux6/RaspberryPi%E6%B4%BB%E7%94%A86%E2%91%A1D-2.pdf | 2022/10/2 | |
i2c python documentation | http://wiki.erazor-zone.de/wiki:linux:python:smbus:doc | 同上 | |
smbus2 Documentation | https://buildmedia.readthedocs.org/media/pdf/smbus2/latest/smbus2.pdf | 同上 | |
バージョン管理された人 | I2C Toolsの簡単な使い方について | https://moba1.hatenablog.com/entry/2019/10/09/114145 | 同上 |
初心者のためのセンサと測定入門 | 高精度温湿度センサー SHT31-DISの使い方 | https://s-design-tokyo.com/use-sht31-dis/ | 同上 |
デバッグ・ラボへようこそ | 温湿度センサーSHT31をクロックストレッチで使う時の備忘録 | https://debuglab.jp/tag/%E3%82%AF%E3%83%AD%E3%83%83%E3%82%AF%E3%82%B9%E3%83%88%E3%83%AC%E3%83%83%E3%83%81/ | 同上 |
秋月電子通商 | SHT31-DIS仕様書(日本語) | https://akizukidenshi.com/download/ds/sensirion/Sensirion_Humidity_Sensors_SHT3x_DIS_Datasheet_V3_J.pdf | 同上 |
取扱説明書 | https://akizukidenshi.com/download/ds/akizuki/AE-SHT3x_manu_v1.0.pdf | 同上 |