ネットワークカメラ

Raspberry Piで画像を取り込みたいと思った時に、それを実現する方法は色々とある。専用のカメラモジュールが用意されているし、以前に書いたようにUSBカメラを接続してキャプチャする事もできる。

監視用によく使われているAxisのネットワークカメラがたまたま手元に来たので、Raspberry Piでできるだけ簡単にネットワークカメラの画像を取り込む方法について試してみた。

AxisのネットワークカメラはWebサーバを内蔵しているので、ブラウザでアクセスするだけで映像をモニタする事ができるし、設定によってモニタ画面のボタンをマウスでクリックした時にその画像を保存する事ができる。と言うことはRaspberry PiでネットワークカメラのWebサーバにアクセスして、ボタンをクリックした時のCGIを実行できれば同じことができるはず。

まずはその前に、モニタ画面でボタンをクリックした時にRaspberry Piへ画像を送れるようにしてみる。FTPを使用するので、Raspberry PiにFTPサーバがインストールされていない場合は

$ sudo apt-get install vsftpd

で最初にインストール。
デフォルトではanonymousログインのみで書き込み禁止になっているので、/etc/vsftpd.conf ファイルを編集してanonymousを禁止、ローカルユーザによるログインと書込みを可にする。

anonymous_enable=NO
local_enable=YES
write_enable=YES

設定ファイルを編集したらサービスを再起動

$ sudo service vsftpd restart

カメラ画像の転送先フォルダも作成しておく。ここでは「camera」とした。

Axisネットワークカメラのイベント設定はアクションルールとレシピの二つに分かれていて、前者は何をイベントとして受け付けるのか、後者はイベントを受け付けた時の動作を設定する。従ってブラウザのモニタ画面でボタンを押された時に画像をFTPサーバへアップロードする場合は、アクションルールに「マニュアルトリガ」、レシピに「FTP転送」を設定する事になる。

スクリーンショット 2014-12-08 11.56.24

スクリーンショット 2014-12-08 11.43.31

この設定をネットワークカメラに保存して、モニタ画面のトリガボタンをON/OFFするとRaspberry Piに画像が転送される事を確認。これが上手く行ったら、あとはトリガボタンがON/OFFされた時に実行されるCGIを、Raspberry Piから直接叩けば良い。

トリガONの時は

カメラのIPアドレス/axis-cgi/io/virtualinput.cgi?action=6:/

が実行されて、OFFの時は

カメラのIPアドレス/axis-cgi/io/virtualinput.cgi?action=6:\

が実行される。

pythonでCGIを実行するのはurllibモジュールを使用すれば良いのだが、認証が必要になるのでそこがちょっとだけ面倒。予め認証用のオブジェクトを作っておき、それをurllibモジュールに登録しておく必要がある。

ネットワークカメラの画像をFTP転送させるプログラムは以下の通り。IPアドレスやカメラのユーザ名、パスワードは適宜変更する。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from urllib import request
from urllib import parse

class camera():
    def __init__(self, topurl, cgiurl, camuser, campass):
        passwd_mgr = request.HTTPPasswordMgrWithDefaultRealm( )
        passwd_mgr.add_password(None, topurl, camuser, campass)
        handler = request.HTTPDigestAuthHandler(passwd_mgr)
        opener = request.build_opener(handler)
        request.install_opener(opener)
        self.cgi = topurl + cgiurl
    def save(self):
        trig_on  = parse.urlencode({'action' : '6:/'})
        trig_off = parse.urlencode({'action' : '6:\\'})
        request.urlopen(self.cgi + trig_on)
        request.urlopen(self.cgi + trig_off)

if __name__ == '__main__':
    netcam = camera('http://192.168.1.90/',
                    'axis-cgi/io/virtualinput.cgi?',
                    'root',
                    'xxxxxxxx')
    netcam.save()

実際のところpythonが動けばRaspberry Pi以外でもこのプログラムは動作するので、画像キャプチャだけならわざわざRaspberry Piを使う必要は無いし、カメラ単体に一定周期や動体検知で画像保存する機能もあるから、それだけで事足りる場合も多いだろう。

何かのセンサー類を使って、特定の条件で画像を取り込みたい。というネットワークカメラ単体では対応が難しいような要件であれば、役に立つかもしれない。

広告

PySide(3)

Qt Designerで作成した .ui ファイルには、フォームとコントロール全ての情報がxml形式で含まれていて

loader = QUiLoader()
UI = loader.load(ファイルパス名)

のように読み込むと、配置したコントロールのオブジェクトが全て含まれたUIオブジェクトが作成される。たとえばLabelコントロールのオブジェクト名を”label”とした場合

UI.label.setText(‘Foo’)

とすればラベルコントロールのテキストを変更することができる。

それだけ分かっていれば、コードのみでPySideのGUIを作成したプログラムをQt Designer版に対応させるのは簡単に行える。

ボタンオブジェクト名 = QtGui.QPushButton(ボタンテキスト)
ボタンオブジェクト名.clicked.connect(関数名)

UI.ボタンオブジェクト名.clicked.connect(関数名)

のように置き換えてしまえば良い。

Quick2Wireを使ってカラーLEDを指定した色で光らせるPySideプログラムを .ui ファイルに対応させて、更に選択した色名を画面にカラー表示させたり、クリックしたボタンの色を変えたりできるように修正すると、最終的にプログラムは以下のようになった。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from PySide.QtCore import Qt
from PySide.QtUiTools import QUiLoader
from quick2wire.gpio import pins, Out

CURRENT_PATH = os.path.dirname(__file__)
COLOR_TBL = ((Qt.black,  'BLACK'), 
             (Qt.red,    'RED'),
             (Qt.green,  'GREEN'),
             (Qt.yellow, 'YELLOW'),
             (Qt.blue,   'BLUE'),
             (Qt.magenta,'MAGENTA'),
             (Qt.cyan,   'CYAN'),
             (Qt.white,  'WHITE'))
BUTTON_COLOR = (Qt.red, Qt.green, Qt.blue)

class ColorLED:
    led = (pins.pin(0, direction=Out),
           pins.pin(1, direction=Out),
           pins.pin(2, direction=Out))
    def __init__(self):
        for e in self.led:
            e.open();
    def __del__(self):
        for e in self.led:
            e.close();
    def set(self, color):
        self.led[0].value = color & 0x01
        self.led[1].value = color & 0x02
        self.led[2].value = color & 0x04

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'QtColorLED.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)
        self.btnRGB = (self.UI.pushButton_r,
                       self.UI.pushButton_g,
                       self.UI.pushButton_b)
        self.btnRGB[0].clicked.connect(lambda: self.click_btn(0))
        self.btnRGB[1].clicked.connect(lambda: self.click_btn(1))
        self.btnRGB[2].clicked.connect(lambda: self.click_btn(2))
        self.led = ColorLED()
        self.color = 0x01
        self.pal = self.UI.pushButton_r.palette()
        self.click_btn(0)
    def click_btn(self, n):
        self.color = self.color ^ (0x01 << n)
        self.led.set(self.color)
        pal = QtGui.QPalette()
        pal.setColor(QtGui.QPalette.Foreground, COLOR_TBL[self.color][0])
        self.UI.label.setPalette(pal)
        self.UI.label.setText(COLOR_TBL[self.color][1])
        if self.color & (0x01 << n) != 0:
            pal.setColor(self.btnRGB[n].backgroundRole(), BUTTON_COLOR[n])
            self.btnRGB[n].setPalette(pal)
        else:
            self.btnRGB[n].setPalette(self.pal)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

実行してボタンをクリックすると、ボタンの色が変わってボタンの組み合わせによる色でLEDが光り、同時に色の名前が表示される。

範囲を選択_0202014-11-13 09.33.45

範囲を選択_021

2014-11-13 09.33.58

範囲を選択_022

2014-11-13 09.34.07

範囲を選択_023

2014-11-13 09.34.18

ソースコードの説明は省くが、やっている事は一目瞭然なので必要ないだろう。
Qtを使うのはほとんど始めてなので、冗長な部分やもっとエレガントな使い方があるのだろうと思うが、Raspberry PiでGUIアプリを動かしてハードを制御するという目標は達したので良しとする。

Qtには色々と高度なコントロールが用意されているので、次はそのへんを使って何か作ってみる予定。

PySide(2)

PySide(Qt)を使用してGUIアプリを作成する時に、コントロールの数が少ない単純デザインであればすべてコードで記述する事は可能だが、複雑なレイアウトになると現実的では無くなる。頑張って書いたとしても、メンテナンス性が悪くなるので調整や追加を行うのはかなり大変だ。

多くのツールキットにはGUIデザイン用のツールが存在していて、QtにはQt Designer、wxPythonにはwxGladeというリソースエディタがある。wxGladeはまだ使った事が無いが、そちらはwxPythonを試してみる時まで置いておくとして、今回も引き続きツールキットとしてPySideを使用するのでツールは必然的にQt Designerとなる。

Qt Designerはかなり重量級のアプリなので、Raspberry Piでも動かせない事はないのだが実際に試してみると動作が遅くて実用的では無かった。なのでQt Designerは別なPCで動かし、デザインしたファイルだけをRaspberry Piに持って行って動かす事にする。

具体的にはWindows7の仮想PCとしてインストールしたUbuntu(Xubuntu)にQt Designerをインストールした。Ubuntuソフトウェアセンターで「Qtデザイナー」を検索すれば出てくるので、そこでインストールすれば良い。

Ubuntuソフトウェアセンター_002

Windowsの場合はQt Projectのダウンロードページから、Qt Online Installer for Windowsをダウンロードして実行すれば必要なツールはすべてインストールされる。Windows版ではQt DesignerはQt Creatorの一部になっているため、Qt Designerだけを使用したい場合でもすべてインストールしなくてはいけないようだ。

Qt Creatorに含まれるQt Designerを使用する場合、最初にプロジェクトを作成してからでないとデザインが行えないようなので、PySideのためだけにQt Designerを使いたい場合にはWindowsではなくLinuxで行うほうが今のところはやりやすい。Windows環境下でもVirtualBox等の仮想PCツールで手軽にLinux環境を構築可能なので、そちらをお勧めする。

XubuntuではインストールしたQt Designerはメニューの開発カテゴリの中に「Qt4デザイナ」という名前で追加されるので、それを選択すれば起動するのだが、起動する前にまずはどんなGUIを作るのかを考えておく。
Qtのレイアウトはコントロールが多層構造になるので、デザインツールを使用する場合でもGUIの構造は予め考えておいたほうが良い。大雑把に紙に書くのも有効。

前回作ったGUIと同じものでは面白く無いので、光らせたカラーLEDの色と同じ名前を同じ色で表示させる事にした。ついでに押したボタンの色も変えて分かりやすくする。

Form - [プレビュー]_018

プレビューだと分からないが、フォーム全体のレイアウトをVertical Layoutにしておき、そこにLabelとHorizontal Layoutを並べて、Horizontal Layoutの中にボタンを三個並べた構造になっている。これをQt Designerで作ってみる。

最初にQt Designerを起動すると、新しいフォームの作成ダイアログが出るのでWidgetを選択。

Qt Designer_003

フォームが表示されたら、左側のウィジェットボックスからLabelとHorizontal Layoutをドラッグしてフォームの上に貼り付ける。

Qt Designer_007

メニューの「フォーム→垂直に並べる」を選択してフォーム全体のレイアウトを指定すると、LabelとHorizontal Layoutが縦に並ぶ。

Qt Designer_008

この状態で左側のウィジェットボックスからHorizontal Layoutの中にPush Buttonをドラッグして三個貼り付ける。ボタンは自動的に横に並ぶ。

Qt Designer_009

右側のオブジェクトインスペクタでボタンを選択して、オブジェクト名の部分をダブルクリックしてボタンに名前を付ける。ここでは pushButton_r、pushButton_g、pushButton_b とした。ボタンのtextプロパティでそれぞれに「RED」「GREEN」「BLUE」のボタンテキストも設定する。

Qt Designer_010

オブジェクトインスペクタでFormを選択して、プロパティのminimumSizeを400×100に設定。フォームをリサイズして指定したサイズよりも小さくならない事を確認する。

Qt Designer_011

オブジェクトインスペクタでLabelを選択して、プロパティのfontを適当なサイズに変更。ここではフォントサイズを28に設定した。

Qt Designer_012

最後に三個のボタン全ての autoFillBackground プロパティをON(チェックマークを付ける)にして完了。適当なファイル名を付けて保存する。ここでは QtColorLED.ui という名前で保存した。

保存したファイルは以下のような内容になっている

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>100</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>400</width>
    <height>100</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="font">
      <font>
       <pointsize>36</pointsize>
      </font>
     </property>
     <property name="text">
      <string>TextLabel</string>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QPushButton" name="pushButton_r">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>RED</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_g">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>GREEN</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_b">
       <property name="autoFillBackground">
        <bool>true</bool>
       </property>
       <property name="text">
        <string>BLUE</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

このuiファイルをRaspberry Piに転送して、とりあえず「表示するだけ」のコードを書いて表示を確認。

Raspberry Pi_019

表示するだけのコードは以下の通り

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from PySide.QtUiTools import QUiLoader

CURRENT_PATH = os.path.dirname(__file__)

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'QtColorLED.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())        

もちろんボタンをクリックしても何も動作しないが、ウィンドウをドラッグしたり、サイズを変えたりすることはできる。次回はこのコードに肉付けして、実際に動作するアプリにしていく予定。

PySide

pythonでGUIを構築するためのライブラリ(ツールキット)はTkinter、wxPython、PyQt、PySide、PyGTK、PyGame等色々と存在しているが、種類が多くなると何を使うべきなのか悩むことになるし、ネット上での情報が分散してしまうので選択肢が多いのは良いことばかりとは限らない。

最も手軽に利用できるのは標準でPythonに含まれているTkinterで、何もインストールせずに使えるし単純なGUIならコードも短く書ける。その反面複雑なGUIになると思ったようにデザインするのが難しいという印象。これについては以前使ってみたPyGameでも似たような感じだったので、VisualStudioのようなインタラクティブにコントロールを配置できるリソースエディタが用意されているものという基準で選択すると、wxPythonとQt(PyQt、PySide)が良さそうに思える。サンプルコードの量などもその二つが豊富なようだ。

今回はその中からPySideを使ってみる事にした。同じQtのPythonバインディングであるPyQtではなくPySideを選んだのはライセンスの違いによるものだが、個人的に使うにはあまり気にしなくても良いかもしれない。機能的にはPyQtのほうが先行していて、コミュニティベースのPySideがその後を追うというのが現状らしいが、wxPythonの開発者がPySideの開発に加わったりと目まぐるしく状況が変化しているので、この先どうなるかは分からない。

QtにはQt DesignerというGUIコントロールを配置するためのリソースエディタが存在していて、PySideを使ってみようと思ったのはそれも理由の一つなのだが、最初はそれを使わずにコードだけでGUIアプリを作成してみた。

まず最初にPySideのインストールだが、Pyhton3では

$ sudo apt-get install python3-pyside

で簡単に行える。しかし、virtualenvでPythonの仮想環境を構築してある場合、このインストール方法では仮想環境下に追加されない。apt-getではなくpipでインストールすれば仮想環境下に追加されるはずなのだが、pipでのインストールは上手く行かなかった。ソースからのビルドも試してみたがやはりダメ。

import sys
sys.path.append('/usr/lib/python3/dist-packages')

と記述しておけば元のパッケージを参照できるようになるので、あまりスマートな方法ではないがとりあえずはこれで動かしておく。

Raspberry Piでやるからには何らかのハードを動かさないと面白く無いので、aitendoの100円コーナーで買った極小フルカラーLEDモジュールをGPIOに接続した。GPIOの制御にはQuick2Wireを使う。
画像1: 極小フルカラーLEDモジュール

RGBの三個のLEDが内蔵されていて、3ビットで7色の表示ができる。PWMを使えばもっと微妙な色の表現も可能だが、今回はそこまではやらない。

ウィンドウにボタンを三個表示させて、クリックするとRGBそれぞれをON/OFFできるようにした。接続はGPIO0(ピン番号17)をR、GPIO1(ピン番号18)をG、GPIO2(ピン番号27)をBに割り当てる。プログラムは以下の通り

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
sys.path.append('/usr/lib/python3/dist-packages')
from PySide import QtCore, QtGui
from quick2wire.gpio import pins, Out

class ColorLED:
    led = [pins.pin(0, direction=Out),
           pins.pin(1, direction=Out),
           pins.pin(2, direction=Out)]
    def __init__(self):
        for e in self.led:
            e.open();
    def __del__(self):
        for e in self.led:
            e.close();
    def set(self, color):
        self.led[0].value = color & 0x01
        self.led[1].value = color & 0x02
        self.led[2].value = color & 0x04

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)    
        self.btn_r = QtGui.QPushButton('RED')
        self.btn_g = QtGui.QPushButton('GREEN')
        self.btn_b = QtGui.QPushButton('BLUE')
        layout = QtGui.QHBoxLayout()
        layout.addWidget(self.btn_r)
        layout.addWidget(self.btn_g)
        layout.addWidget(self.btn_b)
        self.setLayout(layout)
        self.setMinimumSize(400, 100)
        self.btn_r.clicked.connect(lambda: self.click_btn(0x01))
        self.btn_g.clicked.connect(lambda: self.click_btn(0x02))
        self.btn_b.clicked.connect(lambda: self.click_btn(0x04))
        self.led = ColorLED()
        self.color = 0
    def click_btn(self, n):
        self.color = self.color ^ n
        self.led.set(self.color)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

実行するとこのように表示される。

Raspberry Pi_001

次回はQt Designerを使ってもう少し複雑なGUIを作ってみる予定。

Pressure Sensor

大型の台風18号と19号が連続で発生して、それに合わせたわけでは無いと思うが秋月電子通商で大気圧センサをDIP基板化したモジュールが発売されたので、台風19号の上陸前に入手して試してみた。

インターフェースはI2CとSPIが選択可能で、Raspberry Piでは不要だがI2C使用時はジャンパーでプルアップ抵抗も有効にできる。LEDも載っていて通電すると光るのだが、これは不要な気がする。コマンドからの応答が無い時に、通電してないのか他の要因なのかが識別できる程度のメリットしか無い。まあこのモジュールをそのまま製品に使う事は無いだろうし、ブレッドボードで実験するだけなら消費電力が僅かに増えるのも気にはならないだろう。

使用方法はデータシートに記載されている通り、最初にパワーダウンモードから復帰させてあとは24ビットのデータを3つのレジスタから読み出せば良い。読み出した値を4096で割ったものが気圧(ヘクトパスカル)になる。

プログラムは以下の通り

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import quick2wire.i2c as i2c
import time
import sys

LPS25H = 0x5c

def i2c_read(addr, reg):
    with i2c.I2CMaster() as bus:
        data = bus.transaction(
                    i2c.writing_bytes(addr, reg),
                    i2c.reading(addr, 1))
        return data[0][0]

def i2c_write(addr, reg, data):
    with i2c.I2CMaster() as bus:
        bus.transaction(i2c.writing_bytes(addr, reg, data))

def get_press(addr):
    xl = i2c_read(addr, 0x28)
    l  = i2c_read(addr, 0x29)
    h  = i2c_read(addr, 0x2A)
    p = h >> 16 | l >> 8 | xl
    return round(p / 4096, 1)

def get_temp(addr):
    l = i2c_read(addr, 0x2B)
    h = i2c_read(addr, 0x2C)
    t = h >> 8 | l
    if t & 0x8000:
        t = -((t - 1) ^ 0xFFFF)
    return round(42.5 + (t / 480.0), 2)

if __name__ == '__main__':
    whoami = i2c_read(LPS25H, 0x0F)
    if whoami != 0xbd:
        print('device incorrect.')
        sys.exit()
    i2c_write(LPS25H, 0x20, 0x90)
    time.sleep(1)
    try:
        while True:
            pha = get_press(LPS25H)
            print(pha, "hPa")
            temp = get_temp(LPS25H)
            print(temp, "C")
            time.sleep(1)
    except KeyboardInterrupt:
        print('\nbreak')            

実際に台風19号が関東上空を通過している時にこのプログラムを動かして見ていると、グングン気圧が下がっていったのが面白かった。気象庁が一時間おきに発表している東京都の計測データと照らし合わせると1hPa程度低い値になっていたが、これは観測地点の差によるものだろう。

ちなみにこのモジュール内には温度センサも内蔵されていて、同時に読み出してみるとADT7410を使用した温度センサモジュールよりも0.5℃ほど低い値を示した。正確な温度計が無いのでどちらが正しいのかは分からないのだが、大気圧センサに内蔵されている温度センサは気圧の補正用途で、あまり精度は高くないらしい。温度センサの値はだいたいの目安程度に考えたほうが良いだろう。

Quick2Wire(3)

Quick2WireでI2C通信を行うのは簡単で、基本的にはI2CMasterオブジェクトのtransactionメソッドを使用するだけで済む。このメソッドの引数としてI2Cデバイスに対する読み書きを記述する。

説明するよりも実際のコードを見たほうがたぶん早い。お馴染みの温度センサーADT7410をRaspberry PiのI2Cポートに接続して、温度を読み出すプログラムは以下のようになる。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from quick2wire import i2c
from time import sleep

ADT_ADDR = 0x48

def get_temp(addr):
    with i2c.I2CMaster() as bus:
        data = bus.transaction(
                    i2c.writing_bytes(addr, 0x00),
                    i2c.reading(addr, 2))
        h = data[0][0]
        l = data[0][1]
        t = (h << 8 | l) >> 3
        if t & 0x1000:
            t = -((t - 1) ^ 0x1FFF)
        return round(t / 16.0, 2)

if __name__ == '__main__':
    try:
        while True:
            print("current temperature -> %7.2f" % get_temp(ADT_ADDR))
            sleep(1)
    except IOError as err:
        print('\nIOError: {0}'.format(err))            
    except KeyboardInterrupt:
        print('\nbreak')

with文でI2CMasterオブジェクトを作成して、transactionメソッドのパラメータとしてI2CデバイスへのRead/Writeを記述する。これは複数並べて書くことができるので、戻り値は必ず二次元配列(リスト)になる。アドレスが異なるデバイスへのアクセスを記述することもできる。たとえば

with i2c.I2CMaster() as bus:
    data = bus.transaction(
                i2c.writing_bytes(0x48, 0x00),
                i2c.reading(0x48, 2),
                i2c.writing_bytes(0x5c, 0x28),
                i2c.reading(0x5c, 1),
                i2c.writing_bytes(0x5c, 0x29),
                i2c.reading(0x5c, 1),
                i2c.writing_bytes(0x5c, 0x2A),
                i2c.reading(0x5c, 1))

このように書くと、以下のようなデータが帰ってくる

0x48のレジスタ0x00の値 0x48のレジスタ0x01の値
0x5cのレジスタ0x28の値 無効
0x5cのレジスタ0x29の値 無効
0x5cのレジスタ0x2Aの値 無効

複数のI2Cデバイスがぶら下がっているような場合や、一つのI2Cデバイスでも複数のレジスタに同時にアクセスしなければならないような場合に、まとめて記述する事ができるので便利ではないかと思う。ただし、その場合はエラー処理で注意が必要になるかも知れない。

Quick2Wire(2)

前回インストールしたQuick2WireはGPIOのピンに対する入出力と、I2Cによるデバイスとの通信機能を持つので、まずはGPIOの機能について試してみる。

Pythonでプログラムを組んで入出力を行う事はもちろんできるが、Raspberry Piのコンソールから仮想デバイスに対してリダイレクトするだけでも同じことが行える。GPIOピンヘッダの11番ピンがBCMチップのGPIO17で、Quick2WireではPin0(ゼロ)として扱われているので、ここに適当なLEDと抵抗が外付けされているものとする。
ちなみにQuick2WireのGPIO番号とピンヘッダとBCMチップのGPIO番号は以下のように対応している(Raspberry Piのリビジョンがv2の場合)

GPIO 0 1 2 3 4 5 6 7
Header 11 12 13 15 16 18 22 7
BCM 17 18 27 22 23 24 25 4

この状態でコンソールから以下のコマンドを実行する

$ gpio-admin export 17

これで /sys/devices/virtual/gpio/ にgpio17というデバイスが作成されるので、あとはこのデバイスに対してIn/Outの設定と値の入出力を行えば良い。

$ echo out > /sys/devices/virtual/gpio/gpio17/direction
$ echo 1 > /sys/devices/virtual/gpio/gpio17/value

これで接続されているLEDが光るはずだ。
出力されている値はcatコマンドで確認できる。

$ cat /sys/devices/virtual/gpio/gpio17/value
1

使い終わったら以下のようにunexportしておく

$ gpio-admin unexport 17

この一連の仮想デバイスに対する操作は、実はRaspberry Piの基本機能でも可能である。Quick2Wireを使用しなくても、以下のようにすれば同じことができる。

$ echo 17 > /sys/class/gpio/export
$ echo out > /sys/class/gpio/gpio17/direction
$ echo 1 > /sys/class/gpio/gpio17/value
$ echo 0 > /sys/class/gpio/gpio17/value
$ echo 17 > /sys/class/gpio/unexport

以前のRaspbianでは管理者権限が無いとこの操作は行えなかったのだが、現在のバージョンではgpioグループに所属していれば一般ユーザでもGPIOの操作が可能になっている。デフォルトのユーザであるpiは既にgpioグループに所属しているのでそのままで使用できるが、新たに作成したユーザの場合は以下のコマンドでgpioグループに所属させる必要がある。

$ sudo adduser $USER gpio

ユーザをグループに所属させるには usermod コマンドを使用する方法もあるが、元々所属していたグループ情報が失われてしまう危険性があるので、使用しないほうが無難。

PythonでQuick2Wireを使用してGPIOの操作を行う場合は、quick2wire.gpioモジュールをインポートしてその中のPinsオブジェクトを使用すると簡単に入出力が行える。GPIO0(11番ピン)に接続されたLEDを点滅させるプログラムは、以下のようになる。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from quick2wire.gpio import pins, Out
from time import sleep

if __name__ == '__main__':
    try:
        with pins.pin(0, direction=Out) as led:
            while True:
                led.value = 1 - led.value
                sleep(1)
    except KeyboardInterrupt:
        print('\nbreak')

pinオブジェクトの生成時にはIn/Out以外にもプルアップの設定や、信号の変化による割り込みの設定も行える。細かく説明すると長くなるので、入力変化によるイベントのサンプルだけ書いておく。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import select
from quick2wire.gpio import pins, In, Out, Rising, Falling, Both

if __name__ == '__main__':
    led = pins.pin(0, direction=Out)
    btn = pins.pin(7, direction=In, interrupt=Both)
    with led,btn:
        epoll = select.epoll()
        epoll.register(btn, select.EPOLLIN|select.EPOLLET)
        try:
            while True:
                events = epoll.poll()
                for fileno,event in events:
                    if fileno == btn.fileno():
                        led.value = btn.value
        except KeyboardInterrupt:
            print('\nbreak')

GPIO0(11番ピン)にLED、GPIO7(7番ピン)にスイッチが接続されているものとして、スイッチのH/L変化によるイベントでLEDの状態を変化させている。
このサンプルのような単純な処理ではあまりメリットは無いが、selectを使うと複数のI/O処理を登録しておいてイベントで一括処理する事が可能になるので、複雑なプログラムになってくると必要とされる場面もあるかと思う。

Quick2WireのI2C機能については次回に。