Grove の I2C 静電容量タッチセンサのデータを Node.js + johnny-five で得る

# やりたいこと

Groveシステムの [I2C静電容量タッチセンサ](http://wiki.seeed.cc/Grove-I2C_Touch_Sensor/)のデータを、Node.js + johnny-five で得ることで、遠隔地にある何かに誰かが触れたことを検出できるようにする。

きっかけは、書籍[「二足歩行ロボット 工作＆プログラミング（リックテレコム）」](http://www.ric.co.jp/book/contents/book_995.html)で、このタッチセンサを2足歩行ロボット[Rapiro](http://www.rapiro.com/ja/)に組み込んでいるのを見たことです。書籍では、センサ開発元（Seeed Studio）が提供している[ライブラリとサンプルコード](https://github.com/Seeed-Studio/Grove_I2C_Touch_Sensor)を使って Arduino IDE でセンシングを実装していました。ここで、もし Node.js + johnny-five で実装できれば、ネットワーク越しに遠隔監視できると考えました。

[I2C（アイ・スクエア・シー）](https://ja.wikipedia.org/wiki/I2C)のこともよく分かっていない私ですが、上述のArduino用ライブラリとサンプルコードを解読して、johnny-five でセンサのデータを取得することができましたので、報告です。


# 方法

## 機材
* [Arduino UNO R3](https://www.switch-science.com/catalog/789/)
 * Arduino IDE で StandardFirmataを書込済
* [Grove ベースシールド](https://www.switch-science.com/catalog/1293/)
* [Grove I2C タッチセンサ](https://www.switch-science.com/catalog/825/)
 * ベースシールドの I2C コネクタに接続
 * 4個のセンサ電極（feeler）が付属
* USBケーブル（Arduino と PC の接続用）
* PC（Windows10、Node.js が動けばなんでもよい）

以下が接続の様子です。
![C1nkP8VVQAA-5X8.jpg](https://qiita-image-store.s3.amazonaws.com/0/157570/7fa83093-7f01-1050-8507-0f97ea9070e9.jpeg)


## 実装したコード

例として C:\Users\username\touch\app.js として作成

```js:app.js
'use strict';

const five = require('johnny-five');    // johnny-fiveモジュールを使う
const arduino = new five.Board({        // arduinoを接続
    port: 'COM5'                        // COMポート番号（環境による）
});
const MPR121ADDR = 0x5A;    // MPR121静電容量タッチセンサコントローラのアドレス

// MPR121静電容量タッチセンサコントローラの初期化関数
function mpr121Config(device) {
    // Section A: dataがベースラインより大きい時のフィルタリング
    device.i2cWrite(MPR121ADDR, 0x2B, 0x01);    // MHD_R
    device.i2cWrite(MPR121ADDR, 0x2C, 0x01);    // NHD_R
    device.i2cWrite(MPR121ADDR, 0x2D, 0x00);    // NCL_R
    device.i2cWrite(MPR121ADDR, 0x2E, 0x00);    // FDL_R
    // Section B: dataがベースラインより小さい時のフィルタリング
    device.i2cWrite(MPR121ADDR, 0x2F, 0x01);    // MHD_F
    device.i2cWrite(MPR121ADDR, 0x30, 0x01);    // NHD_F
    device.i2cWrite(MPR121ADDR, 0x31, 0xFF);    // NCL_L
    device.i2cWrite(MPR121ADDR, 0x32, 0x02);    // FDL_L
    // Section C: 各電極(ELE0-11)の閾値(T:Touch,R:Release)の設定
    device.i2cWrite(MPR121ADDR, 0x41, 0x0F);    // ELE0_T
    device.i2cWrite(MPR121ADDR, 0x42, 0x0A);    // ELE0_R
    device.i2cWrite(MPR121ADDR, 0x43, 0x0F);    // ELE1_T
    device.i2cWrite(MPR121ADDR, 0x44, 0x0A);    // ELE1_R
    device.i2cWrite(MPR121ADDR, 0x45, 0x0F);    // ELE2_T
    device.i2cWrite(MPR121ADDR, 0x46, 0x0A);    // ELE2_R
    device.i2cWrite(MPR121ADDR, 0x47, 0x0F);    // ELE3_T
    device.i2cWrite(MPR121ADDR, 0x48, 0x0A);    // ELE3_R
    // device.i2cWrite(MPR121ADDR, 0x49, 0x0F);    // ELE4_T    // 電極4以降も使う場合
    // device.i2cWrite(MPR121ADDR, 0x4A, 0x0A);    // ELE4_R
    // device.i2cWrite(MPR121ADDR, 0x4B, 0x0F);    // ELE5_T
    // device.i2cWrite(MPR121ADDR, 0x4C, 0x0A);    // ELE5_R
    // device.i2cWrite(MPR121ADDR, 0x4D, 0x0F);    // ELE6_T
    // device.i2cWrite(MPR121ADDR, 0x4E, 0x0A);    // ELE6_R
    // device.i2cWrite(MPR121ADDR, 0x4F, 0x0F);    // ELE7_T
    // device.i2cWrite(MPR121ADDR, 0x50, 0x0A);    // ELE7_R
    // device.i2cWrite(MPR121ADDR, 0x51, 0x0F);    // ELE8_T
    // device.i2cWrite(MPR121ADDR, 0x52, 0x0A);    // ELE8_R
    // device.i2cWrite(MPR121ADDR, 0x53, 0x0F);    // ELE9_T
    // device.i2cWrite(MPR121ADDR, 0x54, 0x0A);    // ELE9_R
    // device.i2cWrite(MPR121ADDR, 0x55, 0x0F);    // ELE10_T
    // device.i2cWrite(MPR121ADDR, 0x56, 0x0A);    // ELE10_R
    // device.i2cWrite(MPR121ADDR, 0x57, 0x0F);    // ELE11_T
    // device.i2cWrite(MPR121ADDR, 0x58, 0x0A);    // ELE11_R
    // Section D: フィルタの設定
    device.i2cWrite(MPR121ADDR, 0x5D, 0x04);    // よく分かりません
    // Section E: 電極の設定
    device.i2cWrite(MPR121ADDR, 0x5E, 0x0C);    // 12個全ての電極を利用
}

// arduinoの準備ができたら
arduino.on('ready', function() {
    this.i2cConfig();       // I2Cを使うためのjohnny-fiveのおまじない
    mpr121Config(this);     // MPR121静電容量タッチセンサコントローラを初期設定
    // センサの状態の読み込みとタッチの判定
    this.i2cRead(MPR121ADDR, 1, function(bytes){    // センサを4個以上使う場合は2バイト読む？
        if((bytes & 0x01) == 0x01) {        // ch0を判定
            console.log('ch0 is touched');
        }
        if((bytes & 0x02) == 0x02) {        // ch1を判定
            console.log('ch1 is touched');
        }
        if((bytes & 0x04) == 0x04) {        // ch2を判定
            console.log('ch2 is touched');
        }
        if((bytes & 0x08) == 0x08) {        // ch3を判定
            console.log('ch3 is touched');
        }
    });
});
```

## 実行と動作確認

* Node.js コマンドプロンプト にて [johnny-five](http://johnny-five.io/) をインストール

```shell-session
C:\Users\username> npm install johnny-five
```

* Node.js コマンドプロンプト で以下を実行

```shell-session
C:\Users\username\touch> node app.js
```

* タッチセンサに触れた時に 'ch _x_ is touched'（ _x_ はch番号）とコンソールに表示されればOK
 * 同時に複数のセンサに触れても大丈夫です


## 解説

* このセンサには、[MPR121](https://www.switch-science.com/catalog/397/)というコントローラが使われており、そのI2Cアドレスは __0x5A__ だそうで、データの読み書き（__i2cRead( )__ と __i2cWrite( )__）にはそのアドレスを用います（コード中の __MPR121ADDR__）
* 初期設定の関数（__mpr121Config( ) { }__）の中身は、[開発元のサンプルコード](https://github.com/Seeed-Studio/Grove_I2C_Touch_Sensor)を参考にしており、内容については私は理解できていません
 * なお、センサ電極（feeler）を5個以上使う場合は、コメントアウトした部分も使います

　

* __i2cRead(address, bytesToRead, handler(arrayOfBytes))__ は、__繰り返し__、指定addressの値をbytesToReadぶん読み、データarrayOfBytesを伴ってhandlerをコールします
* 各チャンネルのセンサに個別に触れると以下のデータ（bytes）が得られます

|ch |bytes|ビット列|
|:-:|:--:|:-----:|
|0  |0x01|□□□■|
|1  |0x02|□□■□|
|2  |0x04|□■□□|
|3  |0x08|■□□□|
（□=0, ■=1　以下同様）

* 複数同時に触れると、それぞれの bytes が足し合わされた（OR された）値が返ってきます

|chの組み合わせ |bytes|ビット列|
|:-:|:--:|:-----:|
|0, 1|0x03|□□■■|
|0, 2|0x05|□■□■|
|0, 3|0x09|■□□■|
|1, 2|0x06|□■■□|
|1, 3|0x0A|■□■□|
|2, 3|0x0C|■■□□|
|0, 1, 2|0x07|□■■■|
|0, 1, 3|0x0B|■□■■|
|0, 2, 3|0x0D|■■□■|
|1, 2, 3|0x0E|■■■□|
|0, 1, 2, 3|0x0F|■■■■|

　

* 各chに触れているか否かの判定は、戻り値 bytes と所望のchの値との AND が、所望のchの値になるかどうかを見ればよいことになります
 * 例えば、0x0F(■■■■) & 0x01(□□□■) は 0x01(□□□■) となり、ch0に触れられて__いる__ことがわかる
 * 同様に、0x0E(■■■□) & 0x01(□□□■) は 0x00(□□□□) となり、ch0に触れられて__いない__ことがわかる

```
        if((bytes & 0x01) == 0x01) {        // ch0を判定
            console.log('ch0 is touched');
        }
        if((bytes & 0x02) == 0x02) {        // ch1を判定
            console.log('ch1 is touched');
        }
        if((bytes & 0x04) == 0x04) {        // ch2を判定
            console.log('ch2 is touched');
        }
        if((bytes & 0x08) == 0x08) {        // ch3を判定
            console.log('ch3 is touched');
        }
```

　

* 今回のコードでは、4つのセンサ分（1バイト）しかデータを取っていません
* センサ電極（feeler）を追加購入してもっとたくさんつなげますが、その際は、以下の i2cRead(address, bytesToRead, handler(arrayOfBytes)) で複数バイト読んできて…（あとはご自身でご想像・お試しください）

```js
    // センサの状態の読み込みとタッチの判定
    this.i2cRead(MPR121ADDR, 1, function(bytes){ ... }
```


# 所感や今後
よく分からないことだらけでしたが、サンプルコード（C++）を頑張って解読して、試行錯誤の末、なんとか JavaScript（johnny-five）に移植することができました。
これで、書籍[「二足歩行ロボット 工作＆プログラミング（リックテレコム）」](http://www.ric.co.jp/book/contents/book_995.html)の内容をほぼすべて JavaScript（Node.js）に書き換えられそうな気がしてきました。さらに、socket.io と組み合わせることで、遠隔でのセンシングが可能になります。