LinkIt Smart 7688を使ってみる

Python + python-periphery

LinkItにはこれらのデバイスファイルが有ります。


そこで、これらのデバイスファイルを使って、デバイスの制御を試してみます。
python-peripheryは、デバイスファイルを直接操作するライブラリです。
デバイスファイルを直接操作するので、マシン依存が低く、非常に汎用性が高いライブラリです。
root@mylinkit:~# git clone https://github.com/vsergeev/python-periphery.git

root@mylinkit:~# cd python-periphery/

root@mylinkit:~/python-periphery# python setup.py install



GPIOを制御するための、お馴染みのデバイスが有ります。


以下のコマンドでgpio14がON/OFFします。
root@mylinkit:~# echo 14 >/sys/class/gpio/export
root@mylinkit:~# echo out >/sys/class/gpio/gpio14/direction
root@mylinkit:~# echo 1 > /sys/class/gpio/gpio14/value
root@mylinkit:~# echo 0 > /sys/class/gpio/gpio14/value
root@mylinkit:~# echo 14 > /sys/class/gpio/unexport

python-peripheryのGPIOクラスを使ったスクリプトです。
起動時の引数で指定したGPIOがLチカします。
#!/usr/bin/python
#-*- encoding: utf-8 -*-
from periphery import GPIO
import time
import signal
import sys

flag = True

def handler(signal, frame):
  global flag
  print('handler')
  flag = False


argv = sys.argv
argc = len(argv)
#print argc
#print argv
if (argc == 1):
  print 'Usage: python %s gpio#' % argv[0]
  quit()

signal.signal(signal.SIGINT, handler)
pin = int(argv[1])
gpio_out = GPIO(pin, "out")

while flag:
  gpio_out.write(False)
  time.sleep(1.0)
  gpio_out.write(True)
  time.sleep(1.0)


gpio_out.write(False)
gpio_out.close()



PWMデバイスもありました。


以下のコマンドでPWMポートを有効にすることができます。
PWMポートは4つあり、GPIO18、19、20、21にアサインされています。
root@mylinkit:~# echo 0 > /sys/class/pwm/pwmchip0/export
root@mylinkit:~# echo 1 > /sys/class/pwm/pwmchip0/export
root@mylinkit:~# echo 2 > /sys/class/pwm/pwmchip0/export
root@mylinkit:~# echo 3 > /sys/class/pwm/pwmchip0/export
root@mylinkit:~# ls /sys/class/pwm/pwmchip0
device     npwm       pwm1       pwm3       uevent
export     pwm0       pwm2       subsystem  unexport

以下のコマンドで、pwm0(GPIO18)をPWMポートとして、SG90が 0°→ +90° → -90° → 0° に動きます。
root@mylinkit:~# echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period
root@mylinkit:~# echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
root@mylinkit:~# echo 1450000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
root@mylinkit:~# echo 2350000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
root@mylinkit:~# echo 625000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
root@mylinkit:~# echo 1450000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
root@mylinkit:~# echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable

python-peripheryのPWMクラスを使ったスクリプトです。
以下のスクリプトで、pwm0(GPIO18)をPWMポートとして、SG90が 0°→ +90° → -90° → 0° に動きます。
#!/usr/bin/python
#-*- encoding: utf-8 -*-
from periphery import PWM
import time

#Open PWM channel 0, pin 0
#Open /sys/class/pwm/pwmchip0/pwm0
pwm = PWM(0, 0)

#Select the period of PWM signal
#Write /sys/class/pwm/pwmchip0/pwm0/period
pwm.period = 0.02 # 20,000,000 nanoSec

#Change the polarity of the PWM signal
#Write /sys/class/pwm/pwmchip0/pwm0/polarity
pwm.polarity = "normal"

#Enable the PWM signal
#Write /sys/class/pwm/pwmchip0/pwm0/enable
pwm.enable()

#Select the duty cycle
#Write /sys/class/pwm/pwmchip0/pwm0/duty_cycle
#Move to Center
pwm.duty_cycle = 0.0725 # 0.0725 * 20,000,000 = 1,450,000 nanoSec
time.sleep(2)

#Move to Angle +90
pwm.duty_cycle = 0.1175 # 0.1175 * 20,000,000 = 2,350,000 nanoSec
time.sleep(2)

#Move to Angle -90
pwm.duty_cycle = 0.03125 # 0.03125 * 20,000,000 = 625,000 nanoSec
time.sleep(2)

#Move to Center
pwm.duty_cycle = 0.0725 # 0.0725 * 20,000,000 = 1,450,000 nanoSec
time.sleep(2)

#Not unexpoert
pwm.close()



i2cデバイスもありました。
P20=SDA P21=SCLとしてアサインされています。


python-peripheryのI2Cクラスを使ったスクリプトです。
i2cモードのBMP280から温度を読み出す以下のコードが動きました。
#!/usr/bin/python
#--------------------------------------
#    ___  ___  _ ____
#   / _ \/ _ \(_) __/__  __ __
#  / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/  /_/___/ .__/\_, /
#                /_/   /___/
#
#           bme280.py
#  Read data from a digital pressure sensor.
#
#  Official datasheet available from :
#  https://www.bosch-sensortec.com/bst/products/all_products/bme280
#
# Author : Matt Hawkins
# Date   : 25/07/2016
#
# http://www.raspberrypi-spy.co.uk/
#
#--------------------------------------

from periphery import I2C
import time
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte
import sys


DEVICE = 0x76 # Default device I2C address

argv = sys.argv
argc = len(argv)
if (argc == 1):
  device = "/dev/i2c-0"

if (argc == 2):
  pin = int(argv[1])
  if (pin == 0):
    device = "/dev/i2c-0"
  if (pin == 1):
    device = "/dev/i2c-1"
  if (pin == 2):
    device = "/dev/i2c-2"

#bus = smbus.SMBus(0) # Rev 2 Pi, Pi 2 & Pi 3 uses bus 1
#i2c = I2C("/dev/i2c-0")
print "device=",
print device
i2c = I2C(device)

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

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

def getChar(data,index):
  # return one byte from data as a signed char
  result = data[index]
  if result > 127:
    result -= 256
  return result

def getUChar(data,index):
  # return one byte from data as an unsigned char
  result =  data[index] & 0xFF
  return result

def readBME280ID(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 readBME280All(addr=DEVICE):
  # Register Addresses
  REG_DATA = 0xF7
  REG_CONTROL = 0xF4
  REG_CONFIG  = 0xF5

  REG_CONTROL_HUM = 0xF2
  REG_HUM_MSB = 0xFD
  REG_HUM_LSB = 0xFE

  # Oversample setting - page 27
  OVERSAMPLE_TEMP = 2
  OVERSAMPLE_PRES = 2
  MODE = 1

  # Oversample setting for humidity register - page 26
  OVERSAMPLE_HUM = 2
#  bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM)
  msgs = [I2C.Message([REG_CONTROL_HUM, OVERSAMPLE_HUM]), I2C.Message([0], read=False)]
  i2c.transfer(DEVICE, msgs)

  control = OVERSAMPLE_TEMP<<5 | OVERSAMPLE_PRES<<2 | MODE
#  bus.write_byte_data(addr, REG_CONTROL, control)
  msgs = [I2C.Message([REG_CONTROL, control]), I2C.Message([0], read=False)]
  i2c.transfer(DEVICE, msgs)

  # Read blocks of calibration data from EEPROM
  # See Page 22 data sheet
#  cal1 = bus.read_i2c_block_data(addr, 0x88, 24)
  cal1 = [0 for i in range(24)]
  msgs = [I2C.Message([0x88]), I2C.Message(cal1, read=True)]
  i2c.transfer(DEVICE, msgs)
  cal1 = []
  for x in range(24):
    cal1.append(msgs[1].data[x])

#  cal2 = bus.read_i2c_block_data(addr, 0xA1, 1)
  cal2 = []
  msgs = [I2C.Message([0xA1]), I2C.Message([0], read=True)]
  i2c.transfer(DEVICE, msgs)
  cal2.append(msgs[1].data[0])

#  cal3 = bus.read_i2c_block_data(addr, 0xE1, 7)
  cal3 = [0 for i in range(7)]
  msgs = [I2C.Message([0xE1]), I2C.Message(cal3, read=True)]
  i2c.transfer(DEVICE, msgs)
  cal3 = []
  for x in range(7):
    cal3.append(msgs[1].data[x])

  # Convert byte data to word values
  dig_T1 = getUShort(cal1, 0)
  dig_T2 = getShort(cal1, 2)
  dig_T3 = getShort(cal1, 4)

  dig_P1 = getUShort(cal1, 6)
  dig_P2 = getShort(cal1, 8)
  dig_P3 = getShort(cal1, 10)
  dig_P4 = getShort(cal1, 12)
  dig_P5 = getShort(cal1, 14)
  dig_P6 = getShort(cal1, 16)
  dig_P7 = getShort(cal1, 18)
  dig_P8 = getShort(cal1, 20)
  dig_P9 = getShort(cal1, 22)

  dig_H1 = getUChar(cal2, 0)
  dig_H2 = getShort(cal3, 0)
  dig_H3 = getUChar(cal3, 2)

  dig_H4 = getChar(cal3, 3)
  dig_H4 = (dig_H4 << 24) >> 20
  dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)

  dig_H5 = getChar(cal3, 5)
  dig_H5 = (dig_H5 << 24) >> 20
  dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)

  dig_H6 = getChar(cal3, 6)

  # Wait in ms (Datasheet Appendix B: Measurement time and current calculation)
  wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) + ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM)+0.575)
  time.sleep(wait_time/1000)  # Wait the required time

  # Read temperature/pressure/humidity
#  data = bus.read_i2c_block_data(addr, REG_DATA, 8)
  data = [0 for i in range(8)]
  msgs = [I2C.Message([REG_DATA]), I2C.Message(data, read=True)]
  i2c.transfer(DEVICE, msgs)
  data = []
  for x in range(8):
    data.append(msgs[1].data[x])
  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
  hum_raw = (data[6] << 8) | data[7]

  #Refine temperature
  var1 = ((((temp_raw>>3)-(dig_T1<<1)))*(dig_T2)) >> 11
  var2 = (((((temp_raw>>4) - (dig_T1)) * ((temp_raw>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14
  t_fine = var1+var2
  temperature = float(((t_fine * 5) + 128) >> 8);

  # Refine pressure and adjust for temperature
  var1 = t_fine / 2.0 - 64000.0
  var2 = var1 * var1 * dig_P6 / 32768.0
  var2 = var2 + var1 * dig_P5 * 2.0
  var2 = var2 / 4.0 + dig_P4 * 65536.0
  var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0
  var1 = (1.0 + var1 / 32768.0) * dig_P1
  if var1 == 0:
    pressure=0
  else:
    pressure = 1048576.0 - pres_raw
    pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
    var1 = dig_P9 * pressure * pressure / 2147483648.0
    var2 = pressure * dig_P8 / 32768.0
    pressure = pressure + (var1 + var2 + dig_P7) / 16.0

  # Refine humidity
  humidity = t_fine - 76800.0
  humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.0 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity)))
  humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0)
  if humidity > 100:
    humidity = 100
  elif humidity < 0:
    humidity = 0

  return temperature/100.0,pressure/100.0,humidity

def main():

  while True:
    print("-----------------------")

    (chip_id, chip_version) = readBME280ID()
    print "Chip ID     : 0x%x" % chip_id
    if (chip_id == 0x60):
      print "Version     :", chip_version

    temperature,pressure,humidity = readBME280All()

    print "Temperature : {:.2f} C".format(temperature)
    print "Pressure    : {:.2f} hPa".format(pressure)
    if (chip_id == 0x60):
      print "Humidity    : {:.2f} %".format(humidity)
    time.sleep(2)

if __name__=="__main__":
  try:
    main()
  except KeyboardInterrupt:
    pass

0xD0レジスター(ChipIDレジスター)でBMP280(=0x58)か、BME280(=0x60)を識別することができますが、 0x58が正しくとれています。
root@mylinkit:~/test# python ./bmp280_i2c.py
device= /dev/i2c-0
-----------------------
Chip ID     : 0x58
Temperature : 26.31 C
Pressure    : 1013.59 hPa
-----------------------
Chip ID     : 0x58
Temperature : 26.34 C
Pressure    : 1013.63 hPa
-----------------------
Chip ID     : 0x58
Temperature : 26.32 C
Pressure    : 1013.63 hPa
-----------------------
Chip ID     : 0x58
Temperature : 26.35 C
Pressure    : 1013.64 hPa
-----------------------
Chip ID     : 0x58
Temperature : 26.31 C
Pressure    : 1013.60 hPa



SPIデバイスもありました。
P22=MOSI P23=MISO P24=SCK P25=CSとしてアサインされています。


python-peripheryのSPIクラスを使ったスクリプトです。
SPIモードのBMP280から温度を読み出す以下のコードを試してみましたが、0xD0レジスターの読み取りでエラーとなります。
原因が分かりません。
"""
Read BMP280 using periphery
"""
#!/usr/bin/python
#-*- encoding: utf-8 -*-
from periphery import SPI
import sys
import time
import os.path

DEBUG = 1

def calibration_T(adc_T):
  global t_fine
  var1 = ((((adc_T >> 3) - (dig_T1<<1))) * (dig_T2)) >> 11
  var2 = (((((adc_T >> 4) - (dig_T1)) * ((adc_T>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14

  t_fine = var1 + var2
  t_fine = int32(t_fine)
  T = (t_fine * 5 + 128) >> 8
  return T

def calibration_P(adc_P):
  var1 = ((t_fine)>>1) - 64000
  if (DEBUG == 1):print("var1(1) = %d " % var1)
  var2 = (((var1>>2) * (var1>>2)) >> 11) * (dig_P6)
  if(DEBUG == 1):print("var2(2) = %d " % var2)
  var2 = var2 + ((var1*(dig_P5))<<1)
  if(DEBUG == 1):print("var2(3) = %d " % var2)
  var2 = (var2>>2)+((dig_P4)<<16)
  if(DEBUG == 1):print("var2(4) = %d " % var2)
  var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + (((dig_P2) * var1)>>1))>>18
  if(DEBUG == 1):print("var1(5) = %d " % var1)
  var1 = ((((32768+var1))*(dig_P1))>>15)
  if(DEBUG == 1):print("var1(6) = %d " % var1)
  if (var1 == 0):
    return 0
  P = ((((1048576)-adc_P)-(var2>>12)))*3125
  if(P<0x80000000):
    P = (P << 1) / (var1)
  if(P>=0x80000000):
    P = (P / var1) * 2;
  var1 = ((dig_P9) * ((((P>>3) * (P>>3))>>13)))>>12
  var2 = (((P>>2)) * (dig_P8))>>13
  if(DEBUG == 1):
    print("var1 = %d" % var1),
    print("var2 = %d" % var2)
  P = (P + ((var1 + var2 + dig_P7) >> 4))
  return P

def int16(x):
  if x>0xFFFF:
    raise OverflowError
  if x>0x7FFF:
    x=int(0x10000-x)
    if x<2147483648:
      return -x
    else:
      return -2147483648
  return x

def int32(x):
  if x>0xFFFFFFFF:
    raise OverflowError
  if x>0x7FFFFFFF:
    x=int(0x100000000-x)
    if x<2147483648:
      return -x
    else:
      return -2147483648
  return x


if __name__=="__main__":
  t_sb = 5    #stanby 1000ms
  filter = 0  #filter O = off
  spi3or4 = 0 #SPI 3wire or 4wire, 0=4wire, 1=3wire
  osrs_t = 4  #OverSampling Temperature x8
  osrs_p = 4  #OverSampling Pressure x8
  Mode = 3    #Normal mode

  temp_raw = 0
  pres_raw = 0
  t_fine = 0

  # Open spidev with mode 0 and max speed 1MHz
  spi = SPI("/dev/spidev32766.1", 0, 1000000)

  print('Read the contents of the ID register')
  out = [0xD0, 0x00]
  resp = spi.transfer(out)
  if(DEBUG == 1):print resp
  ChipId = resp[1]
  print("ChipId = 0x%x " % ChipId),
  if (ChipId == 0x58): print("BMP280")
  if (ChipId == 0x60): print("BME280")

  # Send a command to the control register[0xF4]
  ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | Mode
  if(DEBUG == 1):print("ctrl_meas_reg = %x" % ctrl_meas_reg)
  spi.transfer([0x74,ctrl_meas_reg])

  # Send a command to the config register[0xF5]
  config_reg    = (t_sb << 5) | (filter << 2) | spi3or4
  if(DEBUG == 1):print("config_reg = %x " % config_reg)
  spi.transfer([0x75,config_reg])

  # Check control[0xF4] & config register[0xF5]
  print('Check Register')
  out = [0xF4, 0xF5, 0x00]
  resp = spi.transfer(out)
  if(DEBUG == 1):
    print resp
    print("ctrl_meas_reg = %x" % resp[1])
    print("config_reg    = %x" % resp[2])
  if(resp[1] != ctrl_meas_reg):
    print("INVALID control register %x" % resp[1])
  if(resp[2] != config_reg):
    print("INVALID config  register %x" % resp[2])

  print('Read calibration data')
  a = []
  for x in range(0x88, 0xA0):
    a.append(x)
  a.append(0)
  resp = spi.transfer(a)
  if(DEBUG == 1):print resp

  dig_T1 = resp[2] * 256 + resp[1]
  dig_T2 = int16(resp[4] * 256 + resp[3])
  dig_T3 = int16(resp[6] * 256 + resp[5])
  if(DEBUG == 1):
    print("dig_T1 = %d" % dig_T1),
    print("dig_T2 = %d" % dig_T2),
    print("dig_T3 = %d" % dig_T3)

  dig_P1 = resp[8] * 256 + resp[7]
  dig_P2 = int16(resp[10] * 256 + resp[9])
  dig_P3 = int16(resp[12] * 256 + resp[11])
  dig_P4 = int16(resp[14] * 256 + resp[13])
  dig_P5 = int16(resp[16] * 256 + resp[15])
  dig_P6 = int16(resp[18] * 256 + resp[17])
  dig_P7 = int16(resp[20] * 256 + resp[19])
  dig_P8 = int16(resp[22] * 256 + resp[21])
  dig_P9 = int16(resp[24] * 256 + resp[23])
  if(DEBUG == 1):
    print("dig_P1 = %d" % dig_P1),
    print("dig_P2 = %d" % dig_P2),
    print("dig_P3 = %d" % dig_P3)
    print("dig_P4 = %d" % dig_P4),
    print("dig_P5 = %d" % dig_P5),
    print("dig_P6 = %d" % dig_P6)
    print("dig_P7 = %d" % dig_P7),
    print("dig_P8 = %d" % dig_P8),
    print("dig_P9 = %d" % dig_P9)

  print('Read temp and pressure')
  while(1):
    a = []
    for x in range(0xF7, 0xFD):
      a.append(x)
    a.append(0)
    resp = spi.transfer(a)
    if(DEBUG == 1):print resp
    # Check valid data
    if (resp[2] != 0 or resp[3] != 0):break
    time.sleep(1)

  pres_raw = (resp[1] << 12) | (resp[2] << 4) | (resp[3] >> 4)  #0xF7, msb+lsb+xlsb=19bit
  if(DEBUG == 1):print("pres_raw = %d " % pres_raw)
  temp_raw = (resp[4] << 12) | (resp[5] << 4) | (resp[6] >> 4)  #0xFA, msb+lsb+xlsb=19bit
  if(DEBUG == 1):print("temp_raw = %d " % temp_raw)

  temp_cal = calibration_T(temp_raw)
  if(DEBUG == 1):print("temp_cal = %d " % temp_cal)
  press_cal = calibration_P(pres_raw)
  if(DEBUG == 1):print("press_cal = %d " % press_cal)
  temp_act = temp_cal / 100.0
  press_act = press_cal / 100.0

  print("-----------------------");
  print("Temperature = %f *C" % temp_act)
  print("Pressure = %f hPa" % press_act)

続く
....