CHIRIMENにI2Cデバイスが繋がんない時に確認すること

[「CHIRIMENでI2Cデバイスを使ってみる」](http://qiita.com/tadfmac/items/04257bfe982ba0f050bb)の続きです。
今回は、CHIRIMENでI2Cデバイスを使おうとしたのに動かない時の対応方法について書いてみたいと思います。

# 1. 物理的にちゃんと繋がってる?

動かねー！というとき、接触不良とか、線が外れてたとか、そういったことも結構ありますので、
最初に確認してみましょう。プログラムず〜〜と眺めて疲れ切ったあとに、繋ぎ方間違ってた！！という脱力感に襲われないように。。。

I2Cは、SCL、SDAという2線式の同期型シリアルインターフェースです。
この2線に+電源(+5V/+3.3Vなど)とGNDの2つを加えた4線を繋ぐ必要があります。

> ポイント。 2線式という名前で呼ばれますが、SCL、SDAだけ繋いでも動かないので注意！

## CHIRIMEN側のI2Cポート

CHIRIMENには「I2C ポート0」と「I2C ポート2」の2つの接続ポートがあります。

![CHIRIMENのI2Cポートの場所](http://img3.dfm.lrv.jp/chr-i2c/port.png)

まずは、つなぐ場所があっているか？確認してみましょう。

## 繋ぎ方の例 (1) 3.3VのI2Cモジュールの場合

I2Cポート2に3.3V動作するI2Cデバイスを繋いでみましょう。

![3.3Vのデバイスを繋いだ例](http://img3.dfm.lrv.jp/chr-i2c/33v.png)

CHIRIMENのI2Cポートは内部で3.3Vにプルアップされています。
ですので、上記のように直結するだけで基本的には動くはずです。

## 繋ぎ方の例 (2) 5VのI2Cモジュールの場合

5VのI2Cモジュールをつなぐ場合ですが、そのモジュールのデータシートに例えば「 VCC=2.7V〜5.5V」のように書かれている場合、5Vにも対応していますが、おそらくそのモジュールは3.3Vでも動作しそうです。
なので、3.3VのI2Cモジュールと同じように接続すれば良いはずです。

厄介なのは、Arduinoなど5Vのマイコンボードで利用することを想定して設計されたモジュールが、モジュール側で5Vから3.3Vに降圧しているような場合で、この場合は5Vをつながないとうまく動かない可能性があります。

その場合は、下記のようなレベル変換回路を試してみると良いと思います。

![3.3Vと5Vレベル変換回路を使って5V用I2Cデバイスを接続する例](http://img3.dfm.lrv.jp/chr-i2c/5v.png)

回路図中の Q1, Q2は Nch-MOSFETです。2N7002などでOK

> ポイント。CHIRIMENのI2Cラインに5Vをプルアップしてしまわないように注意。

上記のようなI2Cデータラインのレベル変換回路はI2Cモジュール側に内臓していることもあります。
まずは、データシートを確認しましょう。

# 2. ちゃんと繋いでるけど動かん

物理的にちゃんとつながってるはずなのに、動かない！！
というときには、下記を試してみてください。

## 原因 0. I2CアドレスとI2Cポートは合っていますか? (2017.2.2追加)

> コメント欄で @dynamis さんより大事な指摘をいただきました。ありがとうございます。

### I2Cポートが正しく指定できているか? を確認

すでに書きましたが、CHIRIMENがサポートするI2Cポートは「0」と「2」の2つです。
CHIRIMENのWebアプリでI2Cを利用する場合、まずはI2Cポートの番号が合っているか？を確認してみてください。
最初にI2CモジュールをCHIRIMENで利用する場合には、CHIRIMEN-OHの[exampleリポジトリ](https://github.com/chirimen-oh/examples)に掲載されている各種サンプルを参考にされるかと思います。しかし、これらサンプルは、0か2のどちらかのポートを指定して記載されていることに注意してください。

例えば、[ADT7410](https://github.com/chirimen-oh/examples/blob/master/i2c-ADT7410/js/main.js)のサンプルは下記のように書かれています。

```main.js
'use strict';

window.addEventListener('load', function (){
  var head = document.querySelector('#head');
  
  // WebI2C Initialized
  navigator.requestI2CAccess()
    .then(function(i2cAccess){
      var port = i2cAccess.ports.get(0); /* <-ここでポート 0 を指定している */
      var adt7410 = new ADT7410(port,0x48);
      setInterval(function(){
        adt7410.read().then(function(value){
          console.log('value:', value);
          head.innerHTML = value ? value : head.innerHTML;
        });
      },1000);
    }).catch(e=> console.error('error', e));
}, false);
```

上記例ではポート「0」を指定しています。
このため、このサンプルコードはポート2につないだモジュールを動かすことができません。
ADT7410をポート2につないで動作させたい場合には、下記のように修正します。

```main.js
'use strict';

window.addEventListener('load', function (){
  var head = document.querySelector('#head');
  
  // WebI2C Initialized
  navigator.requestI2CAccess()
    .then(function(i2cAccess){
      var port = i2cAccess.ports.get(2); /* <-ポート 2 に変更 */
      var adt7410 = new ADT7410(port,0x48);
      setInterval(function(){
        adt7410.read().then(function(value){
          console.log('value:', value);
          head.innerHTML = value ? value : head.innerHTML;
        });
      },1000);
    }).catch(e=> console.error('error', e));
}, false);
```

これだけでポート「2」に接続して動かすことができます。

### I2Cスレーブアドレスが正しく指定できているか? を確認

ポート番号と同様に、[exampleリポジトリ](https://github.com/chirimen-oh/examples)の各種サンプルでは各I2Cモジュールのスレーブアドレスについても特定の値（主にデフォルト値）で記載されています。
しかし、これはモジュールの仕様により様々ではあるのですが、I2Cモジュールの多くは同一I2Cバス上のアドレス衝突を防止するためにスレーブアドレスを変更する機能がなんらかの形で用意されています。

先の[ADT7410](https://github.com/chirimen-oh/examples/blob/master/i2c-ADT7410/js/main.js)のサンプルコードではアドレスは`0x48`を指定しています。

```main.js
'use strict';

window.addEventListener('load', function (){
  var head = document.querySelector('#head');
  
  // WebI2C Initialized
  navigator.requestI2CAccess()
    .then(function(i2cAccess){
      var port = i2cAccess.ports.get(0);
      var adt7410 = new ADT7410(port,0x48); /* <-ここでスレーブアドレス 0x48 を指定している */
      setInterval(function(){
        adt7410.read().then(function(value){
          console.log('value:', value);
          head.innerHTML = value ? value : head.innerHTML;
        });
      },1000);
    }).catch(e=> console.error('error', e));
}, false);
```

しかし、下記サイトでも紹介されているように、例えば秋月電子で販売されている[ADT7410使用「高精度・高分解能　I2C・16Bit 温度センサモジュール」](http://akizukidenshi.com/catalog/g/gM-06675/)の場合、ハンダブリッジによりスレーブアドレスを変更することができます。

- [ADT7410使用「高精度・高分解能　I2C・16Bit 温度センサモジュール」マニュアル](http://akizukidenshi.com/download/ds/akizuki/AE-ADT7410_aw.pdf)
- [参考「温度センサー(ADT7410)の使い方３」by Shigehiro Kimura さん](http://www.geocities.jp/zattouka/GarageHouse/micon/Arduino/Temp/Temp3.htm)

上記資料から、A0、A1をハンダでブリッジすることで、0x48（デフォルト。ブリッジなし）から0x4Bまで4つのスレーブアドレスに設定することができることがわかります。
ソースコード上のスレーブアドレス設定箇所と、デバイス側の設定があっているかを確認するようにしましょう。

> I2Cモジュールのスレーブアドレスの変更方法は様々で、上記は一例です。I2Cモジュールを利用される際はサンプルコードだけでなく、必ずデータシートも確認するようにしましょう！

## 原因 1. GPIOと競合してないですか?

CHIRIMENのI2CポートはGPIOポートとGPIOピンを共用しています。Arduinoとかと一緒ですね。
I2Cモジュールを利用する際は、GPIOの 252,253,256,257は利用しないようにしましょう。

GPIOに接続したパーツ、I2Cモジュールが意図しない動作を行う原因となる可能性があります。回路含めて見直した方が良いでしょう。

## 原因 2. Web I2C API用のPolyfillはインストールされていますか?

CHIRIMEN用のI2Cデバイスのサンプルは、[chirimen-oh/examples](https://github.com/chirimen-oh/examples)にありますが、こちらのサンプルを動作させるためには、[Web I2C APIのPolyfill](https://github.com/club-wot/WebGPIO)を利用する必要があります。
結構忘れがちなのがPolyfillの導入です。
各サンプルには、CIツールを使って簡単に導入する手順が書かれていますが、せっかちな人のために、ファイルを直接ブチこむ乱暴な導入方法を紹介します。

例えば、[ADT7410](https://github.com/chirimen-oh/examples/tree/master/i2c-ADT7410)であれば、下記のようなファイル構成になっています。

![PolyFill導入前](http://img3.dfm.lrv.jp/chr-i2c/pre.png)

ここに[https://github.com/club-wot/WebGPIO](https://github.com/club-wot/WebGPIO) から、下記ファイルをJSフォルダに入れましょう。

- webi2c.js
- worker.js (現在、i2c用とgpio用に分離するPRが発行されていますので、将来ファイル名が変わる可能性があります)

こんな感じになります。

![Polyfillいれたあと](http://img3.dfm.lrv.jp/chr-i2c/post.png)

最後にindex.htmlから読み込みましょう。

**修正前**

```index.html
<!doctype html>

<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
        <title>i2c-ADT7410</title>
        <script src="./bower_components/webgpio/dist/webi2c.js"></script> <!--ここを修正-->
        <script src="./js/ADT7410.js"></script>
        <script src="./js/main.js"></script>
    </head>

    <body>
    <h3 id="head"
            style="color:red; text-align: center; font-size: 90px">TEST</h3>
    </body>
    
</html>
```

**修正後**

```index.html
<!doctype html>

<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
        <title>i2c-ADT7410</title>
        <script src="./js/webi2c.js"></script> <!--乱暴に修正-->
        <script src="./js/ADT7410.js"></script>
        <script src="./js/main.js"></script>
    </head>

    <body>
    <h3 id="head"
            style="color:red; text-align: center; font-size: 90px">TEST</h3>
    </body>
    
</html>
```

これでOKなはずです。
え？CIの方が楽だって？
だったら、そっちでやってくださいw

## 原因 3. CHIRIMENがI2Cデバイスを認識してない?

現在の[ビルド]()/Polyfillでは、I2Cバスへのアクセス（`read8()`などCall）時に発生するエラーが返ってきません。代わりに不定な値が返ってきます。

このような状況ではJavaScriptをいくら眺めていても、思うようにデバッグできないでしょう。
そこで、`i2cdetect`コマンドの出番です。

### i2cdetectで確認してみる

CHIRIMENにI2Cモジュールが正しく接続できているのか？認識できているのか？を確認するために、adb shellによるi2cdetectコマンドを試してみましょう。

#### adbのインストール

まずは、adbをCHIRIMENと接続するPC側にインストールしておいてください。

[参考(mac)](http://stackoverflow.com/questions/31374085/installing-adb-on-mac-os-x)

adb インストール後、

> adb shell

などのコマンドが利用可能になります。

#### adb shellでCHIRIMENに侵入

CHIRIMEN起動後、PCとCHIRIMENをUSBケーブルで接続します。

USB接続後、コマンドラインから

> adb shell

と入力することで、CHIRIMENに侵入できます。

#### i2cdetectを使ってI2Cポートの状況を確認

`adb shell`でCHIRIMENに侵入すると、`i2cdetect`というI2Cポートを確認するコマンドが利用可能になります。

> i2cdetect -r -y 0

と入力することで、I2Cポート 0 の状態が確認できます。

> i2cdetect -r -y 2

とすると、I2Cポート 2 の状態が確認できます。

**何も繋いでいないときの `i2cdetect -r -y 0` 実行結果**

![何もつないでないとき](http://img3.dfm.lrv.jp/chr-i2c/noconnect.png)

この結果を見ると、0x1AのSlaveAddressがBusy状態になっています。
CHIRIMENのGPIOに何も繋いでいない状態でも、I2Cポート 0の0x1Aは常にBusyを返すようです。

> 内部で何かに使われている？ようです。つまり、0x1Aのアドレスを持つデバイスはI2C ポート 0には接続できません。

次に、`0x31` というアドレスを持つデバイスを接続後、同じコマンドを発行してみます。

**0x31というSlave Addressを持つデバイスを繋いだときの `i2cdetect -r -y 0` 実行結果**

![0x31番をつないだ](http://img3.dfm.lrv.jp/chr-i2c/connect-31.png)

i2cdetectコマンドを発行すると、上記結果は通常一瞬で表示されます。
目に見えて遅い時などは、挙動の怪しいデバイスが接続されている可能性が高いので、一度接続を外してi2cdetectを実行してみると良いでしょう。

i2cdetectでは、下記のようなケースの切り分けができると思います。

1. アプリから指定するSlave Address間違っている。(i2cdetectで表示されるSlave Addressが異なるからわかる)
2. 同じSlave AddressのI2Cモジュールが2つ繋がっている (2つ繋いでるはずなのにSlave Address 1つしか表示されない)
3. 挙動の怪しい（開発中などの）I2Cモジュールが繋がっていて動作停止している (i2cdetectの実行が異様に遅くなる)

# まとめ

- CHIRIMENで利用可能なI2Cポートは2つある
- I2CモジュールをCHIRIMENにつなぐときは、配線と電圧を確認しよう
- (現在のVersionの) CHIRIMENのWeb I2C APIの実装ではread8()などでエラーが返らず、不定な値が返る
- I2Cモジュールの問題切り分けには、adb shellでCHIRIMENに侵入して i2cdetectコマンドを使おう

# 余談

[Web I2C API](https://rawgit.com/browserobo/WebI2C/master/index.html)の仕様(草案)では、`read8()`などでエラー発生時にはPromiseの`reject(value)`が返却されるとの記載があります。
現在、[野良リポジトリ](https://github.com/tadfmac/chirimen-expr)で修正に向けた対応と実験を進めています。
そのうちに、Web I2C APIでは正しくエラーが返り、i2cdetectを利用しなくてもある程度のエラーがわかるようになることを目指しています。
ある程度の評価とコードの整理ができたら[CHIRIMEN Open Hardwareコミュニティの公式リポジトリ](https://github.com/chirimen-oh)にフィードバックする予定です。