OrangePi-PCを使ってみる

Python GPIOライブラリ(WiringPi-Python-OP)


Python GPIOライブラリのWiringPi-Python-OPを紹介します。
これはC言語のWiringPi for OrangePiのラッパーで、swigを使ってpythonのライブラリを構築します。
リポジトリにWiringPi for OrangePiが含まれているので、別途これをインストールする必要は有りません。
インストール手順は、こちらに 書いてある通りですが、swigのバージョンが更新されたので、
一部ファイルを修正しないとswigが通りません。
まずはC言語ライブラリをビルドします。
$ sudo apt update
$ sudo apt install git python3-dev python3-setuptools swig
$ git clone --recursive https://github.com/lanefu/WiringPi-Python-OP.git
$ cd WiringPi-Python-OP
$ cd WiringPi/
$ sudo ./build
$ cd ..

ここで、「bindings.i」を一部修正する必要が有ります。
// Header file WiringPi/wiringPi/wiringPiSPI.h
int wiringPiSPIGetFd     (int channel) ;
int wiringPiSPIDataRW    (int channel, unsigned char *data, int len) ;
#int wiringPiSPISetupMode (int channel, int speed, int mode) ;
#int wiringPiSPISetup     (int channel, int speed) ;

swig3以降では行頭の#によるコメントが認められなくなりました。上記の部分を以下のように変更します。
// Header file WiringPi/wiringPi/wiringPiSPI.h
int wiringPiSPIGetFd     (int channel) ;
int wiringPiSPIDataRW    (int channel, unsigned char *data, int len) ;
//int wiringPiSPISetupMode (int channel, int speed, int mode) ;
//int wiringPiSPISetup     (int channel, int speed) ;

「bindings.i」を修正したらswigが通ります。
swigを使ってラッパーをビルドします。
$ swig -python wiringpi.i
$ sudo python3 setup.py install

付属しているテストコードがpython2仕様なので一部変更して実行します。
$ cd tests
$ vi test.py

import wiringpi
io = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS)
print(io.digitalRead(1))
print(io.analogRead(1))

$ sudo python3 test.py
0
0

まずはLチカを試してみます。Pin#11のLEDが点滅します。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wiringpi
import time

led = 0
wiringpi.wiringPiSetup()
wiringpi.pinMode(led,wiringpi.OUTPUT)
wiringpi.digitalWrite(led,wiringpi.LOW)

for _ in range(3):
  wiringpi.digitalWrite(led,wiringpi.HIGH)
  time.sleep(1)
  wiringpi.digitalWrite(led,wiringpi.LOW)
  time.sleep(1)

このライブラリはC言語ライブラリのpythonラッパーなので、GPIOの番号はC言語ライブラリのwPi番号となります。
wPi番号 物理ピン番号 wPi番号

1 2
8
3 4
9
5 6
7
7 8 15

9 10 16
0
11 12 1
2
13 14
3
15 16 4

17 18 5
12 19 20
13 21 22 6
14 23 24 10

25 26 11
30 27 28 31
21 29 30
22 31 32 26
23 33 34
24 35 36 27
25 37 38 28

39 40 29

GPIOのポート状態も正しく取れて、gpio readall と同じ結果になりました。
以下のコードで各GPIOのmodeとaltを取得することができます。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wiringpi
LOW = wiringpi.LOW
HIGH = wiringpi.HIGH
PINS = {
   0 : {'name' : '3.3v', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 0},
   1 : {'name' : 'PA12', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 3},
   2 : {'name' : 'PA11', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 5},
   3 : {'name' : 'PA6',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 7},
   3 : {'name' : 'PA13', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 8},
   4 : {'name' : 'PA14', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 10},
   5 : {'name' : 'PA1',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 11},
   6 : {'name' : 'PD14', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 12},
   7 : {'name' : 'PA0',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 13},
   8 : {'name' : 'PA3',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 15},
   9 : {'name' : 'PC4',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 16},
  10 : {'name' : 'PC7',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 18},
  11 : {'name' : 'PC0',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 19},
  12 : {'name' : 'PC1',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 21},
  13 : {'name' : 'PA2',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 22},
  14 : {'name' : 'PC2',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 23},
  15 : {'name' : 'PC3',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 24},
  16 : {'name' : 'PA21', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 26},
  17 : {'name' : 'PA19', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 27},
  18 : {'name' : 'PA18', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 28},
  19 : {'name' : 'PA7',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 29},
  20 : {'name' : 'PA8',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 31},
  21 : {'name' : 'PG8',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 32},
  22 : {'name' : 'PA9',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 33},
  23 : {'name' : 'PA10', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 35},
  24 : {'name' : 'PG9',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 36},
  25 : {'name' : 'PA20', 'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 37},
  26 : {'name' : 'PG6',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 38},
  27 : {'name' : 'PG7',  'mode' : 0, 'smode' : '' , 'value' : LOW,  'pin' : 40}
}

ALTS = [
   "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
]

wiringpi.wiringPiSetupPhys()

for i in PINS:
    pin = PINS[i]['pin']
    print("i={} pin={}".format(i, pin))
    alt=wiringpi.getAlt(pin)
    value=wiringpi.digitalRead(pin)
    #print("pin={} alt={} value={}".format(pin, ALTS[alt] ,value))
    PINS[i]['mode']=alt
    PINS[i]['smode']=ALTS[alt]
    PINS[i]['value']=value

for i in PINS:
    print("name={} mode={} {} value={}".format(PINS[i]['name'], PINS[i]['mode'], PINS[i]['smode'], PINS[i]['value']))



RaspberryPiではWiringPiライブラリが、ほぼ標準のライブラリとなっています。
WiringPiのpythonパッケージも、aptでインストールすることができます。

驚いたのは、WiringPi pythonパッケージを使った、i2cに関する情報がネット上にほとんど見当たらないことです。
WiringPiのサンプルコードは多数公開されていますが、ほとんどがC言語のサンプルです。
こち らこ ちらで 紹介されているのを発見しましたが、他にはほとんどありませんでした。

こ ちらにsmbusライブラリを使って、BMP180のi2cセンサーから、温度と気圧を読み出すコードが公開されています。
そこで、これをpython-peripheryライブラリに移植してみます。
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/
#
#--------------------------------------
##import smbus
import wiringpi
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
wiringpi.wiringPiSetup()
i2c = wiringpi.I2C()
dev = i2c.setup(DEVICE)

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)
  chip_id = i2c.readReg8(dev,REG_ID)
  chip_version = i2c.readReg8(dev,REG_ID+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 = []
  for x in range(22):
    data = i2c.readReg8(dev,REG_CALIB+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)
  i2c.writeReg8(dev,REG_MEAS,CRV_TEMP)
  time.sleep(0.005)
##  (msb, lsb) = bus.read_i2c_block_data(addr, REG_MSB, 2)
  msb = i2c.readReg8(dev,REG_MSB)
  lsb = i2c.readReg8(dev,REG_MSB+1)
  UT = (msb << 8) + lsb

  # Read pressure
##  bus.write_byte_data(addr, REG_MEAS, CRV_PRES + (OVERSAMPLE << 6))
  i2c.writeReg8(dev,REG_MEAS,CRV_PRES + (OVERSAMPLE << 6))
  time.sleep(0.04)
##  (msb, lsb, xsb) = bus.read_i2c_block_data(addr, REG_MSB, 3)
  msb = i2c.readReg8(dev,REG_MSB)
  lsb = i2c.readReg8(dev,REG_MSB+1)
  xsb = i2c.readReg8(dev,REG_MSB+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

  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:
    pass
 
if __name__=="__main__":
   main()

今回、BMP180ではなく、BMP085を使いましたが、何とか動きました。
しかし情報のあまりの少なさには驚きました。
嘘だと思ったら探してみてください。
$ sudo python3 bmp180.py
Chip ID     : 85
Version     : 2
Press CTRL+C to exit
Temperature : 19.6 C
Pressure    : 1007.07 mbar
Temperature : 19.6 C
Pressure    : 1007.02 mbar
Temperature : 19.6 C
Pressure    : 1007.07 mbar

このライブラリが提供しているClassをこ ちらで見ることができます。
残念ながらSPIはサポートしていません。

次回
はadafruit_blinkaライブラリを紹介します。