FreeBSD + RaspberryPi で I2C 通信

#センサーさんやキャラクター液晶さんと「お話」したい
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 のブログで調査しています。

https://vzaigrin.wordpress.com/2014/04/28/working-with-i2c-in-freebsd-on-raspberry-pi/

ようするに 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用ピン)

図１
![Raspberry Pi GPIO](https://qiita-image-store.s3.amazonaws.com/0/103137/b6aba983-14aa-b7fc-5310-3371ed5650bd.png)

は/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

![advent-i2c-raspi.jpg](https://qiita-image-store.s3.amazonaws.com/0/103137/781328f2-0ae6-ebbe-c222-2be0a23238b4.jpeg)

というふうに線を出して、ブレッドボードに刺した液晶につなげます

![advent-i2c-lcd.jpg](https://qiita-image-store.s3.amazonaws.com/0/103137/8d0dac8b-64e7-1996-3c03-939ffa0a0118.jpeg)

そして

```
pi2c -f /dev/iic1 -s
```
と実行すると、
```
Scanning I2C devices on /dev/iic1: 3e
```
と出て、この液晶のアドレスが0x3eだとわかります。
で、文字を出すんですが、

http://akizukidenshi.com/download/ds/xiamen/AQM1602.pdf
にあるデータシートにある初期化手順を踏んで書き込みや、周波数調整など面倒なので

https://github.com/s-mitu/RaspberryPi/tree/master/FreeBSD/i2c-lcd

にシェルスクリプトを置きました。

![advent-i2c-lcd2.jpg](https://qiita-image-store.s3.amazonaws.com/0/103137/952b4cb0-e0c9-7205-7cbe-99bd0f367040.jpeg)


```
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を仮定)が参考になるかもしれません。

https://github.com/s-mitu/RaspberryPi/blob/master/FreeBSD/i2c-temp/i2c-temp.c