raspberrypi pigpioのライブラリ BME280等

GPIOを制御するライブラリは数あれど、最近はpigpioが旬である。
すでにOSに組み込まれているので以下のおまじないをしておけば
常駐する

sudo systemctl enable pigpiod
sudo systemctl atart pigpiod
ちなみに常駐終了で次回から常駐してくれる
解除は
disable
にすればよい

pigpioの本家にlibraryがあって、代表的なセンサーのpythonでのサンプルが提供されている
http://abyz.me.uk/rpi/pigpio/examples.html#Python%20code
python codeにコードが記載されている

ちなみに上記のHPからダウンロードしたBME280でpigpioのサンプルは

#!/usr/bin/env python

import time

# BME280.py
# 2016-08-05
# Public Domain

"""
Code to read the BME280 pressure, humidity, and temperature sensor._

The BME280 has both I2C and SPI interfaces, this code supports both.

Only 4-wire SPI is supported.  You will need to make changes to the
code to support 3-wire (e.g. to set the sensor into 3-wire mode and
to do 3-wire SPI reads).

The code only supports forced mode of operation, where each reading
is requested.  For simplicity the pressure, humidity, and temperature
are all read.  Just discard values you are not interested in.

There seemed to be a discrepancy between the documented interface
method of reading the trimming parameter H5 and that used by the
example code.  I have tried to follow the documented interface.
"""
# Interface

I2C=0
SPI=1

AUX_SPI=256

# Sampling

OVER_SAMPLE_1 = 1
OVER_SAMPLE_2 = 2
OVER_SAMPLE_4 = 3
OVER_SAMPLE_8 = 4
OVER_SAMPLE_16 = 5

class sensor:
   """
   A class to read the BME280 pressure, humidity, and temperature sensor._
   """

   # BME280 Registers

   _calib00    = 0x88

   _T1         = 0x88 - _calib00
   _T2         = 0x8A - _calib00
   _T3         = 0x8C - _calib00

   _P1         = 0x8E - _calib00
   _P2         = 0x90 - _calib00
   _P3         = 0x92 - _calib00
   _P4         = 0x94 - _calib00
   _P5         = 0x96 - _calib00
   _P6         = 0x98 - _calib00
   _P7         = 0x9A - _calib00
   _P8         = 0x9C - _calib00
   _P9         = 0x9E - _calib00

   _H1         = 0xA1 - _calib00

   _chip_id    = 0xD0
   _reset      = 0xE0

   _calib26    = 0xE1

   _H2         = 0xE1 - _calib26
   _H3         = 0xE3 - _calib26   
   _xE4        = 0xE4 - _calib26
   _xE5        = 0xE5 - _calib26
   _xE6        = 0xE6 - _calib26
   _H6         = 0xE7 - _calib26

   _ctrl_hum   = 0xF2
   _status     = 0xF3
   _ctrl_meas  = 0xF4
   _config     = 0xF5

   _rawdata    = 0xF7

   _p_msb      = 0xF7 - _rawdata
   _p_lsb      = 0xF8 - _rawdata
   _p_xlsb     = 0xF9 - _rawdata
   _t_msb      = 0xFA - _rawdata
   _t_lsb      = 0xFB - _rawdata
   _t_xlsb     = 0xFC - _rawdata
   _h_msb      = 0xFD - _rawdata
   _h_lsb      = 0xFE - _rawdata

   _os_ms = [0, 1, 2, 4, 8, 16]

   def __init__(self, pi, sampling=OVER_SAMPLE_1, interface=I2C,
                   bus=1, address=0x76,
                   channel=0, baud=10000000, flags=0):
      """
      Instantiate with the Pi.

      Optionally the over sampling rate may be set (default 1).

      Optionally the interface to be used may be specified as
      I2C (default) or SPI.

      For I2C the default bus is 1 and default address is 0x76.
      The address will be 0x76 if SDO is pulled to ground and
      0x77 if SDO is pulled to 3V3.  For I2C the CS pin (if
      present) should be pulled to 3V3.

      For SPI the default channel is 0, baud is 10Mbps, and flags
      is 0 (main SPI, mode 0).

      Example using I2C, bus 1, address 0x76

      s = BME280.sensor(pi)


      Example using main SPI, channel 0, baud 10Mbps

      s = BME280.sensor(pi, interface=SPI)


      Example using auxiliary SPI, channel 2, baud 50k

      s = BME280.sensor(pi, sampling=OVER_SAMPLE_4,
             interface=SPI, channel=2, flags=AUX_SPI, baud=50000)


      GPIO       pin  pin    GPIO
      3V3         1    2      5V
      2 (SDA)     3    4      5V
      3 (SCL)     5    6      0V
      4           7    8      14 (TXD)
      0V          9   10      15 (RXD)
      17 (ce1)   11   12      18 (ce0)
      27         13   14      0V
      22         15   16      23
      3V3        17   18      24
      10 (MOSI)  19   20      0V
      9 (MISO)   21   22      25
      11 (SCLK)  23   24      8 (CE0)
      0V         25   26      7 (CE1)
                 .......
      0 (ID_SD)  27   28      1 (ID_SC)
      5          29   30      0V
      6          31   32      12
      13         33   34      0V
      19 (miso)  35   36      16 (ce2)
      26         37   38      20 (mosi)
      0V         39   40      21 (sclk)
      """
      self.pi = pi

      if interface == I2C:
         self.I2C = True
      else:
         self.I2C = False

      self.sampling = sampling

      if self.I2C:
         self.h = pi.i2c_open(bus, address)
      else:
         self.h = pi.spi_open(channel, baud, flags)

      self._load_calibration()

      self.measure_delay = self._measurement_time(sampling, sampling, sampling)

      self.t_fine = 0.0

   def _measurement_time(self, os_temp, os_press, os_hum):
      ms = ( (1.25  + 2.3 * sensor._os_ms[os_temp]) +
             (0.575 + 2.3 * sensor._os_ms[os_press]) +
             (0.575 + 2.3 * sensor._os_ms[os_hum]) )
      return (ms/1000.0)

   def _u16(self, _calib, off):
      return (_calib[off] | (_calib[off+1]<<8))

   def _s16(self, _calib, off):
      v = self._u16(_calib, off)
      if v > 32767:
         v -= 65536
      return v

   def _u8(self, _calib, off):
      return _calib[off]

   def _s8(self, _calib, off):
      v = self._u8(_calib,off)
      if v > 127:
         v -= 256
      return v

   def _write_registers(self, data):
      if self.I2C:
         self.pi.i2c_write_device(self.h, data)
      else:
         for i in range(0, len(data), 2):
            data[i] &= 0x7F
         self.pi.spi_xfer(self.h, data)

   def _read_registers(self, reg, count):
      if self.I2C:
         return self.pi.i2c_read_i2c_block_data(self.h, reg, count)
      else:
         c, d = self.pi.spi_xfer(self.h, [reg|0x80] + [0]*count)
         if c > 0:
            return c-1, d[1:]
         else:
            return c, d

   def _load_calibration(self):

      c, d1 = self._read_registers(sensor._calib00, 26)

      self.T1 = self._u16(d1, sensor._T1)
      self.T2 = self._s16(d1, sensor._T2)
      self.T3 = self._s16(d1, sensor._T3)

      self.P1 = self._u16(d1, sensor._P1)
      self.P2 = self._s16(d1, sensor._P2)
      self.P3 = self._s16(d1, sensor._P3)
      self.P4 = self._s16(d1, sensor._P4)
      self.P5 = self._s16(d1, sensor._P5)
      self.P6 = self._s16(d1, sensor._P6)
      self.P7 = self._s16(d1, sensor._P7)
      self.P8 = self._s16(d1, sensor._P8)
      self.P9 = self._s16(d1, sensor._P9)

      self.H1 = self._u8(d1, sensor._H1)

      c, d2 = self._read_registers(sensor._calib26, 7)

      self.H2 = self._s16(d2, sensor._H2)

      self.H3 = self._u8(d2, sensor._H3)

      t = self._u8(d2, sensor._xE5)

      t_l = t & 15
      t_h = (t >> 4) & 15

      self.H4 = (self._u8(d2, sensor._xE4) << 4) | t_l

      if self.H4 > 2047:
         self.H4 -= 4096

      self.H5 = (self._u8(d2, sensor._xE6) << 4) | t_h

      if self.H5 > 2047:
         self.H5 -= 4096

      self.H6 = self._s8(d2, sensor._H6)

   def _read_raw_data(self):

      # Set oversampling rate and force reading.

      self._write_registers(
         [sensor._ctrl_hum, self.sampling,
          sensor._ctrl_meas, self.sampling << 5 | self.sampling << 2 | 1])

      # Measurement delay.

      time.sleep(self.measure_delay)

      # Grab reading.

      c, d = self._read_registers(sensor._rawdata, 8)

      msb = self._u8(d, sensor._t_msb)
      lsb = self._u8(d, sensor._t_lsb)
      xlsb = self._u8(d, sensor._t_xlsb)
      raw_t = ((msb << 16) | (lsb << 8) | xlsb) >> 4

      msb = self._u8(d, sensor._p_msb)
      lsb = self._u8(d, sensor._p_lsb)
      xlsb = self._u8(d, sensor._p_xlsb)
      raw_p = ((msb << 16) | (lsb << 8) | xlsb) >> 4

      msb = self._u8(d, sensor._h_msb)
      lsb = self._u8(d, sensor._h_lsb)
      raw_h = (msb << 8) | lsb

      return raw_t, raw_p, raw_h

   def read_data(self):
      """
      Returns the temperature, pressure, and humidity as a tuple.

      Each value is a float.

      The temperature is returned in degrees centigrade.  The
      pressure is returned in Pascals.  The humidity is returned
      as the relative humidity between 0 and 100%.
      """

      raw_t, raw_p, raw_h = self._read_raw_data()

      var1 = (raw_t/16384.0 - (self.T1)/1024.0) * float(self.T2)
      var2 = (((raw_t)/131072.0 - (self.T1)/8192.0) *
              ((raw_t)/131072.0 - (self.T1)/8192.0)) * (self.T3)

      self.t_fine = var1 + var2

      t = (var1 + var2) / 5120.0

      var1 = (self.t_fine/2.0) - 64000.0
      var2 = var1 * var1 * self.P6 / 32768.0
      var2 = var2 + (var1 * self.P5 * 2.0)
      var2 = (var2/4.0)+(self.P4 * 65536.0)
      var1 = ((self.P3 * var1 * var1 / 524288.0) + (self.P2 * var1)) / 524288.0
      var1 = (1.0 + var1 / 32768.0)*self.P1
      if var1 != 0.0:
         p = 1048576.0 - raw_p
         p = (p - (var2 / 4096.0)) * 6250.0 / var1
         var1 = self.P9 * p * p / 2147483648.0
         var2 = p * self.P8 / 32768.0
         p = p + (var1 + var2 + self.P7) / 16.0
      else:
         p = 0

      h = self.t_fine - 76800.0

      h = ( (raw_h - ((self.H4) * 64.0 + (self.H5) / 16384.0 * h)) *
            ((self.H2) / 65536.0 * (1.0 + (self.H6) / 67108864.0 * h *
            (1.0 + (self.H3) / 67108864.0 * h))))

      h = h * (1.0 - self.H1 * h / 524288.0)

      if h > 100.0:
         h = 100.0
      elif h < 0.0:
         h = 0.0

      return t, p, h

   def cancel(self):
      """
      Cancels the sensor and releases resources.
      """
      if self.h is not None:

         if self.I2C:
            self.pi.i2c_close(self.h)
         else:
            self.pi.spi_close(self.h)

         self.h = None

if __name__ == "__main__":

   import time
   import BME280
   import pigpio

   pi = pigpio.pi()

   if not pi.connected:
      exit(0)

   s = BME280.sensor(pi)

   stop = time.time() + 60

   while stop > time.time():
      t, p, h = s.read_data()
      print("h={:.2f} p={:.1f} t={:.2f}".format(h, p/100.0, t))
      time.sleep(0.9)

   s.cancel()

   pi.stop()

smbusを使ったサンプルは

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
import sys
import smbus
#I2C
bus_number  = 1


#i2c
def writeReg(reg_address, data):
    bus.write_byte_data(i2c_address,reg_address,data)

def get_calib_param():
    calib = []
    
    for i in range (0x88,0x88+24):
        calib.append(bus.read_byte_data(i2c_address,i))
    calib.append(bus.read_byte_data(i2c_address,0xA1))
    for i in range (0xE1,0xE1+7):
        calib.append(bus.read_byte_data(i2c_address,i))

    digT.append((calib[1] << 8) | calib[0])
    digT.append((calib[3] << 8) | calib[2])
    digT.append((calib[5] << 8) | calib[4])
    digP.append((calib[7] << 8) | calib[6])
    digP.append((calib[9] << 8) | calib[8])
    digP.append((calib[11]<< 8) | calib[10])
    digP.append((calib[13]<< 8) | calib[12])
    digP.append((calib[15]<< 8) | calib[14])
    digP.append((calib[17]<< 8) | calib[16])
    digP.append((calib[19]<< 8) | calib[18])
    digP.append((calib[21]<< 8) | calib[20])
    digP.append((calib[23]<< 8) | calib[22])
    digH.append( calib[24] )
    digH.append((calib[26]<< 8) | calib[25])
    digH.append( calib[27] )
    digH.append((calib[28]<< 4) | (0x0F & calib[29]))
    digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F))
    digH.append( calib[31] )
    
    for i in range(1,2):
        if digT[i] & 0x8000:
            digT[i] = (-digT[i] ^ 0xFFFF) + 1

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

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

def readData():
    data = []
    for i in range (0xF7, 0xF7+8):
        data.append(bus.read_byte_data(i2c_address,i))
    pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
    hum_raw  = (data[6] << 8)  |  data[7]
    
    return compensate_T(temp_raw),compensate_P(pres_raw),compensate_H(hum_raw)



def compensate_P(adc_P):
    global  t_fine
    pressure = 0.0
    
    v1 = (t_fine / 2.0) - 64000.0
    v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5]
    v2 = v2 + ((v1 * digP[4]) * 2.0)
    v2 = (v2 / 4.0) + (digP[3] * 65536.0)
    v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8)  + ((digP[1] * v1) / 2.0)) / 262144
    v1 = ((32768 + v1) * digP[0]) / 32768
    
    if v1 == 0:
        return 0
    pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125
    if pressure < 0x80000000:
        pressure = (pressure * 2.0) / v1
    else:
        pressure = (pressure / v1) * 2
    v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096
    v2 = ((pressure / 4.0) * digP[7]) / 8192.0
    pressure = pressure + ((v1 + v2 + digP[6]) / 16.0)  
    return pressure/100
    #print ("気圧 : %7.2f hPa" % (pressure/100))

def compensate_T(adc_T):
    global t_fine
    v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1]
    v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2]
    t_fine = v1 + v2
    temperature = t_fine / 5120.0
    #print ("温度 : %-6.2f " % (temperature)) 
    return t_fine / 5120.0
def compensate_H(adc_H):
    global t_fine
    var_h = t_fine - 76800.0
    if var_h != 0:
        var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h)))
    else:
        return 0
    var_h = var_h * (1.0 - 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 ("湿度 : %6.2f " % (var_h))
    return var_h

def setupBME280():
    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

    writeReg(0xF2,ctrl_hum_reg)
    writeReg(0xF4,ctrl_meas_reg)
    writeReg(0xF5,config_reg)


#i2c
i2c_address = 0x76

bus = smbus.SMBus(bus_number)
digT = []
digP = []
digH = []
t_fine = 0.0
setupBME280()
get_calib_param()

if __name__ == '__main__':
    try:
        while True:          
            t,p,h = readData()
            print ("温度 : %6.2f 度" % (t))
            print ("湿度 : %6.2f " % (h))
            print ("気圧 : %7.2f hPa" % (p))
            print ("----------------------")
            time.sleep(3)

    except KeyboardInterrupt:
        sys.exit(0)

実行させてみると小数点2桁目が異なるがほぼ同じ結果を得られる

カテゴリー iot