サーマルカメラ(サーモ AI デバイス TiD) Python BME280編

BOSCH 温湿度気圧センサ BME280の温湿度・気圧データを、I2C通信で取得します。

***
1. [紹介編](https://qiita.com/MH-SS/items/c3d12abe871bfd62d1df)
2. [センサ編](https://qiita.com/MH-SS/items/61021e58b3a5a7f81b3d)
3. [センサケース編](https://qiita.com/MH-SS/items/ab142942c702eb6e545f)
4. [Raspberry Pi編](https://qiita.com/MH-SS/items/73f09db1ad1890c90987)
5. Python編  
    5.1 [Form編](https://qiita.com/MH-SS/items/035682853d18dc2074f2)  
    5.2 [オムロン 非接触温度センサ D6T-44L-06編](https://qiita.com/MH-SS/items/f9301bde143de482a17a)  
    5.3 [Pololu 測距センサ VL53L0X編](https://qiita.com/MH-SS/items/df3c33a6256a533d4eb5)  
    5.4 [BOSCH 温湿度・気圧センサ BME280](https://qiita.com/MH-SS/items/aff10e7b4d84c1b03714)  
    5.5 [シャットダウン・再起動スイッチ編](https://qiita.com/MH-SS/items/82db17524553e56d7605)  
    5.6 [OpenCV編](https://qiita.com/MH-SS/items/bcc9092bb9b6977014a1)  
    5.7 [高速化編](https://qiita.com/MH-SS/items/154dd994d73b4c93854f)  

***

![bme280.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569469/0b81da65-87e4-112c-bfad-db3a56c47f57.jpeg)
Raspberry Pi上のPythonでI2Cを制御する為に、smbusモジュールを使用します。
smbusモジュールがインストールされていない場合、下記コマンドでインストールしてください。

```
pip install smbus
```

bme280.pyはパッケージとしてimportして使用しますが、テスト用に単体で温湿度・気圧データを取得できます。
<a href="https://mhss.server-on.net/a656d/index/tid3/development/python/bme280/bme280.zip" target="_blank">bme280.py (ZIPで圧縮済)</a>

```
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
BM280 script.
=============
Temperature: -40 to +85 [degC]
Dumidity: 0 ot 100[%]
barometer: 300 to 1100[hPa]
"""
i2c_enable = False
try:
    import smbus
    i2c_enable = True
except:
    i2c_enable = False

import random
import time


class BME280():
    def __init__(self):
        self.bus_number  = 1
        self.i2c_address = 0x76
        self.i2c_enable = i2c_enable

        if self.i2c_enable:
            self.bus = smbus.SMBus(self.bus_number)
        else:
            self.bus = self.SMBus(self.bus_number)
            return

        self.digT = []
        self.digP = []
        self.digH = []

        self.t_fine = 0.0

        self.setup()
        self.get_calib_param()

    @property
    def barometer(self):
        result = float(f'{random.uniform(1020.0, 1040.0):.2f}')
        if self.i2c_enable == False:
            return result

        # For I2C error at pushed power switch.
        try:
            pres_raw = self._read_bus_data('barometer')
        except:
            return result

        pressure = 0.0
        
        v1 = (self.t_fine / 2.0) - 64000.0
        v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * self.digP[5]
        v2 = v2 + ((v1 * self.digP[4]) * 2.0)
        v2 = (v2 / 4.0) + (self.digP[3] * 65536.0)
        v1 = (((self.digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8)  + ((self.digP[1] * v1) / 2.0)) / 262144
        v1 = ((32768 + v1) * self.digP[0]) / 32768
        
        if v1 == 0:
            return 0
        pressure = ((1048576 - pres_raw) - (v2 / 4096)) * 3125
        if pressure < 0x80000000:
            pressure = (pressure * 2.0) / v1
        else:
            pressure = (pressure / v1) * 2
        v1 = (self.digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096
        v2 = ((pressure / 4.0) * self.digP[7]) / 8192.0
        pressure = pressure + ((v1 + v2 + self.digP[6]) / 16.0)  
    
        #print('pressure : %7.2f hPa' % (pressure/100))
        result = float(f'{pressure/100:7.2f}')
        return result

    def get_calib_param(self):
        if self.i2c_enable == False:
            return

        calib = []

        for i in range (0x88,0x88+24):
            calib.append(self.bus.read_byte_data(self.i2c_address,i))
        calib.append(self.bus.read_byte_data(self.i2c_address,0xA1))
        for i in range (0xE1,0xE1+7):
            calib.append(self.bus.read_byte_data(self.i2c_address,i))

        self.digT.append((calib[1] << 8) | calib[0])
        self.digT.append((calib[3] << 8) | calib[2])
        self.digT.append((calib[5] << 8) | calib[4])
        self.digP.append((calib[7] << 8) | calib[6])
        self.digP.append((calib[9] << 8) | calib[8])
        self.digP.append((calib[11]<< 8) | calib[10])
        self.digP.append((calib[13]<< 8) | calib[12])
        self.digP.append((calib[15]<< 8) | calib[14])
        self.digP.append((calib[17]<< 8) | calib[16])
        self.digP.append((calib[19]<< 8) | calib[18])
        self.digP.append((calib[21]<< 8) | calib[20])
        self.digP.append((calib[23]<< 8) | calib[22])
        self.digH.append( calib[24] )
        self.digH.append((calib[26]<< 8) | calib[25])
        self.digH.append( calib[27] )
        self.digH.append((calib[28]<< 4) | (0x0F & calib[29]))
        self.digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F))
        self.digH.append( calib[31] )

        for i in range(1,2):
            if self.digT[i] & 0x8000:
                self.digT[i] = (-self.digT[i] ^ 0xFFFF) + 1

        for i in range(1,8):
            if self.digP[i] & 0x8000:
                self.digP[i] = (-self.digP[i] ^ 0xFFFF) + 1

        for i in range(0,6):
            if self.digH[i] & 0x8000:
                self.digH[i] = (-self.digH[i] ^ 0xFFFF) + 1 

    @property
    def humidity(self):
        result = float(f'{random.uniform(10.0, 60.0):.2f}')
        if self.i2c_enable == False:
            return result

        # For I2C error at pushed power switch.
        try:
            hum_raw = self._read_bus_data('humidity')
        except:
            return result

        var_h = self.t_fine - 76800.0
        if var_h != 0:
            var_h = (hum_raw - (self.digH[3] * 64.0 + self.digH[4]/16384.0 * var_h))\
                * (self.digH[1] / 65536.0\
                    * (1.0 + self.digH[5] / 67108864.0 * var_h \
                        * (1.0 + self.digH[2] / 67108864.0 * var_h)))
        else:
            return 0
        var_h = var_h * (1.0 - self.digH[0] * var_h / 524288.0)
        if var_h > 100.0:
            var_h = 100.0
        elif var_h < 0.0:
            var_h = 0.0
        #print('hum : %6.2f ％' % (var_h))
        result = float(f'{var_h:.2f}')
        return result

    def _read_bus_data(self, data_type):
        """
        data_tpye: 'temperature' or 'hidumity' or 'barometer'
        """
        data = []

        for i in range (0xF7, 0xF7+8):
            data.append(self.bus.read_byte_data(self.i2c_address,i))

        if data_type == 'barometer':
            return (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        elif data_type == 'humidity':
            return (data[6] << 8) | data[7]
        elif data_type == 'temperature':
            return (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
        else:
            return [0]

    def read_data(self):
        t = self.temperature
        h = self.humidity
        p = self.barometer
        return [t, h, p]

    def setup(self, ):
        osrs_t = 1            #Temperature oversampling x 1
        osrs_p = 1            #Pressure oversampling x 1
        osrs_h = 1            #Humidity oversampling x 1
        mode   = 3            #Normal mode
        t_sb   = 5            #Tstandby 1000ms
        filter = 0            #Filter off
        spi3w_en = 0          #3-wire SPI Disable
    
        ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode
        config_reg    = (t_sb << 5) | (filter << 2) | spi3w_en
        ctrl_hum_reg  = osrs_h
    
        self.write_reg(0xF2,ctrl_hum_reg)
        self.write_reg(0xF4,ctrl_meas_reg)
        self.write_reg(0xF5,config_reg)

    # Dummy SMBus.
    class SMBus():
        def __init__(self, *args, **kwargs):
            pass

    @property
    def temperature(self):
        result = float(f'{random.uniform(20.0, 40.0):.2f}')
        if self.i2c_enable == False:
            return result

        # For I2C error at pushed power switch.
        try:
            temp_raw = self._read_bus_data('temperature')
        except:
            return result

        v1 = (temp_raw / 16384.0 - self.digT[0] / 1024.0) * self.digT[1]
        v2 = (temp_raw / 131072.0 - self.digT[0] / 8192.0) * (temp_raw / 131072.0 - self.digT[0] / 8192.0) * self.digT[2]
        self.t_fine = v1 + v2
        temperature = self.t_fine / 5120.0

        result = float(f'{temperature:.2f}')
        return result

    def write_reg(self, reg_address, data):
        if self.i2c_enable == False:
            return

        try:
            self.bus.write_byte_data(self.i2c_address, reg_address, data)
        except:
            self.i2c_enable = False
            self.bus = self.SMBus(self.bus_number)


if __name__ == '__main__':
    em = BME280()
    while True:
        print(em.read_data())
        #print(em.barometer)
        #print(em.humidity)
        #print(em.temperature)
        time.sleep(0.5)
```

***
[YouTube: サーマルカメラ(サーモ AI デバイス TiD) Python編](https://www.youtube.com/watch?v=fgwBsISDtXo)
[web: サーモ AI デバイス TiD Python BME280編](https://mhss.server-on.net/?f401c=Index&ae504=tid3/development/python/bme280) (URLが変更されました)