LoginSignup
0
0

More than 1 year has passed since last update.

差圧センサDLHR-F50DをRaspberry Pi + Pythonで使えるようにする

Posted at

All sensors (Amphenol)製 差圧センサー DLHR-F50D-E1BD-C-NAV8 をRaspberry Pi + Pythonで動かしてみた記録です。

はじめに

差圧を計測してあれこれ検出するデバイスを作ってみたくて、買ってきたセンサーで値を取得するところまでの記録となります。
I2Cでアドレスを指定せずにデータを読み書きした経験がなく、「アドレスは指定するもの」という思い込みでread_byte の存在を知らずステータスの読み出しではまりました。

環境

  • Raspberry Pi 4 model B 4GB(Raspberry Pi OS arm64 Release:10 Codename:buster )
  • Python: Python 3.9.2

センサーの選定

趣味の工作なので手に入るならOKな感じで選びます。

要求

項目 説明
高感度 数Paを検出したい
測定周波数 40Hz
ポート チューブ接続が可能
電源電圧 3.3Vで動くとうれしい
接続 デジタル、I2Cだと楽
パッケージ 使いやすければよし

候補

メーカー 型式 仕様
All sensors (Amphenol) DLHR-F50D ±125Pa
Honeywell HSCDRRN001ND2A3 ±125Pa
Sensirion SDP810-125PA ±125Pa
Omron D6F-PH0505 ±50Pa

お財布と相談しながら実験のしやすさ等考慮した結果

を選定。

主な仕様

項目 仕様
メーカー All sensors (Amphenol)
型式 DLHR-F50D-E1BD-C-NAV8
レンジ ±125Pa(±0.5inH2O)  
デジタル分解能 17.7bit
データ更新時間 4.1ms max(Single測定) , 61.9ms max(16回平均化)
耐圧 25kPa
電圧 1.75 to 3.60 Vdc
接続 I2C / SPI
パッケージ DIP8ピン

コード

仕様書を読んで書きます

from smbus2 import SMBus
import time

ADDRESS = 0x29

OS_DIG = 0.5 * 2 ** 24 # For Differential Operating Range sensors
FSS_IN_H2O = 2 * 0.5 # For Differential Operating Range sensors : 2 x Full Scale Pressure.
PA_CONVERSION = 249.089 # Conversion factor from "inH2O" to "Pa"
FSS_PA = FSS_IN_H2O * PA_CONVERSION # FSS/2 for "Pa"

START_SINGLE = 0xAA
START_AVERAGE2 = 0xAC
START_AVERAGE4 = 0xAD
START_AVERAGE8 = 0xAE
START_AVERAGE16 = 0xAF

class DifferentialPressureSensorDLHR_F50D():
    '''
    ### Class for using All sensoers (Amphenol) DLHR-F50D-E1BD-C-NAV8 with Raspberry Pi
    For simple use, you can instantiate it and then call the method "read_p()" to get the differential pressure in the return value.
    '''
    def __init__(self) -> None:
        self.address = ADDRESS
        self.status = 0x00
        self.power = False
        self.busy = False
        self.mode = 0x00
        self.memory_error = False
        self.alu_error = False
        self.rawp = 0
        self.rawt = 0
        self.pressure = 0
        self.temprature = 0
        self.bus = SMBus(1)
        pass

    def send_start(self) -> None:
        '''Send Measurement Commands to the sensor when called'''
        self.bus.write_byte(self.address, START_AVERAGE4)

    def status_read(self) -> None:
        self.status = self.bus.read_byte(self.address)
        if self.status & 0x40 == 0x40:
            self.power = True
        else:
            self.power = False
        if self.status & 0x20 == 0x20:
            self.busy = True
        else:
            self.busy = False
        self.mode = (self.status & 0x18) >> 3
        if self.status & 0x04 == 0x04:
            self.memory_error = True
        else:
            self.memory_error = False
        if self.status & 0x01 == 0x01:
            self.alu_error = True
        else:
            self.alu_error = False

    def read_busy(self) -> bool:
        '''
        ### After reading the status register, it retrieves only the Busy status from the result and returns the result.
        Return:
            Sensor busy status (bool)
        '''
        self.status = self.bus.read_byte(self.address)
        if self.status & 0x20 == 0x20:
            self.busy = True
        else:
            self.busy = False
        return self.busy

    def poll_busy(self):
        '''Keep reading the status until Busy becomes False.'''
        while self.read_busy():
            pass

    def correction_p(self) -> None:
        '''Returns the raw pressure value as a translated pressure value.'''
        self.pressure = 1.25 * ((self.rawp - OS_DIG)/ 2**24) * FSS_PA

    def correction_t(self) -> None:
        '''Returns the raw temperature value as a translated temperature value.'''
        self.temprature = (self.rawt * 125)/ 2**24 - 40

    def read(self):
        '''Get the raw value from the sensor.'''
        data = self.bus.read_i2c_block_data(self.address, 0x00, 7)
        self.rawp = (data[1] << 16) | (data[2] << 8) | data[3]
        self.rawt = (data[4] << 16) | (data[5] << 8) | data[6]

    def read_p(self) -> float:
        '''
        ### Get the corrected differential pressure
        Return:
            Corrected differential pressure
        '''
        self.send_start()
        self.poll_busy()
        self.read()
        self.correction_p()
        return self.pressure

    def read_t(self) -> float:
        '''
        ### Get the corrected temprature
        Return:
            Corrected temprature
        '''
        self.send_start()
        self.poll_busy()
        self.read()
        self.correction_t()
        return self.temprature

def main(): # Sample usage
    ZERO_OFFSET = 2 # Zero point correction
    dlhr_f50d = DifferentialPressureSensorDLHR_F50D()
    while True:
        p = dlhr_f50d.read_p()
        print("pressure :" + str(round(p - ZERO_OFFSET ,4)))
        time.sleep(1)

if __name__ == '__main__':
    main()

はまりポイント

このセンサーはEOCピンの状態を監視することで、測定開始コマンド書き込み後にセンサーが読み取りReadyになったのかを知ることができますが、EOCピンを使用しない場合は、Status Readコマンドを送信し続けてBusyをポーリングすることができます。

仕様書によると、ステータスを読み出すには

When requesting sensor status over I2C, the host simply performs a 1-byte read transfer.
Read Sensor Status | i2C | Read of 1 byte from device.

とだけ書いてあります。
これをよく考えずにRead Sensor Dataでは最初の1バイトで返ってくるみたいだし、
0x00を読めばよいと勝手に解釈して、

from smbus2 import SMBus
bus = SMBus(1)
status = bus.read_byte_data(0x29, 0x00)
print(status)

こう書くと電源のステータスと合わせてstatus = 0b01100000 = 0x60 = 96が返ってきて永遠にBusyのままです。
まだMeasurement Commandを送ってないのにBusyが立ってるのはおかしいので、「壊しちゃったかな?」とか考えながら、とりあえずMeasurement CommandしてからしばらくしてRead Sensor Dataするとちゃんと値が返ってきます。
Busyが正しく読めないとtime.sleep()などで待ってから読み出すしかありません。
困ったなぁと思いながらしばらく経ってsmbus2のページをよく読んでみると、1バイト読み出す関数は2つあって、

レジスタアドレスを指定して読み出すread_byte_data

read_byte_data(i2c_addr, register, force=None)

単に1バイトを読み出すread_byteがありました。

read_byte(i2c_addr, force=None)

Read of 1 byte from device.

と指示され、読み出すアドレスの指定がないわけですから、read_byte_dataではなく read_byteを使わないといけなかったわけです。
Measurement Commandwrite_byte_dataじゃなくてちゃんとwrite_byte使って書いてたのになぜ気が付かなかったのか...

参考リンク

センサー仕様書
smbus2

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0