Raspberry Pi 3で、pigpioを使ってI2CとSPIインタフェースを使ったpythonスクリプトを作成します。pigpioでpythonインタフェースに関する詳細は、pigpio libraryに記述されています。
pigpioを使ったI2Cインタフェースの確認のために、Raspberry Pi 3に環境センサー「BME280」を接続します。接続の詳細については、「raspberry pi 3でC言語による環境センサー「BME280」の接続」を参照してください。pigpioを使ったSPIインタフェースの確認のために、Raspberry Pi 3にADC「MCP3208」を接続します。接続の詳細については、「Raspberry PiでADC「MCP3208」のspi接続」を参照してください。
メインメニューの「設定」→「Raspberry Pi の設定」の「インターフェイス」タブからRaspberry Pi 3の設定を次のようにします。
- I2C:Enabled
- SPI:Disabled
- Remote GPIO:Enabled
pigpioによる環境センサー「BME280」の実装
Raspberry Pi 3に環境センサー「BME280」をI2Cインタフェースにより接続し、次のコマンドで接続を確認し、アドレス「0×76」になっていることを確認します。
$ i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- 76 --
次のPythonスクリプト「BME280.py」を作成します。
#!/usr/bin/env python import time 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, bus=1, address=0x76, channel=0, baud=10000000, flags=0): self.pi = pi self.sampling = sampling self.h = pi.i2c_open(bus, address) 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): self.pi.i2c_write_device(self.h, data) def _read_registers(self, reg, count): return self.pi.i2c_read_i2c_block_data(self.h, reg, count) 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: self.pi.i2c_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()
Pythonスクリプト「BME280.py」を次のコマンドで実行します。
$ python3 BME280.py h=57.33 p=1019.2 t=19.55 h=57.34 p=1019.2 t=19.56 h=57.34 p=1019.2 t=19.56 h=57.33 p=1019.2 t=19.57 h=57.36 p=1019.3 t=19.57 h=57.38 p=1019.3 t=19.56 h=57.38 p=1019.2 t=19.57 h=57.39 p=1019.3 t=19.58
ロジアナでI2C信号「SCL (シリアルクロック)」「SDA (シリアルデータ)」を測定しました。
pigpioによるADC「MCP3208」の実装
Raspberry Pi 3にADC「MCP3208」をSPIインタフェースにより接続し、次のPythonスクリプト「spitest.py」を作成します。「
import time import pigpio #MCP3208から値を取得するクラス class MCP3208_Class: channel = 1 baud = 50000 flags = 0 """コンストラクタ""" def __init__(self, pi,ref_volts): self.pi = pi self.ref_volts = ref_volts self.h = pi.spi_open(self.channel, self.baud, self.flags) """電圧取得""" def GetVoltage(self,ch): # c, raw = self.pi.spi_xfer(self.h,[0x6,(8+ch)<<4,0]) # c, raw = self.pi.spi_xfer(self.h,[0x6,ch<<6,0]) c, raw = self.pi.spi_xfer(self.h,[1,(8+ch)<<4,0]) print("c: {0} raw: {1}".format(c, raw)) raw2 = ((raw[1]&3) << 8) + raw[2] volts = (raw2 * self.ref_volts ) / float(1023) volts = round(volts,4) return volts """終了処理""" def Cleanup(self): self.pi.spi_close(self.h) """メイン関数""" if __name__ == '__main__': pi = pigpio.pi() if not pi.connected: exit(0) ADC = MCP3208_Class(pi,ref_volts=3.3) try: while True: volts = ADC.GetVoltage(ch=0) print("volts ch0: {:8.2f}".format(volts)) volts = ADC.GetVoltage(ch=1) print("volts ch1: {:8.2f}".format(volts)) volts = ADC.GetVoltage(ch=2) print("volts ch2: {:8.2f}".format(volts)) time.sleep(1) except KeyboardInterrupt : #Ctl+Cが押されたらループを終了 print("\nCtl+C") except Exception as e: print(str(e)) finally: ADC.Cleanup() print("\nexit program")
Pythonスクリプト「spitest.py」を次のコマンドで実行します。
$ python3 spitest.py c: 3 raw: bytearray(b'\x00\x02\xff') volts ch0: 2.47 c: 3 raw: bytearray(b'\x00\x01\x96') volts ch1: 1.31 c: 3 raw: bytearray(b'\x00\x00\x03') volts ch2: 0.01 c: 3 raw: bytearray(b'\x00\x03\x17') volts ch0: 2.55 c: 3 raw: bytearray(b'\x00\x01\x95') volts ch1: 1.31 c: 3 raw: bytearray(b'\x00\x00\x03') volts ch2: 0.01
SPIインタフェースを「」にすると、MCP3208から「0」の値が入力されます。
ロジアナでSPI信号「MOSI(Master-Out Slave-In)」「MISO(Master-In Slave-Out)」「SCLK(Serial Clock)」「CE0/CE1」を測定しました。SCLKが50kHzになっていることが確認できます。