OrangePi-PCを使ってみる

Python i2cライブラリ(python-periphery)

今まで、smbusライブラリとorangepi_PC_gpio_pyH3 ライブラリを使ってBMP085のi2cセンサーか ら、温度と気圧を読み出しました。
どちらもそれなりに有名なライブラリですが、今回はこれをpython-peripheryライブラリに移植してみます。
このライブラリは、ほとんどネット上で紹介されていませんが、なかなかよくできたライブラリです。
非常に移植性が高く、/dev/i2c-0のデバイスファイルが有れば、どんなボードでも動きます。

また、GPIOの操作も/sys/class/gpioのデバイスファイルを直接操作するので、
OPi-ZERO-Plus2やOPi-2GI-IOTなど、WiringPiライブラリが存在しないボードでも、ポート番号(PAxxとか PCxx)さえ分かれば動き ます。

python-peripheryのインストールは以下の通りです。
$ git clone https://github.com/vsergeev/python-periphery.git
$ cd python-periphery/
$ sudo python3 setup.py install

変更した箇所は行頭が##のコードです。
#!/usr/bin/python
#--------------------------------------
#    ___  ___  _ ____          
#   / _ \/ _ \(_) __/__  __ __
#  / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/  /_/___/ .__/\_, /  
#                /_/   /___/   
#
#           bmp180.py
#  Read data from a digital pressure sensor.
#
# Author : Matt Hawkins
# Date   : 17/02/2017
#
# http://www.raspberrypi-spy.co.uk/
#
# https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/bmp180.py
#
#--------------------------------------
##import smbus
from periphery import I2C
import time
from ctypes import c_short
 
DEVICE = 0x77 # Default device I2C address
 
##bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
##bus = smbus.SMBus(1) # Rev 2 Pi uses 1
i2c = I2C("/dev/i2c-0")
 
def convertToString(data):
  # Simple function to convert binary data into
  # a string
  return str((data[1] + (256 * data[0])) / 1.2)

def getShort(data, index):
  # return two bytes from data as a signed 16-bit value
  return c_short((data[index] << 8) + data[index + 1]).value

def getUshort(data, index):
  # return two bytes from data as an unsigned 16-bit value
  return (data[index] << 8) + data[index + 1]

def readBmp180Id(addr=DEVICE):
  # Chip ID Register Address
  REG_ID     = 0xD0
##  (chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2)
  msgs = [I2C.Message([REG_ID]), I2C.Message([0,0], read=True)]
  i2c.transfer(DEVICE, msgs)
  chip_id = msgs[1].data[0]
  chip_version = msgs[1].data[1]
  return (chip_id, chip_version)
 
def readBmp180(addr=DEVICE):
  # Register Addresses
  REG_CALIB  = 0xAA
  REG_MEAS   = 0xF4
  REG_MSB    = 0xF6
  REG_LSB    = 0xF7
  # Control Register Address
  CRV_TEMP   = 0x2E
  CRV_PRES   = 0x34
  # Oversample setting
  OVERSAMPLE = 3    # 0 - 3
 
  # Read calibration data
  # Read calibration data from EEPROM
##  cal = bus.read_i2c_block_data(addr, REG_CALIB, 22)
  cal = [0 for i in range(22)]
  msgs = [I2C.Message([REG_CALIB]), I2C.Message(cal, read=True)]
  i2c.transfer(DEVICE, msgs)
  cal = []
  for x in range(22):
    data = msgs[1].data[x]
    cal.append(data)

  # Convert byte data to word values
  AC1 = getShort(cal, 0)
  AC2 = getShort(cal, 2)
  AC3 = getShort(cal, 4)
  AC4 = getUshort(cal, 6)
  AC5 = getUshort(cal, 8)
  AC6 = getUshort(cal, 10)
  B1  = getShort(cal, 12)
  B2  = getShort(cal, 14)
  MB  = getShort(cal, 16)
  MC  = getShort(cal, 18)
  MD  = getShort(cal, 20)

  # Read temperature
##  bus.write_byte_data(addr, REG_MEAS, CRV_TEMP)
  msgs = [I2C.Message([REG_MEAS,CRV_TEMP]), I2C.Message([0], read=False)]
  i2c.transfer(DEVICE, msgs)
  time.sleep(0.005)
##  (msb, lsb) = bus.read_i2c_block_data(addr, REG_MSB, 2)
  msgs = [I2C.Message([REG_MSB]), I2C.Message([0,0], read=True)]
  i2c.transfer(DEVICE, msgs)
  msb = msgs[1].data[0]
  lsb = msgs[1].data[1]
  UT = (msb << 8) + lsb

  # Read pressure
##  bus.write_byte_data(addr, REG_MEAS, CRV_PRES + (OVERSAMPLE << 6))
  msgs = [I2C.Message([REG_MEAS,CRV_PRES + (OVERSAMPLE << 6)]), I2C.Message([0], read=False)]
  i2c.transfer(DEVICE, msgs)
  time.sleep(0.04)
##  (msb, lsb, xsb) = bus.read_i2c_block_data(addr, REG_MSB, 3)
  msgs = [I2C.Message([REG_MSB]), I2C.Message([0,0,0], read=True)]
  i2c.transfer(DEVICE, msgs)
  msb = msgs[1].data[0]
  lsb = msgs[1].data[1]
  xsb = msgs[1].data[2]
  UP = ((msb << 16) + (lsb << 8) + xsb) >> (8 - OVERSAMPLE)

  # Refine temperature
  X1 = ((UT - AC6) * AC5) >> 15
  X2 = (MC << 11) / (X1 + MD)
  B5 = X1 + X2
  temperature = int(B5 + 8) >> 4

  # Refine pressure
  B6  = B5 - 4000
  B62 = int(B6 * B6) >> 12
  X1  = (B2 * B62) >> 11
  X2  = int(AC2 * B6) >> 11
  X3  = X1 + X2
  B3  = (((AC1 * 4 + X3) << OVERSAMPLE) + 2) >> 2

  X1 = int(AC3 * B6) >> 13
  X2 = (B1 * B62) >> 16
  X3 = ((X1 + X2) + 2) >> 2
  B4 = (AC4 * (X3 + 32768)) >> 15
  B7 = (UP - B3) * (50000 >> OVERSAMPLE)

  P = (B7 * 2) / B4

  X1 = (int(P) >> 8) * (int(P) >> 8)
  X1 = (X1 * 3038) >> 16
  X2 = int(-7357 * P) >> 16
  pressure = int(P + ((X1 + X2 + 3791) >> 4))

  return (temperature/10.0,pressure/100.0)

def main():
    
  (chip_id, chip_version) = readBmp180Id()
  print("Chip ID     : {0}".format(chip_id))
  print("Version     : {0}".format(chip_version))

  print
 
  (temperature,pressure)=readBmp180()
  print("Temperature : {0} C".format(temperature))
  print("Pressure    : {0} mbar".format(pressure))
 
if __name__=="__main__":
   main()

msgsは構造体のリストになっていて、最初の構造体がレジスター・アドレス、2番目が読み込むデータ領域とread/writeのフラ グです。
キャリブレーションデータを22個読むときには、2番目には22個のリストを渡す必要が有ります。
ちょっと移植にてこずりましたが、何とか動きました。
$ sudo python ./readBMP085-3.py
Chip ID     : 85
Version     : 2

Temperature : 25.0 C
Pressure    : 1009.09 mbar



i2cdetectというツールが有ります。
これを使うとi2cバス上のデバイスをスキャンすることができます。
$ sudo i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 77

python-peripheryライブラリでも同じことができます。
#!/usr/bin/env python

from periphery import I2C

i2c = I2C("/dev/i2c-0") # 0 indicates /dev/i2c-0

def scan(address):
  try:
    msgs = [I2C.Message([0x00]), I2C.Message([0,0], read=True)]
    i2c.transfer(address, msgs)
    print('{:x}'.format(address)),
  except: # exception if read_byte fails
    pass
    print('--'),

print('   '),
for index in range(16):
  print(' {:x}'.format(index)),
print

print('00:          '),
for device in range(3,16):
  scan(device)
for index in range(1,7):
  start = index*16
  end   = (index+1)*16
  print('\n{:x}: '.format(start)),
  for device in range(start,end):
    scan(device)
print('\n70: '),
for device in range(0x70,0x78):
  scan(device)

実行すると同じ結果となります。
$ sudo python ./i2c_periphery.py
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:           -- -- -- -- -- -- -- -- -- -- -- -- --
10:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30:  -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60:  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70:  -- -- -- -- -- -- 76 77

次回はWiring-Python-OPライブラリ を紹介します。