OrangePi-PCを使ってみる

Python GPIOライブラリ(orangepi_PC_gpio_pyH3)


OrangePi-PCで使えるPython GPIOライブラリのorangepi_PC_gpio_pyH3を紹介します。
このライブラリはA20-OLinuXino-MICROボード用のpyA20ライブラリをH3向けに移植したもので、
比較的早い時期に公開されたので、OrangePiではよく使われています。
pyA20ライブラリのマニュアルがこちらに公 開されていますが、GPIOだけでなくSPIやi2cもサポートしています。

インストールは以下の手順で行います。
$ sudo apt update
$ sudo apt install git python3-dev python3-setuptools
$ git clone https://github.com/duxingkei33/orangepi_PC_gpio_pyH3.git
$ cd orangepi_PC_gpio_pyH3
$ sudo python3 setup.py install

このライブラリのAPIはRaspberryPiのpython-rpi-gpioライブラリとよく似ています。
RaspberryPiのpython-rpi-gpioライブラリで GPIO17(Pin#11) のLチカを行う場合のコードは以下の様になります。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time

led = 17
#GPIO.setmode(GPIO.BOARD)
GPIO.setmode(GPIO.BCM)
GPIO.setup(led,GPIO.OUT)

for _ in range(3):
    GPIO.output(led,True)
    time.sleep(1.0)
    GPIO.output(led,False)
    time.sleep(1.0)

GPIO.cleanup()

orangepi_PC_gpio_pyH3ライブラリで PA1(Pin#11) のLチカを行う場合のコードは以下の様になります。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pyA20.gpio import gpio
from pyA20.gpio import port
import time

led = port.PA1
gpio.init()
gpio.setcfg(led,gpio.OUTPUT)

for _ in range(3):
    gpio.output(led,True)
    time.sleep(1.0)
    gpio.output(led,False)
    time.sleep(1.0)

#gpio.cleanup()

python-rpi-gpioライブラリと、orangepi_PC_gpio_pyH3ライブラリのピン番号の対応は以下の通りです。
python-rpi-gpio orangepi_PC_gpio_pyH3 physical Pin# orangepi_PC_gpio_pyH3 python-rpi-gpio


1 2

2 port.PA12 3 4

3 port.PA11 5 6

4 port.PA6 7 8 port.PA13 14


9 10 port.PA14 15
17 port.PA1 11 12 port.PD14 18
27 port.PA0 13 14

22 port.PA3 15 16 port.PC4 23


17 18 port.PC7 24
10 port.PC0 19 20

9 port.PC1 21 22 port.PA2 25
11 port.PC2 23 24 port.PC3 8


25 26 port.PA21 7
0 port.PA19 27 28 port.PA18 1
5 port.PA7 29 30

6 port.PA8 31 32 port.PG8 12
13 port.PA9 33 34

19 port.PA10 35 36 port.PG9 16
26 port.PA20 37 38 port.PG6 20


39 40 port.PG7 21

以下のコードでオンボードの赤のLEDがLチカします。
オンボードの緑のLEDは、メモリ上のアドレスがちょっと特殊なので、このライブラリでは制御できません。
# -*- coding: utf-8 -*-
from pyA20.gpio import gpio
from pyA20.gpio import port
import time

led = 15
gpio.init()
gpio.setcfg(led,gpio.OUTPUT)

for _ in range(3):
    gpio.output(led,True)
    time.sleep(1.0)
    gpio.output(led,False)
    time.sleep(1.0)



こ ちらにsmbusライブラリを使って、BMP180のi2cセンサーから、温度と気圧を読み出すコードが公開されています。
そこで、これをorangepi_PC_gpio_pyH3ライブラリに移植してみます。
BMP180とは以下の様に接続します。
BMP180 Opi(Pin#)
3V3 3.3V
ECC *NotUse*
CLR *NotUse*
SCL SCL(#5)
SDA SDA(#3)
5V *NotUse*
GND GND

このライブラリのために、変更した箇所は行頭が##のコードです。
#!/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 pyA20 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


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)
  i2c.write([REG_ID])
  (chip_id, chip_version) = i2c.read(2)

  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 = []
  i2c.write([REG_CALIB])
  cal = i2c.read(22)

  # 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)
  i2c.write([REG_MEAS, CRV_TEMP])
  time.sleep(0.005)
##  (msb, lsb) = bus.read_i2c_block_data(addr, REG_MSB, 2)
  i2c.write([REG_MSB])
  (msb, lsb) = i2c.read(2)
  UT = (msb << 8) + lsb

  # Read pressure
##  bus.write_byte_data(addr, REG_MEAS, CRV_PRES + (OVERSAMPLE << 6))
  i2c.write([REG_MEAS, CRV_PRES + (OVERSAMPLE << 6)])
  time.sleep(0.04)
##  (msb, lsb, xsb) = bus.read_i2c_block_data(addr, REG_MSB, 3)
  i2c.write([REG_MSB])
  (msb, lsb, xsb) = i2c.read(3)
  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():
    
  i2c.init("/dev/i2c-0") #Initialize module to use /dev/i2c-0
  i2c.open(DEVICE) #The slave device address is 0x77

  (chip_id, chip_version) = readBmp180Id()
  print("Chip ID     : {0}".format(chip_id))
  print("Version     : {0}".format(chip_version))
  print
 
  try:
    print ("Press CTRL+C to exit")
    while True:
      (temperature,pressure)=readBmp180()
      print("Temperature : {0} C".format(temperature))
      print("Pressure    : {0} mbar".format(pressure))
      time.sleep(1)

  except KeyboardInterrupt:
    i2c.close()
 
if __name__=="__main__":
   main()

今回、BMP180ではなく、BMP085を使いましたが、あっさりと動きました。
$ sudo python3 bmp180.py
Chip ID     : 85
Version     : 2
Press CTRL+C to exit
Temperature : 24.5 C
Pressure    : 1006.61 mbar
Temperature : 24.5 C
Pressure    : 1006.63 mbar
Temperature : 24.5 C
Pressure    : 1006.63 mbar

SPIも動くはずなんですが、読み込みのAPIを発行すると落ちます。
今のところ、原因が分かりません。
$ sudo python3 bmp280.py
Read the contents of the ID register
*** stack smashing detected ***: terminated
中止

次回はOPI.GPIOライブラリを紹介します。