2014年1月8日水曜日

OpenCV で、C のソースを Python にする

OpenCV の C のソースを Python に変換するときのメモ。
C にしておくと、ソースとオブジェクトの二つのファイルの管理が必要となったり、ライブラリの置く場所など、いろいろめんどう。
というわけで、Python にしてしまおうという計画。

言語文法上で注意するのはこれくらい?
  1. C の文は「;」で終了するが、Python にはない。
  2. C の変数宣言では型が必要だが、Python は変数名だけ。
  3. C では「&」をつけて、その変数のポインタを求めたりするが、Python では不要。
  4. True/False や Not の表記
  5. nil や NULL や None の表記
  6. Python のインデント。
「import cv」とすると、メソッド名などに「cv.」をつけることになる。
しかし、cv. の後のメソッド名には「cv」がつかない。
ただし、定数名の頭についている「CV」はそのまま残る。
こんな感じ。

C:
frame = cvQueryFrame(capture);
cvSmooth(frame, frame, CV_GAUSSIAN, 3, 3);

Python:
frame = cv.QueryFrame(capture)
cv.Smooth(frame, frame, cv.CV_GAUSSIAN, 3, 3)

C の cvQueryFrame は Python では cv.QueryFrame となる。
ただし定数は、C で CV_GAUSSIAN、Python で cv.CV_GAUSSIAN となる。

厄介なのがマクロ(?)。Python にはない。ふつーにタプルで表現したりする。

C:
const int w = frame->width;
const int h = frame->height;
imgR = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);

Python:
the_size = cv.GetSize(frame)
imgR = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
cv.Circle(frame, (gX, gY), 80, (0, 0, 255), 6, 8, 0)

そのほか、調べないとわからないもの。

C:
cvMoments(imgResult, &moments, 0);
double m00 = cvGetSpatialMoment(&moments, 0, 0);

Python:
moments = cv.Moments(cv.GetMat(imgResult, 1), 0)
m00 = cv.GetSpatialMoment(moments, 0, 0)


サンプルとして、次のページにあった赤い色のエリアを抽出し、重心を求めるソースの C 版と Python 版を示す。
http://d.hatena.ne.jp/shokai/20090203/1233609981
http://opencv-srf.blogspot.ro/2010/09/object-detection-using-color-seperation.html

これは Mac (10.9.1 Mavericks) と Raspberry Pi で動作確認した。

C:
#include <opencv/cv.h>
#include <opencv/highgui.h>

// This function threshold the HSV image and create a binary image
IplImage* GetThresholdedImage(IplImage* imgHSV) {
    IplImage* imgResult = cvCreateImage(cvGetSize(imgHSV), IPL_DEPTH_8U, 1);
    cvInRangeS(imgHSV, cvScalar(150, 160, 60), cvScalar(200, 256, 256), imgResult);
    return imgResult;
}

int main() {
    CvCapture* capture = NULL;
    IplImage *frame = NULL;
    IplImage *imgR = NULL, *imgG = NULL, *imgB = NULL;
    IplImage *imgThreshold_R = NULL, *imgThreshold_G = NULL, *imgThreshold_B = NULL;
    IplImage *imgTmp = NULL;
    IplImage *imgHSV = NULL;
    IplImage *imgResult = NULL;
    CvMoments moments;
    
    capture = cvCaptureFromCAM(0);
    if (!capture) {
        printf("Capture failure\n");
        return -1;
    }
    
    cvNamedWindow("Video");
    cvNamedWindow("Results");
    
    
    // iterate through each frames of the video
    while (true) {
        
        frame = cvQueryFrame(capture);
        if (!frame) {
            break;
        }
        
        frame = cvCloneImage(frame);
        cvSmooth(frame, frame, CV_GAUSSIAN, 3, 3); // smooth the original image using Gaussian kernel
                
        if (1) {
            const int w = frame->width;
            const int h = frame->height;
            
            imgR = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgG = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgB = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_R = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_G = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_B = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgResult = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgTmp = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);

            cvSplit(frame, imgB, imgG, imgR, NULL); // BGRを分解
            
            // 赤の要素が100以上で、緑と青より1.5倍以上あるピクセルを抽出
            cvThreshold(imgR, imgThreshold_R, 100, 255, CV_THRESH_BINARY);
            cvDiv(imgR, imgG, imgTmp, 10); // 10倍
            cvThreshold(imgTmp, imgThreshold_G, 15, 255, CV_THRESH_BINARY);
            cvDiv(imgR, imgB, imgTmp, 10);
            cvThreshold(imgTmp, imgThreshold_B, 15, 255, CV_THRESH_BINARY);
            cvAnd(imgThreshold_G, imgThreshold_B, imgTmp, NULL);
            cvAnd(imgTmp, imgThreshold_R, imgResult, NULL);
            
            cvMoments(imgResult, &moments, 0);
            double m00 = cvGetSpatialMoment(&moments, 0, 0);
            double m10 = cvGetSpatialMoment(&moments, 1, 0);
            double m01 = cvGetSpatialMoment(&moments, 0, 1);
            int gX = m10 / m00;
            int gY = m01 / m00;
            cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);
        } else {
            imgHSV = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3);
            cvCvtColor(frame, imgHSV, CV_BGR2HSV); // Change the color format from BGR to HSV
            imgResult = GetThresholdedImage(imgHSV);
            cvSmooth(imgResult, imgResult, CV_GAUSSIAN, 3, 3); // smooth the binary image using Gaussian kernel
        }
        cvShowImage("Results", imgResult);
        cvShowImage("Video", frame);
        
        
        // Clean up used images
        cvReleaseImage(&imgHSV);
        cvReleaseImage(&imgResult);
        cvReleaseImage(&frame);
        
        // Wait 50mS
        int c = cvWaitKey(10);
        // If 'ESC' is pressed, break the loop
        if ((char)c == 0x1b ) {
            break;
        }
    }
    
    cvDestroyAllWindows() ;
    cvReleaseCapture(&capture);
    
    return 0;
}

Python:
#!/usr/bin/env python
# coding: UTF-8
"""
DetectingRedObjects.py

"""
import cv

def GetThresholdedImage(imgHSV):
    imgResult = cv.CreateImage(cv.GetSize(imgHSV), cv.IPL_DEPTH_8U, 1)
    cv.InRangeS(imgHSV, cv.Scalar(150, 160, 60), cv.Scalar(200, 256, 256), imgResult)
    return imgResult

def main():
    capture = None
    frame = None
    imgR = imgG = imgB = None
    imgThreshold_R = imgThreshold_G = imgThreshold_B = None
    imgTmp = imgHSV = imgResult = None
    moments = None
    
    capture = cv.CaptureFromCAM(0)
    if (not capture):
        print("Capture failure")
        return -1
    
    cv.NamedWindow("Video")
    cv.NamedWindow("Results")
    
    
    # iterate through each frames of the video
    while (True):
        frame = cv.QueryFrame(capture)
        if (not frame):
            break;
        
        frame = cv.CloneImage(frame)
        cv.Smooth(frame, frame, cv.CV_GAUSSIAN, 3, 3) # smooth the original image using Gaussian kernel
        
        if (True):
            # 赤色領域を検出(3) 重心も計算
            # http://d.hatena.ne.jp/shokai/20090203/1233609981
            the_size = cv.GetSize(frame)
            # w = frame.width
            # h = frame.height
            
            imgR = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgG = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgB = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_R = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_G = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_B = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgResult = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgTmp = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            
            cv.Split(frame, imgB, imgG, imgR, None) # BGRを分解
            
            # 赤の要素が100以上で、緑と青より1.5倍以上あるピクセルを抽出
            cv.Threshold(imgR, imgThreshold_R, 100, 255, cv.CV_THRESH_BINARY)
            cv.Div(imgR, imgG, imgTmp, 10) # 10倍
            cv.Threshold(imgTmp, imgThreshold_G, 15, 255, cv.CV_THRESH_BINARY)
            cv.Div(imgR, imgB, imgTmp, 10)
            cv.Threshold(imgTmp, imgThreshold_B, 15, 255, cv.CV_THRESH_BINARY)
            cv.And(imgThreshold_G, imgThreshold_B, imgTmp, None)
            cv.And(imgTmp, imgThreshold_R, imgResult, None)
            
            # cv.Moments(imgResult, moments, 0)
            moments = cv.Moments(cv.GetMat(imgResult, 1), 0)
            m00 = cv.GetSpatialMoment(moments, 0, 0)
            m10 = cv.GetSpatialMoment(moments, 1, 0)
            m01 = cv.GetSpatialMoment(moments, 0, 1)
            if (m00 != 0):
                gX = int(m10 // m00)
                gY = int(m01 // m00)
                # cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);
                cv.Circle(frame, (gX, gY), 80, (0, 0, 255), 6, 8, 0)
        else:
            # Color Detection & Object Tracking
            # http://opencv-srf.blogspot.ro/2010/09/object-detection-using-color-seperation.html
            imgHSV = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_8U, 3)
            cv.CvtColor(frame, imgHSV, cv.CV_BGR2HSV) # Change the color format from BGR to HSV
            imgResult = GetThresholdedImage(imgHSV)
            cv.Smooth(imgResult, imgResult, cv.CV_GAUSSIAN, 3, 3) # smooth the binary image using Gaussian kernel

        cv.ShowImage("Results", imgResult)
        cv.ShowImage("Video", frame)
        
        
        # Clean up used images
        # cv.ReleaseImage(imgHSV)
        # cv.ReleaseImage(imgResult)
        # cv.ReleaseImage(frame)
        
        # Wait 50mS
        c = cv.WaitKey(10)
        # If 'ESC' is pressed, break the loop
        if (c == 0x1b):
            break

    cv.DestroyAllWindows()
    # cv.ReleaseCapture(capture)
    return 0


if __name__ == '__main__':
    main()

2013年12月13日金曜日

Raspberry Pi (その前に Mac で)+ OpenCV + OpenNI + Xtion Pro Live

まず、Mac でいろいろやってみる。
OpenCV は MacPorts で opencv @2.4.7_1+openni+python27 をインストールする。
OpenNI も MacPorts で一応入れたものの・・・openni @1.5.7.10_0
OpenNI-MacOSX-x64-2.2 をダウンロード。
「OpenNI-MacOSX-x64-2.2/Samples/Bin」にあるサンプルは動作している。

次に、Samples のソースをいじる前に、make できることを確認する。
make すると deprecated のエラーがでてしまう。
Makefile を修正してコンパイルオプションに「-Wno-deprecated」を追加する。
例を示す。
<<修正前>>
CFLAGS += -Wall -D_CLOSEST_POINT
<<修正後>>
CFLAGS += -Wall -D_CLOSEST_POINT -Wno-deprecated



ClosestPointViewer を make するには、MWClosestPoint を先に make しておかなければならない。
MWClosetPointApp を make するときも、先に MWClosestPoint を make しておかなければならない。

まず、MWClosestPoint/Bin は削除してから、MWClosestPoint を make する。

次に、ClosestPointViewer/Bin/ にある Intermediate と x64-Release の二つのディレクトリは空にする。
MWClosestPoint/Bin/x64-Release/libMWClosestPoint.dylib を、先ほど空にした ClosestPointViewer/Bin/x64-Release/ にコピーする。
コピーコマンドはこれ。
$ cp MWClosestPoint/Bin/x64-Release/libMWClosestPoint.dylib ClosestPointViewer/Bin/x64-Release/
そして、ClosestPointViewer を make する。

同様に、MWClosestPointApp/Bin/ にある Intermediate と x64-Release の二つのディレクトリは空にする。
MWClosestPoint/Bin/x64-Release/libMWClosestPoint.dylib を、先ほど空にした MWClosestPointApp/Bin/x64-Release/ にコピーする。
コピーコマンドはこれ。
$ cp MWClosestPoint/Bin/x64-Release/libMWClosestPoint.dylib MWClosestPointApp/Bin/x64-Release/
MWClosetPointApp を make する。

make できることが確認できたので、ソースをいろいろいじってみることにする。

2013年12月5日木曜日

Raspberry Pi + ビュートローバー ARM VS-WRC103LV (Mac に LPCXpresso をインストール)

ビュートローバー ARM VS-WRC103LV のソフトを作る話のメモ。
Mac に LPCXpresso をインストールし、C でソフトを作る。
NXP のダウンロードサイトから LPCXpresso をダウンロードしてインストールする。
アクティベーションがちょっと面倒。

2014.04.03 時点では、lpcxpresso_7.1.1_125.pkg がダウンロードされる。
これを開くとインストーラーが動き出す。アプリケーションフォルダに「lpcxpresso_7.1.1_125」という名前のフォルダがつくられるが、どれがアプリの本体なのかわからない。
「Open lpcxpresso」というファイルがあるけれど、どこかのファイルへのエイリアスになっている。調べると・・・「Open lpcxpresso -> lpcxpresso/lpcxpresso.app/Contents/MacOS/lpcxpresso」と、なっている。
lpcxpresso フォルダの中をみると「lpcxpresso」という名前のアプリケーションがある。これだね。これをダブルクリックすると立ち上がる。Dock のオプションで「Dock に追加」をしておくと、今後は Dock から起動できる。

で、この lpcxpresso_7.1.1_125.pkg は、USB 関係で使われる SciInit() で問題が発生する。帰ってこない。ここで止まってしまう。
これでは使い物にならないので、7.1.1 から 7.0.0 に戻した。

2014.05.23 現在、バージョン 7.2.0_153 が公開されていて、これは SciInit() の問題はない。7.1.1 は使えないが、 7.2.0_153 は使える。
ほかにも問題が改善されている。原因不明だが、ビュートローバーのセンサーの値を返すように修正し、その後、リビジョンを返す関数を追加した。しかし、そのリビジョンはなぜかセンサーの値になっていた。この問題は 7.0.0 で発生していたが、7.2.0 では正しくリビジョンを返すようになった。かなりいい加減な C の処理系らしい。

サンプルでついてくる VS-WRC103LV_Sample_LED_20110118_1104.zip を解凍してビルド。
その後、VS-WRC103LV に書き込む。Debug フォルダに作成された「VS-WRC103LV_Sample_LEDなんたらかんたら.bin」をマウントした VS-WRC103LV にコピーするのだが、実行すると動かない。
ファインダからコピーすると不可視ファイルをいくつかつくるので、それが邪魔をしてうまくいかないと思われる。ちなみに Ubuntu Desktop でも試してみるが同様にうまくいかない。Mac でビルドした bin ファイルを Windows でコピーすると動くので、LPCXpresso は正しく動いているようだ。
Mac のターミナルでコピーすると正常に動いたので、そのときの手順をメモしておく。

  1. VS-WRC103LV を Mac に接続する
    VS-WRC103LV のボタンを押したまま Mac に接続し、マウントされたらボタンを話す。このとき、VS-WRC103LV の電源は OFF にしておく。
    マウントした VS-WRC103LV をファインダで開かないで、そっとしておく。

  2. ターミナルから次のコマンドで、古い farmware.bin を削除する
    $ rm /Volumes/CRP\ DISABLD/firmware.bin

  3. 新しい bin ファイルをコピーする
    「New_VS-WRC103LV_Firmware.bin」をコピーしている例。
    $ cp New_VS-WRC103LV_Firmware.bin /Volumes/CRP\ DISABLD/

  4. VS-WRC103LV のマウントを解除し、イジェクトする
    イジェクトする前に df コマンドで、Filesystem 名の確認を行う。
    ここでは「/dev/disk1」になっている。
    $ df -h
    Filesystem Size Used  Avail Capacity iused ifree %iused Mounted on
    /dev/disk1 32Ki 7.0Ki  25Ki    22%      16     0  100%  /Volumes/CRP DISABLD

    df コマンドで調べた Filesystem 名で VS-WRC103LV をイジェクトする。
    $ sudo diskutil eject /dev/disk1

  5. VS-WRC103LV を Mac から取り外す

付属している前進・後退のソフトをちょっと修正して LED を点滅させている例。
#include "lpc13xx.h"
#include "gpio.h"
#include "vs-wrc103.h"
#include "ixbus.h"

int main(void) {
    int ii;
    const unsigned short MainCycle = 60;
    Init(MainCycle);
    LED(3);
    while (getSW() != 1) {
        // ボタンが押されるまで待つ
        Wait(50);
        LED(0);
        Wait(50);
        LED(3);
    }
    LED(0);
    while (getSW() == 1) {
        ; // ボタンが離されるまで待つ
    }

    while (1) {
        // 前進
        Mtr_Run_lv(10000, -10000, 0, 0, 0, 0);
        LED(3);
        Wait(1000);
        // 停止
        Mtr_Run_lv(0, 0, 0, 0, 0, 0);
        LED(0);
        Wait(1000);
        // 後進
        Mtr_Run_lv(-10000, 10000, 0, 0, 0, 0);
        for (ii = 0; ii < 10; ii++) {
            LED(1);
            Wait(50);
            LED(2);
            Wait(50);
        }
        // 停止
        Mtr_Run_lv(0, 0, 0, 0, 0, 0);
        LED(0);
        Wait(1000);
    }
    return 0;
}

=== そのほか
LPCXpresso を起動した状態で、何もしていないのにエラーがでている。
これは気にしなくてもいい?
Description Resource Path Location Type
make: *** [src/main.o] Error 1     C/C++ Problem


Linux では注意!
ソースの中では「lpc13xx.h」となっているが、実際は「LPC13xx.h」と、大文字も使っているファイルがいくつかある。Mac/Win ではファイル名の大文字と小文字を区別しないので問題は起きない。でも Linux では大文字と小文字を区別するので、ファイルが見つからないというエラーが発生する。ファイル名を直すか、ソースを直す処置が必要。


LPCXpresso は Eclipse 
Eclipse のプラグインがそのまま使える。
MercurialEclipse をインストールして使っている。



LPCXpresso の設定変更のメモ:
タブはスペースにしたい。
General -> Editors -> Text Editors の Insert spaces for tabs にチェック。
C/C++ -> Code Style -> Formatter で、新しい profile を作成する。
その中で、Indentation を Spaces only にする。

Code Style の設定は、Source メニューの Format の時に使われる。

2013年11月2日土曜日

Raspberry Pi + I2C LCD module

Raspberry Pi に I2C で秋月電子の LCD ディスプレイをつないでみる。
そのときの記録。

使用した LCD ディスプレイは「I2C接続小型LCDモジュールピッチ変換キット」。
これは、「I2C接続小型LCDモジュール 8x2行」と「I2C接続小型LCDモジュール用ピッチ変換基板」をセットにしたもの。LCDモジュールだけではブレッドボードに取り付けるのが大変なので、ピッチ変換基板も必要となる。

I2C の準備はこちら「Raspberry Pi + I2C」を参照。

Python でソフトを作る。
ソースはこれ。日付と時刻を表示する。

#!/usr/bin/env python
# coding: UTF-8

'''
$ sudo apt-get install python-smbus
$ sudo apt-get install i2c-tools

comment out this line in /etc/modprobe.d/raspi-blacklist.conf
blacklist i2c-bcm2708

add the following lines to /etc/modules  
 i2c-dev 
 i2c-bcm2708
and then reboot.

search all addr.
$ sudo i2cdetect -y 1
'''

import smbus
import RPi.GPIO as GPIO
import time
import math
import datetime

class st7032i:
    def __init__(self, addr = 0x3e, ch = 1, contrast = 0x20):
        self.addr = addr
        self.ch = ch
        self.bus = smbus.SMBus(ch)
        self.contrast = contrast
        self.reset()
        
    def reset(self):
        contrast_h = 0x70 | (self.contrast & 0x0f)
        contrast_l = 0x54 | ((self.contrast >> 4) & 0x03)
        self.bus.write_i2c_block_data(self.addr, 0, [0x38, 0x39, 0x14, contrast_h, contrast_l, 0x6c])

        time.sleep(0.25)
        self.bus.write_i2c_block_data(self.addr, 0, [0x0c, 0x01, 0x06])
        time.sleep(0.05)
        
    def clear(self):
        self.bus.write_i2c_block_data(self.addr, 0, [0x01])
        
    def mov_to(self, row = 0, col = 0):
        self.bus.write_i2c_block_data(self.addr, 0, [0x80 + 0x40 * row + col])
        
    def put_str(self, the_str):
        self.bus.write_i2c_block_data(self.addr, 0x40, map(ord, the_str))
        
if __name__ == '__main__':
  try:
    my_lcd = st7032i(0x3e, 1)
    while True:
        time_str = datetime.datetime.now().strftime("%H:%m:%S")
        date_str = datetime.datetime.now().strftime("%y/%m/%d")
        my_lcd.clear()
        my_lcd.mov_to(0, 0)
        my_lcd.put_str(date_str)
        my_lcd.mov_to(1, 0)
        my_lcd.put_str(time_str)
        print date_str + ' ' + time_str
        time.sleep(1)
  except:
    print "Error accessing default I2C bus"


回路は、LCD モジュールに I2C の信号と電源(3.3V)を供給するだけ。

2013年9月10日火曜日

Raspberry Pi + I2C

Raspberry Pi に I2C デバイスを接続する。
その準備。

/etc/modules に次を追加する。順番も大切? ここは「i2c-dev」だけという説もある。
i2c-bcm2708
i2c-dev

/etc/modprobe.d/raspi-blacklist.conf を修正する。2行あるので、i2c-bcm2708 の行をコメントアウトする。
blacklist spi-bcm2708
blacklist i2c-bcm2708
こんな感じ。
blacklist spi-bcm2708
# blacklist i2c-bcm2708

Python ライブラリのインストールもする。その前に「$ sudo apt-get update」もしておく。
$ sudo apt-get install python-smbus
これもインストールする。(上記 python-smbus をインストールするときに、これも一緒に自動的にインストールされるので、このコマンドは実行しなくても良い)
$ sudo apt-get install i2c-tools

ここで reboot。

接続されている I2C デバイスの確認はこれ。(古い Raspberry Pi ならパラメタは「0」)
$ sudo i2cdetect -y 1

あとは $sudo i2cset と $ sudo i2cget

2013年8月16日金曜日

Raspberry Pi RPIO

ここではパラレル入出力で LED ピカピカをやってみます。

Raspberry Pi で、GPIO を使おうとすると結構大変です。
何が大変かというと、ライブラリがいくつかあったり、ポートの番号の表現がいろいろあるからです。
古い Raspberry Pi と、新しいものとで少し違ったり、使うライブラリによって番号の割り振りが異なることがあります。

RPIOは root 権限が必要なので、Python を root で起動します。
$ sudo python

手始めに、インタプリタから直接いろいろやってみます。
最初にライブラリ(python-rpi.gpio)をインポートします。このライブラリははじめから入っているので、インストールする必要はありません。
>>> import RPi.GPIO as GPIO

ここで使っているピンは、GPIO09と GPIO10 です。ピン番号で言うと GPIO09 が 21 番ピン、GPIO10 は 19 番ピンになります。
回路図はこうです。


GPIO09 を出力にセットして、最初は0、次に1を出力してみます。
>>> GPIO.setup(9, GPIO.OUT)
>>> GPIO.output(9, 1)
>>> GPIO.output(9, 0)

今度は入力です。となりの GPIO10 をプルアップ抵抗付きの入力端子にします。
そして、ポートの状態を読み込みます。
>>> GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_UP)
>>> GPIO.input(10)
False
>>> GPIO.input(10)
True

最後は・・・
GPIO.cleanup()

まとめて、Python のプログラムにするとこんな感じ。
GPIO10 が0になるまで GPIO09 を0にしたり1にしたりを繰り返します。
#!/usr/bin/env python
# coding: UTF-8

# import RPIO as GPIO
import RPi.GPIO as GPIO
import time

pin_out = 9
pin_in = 10
sleep_sec = 0.5

GPIO.setmode(GPIO.BCM)
GPIO.setup(pin_out, GPIO.OUT)
GPIO.setup(pin_in, GPIO.IN, pull_up_down=GPIO.PUD_UP)
out_value = False
loop = True
while (loop):
    GPIO.output(pin_out, out_value)
    out_value = not out_value
    loop = GPIO.input(pin_in)
    time.sleep(sleep_sec)
GPIO.cleanup()

class にしたバージョンはこれ。
#!/usr/bin/env python
# coding: UTF-8
# http://pythonhosted.org/RPIO/rpio_py.html#ref-rpio-py-rpigpio

# import RPIO as GPIO
import RPi.GPIO as GPIO
import time

class test_gpio():
    
    def __init__(self):
        self.pin_out = 9
        self.pin_in = 10
        self.sleep_sec = 0.5
        self.loop = True
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.pin_out, GPIO.OUT)
        GPIO.setup(self.pin_in, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    def main(self):
        out_value = False
        while (self.loop):
            GPIO.output(self.pin_out, out_value)
            out_value = not out_value
            self.loop = GPIO.input(self.pin_in)
            time.sleep(self.sleep_sec)
        GPIO.cleanup()
    

if __name__ == '__main__':
    test = test_gpio()
    test.main()

こんどは割り込みを使います。
このプログラムでは sleep でお休みしていますが、普通のプログラムではいろいろなことをやっているときにイベントが発生すれば、その処理をひとまず中断して、そのイベントに対応した処理を実行します。
主たる処理は中断を気にしないで処理を続けることができます。
割り込み処理は RPIO をインポートします。
インストールしてない場合は、先にインストールしておいてください。
#!/usr/bin/env python
# coding: UTF-8
# http://pythonhosted.org/RPIO/rpio_py.html#ref-rpio-py-rpigpio

import RPIO as GPIO
# import RPi.GPIO as GPIO
import time

class test_gpio():
    
    def __init__(self):
        self.pin_out = 9
        self.pin_in = 10
        self.sleep_sec = 0.5
        self.loop = True
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.pin_out, GPIO.OUT)
        GPIO.setup(self.pin_in, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.add_interrupt_callback(self.pin_in,
                                    self.call_back,
                                    edge='falling',     # rising, falling or both
                                    pull_up_down=GPIO.PUD_UP,
                                    threaded_callback=True,
                                    debounce_timeout_ms=100)
        GPIO.wait_for_interrupts(threaded=True)

    def call_back(self, gpio_id, val):
        print("gpio %s: %s" % (gpio_id, val))
        if gpio_id == self.pin_in:
            self.loop = False
        
    def main(self):
        out_value = False
        while (self.loop):
            GPIO.output(self.pin_out, out_value)
            out_value = not out_value
            time.sleep(self.sleep_sec)
        GPIO.cleanup()
    

if __name__ == '__main__':
    test = test_gpio()
    test.main()

2013年7月27日土曜日

Raspberry Pi を Wifi で接続する

Raspberry Pi を wifi 接続する。

いろいろやってみて、これがうまくいったので、その内容をメモ。
技術評論社の「Raspberry Pi [実用]入門」(Japanese Raspberry Pi Users Group 著)に書いてあった方法。
wicd も、標準で入っている wifi config も使用せずにできてしまいました。
詳細は上記「Raspberry Pi [実用]入門」を購入してくださいませ。
ちなみに、このときのアダプタは「Logitec Corp. LAN-W150N/U2 Wireless LAN Adapter」です。

/etc/network/interfaces の内容。
auto lo

iface lo inet loopback
iface eth0 inet static
address 192.168.0.102
netmask 255.255.255.0
gateway 192.168.0.1

allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp

iface my_wifi inet static
address 192.168.0.103
netmask 255.255.255.0
gateway 192.168.0.1

/etc/wpa_supplicant/wpa_supplicant.conf の内容。
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
        ssid="my_wifi"
        proto=RSN
        key_mgmt=WPA-PSK
        pairwise=CCMP TKIP
        group=CCMP TKIP
        psk="YOUR PASSWORD"
        id_str="my_wifi"
}



==== ここからは以前の設定内容 ====
残念ながら、 wicd もインストールしないとうまく動いてくれなかった。
そのときの記録を残す。
  1. WiFi Configで Scanしてパスワードを登録しておく
  2. wicd をインストールする。$ sudo apt-get install wicd
  3. wicd を GUI で起動
  4. 次の値を設定
    Automaticaly connect to this network
    以下、Properities
    Use Static IPs
    WPA 1/2 (Passphrase)
    Preshared key にパスワード
  5. reboot
固定 IP にする場合は、この下に書いた「/etc/network/interfaces」の修正を行う。

ここで使用した wifi usb アダプタは次の二つ。
Logitec Corp. LAN-W150N/U2 Wireless LAN Adapter
BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]


=== ここから先は、結局、うまくいかなかったときの記録 ===
Raspberry Pi を標準でインストールされているアプリ「WiFi Config」で設定したり、「wicd」をインストールすることなく、Wifi 接続を可能にする。
次の二つのファイルを修正する。
eth0 も wlan0 も固定 IP としている。

ここで使用した wifi usb ドングルは「BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]」と「Logitec Corp. LAN-W150N/U2 Wireless LAN Adapter」で試してみた。

この方法では残念ながら eth0 と wlan0 の切り替えは、コマンド sudo ifup eth0 や sudo ifdown wlan0 などで、いちいち up/down させないとだめみたい。
wlan0 も eth0 も有効になっている場合があるので、sudo ifdown eth0 をしないと wlan0 が使えないこともあったり。

やはり wicd をインストールしないとだめか。


/etc/network/interfaces
auto lo
iface lo inet loopback

# allow-hotplug eth0 
# auto eth0
iface eth0 inet static
 address 192.168.0.102
 netmask 255.255.255.0
 gateway 192.168.0.1

allow-hotplug wlan0
auto wlan0
# iface wlan0 inet manual
# wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface wlan0 inet static
 address 192.168.0.103
 netmask 255.255.255.0
 gateway 192.168.0.1
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf

iface default inet dhcp

/etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
 ssid="YOUR SSID"
 proto=RSN
 key_mgmt=WPA-PSK
 pairwise=CCMP TKIP
 group=CCMP TKIP
 psk="YOUR PASSWORD"
}