気圧を見たいのか、レジスタ触りたいのか
私は釣りが趣味でして、ルアーなら色々やりますが主にバス釣りに行きます。
嘘か誠か、魚が釣れやすくなる(活性が上がる)時って、少なからず気圧変化が影響しているとかしていないとか。実際釣りしてて謎に爆釣するタイミングとかあるのですが、本当に気圧変化と関係があったら面白そうです。何はともあれ、気圧計の説明書を見たところライブラリ無しでも動かせそうだったので、そのままの勢いでチャレンジしてみます。デジタルデバイスいいね!
今回の目的
最終的に気圧のログを取り、直近1時間ぐらいの気圧変化を確認出来るようにしようと考えています。魚が釣れた時に気圧変化を確認し、大きく変化していたなら釣果と気圧変化に因果関係がある可能性がありますね!普通に家の周りの天気予測に使えるかもしれない。
この辺の思い付き機能は後程実装するとして、本記事ではレジスタを操作して気圧センサと格闘します。
では行こう。
材料
- Raspberry Pi 4B
- LPS25HB使用 気圧センサーモジュールDIP化キット
- 温湿度センサ モジュール DHT11
今回の心臓部はラズパイです。電子部品用の入出力を搭載したクレジットカードサイズのLinuxマシンです。
気圧センサのLPS25HBを使用したモジュールはいくつかありますが、今回は上記のものを使用。600円ぐらいで買えちゃったので。
電圧レギュレータが付いていないのでArduinoとかに繋ぐ時は別途レベルコンバータが必要です。本当はこの辺全部入りのこちらの方がオススメだったります。
気圧だけ取るのも何だか寂しいのでとりあえず温湿度も取れるように超定番温湿度センサのDHT11を使用します。こちらも300円ぐらいだったはず。
今後実装する機能との親和性を考えて開発言語はPython
を選択します。
配線しようぜ!
プログラミングしている時も楽しいですが、電子工作は配線も楽しいですね!
実際の配線
ラズパイ用GPIO拡張ボード、便利ですよ。
カオスな配線図ですが、順番に見ていきましょう。
先にちゃちゃっと気温、湿度センサ
配線とスクリプトはこちらの記事を参考にさせて頂きました。
データを読み出すピン(私はGPIO18番に繋いでいます。)の間に挟む抵抗は5.1kΩを挟んでいます。プルアップ抵抗という繋ぎ方ですね。
DHT11についてググっているとライブラリをクローンされている方が多かったのですが、Python3が使える環境であればpip出来ます。便利なので
pip3 install DHT11
を叩いてライブラリをインストールしましょう。
こちらが基本スクリプトです。
import RPi.GPIO as GPIO
import dht11
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
instance = dht11.DHT11(pin=18)
result = instance.read()
if result.is_valid():
print("Temperature: %-3.1f C" % result.temperature)
print("Humidity: %-3.1f %%" % result.humidity)
GPIO.cleanup()
実行結果はこちら
Temperature: 25.2 C
Humidity: 26.0 %
上手く動きましたね!
気圧センサを取説見ながら動かそう
ではLPS25HBを動かしましょう。商品ページから取り扱い説明書を拝見します。24ビットの読み取り値を4096.0
で割れば良いらしいので簡単かも!?やってみましょう!
まずは配線
I2Cで接続
今回はI2Cを用いてLPS25HBと通信します。取説のピンアサインを参考に電源、GND、SCL、SDAをそれぞれラズパイの対応するピンに接続します。電源は必ず3.3Vを使用してください。ここまでは簡単。
I2Cモードに設定
実際にLPS25HBをI2Cモードにする為に、5ピンを電源に接続します。
もう一つ、4ピンを電源かGNDに接続します。この接続次第で後に使うスレーブアドレスが変化します。今回はGNDに繋いだので、それに対応するアドレスが設定されます。
ラズパイから接続を確認
配線が完了したらラズパイの設定を確認しましょう。
Raspberry Pi の設定をクリックして
確認できたら以下のコマンドをしばきます。
i2cdetect -y 1
全てのアドレスに対してI2C接続されたデバイスを表示します。
私の場合は下記の実行結果となりました。
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- 5c -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
5Cとなってますね。きちんと接続できており、デバイスのアドレスもわかりました。
ちなみに4ピンを電源に繋ぐと下位ビットが立って、5Dになります。アドレスの競合がある時なんかに調整できますね。
Who am I ?
接続が完了したら、正しく通信出来ることを確認する必要があります。
説明書によると、WHO_AM_I
と名付けられたレジスタをどついて所定のデータを読めればOKとのこと。
import smbus
i2c = smbus.SMBus(1) # ラズパイのI2Cオブジェクト
you_are_lps = i2c.read_i2c_block_data(0x5C, 0x0F, 1)
print(you_are_lps)
print(hex(you_are_lps[0]))
I2C通信用モジュールをインポートしてオブジェクトを生成し、
read_i2c_block_data
でデータを読みに行きましょう。
説明書通り、0x5C
というアドレスのデバイスの0x0F
というアドレスのレジスタを1
バイト読みます。実行結果は・・・
[189]
0xbd
10進の189が帰ってきました。16進のBDですので説明書通りの挙動のようです。
You Are "LPS25"!
準備はOK。気圧を読もうぜ
どうやらここまで問題無さそうなので、いよいよ気圧を読みに行きましょう。
取説によるとPRESS_OUT_XL
とPRESS_OUT_L
とPRESS_OUT_H
を読んで4096で割れば良いみたいなので、それぞれ対応する0x28
と0x29
と0x2A
を叩いてサクッと完成させましょう。
import smbus
# 各アドレスを定数化
ADDR = 0x5C
CTRL_REG1 = 0x20
WRITE_REG1 = 0x90
PRESS_OUT_XL = 0x28
bus = smbus.SMBus(1)
# 読み取りモードに設定
bus.write_byte_data(ADDR, CTRL_REG1, WRITE_REG1)
data = bus.read_i2c_block_data(ADDR, PRESS_OUT_XL, 3)
pressure = (data[2]<<16 | data[1] << 8 | data[0]) / 4096.0
print ("気圧は・・・ : %.2f hPa" %pressure)
取説によるとCTRL_REG1
に0x90
を書き込む事で動き出すようです。
その操作を記述した上で、
data = bus.read_i2c_block_data(ADDR, PRESS_OUT_XL, 3)
と記述して、アドレス0x28
から3バイト(24ビット)読み取ります。
リストで帰ってくるので、この24ビットデータを4096で割ります。
PRESS_OUT_H
から順に読んでドッキングすればいいので、
pressure = (data[2]<<16 | data[1] << 8 | data[0]) / 4096.0
と記述して、順番にデータをビットシフトして4096で割ります。この部分、ちょっと説明が難しいんですが、data[]
の中身が2進数でいう
data[0] = 1111,0000
data[1] = 1111,0000
data[2] = 1111,0000
だったとして
1111,0000 //シフトなし
1111,0000,0000,0000 //8ビット左シフト
1111,0000,0000,0000,0000,0000 //16ビット左シフト
ってなるイメージです。今回の場合24ビットで読み出すので、こうしてスライドしてドッキングします。すると
1111,0000,1111,0000,1111,0000
と24ビットデータになるわけです。
ここまで出来たら後は実行するのみ!
実行結果
pi@raspberrypi:~ $ /usr/bin/python3 /home/pi/Desktop/lps25.py
気圧は・・・ : 2907.36 hPa
ん?なんか災害級の高気圧が発生している・・・いえいえ、正しく読めていないようですね。
取説にも特にこれ以上の事書いてないしなぁ・・・
というわけでググってみました。
data = bus.read_i2c_block_data(ADDR, PRESS_OUT_XL | 0x80, 3)
って感じでアドレスPRESS_OUT_XL
を0x80
でマスクすればいいという情報を発見。
PRESS_OUT_XL
のアドレスは0x28
なので、アドレスをA8
に変えればいいんだよね?
取説にレジスタの一覧あったけどそんなアドレス見当たらなかったけどなぁ・・・
ここで読み取り幅を指定する必要があったようです。
レジスタ(今回の場合0x28
)指定の際に最上位ビットを立てる事で連続した値を読めるモードになるとの事!
とりあえずプログラムを書き換えましょう。
import smbus
# 各アドレスを定数化
ADDR = 0x5C
CTRL_REG1 = 0x20
WRITE_REG1 = 0x90
PRESS_OUT_XL = 0x28
bus = smbus.SMBus(1)
# 読み取りモードに設定
bus.write_byte_data(ADDR, CTRL_REG1, WRITE_REG1)
# ここを書き換えた
data = bus.read_i2c_block_data(ADDR, 0xA8, 3)
pressure = (data[2]<<16 | data[1] << 8 | data[0]) / 4096.0
print ("気圧は・・・ : %.2f hPa" %pressure)
実行結果
pi@raspberrypi:~ $ /usr/bin/python3 /home/pi/Desktop/lps25.py
気圧は・・・ : 1000.07 hPa
妥当な値になりました!やったね!しかし取説に無い謎アドレスは一体なんだったのか・・・ググらなければ不良だと思ってもう一つ買うところでした。先駆者の方々に感謝。
とはいえ、何だかんだで上手く動いたので良しとしましょう!これ、原因ご存じの方いらっしゃれば是非ご教示頂きたいです・・・。
コメントにてご教示頂きました!ありがとうございます!
終わりに
今回はラズパイ×気圧センサ&気温センサで配線したりレジスタを書き換えたり読み出したり、マイコン的アプローチで勉強できました。
今回データの読み出しが出来るようになったので、次回はログを取ったり任意にログを確認出来るようにします。