IoT何をいまさら(38) ラズパイに3角測量センサ接続2

昨日は、ラズパイのI2Cにシャープ製測距センサGP2Y0E03をつなげて距離が測れている雰囲気が出て良かったね、で終わっていました。今日は「移植」したソフトウエアについて見ていきたいと思います。元は、MbedOSでcppで書いたのですが、今回はPythonであります。I2Cの制御には変わらないので、遠くからみたら「一緒」ですが、細かいところの書き方はちょっと「違う」のでした。

まず今回取り扱っているセンサですが、先頭の画像のものです。本体11mmx16.7mmほどの大きさに2つの「眼」がついているセンサです。一方の目から光(IR)を真っすぐ前に発し、もう一方の目に映るその反射スポットの位置から3角測量の原理で距離を得ます。センサ的には通常4~50cm距離用とのことですが、今回は、128cmまでのレンジに拡張して(自己責任で)測定させています。センサについては、「第15回3角測量で距離を、シャープ測距センサ」の回で実験してみているのでそちらをご参照くだされ。

さて上の回では、GP2Y0E03センサをSTマイクロエレクトロニクス製Nucleo-F072RBボードに取り付けて実験しておりました。開発環境は、Arm社のWeb開発環境である、Mbed Compilerを使わせてもらっています。そこでMbedOSのI2CのAPIをベースにして実験用のプログラムを書いたのでした。今回は、ラズパイ上のPython3で良く使われているモジュール

smbus

でI2Cのインタフェース部分を書きなおしてみます。

MbedOS上でGP2Y0E03のために作った関数は3つ、以下のものです。

  1. int GP2Y0E03::writeCommand(char regadr, char dat)
  2. int GP2Y0E03::readData(char regadr, char* dat)
  3. int GP2Y0E03::readDistance(int* dist)

何十個かレジスタを内蔵しているGP2Y0E03のレジスタへの書き込みを行うためのwriteCommand、レジスタからのデータ読み出しを行うためのreadData、およびreadData2回分を1回にまとめた上で、距離を算出して戻すためのreadDistanceの3つです。基本、writeCommand、readDataが出来ればよろしい。

まずは、MbedOS上のwriteCommandのソース

int GP2Y0E03::writeCommand(char regadr, char dat)
{
    status =0;
    wdata[0] = regadr;
    wdata[1] = dat;
    status = i2cRef->write(DMS_ADDR, wdata, 2, 0);
    return status;
}

書き込み操作をおこなっているのが、i2cRef->writeですね。i2cRefにはi2cバスを指すオブジェクトが入っているので、それに、I2CのアドレスDMS_ADDRを指定し、さらに書き込みデータとして、最初のバイトにレジスタアドレスregadr, そして第2のバイトで書き込みデータを与えています。ほぼ、I2Cバスを流れるデータのイメージ通り。そこで、

DMS_ADDRは0x80

としていました。I2Cバスのビットイメージそのものではあるのですが、最下位ビットはI2Cバスのアドレスではなく、RWの方向制御のビットです。MbedOSのこのAPIでは7ビットのアドレスを1ビット左シフトしてバス上流れるイメージで指定する必要がありました。これに対して smbusモジュールでは、

I2Cの定義通り, 7ビットでアドレス指定

する必要があります。つまり、1ビット左シフトして 0x40。(これに引きずられてレジスタアドレスまでシフトしないように。レジスタアドレスはGP2Y0E03内部の話なのでそのまま)Pythonで書きなおしたものはこちら、

    def writeCommand(self, regadr, cmd):
        self.i2cRef.bus.write_byte_data(self.DMS_ADDR, regadr, cmd)

self.i2cRef.busの中にsmbusからインポートした SMBus(bus番号) が入っておるとお考えくだされ。なお、前回の検討から、ピン3、ピン5のI2Cバスに接続するのであれば、

bus番号は1

であります。writeCommandがあれば、

  • レジスタアドレス0xEEに、マジックナンバ0x06書き込めばセンサリセット
  • レジスタアドレス0x35に、データ0x1を書き込めば128cmレンジ設定

など所望の動作をいろいろさせることができます。

さて、コード上の差異が大きいのは、readの方ですかね。MbedOS用のcppの場合、以下のように2段階でreadしてました。

int GP2Y0E03::readData(char regadr, char* dat)
{
    status =0;
    wdata[0] = regadr;
    rdata[0] = 0;
    status = i2cRef->write(DMS_ADDR, wdata, 1, 0);
    if (status !=0) return status;
    status = i2cRef->read(DMS_ADDR, rdata, 1, 0);
    *dat = rdata[0];
    return status;
}

まず最初のwriteで読み出すレジスタアドレスを書き込み、引き続くreadで指定したレジスタからデータを読み出す、という具合でした。しかし、Python、smbusモジュールではもっと簡単。

    def readData(self, regadr):
        return self.i2cRef.bus.read_byte_data(self.DMS_ADDR, regadr)

smbusモジュールで定義されている read_byte_dataは、読み出しレジスタアドレスを指定すれば、この2ステップを連続してやってくれます。なお、戻り値は読み出した値(cppの方は、読み出し結果のステータス、読み出し値は引数ポインタへ)です。これを2回使えば、2つのレジスタに分かれて格納されている測定距離を読み出すことができます。

    def readDistance(self):
        """read Distance method
        
        128cmレンジ設定時、cm単位の値を返す
        """
        distH = self.i2cRef.bus.read_byte_data(self.DMS_ADDR, self.DMS_DISTH)
        distL = self.i2cRef.bus.read_byte_data(self.DMS_ADDR, self.DMS_DISTL)
        dist = ((distH *16) + (distL & 0xF)) >>5
        return dist

なお、距離読み出しデータの上位レジスタは0x5E番地、下位レジスタ(4ビット)は0x5F番地です。

こうしてラズパイにI2Cセンサを接続できたので、次はJetson Nanoに接続してみますかね。

IoT何をいまさら(37) ラズパイに3角測量センサ接続

IoT何をいまさら(39) mCube Inc.、IoTじゃないIoMT