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になっていることが確認できます。

