超小型気圧センサーLPS25HBから気圧データと温度データを読み取る方法を紹介します。
使用するマイコンはRaspberry Pi、プログラム言語はPythonです。
サンプルプログラムは、センサーからデータを読み取るだけでなく測定条件設定も行います。
通信方式は、I2C通信とSPI通信の2種類を記載しているので参考にしてください。
また、動作確認中に通信トラブルがあったので、その原因と対策も紹介します。
センサーの主な仕様
気圧測定
測定範囲:260hPa~1260hPa
精度:±0.1hPa(25℃時)、±1hPa(0℃~80℃時)
分解能:24bitデータ
温度測定
測定範囲:-30℃~+105℃
精度:±2℃(0℃~65℃)
分解能:16bitデータ
準備
LPS25HBの購入
LPS25HBは10端子のHLGAと呼ばれる形状です。
センサーを手ではんだ付けするのは不可能なのでモジュールの購入になると思います。
下の写真は秋月電子通商で購入したLPS25HBモジュールです。今回はサンプルプログラムでこのモジュールからデータを読み込みます。
また、このモジュールは、I2C通信とSPI通信のどちらの通信方式にも対応しています。
Raspberry Piの通信設定とPythonの操作方法
Raspberry PiのI2C通信とSPI通信設定を有効にします。
下の記事でRaspberry Piの通信設定とPythonの基本操作を説明しています。
I2C通信でデータを取得する
モジュールとRaspberry Piの接続
接続図
接続写真
I2C通信プログラミング
プログラムコード
Pythonプログラムです。
import smbus
import time
#I2C設定
i2c = smbus.SMBus(1)
address = 0x5C
#Lセンサーの設定
i2c.write_byte_data(address, 0x20, 0x90)
#ファイルオープン(書き込みモード)
f = open("lps25hb.csv", "w")
#10回繰り返す
for i in range(10):
#データ読み込み
pxl = i2c.read_byte_data(address, 0x28)
pl = i2c.read_byte_data(address, 0x29)
ph = i2c.read_byte_data(address, 0x2A)
tl = i2c.read_byte_data(address, 0x2B)
th = i2c.read_byte_data(address, 0x2C)
#データ変換
prs = ph << 16 | pl << 8 | pxl
tmp = th << 8 | tl
#極性判断(温度)
if tmp >= 32768:
tmp -= 65536
#物理量に変換
prs = prs / 4096
tmp = 42.5 + tmp / 480
#表示
print('Pressure: ' + str(prs))
print('Temperature: ' + str(tmp))
#データのCSV変換とファイルへの書き込み
f.write(str(i + 1) + ',' + str(prs) + ',' + str(tmp) + '\n')
#一時停止
time.sleep(1)
f.close()
プログラムの説明
プログラムの流れは以下の通りです。
- I2C通信の設定
- センサーの設定
- 保存用ファイルをオープン
- センサーから気圧と温度データを取得
- データを物理量に変換し画面に表示
- CSV形式でファイルにデータを書き込む
- 10回繰り返したらファイルをクローズして終了
1.I2C通信の設定
#I2C設定
i2c = smbus.SMBus(1)
address = 0x5C
i2c = smbus.SMBus(1)
インスタンス生成です。使用するBus1を指定します。
address = 0x5C
アドレス指定です。LPS25HBのアドレスは0x5C(16進数)で、通信時に指定します。
Raspberry PiにLPS25HBを接続した状態で、LXTerminalから「i2cdetect -y 1」コマンドを入力すると、アドレスが0x5Cであることを確認出来ます。
2.センサーの設定
#センサーの設定
i2c.write_byte_data(address, 0x20, 0x90)
設定は write_byte_data(adr, reg, dat)関数を使用します。
第1引数 adr:I2C通信のアドレス
第2引数 reg:レジスタ(設定したい項目の記憶場所)
第3引数 dat:データ(レジスタに設定する内容)
サンプルプログラムではCTRL_REG1レジスタ設定をしています。レジスタの設定内容は以下の通りです。
i2c.write_byte_data(address, 0x20, 0x90)
0x20(CTRL_REG1レジスタ)
SIM(SPIモード 4wire/3wire)
RESET_AZ(オートゼロ機能リセット)
BDU(Block data update)
DIFF_EN(割り込みの発生 有効/無効)
ODR2-ODR0(出力データレート)
PD(パワーダウンモード)
0x90
SIM(0:SPIモード 4wire(デフォルト))
RESET_AZ(0:通常モード(デフォルト))
BDU(0:continuous update(デフォルト))
DIFF_EN(0:割り込みの発生無効(デフォルト))
ODR2-ODR0(001:出力データレート 気圧1Hz、温度1Hz)
PD(1:アクティブモード)
データシートの『8.6 CTRL_REG1 (20h)』で詳細を確認できます。
PD | ODR2 | ODR1 | ODR0 | DIFF_EN | BDU | RESET_AZ | SIM |
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
9 | 0 |
3.保存用ファイルをオープン
#ファイルオープン(書き込みモード)
f = open("lps25hb.csv", "w")
“lps25hb.csv”ファイルを書き込みモードでオープンします。
ファイルが存在しない場合は新規作成します。存在する場合は上書き保存します。
上書きせずに追記する場合は、2番目の引数を “w” から “a” にします。
ファイルは、/home/pi ディレクトリに保存されます。
4.センサーから気圧と温度データを取得
#データ読み込み
pxl = i2c.read_byte_data(address, 0x28)
pl = i2c.read_byte_data(address, 0x29)
ph = i2c.read_byte_data(address, 0x2A)
取得は read_byte_data(adr, reg)関数を使用します。
第1引数 adr:I2C通信のアドレス
第2引数 reg:レジスタ(測定データの記憶場所)
気圧データは3つのレジスタから、温度データは2つのレジスタから取得します。
気圧データの取得
i2c.read_byte_data(address, 0x28)
i2c.read_byte_data(address, 0x29)
i2c.read_byte_data(address, 0x2A)
24bitデータです。レジスタアドレスは0x28、0x29、0x2A
tl = i2c.read_byte_data(address, 0x2B)
th = i2c.read_byte_data(address, 0x2C)
温度データの取得
i2c.read_byte_data(address, 0x2B)
i2c.read_byte_data(address, 0x2C)
16bitデータです。レジスタアドレスは0x2B、0x2C
5.データを物理量に変換し画面に表示
#データ変換
prs = ph << 16 | pl << 8 | pxl
tmp = th << 8 | tl
①気圧データを24bit、②温度データを16bitにします。
#極性判断(温度)
if tmp >= 32768:
tmp -= 65536
if tmp >= 32768:
tmp -= 65536
2の補数表現を符号ありに変換します。
16bitデータ | 符号なし | 符号あり |
1000000000000000 | 32768 | -32768 |
1000000000000001 | 32769 | -32767 |
: | : | : |
1111111111111110 | 65534 | -2 |
1111111111111111 | 65535 | -1 |
0000000000000000 | 0 | 0 |
0000000000000001 | 1 | 1 |
0000000000000010 | 2 | 2 |
: | : | : |
0111111111111110 | 32766 | 32766 |
0111111111111111 | 32767 | 32767 |
2の補数の詳しい説明はこちら
「2の補数」(2021年10月12日 (火) 17:00 UTCの版)『ウィキペディア日本語版』
#物理量に変換
prs = prs / 4096
tmp = 42.5 + tmp / 480
prs = prs / 4096
tmp = 42.5 + tmp / 480
データを物理量に変換します。
気圧は4096で割り、温度は480で割ります(データシートを参考にしています)。
#表示
print('Pressure: ' + str(prs))
print('Temperature: ' + str(tmp))
print(‘Pressure: ‘ + str(prs))
print(‘Temperature: ‘ + str(tmp))
気圧データと温度データを画面に表示します。
6.CSV形式でファイルにデータを書き込む
#データのCSV変換とファイルへの書き込み
f.write(str(i + 1) + ',' + str(prs) + ',' + str(tmp) + '\n')
f.write(str(i + 1) + ‘,’ + str(prs) + ‘,’ + str(tmp) + ‘\n’)
f.write()のカッコの中をファイルに書き込みます。
カッコの中は、データをカンマ区切りのCSV形式にしています。
str(i+1) と str(out)をカンマで区切っています。最後に改行コード(\n)を追加します。
CSV形式の詳しい説明はこちら
「Comma-Separated Value」(2021年10月25日 (月) 05:33 UTCの版)『ウィキペディア日本語版』
7.10回繰り返したらファイルをクローズして終了します
#10回繰り返す
for i in range(10):
for i in range(10):
繰り返し命令です。10回繰り返します。
※この『for i in range(10):』は、15行目のfor文です。
f.close()
f.close()
ファイルをクローズします。
プログラムの実行結果
プログラムを実行します。
①Runボタンを押します。
②気圧データと温度データを1秒間隔で10回画面に表示します。
プログラムを実行すると、/home/pi/ディレクトリにlps25hb.csvファイルが作成されます。
③CSV形式で10回分のデータが保存されています。
lps25hb.csvファイルの内容
以上、LPS25HBからI2C通信で気圧データと温度データを読み込んでCSV形式で保存するサンプルでした。
発生したトラブル
症状
I2C通信のreadでも writeでも、数回通信を繰り返すと Remote I/O error を発生して止まります。
原因と対策
プログラムを確認しましたが悪い場所が分からなかったので、オシロスコープを使って信号を確認したところ、電位が定まっていない信号があることが分かりました。
電位の異常なのでハードウェア周りに問題が有る可能性が高いと考え調査したところ、モジュールの4番ピン(I2Cアドレス決定ピン)が未接続になっており、本来は電源かGNDに接続して電位を確定する必要がありました。
4番ピンが未接続であっても「i2cdetect」コマンドの返送が[0x5C]と返ってきていたので、正常に動作していると思い、気づかずに4番ピンを未接続のままにしていたことが原因です。
対策は、4番ピンをGNDに接続し電位を確定させました。
それからはエラーは発生していません。
SPI通信でデータを取得する
モジュールとRaspberry Piの接続
接続図
接続写真
SPI通信プログラミング
プログラムコード
Pythonプログラムです。
import spidev
import time
#SPI設定
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 100000
spi.mode = 3
s_dat = [0x00, 0x00]
t_dat = [0x00, 0x00, 0x00]
p_dat = [0x00, 0x00, 0x00, 0x00]
#Lセンサーの設定
s_dat[0] = 0x20
s_dat[1] = 0x90
readByteArry = spi.xfer2(s_dat)
#ファイルオープン(書き込みモード)
f = open("lps25hb.csv", "w")
#繰り返し
for i in range(10):
#設定
p_dat[0] = 0x28
p_dat[0] |= 0x80
p_dat[0] |= 0x40
t_dat[0] = 0x2B
t_dat[0] |= 0x80
t_dat[0] |= 0x40
#データ読み込み
readByteArry = spi.xfer2(p_dat)
readByteArry = spi.xfer2(t_dat)
#データ変換
prs = p_dat[3] << 16 | p_dat[2] << 8 | p_dat[1]
tmp = t_dat[2] << 8 | t_dat[1]
#極性判断(温度)
if tmp >= 32768:
tmp -= 65536
#物理量(気圧)に変換
prs = prs / 4096
#物理量(温度)に変換
tmp = 42.5 + tmp / 480
#表示
print('Pressure: ' + str(prs))
print('Temperature: ' + str(tmp))
#データのCSV変換とファイルへの書き込み
f.write(str(i+1) + ',' + str(prs) + ',' + str(tmp) + "\n")
#一時停止
time.sleep(1)
f.close()
spi.close
プログラムの説明
プログラムの流れは以下の通りです。
- SPI通信の設定
- センサーの設定
- 保存用ファイルをオープン
- センサーから気圧と温度データを取得
- データを物理量へ変換し画面に表示
- CSV形式でファイルにデータを書き込む
- 10回繰り返したらファイルをクローズして終了
1.SPI通信の設定
#SPI設定
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 100000
spi.mode = 3
spi.open(0,0)
Bus0をチップセレクト0でオープンします。
spi.max_speed_hz = 100000
クロックのスピードを10kHzにします。
spi.mode = 3
SPI通信のハードウェア設定です。(mode 3は負論理の立下りエッジ検出)
SPI通信の詳しい説明はこちら
「シリアル・ペリフェラル・インタフェース」(2021年10月21日 (木) 02:31 UTCの版)『ウィキペディア日本語版』
2.センサーの設定
#センサーの設定
s_dat[0] = 0x20
s_dat[1] = 0x90
readByteArry = spi.xfer2(s_dat)
s_dat[0] = 0x20
CTRL_REG1レジスタを指定
s_dat[1] = 0x90
指定したレジスタの設定
CTRL_REG1レジスタは、I2C通信の2.センサーの設定に詳しい説明があります。
3.保存用ファイルをオープン
#ファイルオープン(書き込みモード)
f = open("lps25hb.csv", "w")
“lps25hb.csv”ファイルを書き込みモードでオープンします。
ファイルが存在しない場合は新規作成します。存在する場合は上書き保存します。
上書きせずに追記する場合は、2番目の引数を “w” から “a” にします。
ファイルは、/home/pi ディレクトリに保存されます。
4. センサーから気圧と温度データを取得
#設定
p_dat[0] = 0x28
p_dat[0] |= 0x80
p_dat[0] |= 0x40
t_dat[0] = 0x2B
t_dat[0] |= 0x80
t_dat[0] |= 0x40
気圧:0x28
温度:0x2B
気圧、温度データを読み込むためのアドレス設定です。
共通:0x80
[読み込み/書き込み] を [読み込み]に設定します。
共通:0x04
読み込みを自動インクリメントに設定します。
アドレスについては、I2C通信の4.センサーから気圧と温度データを取得に詳しい解説があります。
#データ読み込み
readByteArry = spi.xfer2(p_dat)
readByteArry = spi.xfer2(t_dat)
気圧データと温度データを取得します。
p_dat:気圧データ
t_dat:温度データ
5.データを物理量に変換し画面に表示
(I2C通信の5.データを物理量に変換し画面に表示で詳しい解説をしています。)
#データ変換
prs = p_dat[3] << 16 | p_dat[2] << 8 | p_dat[1]
tmp = t_dat[2] << 8 | t_dat[1]
8bitのデータを、気圧データ24bitと温度データ16bitに変換します。
#極性判断(温度)
if tmp >= 32768:
tmp -= 65536
if(out_x >= 2048):
out_x = out_x – 4096
2の補数表現を符号ありに変換します。
#物理量(気圧)に変換
prs = prs / 4096
#物理量(温度)に変換
tmp = 42.5 + tmp / 480
データを物理量に変換します。
気圧は4096で割り、温度は480で割ります(データシートを参考にしています)
#表示
print('Pressure: ' + str(prs))
print('Temperature: ' + str(tmp))
気圧データと温度データを画面に表示します。
6.CSV形式でファイルにデータを書き込む
#データのCSV変換とファイルへの書き込み
f.write(str(i+1) + ',' + str(prs) + ',' + str(tmp) + "\n")
f.write(str(i+1) + ‘,’ + str(prs) + ‘,’ + str(tmp) + “\n”)
f.write()のカッコの中をファイルに書き込みます。
カッコの中は、データをカンマ区切りのCSV形式にしています。
str(i+1) と str(out)をカンマで区切っています。最後に改行コード(\n)を追加します。
7.10回繰り返したらファイルをクローズして終了します
#繰り返し
for i in range(10):
for i in range(10):
繰り返し命令です。10回繰り返します。
※この『for i in range(10):』は、23行目のfor文です。
f.close()
f.close()
ファイルをクローズします。
プログラムの実行結果
プログラムの実行結果は、I2C通信時の実行結果と同じなので割愛します。
まとめ
LPS25HBモジュールからRaspberry PiにI2C通信とSPI通信でデータを読み込んで表示と保存するサンプルでした。如何でしたでしょうか。
今回は、I2C通信でトラブルが発生したので原因と対策も記載しました。
完全に通信が出来ないわけではなかったので、ソフトウェアを疑っていたのですが、ソースコードを極限まで削除してもダメだったので、信号を調べて原因が分かったという感じです。
プログラミングやソフトウェアの記事です。
コメント