2013年8月10日土曜日

夏休みの工作 ~python-arduino連携でI2Cを辛うじて使う(今日の分)~

センサーとかモータードライバ(まだ碌なのがないが)を少ないPIN数で使うためにI2Cが必要だったのでありがたい外部ライブラリを勝手に拡張してみた。

今回の参考資料:Tristan Hearn氏が作ってくださった以下のライブラリ。どうやらNASAの方でお勤めの方っぽい((((゜Д゜;))))。ちなみにライブラリはMITライセンス。
 ↓入手場所
https://github.com/thearn/Python-Arduino-Command-API

↓MITライセンスに基づく告知(ここから)
Copyright (c) 2012-2013 Tristan A. Hearn <tristanhearn@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

↑(ここまで)

今回は上記ライブラリのソースを勝手に弄ってしまうお話です。MITライセンス万歳。

↓python側のプログラム。これを自分のプログラムの上のほうに差し込んでおきます。要上記ライブラリとそれを動かすのに必要なもの

from Arduino import Arduino
class Arduino_C(Arduino):
    def __init__(self,baud=9600,port=None,timeout=2):
        Arduino.__init__(self, baud, port, timeout)

    def i2c_write_1(self,device,data):
        print "i2cwrite"
        cmd_str=''.join(["@i2cw1%",str(device),"%",str(data),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")
        print "i2c return",hex(int(rd))
    def i2c_write_2(self,device,data1,data2):
        print "i2cwrite"
        cmd_str=''.join(["@i2cw2%",str(device),"%",str(data1),"%",str(data2),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")
        print "i2c return",hex(int(rd))

    def i2c_read_1(self,device,location):
        cmd_str=''.join(["@i2cr1%",str(device),"%",str(location),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")

        output=int(rd)
        return output

    def i2c_read_2(self,device,location):
        cmd_str=''.join(["@i2cr2%",str(device),"%",str(location),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd1 = self.sr.readline().replace("\r\n","")
        rd2 = self.sr.readline().replace("\r\n","")
        output=[int(rd1),int(rd2)]
        return output

↑筆者の頭脳とやる気の関係で1バイトまたは2バイトの書き込み、読み込みの4つの関数を追加しただけ、せめてバイト数ぐらいは可変にすべきだが・・・しょうがないよね、夏風邪引いてるしね。

引数のdeviceとかdata、locationとかは16進数(pythonでも0x00表記)でやると頭で変換しなくていいから楽。

↓以下、arduino側のソース(prototype.ino)に追加した部分のその1。serial_parser関数より上のどこかに差し込む。

void i2c_write_1b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int wdata = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(wdata);
    Wire.endTransmission();
    Serial.println(id);
}

void i2c_write_2b(String data){
    String sdata[3];
    split(sdata,3,data,'%');
    int id = Str2int(sdata[0]);
    int wdata1 = Str2int(sdata[1]);
    int wdata2 = Str2int(sdata[2]);
    Wire.beginTransmission(id);
    Wire.write(wdata1);
    Wire.write(wdata2);
    Wire.endTransmission();
    Serial.println(id);
}

void i2c_read_1b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int location = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(location);
    Wire.endTransmission(false);
    Wire.requestFrom(int(id),1,false);
    int out1 = Wire.read();              
    Wire.endTransmission(true);
    Serial.println(out1);
   
}

void i2c_read_2b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int location = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(location);
    Wire.endTransmission(false);
    Wire.requestFrom(int(id),2,false);
    int out1 = Wire.read();              
    int out2 = Wire.read();
    Wire.endTransmission(true);
    Serial.println(out1);
    Serial.println(out2);
}

↑ここまで

↓その2。serial_parserのcmd==(コマンド)のelse ifが続いているあたりに自然に差し込む。

else if (cmd =="i2cw1"){
   i2c_write_1b(data);
  }
    else if (cmd =="i2cw2"){
   i2c_write_2b(data);
  }
  else if (cmd =="i2cr1"){
   i2c_read_1b(data);
  }
  else if (cmd =="i2cr2"){
   i2c_read_2b(data);
  }

↑ここまで

明らかに命令の種類が足りてないような気がするが、ストリナとかで売ってるようなジャイロとかならこれである程度読み書きできるはず。読み込んだデータバイトの処理(2つのバイトを接続したりとか単位換算とか)なんかはpython側で。

いつかHearn氏が正規(?)のI2C実装をしてくれることを祈りつつ今回は終了。

P.S : prototype.ino のsetup() 内に Wire.begin(); を入れないと何も機能しないので注意(10分ほどはまった)

1 件のコメント:

  1. i2c_write_1b と i2c_write_2b は重要な修正あり。これを使用しないこと

    返信削除