タグ別アーカイブ: I2C

IOError: [Errno 5] Input/output error

Raspberry piでデバイスとI2C通信をしている時に
IOError: [Errno 5] Input/output error
に出会う時が有ります.基本的な設定が済んでいるものとして、このようなエラーが出る場合はいくつかの原因が考えられます。

1)信号線が長い、ノイズの影響を受けているなどで、信号そのものの波形が劣化している場合

 この場合は具体的にはオシロスコープを使って波形を確認した方が良いでしょう。オシロスコープが無い場合は信号線を出来るだけ短くすること、プルアップ抵抗が適切なものが入っているか確認する事と通信スピードを落としてみるのも有効です。

2)相手が準備できていないのにコマンドを送っている、データをリクエストしている場合
 コマンドを送る前にWaitを入れることになります.デバイスによってはコマンドを受け付けた後にいくら処理に時間がかかってそのあとにリクエストを受け付けると言ったシーケンスを明記しているものも有ります。詳しくはそれぞれのデバイスのリファレンスを参照の上で最適化するようにします。

3)初期化シーケンスをコマンド送信ごとに送ってしまっている
 本来であればSPI通信を始める場合の初期化処理は一度で良いのですが、スクリプトになっていとそれを外から呼び出した時に何度も短い間隔で呼び出して気づかない事も有ります。初期化は誰がやっているか、どのタイミングでやっているか確認し、スクリプトの最適化を行います.

シリアルインタフェイス I2CとSPIの選択

センサーや表示装置などのデバイスモジュールは多くの種類があり小型になり、また手頃な価格で入手できるようになりました。これらをマイコンまたはRaspberry piなどの小型Linux機で扱うのに容易なシリアルインターフェイスで制御できるものも多く有ります.

シリアルインターフェイスにはUART,I2C,SPI,Single wireなどがあります。UARTは主に一対一の通信に用いられます.Raspberry piでRaspbianをOSとして使っている場合はこのUARTはシリアルコンソール用と割り当てられています。
SPI通信ではマスターとスレーブを定義し、一つのマスターに対して複数のスレーブを持つことができます.スレーブの切り替えはSS(Slave select、CS:chip select, CE:Chip enableとも呼ばれる)信号で行います.Raspberry piには二つのCE信号線が有ります。
I2C通信では二つの信号線、データ線SDAとクロック線SCLによって通信します。(この他にモジュールはVccとGNDも接続します )SPIではMOSI,MISO,SCLK,SSの信号線が必要だったのと比較して信号線が少なくて済みますね。I2Cも複数のモジュールをスレーブとしてつなげることができます.同時に複数のデバイスをつなげた場合はそれそののデバイスが持つアドレスによってデータのやり取りの相手を指定します.同じバスで使うデバイスの持つアドレスが競合しないように気をつけなければなりませんが、標準の7ビットアドレスモードで使う場合は最大で112個のデバイスを扱うことができるのです.

センサーモジュールによっては同じ機能のものでもI2C用とSPI用と2種類有るものや、一つのモデルで設定でI2CとSPIを切り替えられるものも有ります.またI2CのみまたはSPIのみに対応しているデバイスもあります。

自分が実現したい仕様に対して、用いるデバイスがどちらのインターフェイスを持っているか、それと制御するマイコンまたはLinuxボードのインターフェイスの制約条件とを勘案してよりベターな組み合わせを模索する必要が有ります

Raspberry pi とArduinoとのI2C通信

ここではRaspberry pi をマスター、ArduinoをスレーブとしたI2C通信について紹介します.

接続を簡単にするためにArduino側は3.3V動作にします。Arduino Unoは5V動作なのでI2Cレベルコンバーターを間に挟むなどの対応が必要です.Google検索で得た情報では、コンバーターなしで接続して良いとかどうとか、海外のブログで見つけましたが確認はしていません.

そこで3.3Vで動作しながらArduino IDEからもプログラミング可能なI2Cインターフェイス基板を使います.これはATmega328が使われていて、標準のシールドは使えないものの中身的にはArduinoと同様だともいえます。

接続は下記の通り
AVR board <-> Raspberry pi
27 SDA <-> 3
28 SCL <-> 5
GND <-> GND
Vcc <-> 3.3V

レベルコンバーター不要、電源もRaspberry pi 側からもらうので結線も楽チンですね。

さてハードウェアの接続の次はソフトの準備です。

Arduino側のコード。Arduino IDEは0.22、PCはMac OS X 10.9で確認。
WireライブラリーはArduino 1.01でメソッドが変更になっていますのでご自分の環境に合わせて適宜修正して使ってください。
wire.read wire.write がそれぞれ wire.receive wire.send に変更


#include 

#define SLAVE_ADDRESS 0x04
int number = 0;
int state = 0;

void setup() {
    pinMode(0, OUTPUT);
    pinMode(1, OUTPUT);
    digitalWrite(0, LOW); // set the LED off
    digitalWrite(1, LOW); // set the LED off
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);

    // define callbacks for i2c communication
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);
}

void loop() {
    delay(100);
}

// callback for received data
void receiveData(int byteCount){

    while(Wire.available()) {
        number = Wire.receive();
        if (number == 1){

            if (state == 0){
                digitalWrite(1, HIGH); // set the LED on
                state = 1;
            }
            else{
                digitalWrite(1, LOW); // set the LED off
                state = 0;
            }
         }
     }
}

// callback for sending data
void sendData(){
    Wire.send(number);
}

I2Cから文字”1″を受け取ったらI2Cインターフェイス基板上のLEDをオンオフ切り替えを行うものです.受け取った文字列をそのままマスターに送り返す機能も持っています.
Raspberry piからsudo i2cdetect 1でArduinoが認識されているかどうか確認しましょう。
上のコードを使った場合、04にスレーブが検出できれば、上手く動作している事になります。

次にRaspberry Pi側のサンプルコードを紹介します.こちらはPythonで記述しています.



import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)

# This is the address we setup in the Arduino Program
address = 0x04

def writeNumber(value):
    bus.write_byte(address, value)
    # bus.write_byte_data(address, 0, value)
    return -1

def readNumber():
    number = bus.read_byte(address)
    # number = bus.read_byte_data(address, 1)
    return number

while True:
    var = input("Enter 1 - 9: ")
    if not var:
        continue

    writeNumber(var)
    print "RPI: Hi Arduino, I sent you ", var
    # sleep one second
    time.sleep(1)

    number = readNumber()
    print "Arduino: Hey RPI, I received a digit ", number
    print

スレーブ側に一文字送信して、スレーブ側から送り返してくる文字を端末に表示する簡単なスクリプトです.連続して文字を送りつけると、通信がこけることがあります.エラーでスクリプトが停止します.ここでは動作確認のためのスクリプトの紹介に止めますが、実際に運用する事を考えた場合にはI2C通信のところでtry,except文によるエラー処理を入れておくのは言うまでもありませんね。

I2Cインターフェイス基板には制御に便利なPython ライブラリーが付いています.LCDの表示制御、DIOのインアウト、アナログ入力、電子ブザー出力。Raspberry piを使って何かを制御して、それを簡単に表示する、小型機器の応用に便利です.
専用ライブラリーについて

CIMG2378

I2C接続可能なLCD/ACM1602の使いこなし

Raspberry piにLCDモジュールを接続する場合にその選択肢は案外少ないものです.
まずは3.3Vで動作する事。Raspberry Pi の少ないDIOピンを節約したいのでバスインターフェイスではなくシリアルインターフェイスで接続できるのが望ましい。表示文字数も少ないよりも多い方が良いな。と言う事で選定したのがACM1602.

さっそく秋月電子で購入
CIMG2378

Raspberry piのI2Cを有効にするための諸設定は済んでいるものとして。
I2Cの2線SDAをSCLそしてVccとGNDを接続。

シェルから以下のコマンドを投げてみる。

pi@raspberrypi ~ $ sudo i2cdetect 1

しかし、呼べども答えず。返事を返してくれるデイバイスは誰もいません。

I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

接続を一通り確認したり、他のI2CデバイスとRaspberry Piとが通信できている事を確認し行き詰まった。
そこでGoogle検索の出番。ひたすら調べた結果次の事が判明。

  • デフォルトの通信速度では通信がつまずくことがある
  • Write専用で一度でもReadを掛けるとモジュールがフリーズする。製品に添付の資料にも明記しているが、Readを掛けた時の状態に付いては記載がない。

一つ目については通信スピードを落とせば同じバスに繋がるデバイスもそのスピードダウンの影響を受けるがまあよしと考えられる範囲ですが、二つ目については一度でもi2cdetectを掛けるとACM1601はフリーズ状態になりその都度モジュールの電源再投入して立ち上げ直す必要になるのです.Raspberry Pi本体のシステムをリスタートする必要はないのですが、やはり使いにくいことには変わりないですね。

一応I2Cの通信スピードを変更する手順について書き留めておきます。

sudo modprobe -r i2c-bcm2708
sudo modprobe i2c-bcm2708 baudrate=50000
sudo i2cdetect 1

この時点ではもちろんACM1602からの返答は有りません。ここでLCD電源を一度オフにしてオン ※1
これ以降はi2cdetectを掛けないでくださいね。するとまたACM1602がフリーズに落ちますので。
無事ACM1602が使えるようになっているはずなので、I2Cを通じて制御コマンドを渡して動作を確認してください。

通信速度をデフォルトのままだとI2Cでコマンドを渡した時にPythonなら「IOError: [Errno 5] Input/output error」が出ます。
上記コードをその都度シェルから入力するのが面倒だ、この設定をデフォルトにしたい場合は、/etc/modprobe.d/i2c.conf というファイル(ファイル名は .conf で終わっていれば何でもよい)を作って以下のように書いておけば良いようだ。

options i2c_bcm2708 baudrate=50000

でもi2cdetectを不用意に投げないようにご注意を

結論。秋月で買えるACM1602(I2Cインタフェイスキャラクターディスプレイ LCD 3.3V)はRaspberry pi との組み合わせでは使いにくい.

それからI2Cを使う場合sudo権限が必要なので、実行するPythonスクリプトなどはsudo権限を使って実行することを忘れずに。

LCDモジュールの取り扱いをもっと簡単に出来ないか、複数のI2Cデバイスをぶら下げた時に安心してi2cdetectを投げられるようにならないか。そういった悩みを解消するのに、i2cインターフェイス基板が便利かもしれませんね。
i2cインターフェイス基板
デジタルIOも拡張でき、アナログ入力にも対応。Raspberry piを使った電子工作を便利にする事だと思います.

I2Cinterface説明図.006

I2Cインターフェイス基板 LCD16x2付き (キット)

概要

Raspberry Piなど小型で安価なLinuxボードが市販されるようになりました。これらボードはGPIOを搭載しセンサーやアクチュエーターの制御を容易にします。そしてPICやAVRといったマイクロコントローラーよりもプログラミングをずっと容易にし、インターネットとの親和性も大変良く、電子工作としても製品組み込みとしてもいま大きく注目を集めています。

しかし、実勢に実機を使って細かく検討していくといくつかの問題点も見えてきます.

  • Raspberry Piの表示デバイスとしてLCDを使うと接続に多数の信号線が必要となり、Raspberry Piの数少ないGPIOをそれだけで消費してしまう。
  • なんといっても多ピンあるLCDとの配線はめんどう
  • I2C接続できるLCDもあるが種類が少ない(例)秋月電子通商で販売されているI2C接続小型LCDモジュール AQM0802Aが接続できるそうだが、表示が8×2で物足りない
  • 16×2表示できるLCDACM1602NI-FLW-FBW-M01もあるが、実際に試したところRaspberryPiからではコントロールするには制限が有ることがわかった。
  • Raspberry Piにはアナログ入力が無い。ADC用IC(MCP3204など)を使う必要が有る。(Beaglebone Blackにはアナログ入力があるがMax1.8Vと制約が有る)
  •  SPI デバイス、I2Cデバイス、シリアル通信を複数接続するとあっという間にGPIOが足りなくなる
  •  アナログ出力、ハードウェアPWMが1チャンネルしか無い。

そこでこれらの不満を解消するためのインターフェイス基板の提案です. 続きを読む

i2cinterface99.py / i2cインターフェイスボード用Pythonライブラリー

I2CでLCD,LED,デジタルIO,アナログIO,圧電スピーカーを制御可能なインターフェイスボード”I2Cインターフェイス基板 LCD16x2付き (キット)“および”I2Cインターフェイス基板 LCD20x4付き (キット)“のPython用ライブライーについて説明します。

準備

raspbian OSのI2Cを有効化してください。

sudo raspi-config

Advenced Options からI2Cを有効にします。
PythonからI2C制御するためのライブラリーをインストールします。

$ sudo apt-get install python-smbus

使用方法

import i2cinterface99 #ライブラリーの読み込み
ii =  i2cinterface99.i2cinterface99() #モジュールの初期化
ii.led(1,1) #メソッドの呼び出し

Functions

led()
LEDの明滅
setSound()
圧電スピーカーからの発信音のパラメータ設定
sound()
圧電スピーカーからの発信音オンオフ
setPinMode()
IOピンの入力/出力設定
digitalWrite()
デジタル出力オンオフ切り替え
digitarRead()
デジタル入力
analogWrite()
アナログ出力値設定
analogRead()
アナログ入力
lcdInit()
LCD制御の初期化
lcdClear()
LCD表示のクリア
lcdSetCursorMode()
カーソルの設定、明滅設定
lcdPrint()
LCDへの文字列の表示
lcdWrite()
LCDへ文字コードで書き込み
lcdSetPosition()
LCDの表示位置を指定
lcdDisplay()
LCDの表示/非表示設定
lcdScroll()
表示の左右スクロール
lcdAutoScroll()
オートスクロールの有効/無効
reset()
I2C通信のコマンドバッファのリセット

ダウンロード

i2cinterface99.py20140109

専用ハードウェア

本ライブラリーは「I2Cインターフェイス基板 LCD16x2付き (キット)」を制御するためのものです。
製品についての詳しい情報はこちらでご覧ください.
I2Cインターフェイス基板 LCD16x2付き (キット)