#センサーさんやキャラクター液晶さんと「お話」したい
RaspberryPi で IoT 気取りで何かやりたいとなるとセンサーの値を取得して、それに合わせてプログラムが起動したり、データを集めたりとかやってみたいと思うわけですが、私のように電子工作に明るくない身としては、接続はなるべく簡単に接続できるとありがたいわけです。そういった向きにぴったりなのがI2Cです。
とりあえずセンサーやら繋ぎたいパーツと Raspberry Pi を二本の信号線(と電源線)で繋いでやると、センサーさんとお話が出来るようになります。電子工作系パーツ屋さんには I2C が喋れるセンサーやキャラクタ液晶などのパーツが結構売ってるので、そういうのを買ってきて繋げば制御できます(出来ないこともありますが…)。
しかも複数のパーツを数珠つなぎに配線することができるので(パーティーライン)Raspberry Pi の pin を消費せずにたくさんのパーツをつなぐことが出来ます。どのデバイスとお話するかは各デバイスに固定の「アドレス」があるので、それを指定してあげます。
FreeBSD にはI2C を扱うコマンド、その名もまんま "i2c(/usr/sbin/i2c)" というのがあるので、簡単なデータのやり取りならばプログラミングも不要です。
#上記には若干嘘が…
というわけなのですが、上記には嘘というか Raspberry Pi 特有の重要な補足が必要で、実は FreeBSD の標準 i2c コマンドは、Raspberry Pi では現状動きません。実行すると
Device not configured
などと怒られてしまいます。
なぜ動かないのかは Vadim Zaigrin のブログで調査しています。
ようするに Raspberry Pi の i2c ドライバでは実装されてない機能を i2c コマンドでは使用しているので、Raspberry Pi では i2c が動かないというわけです。
Vadim Zaigrin さんは原因を調べただけでなく、Raspberry Pi で動くように i2c コマンドを作り変えたバージョンを公開しています。
ブログ記事
https://vzaigrin.wordpress.com/2014/05/18/changing-i2c8-utility-in-freebsd-to-work-on-raspberry-pi/
ソースの github
https://github.com/vzaigrin/newi2c
パッチを投げたら FreeBSD 本流チームからダメ出し食らってしまったそうなので At your own risk で
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=189914
上記ソースの使い方は、newi2c.c を単純にcc すればコンパイル終了です。私は標準と混ざらないように pi2c という名前で実行ファイルを作って/usr/local/bin に入れています。以下でも標準でないことを強調するため、コマンドライン実行例のコマンド名を pi2c として説明します。
git clone https://github.com/vzaigrin/newi2c
cd newi2c
cc -o pi2c newi2c.c
install -c pi2c /usr/local/bin
#コマンドの使い方実例
コマンドオプションの詳細はお約束の man i2c で。
FreeBSD では i2c コントローラーは /dev/iic? という形で生えていて、i2c コマンドでは -f オプションで渡してあげます。
Raspberry Pi type B(+)や Raspberry Pi2では /dev/iic0 と /dev/iic1 が生えてますが、例の GPIO 端子からI2Cの端子もあるのですが(図のGPIO02=SDA とGPIO03=SCLが i2c用ピン)
は/dev/iic1です。オプション指定なしだと /dev/iic0 が使われてしまいますので注意してください。
ちなみに Raspberry Pi type B にはこの端子の下に 4 x 2 の穴が空いています。その穴がI2CやSPIの端子で、それが /dev/iic0 なので、ピンをハンダ付けして生やしてやれば、/dev/iic0 もつかえるそうです(まだ試してない)。
実例として、秋月電子で売っていたキャラクター液晶を繋いでみましょう。
8x2 http://akizukidenshi.com/catalog/g/gK-06795/
16x2 http://akizukidenshi.com/catalog/g/gK-08896/
写真では16x2 を使っています。
Raspberry Pi からは
赤 3.3V
青 SDA
緑 SCL
黒 GND
というふうに線を出して、ブレッドボードに刺した液晶につなげます
そして
pi2c -f /dev/iic1 -s
と実行すると、
Scanning I2C devices on /dev/iic1: 3e
と出て、この液晶のアドレスが0x3eだとわかります。
で、文字を出すんですが、
http://akizukidenshi.com/download/ds/xiamen/AQM1602.pdf
にあるデータシートにある初期化手順を踏んで書き込みや、周波数調整など面倒なので
にシェルスクリプトを置きました。
i2c-lcd.sh -w 16 "Raspberry Pi de I2c"
のような感じで文字が出力できます。「-w」は文字の折り返しで、8x2 に合わせてデフォルトが 8 なので、16 を指定しています。
#問題点
いきなり実例がややこしい例になってしまいました。
もっと簡単な「センサーデーター読みこむだけ」のようなことをやりたかったのですが、実はこの i2c コマンドも問題があって、連続したバイトの読み込みがうまく行きません。
Raspberry Pi 用変更した i2c コマンドは、ioctl() で I2CRDWR コマンドでstruct iic_msg と struct iic_rdwr_data を渡して読み書きを行います。
直接ioctl()で渡すのはstruct iic_rdwr_dataで、その中身 struct iic_msg の配列とその要素数nmsgsです。
struct iic_rdwr_data {
struct iic_msg *msgs;
uint32_t nmsgs;
};
読み込み時は struct iic_msg の flags とslaveに IIC_M_RDフラグをセットし、len に読み込みサイズ、buf に配列アドレスを渡します。
struct iic_msg
{
uint16_t slave;
uint16_t flags;
#define IIC_M_WR 0 /* Fake flag for write */
#define IIC_M_RD 0x0001 /* read vs write */
#define IIC_M_NOSTOP 0x0002 /* do not send a I2C stop after message */
#define IIC_M_NOSTART 0x0004 /* do not send a I2C start before message */
uint16_t len; /* msg length */
uint8_t * buf;
};
i2c コマンドでは、struct iic_msg の len を 1 とし、struct iic_rdwr_dataのnmsgs を読み込むバイト数にする形にして読み込んでいるのですが、自分の環境ではstruct iic_msg の len を読み込むバイト数として、nmsgs は1(+書き込み用struct iic_msgの数)としないとうまく読み込めませんでした。
センサーの読み込みを行いたい場合は、i2c コマンドのコードを参考にしつつ、上記に注意しながらプログラムを書けば、読み込めるようになります。
超適当なサンプルですが、温度センサー
http://www.marutsu.co.jp/pc/i/242757/
用の温度取得プログラム(i2cアドレスは0x48を仮定)が参考になるかもしれません。