Technote

by sizuhiko

web-i2cエレメントをより使いやすくしました。

先日のMaker Faire Tokyo 2018 に CHIRIMENコミュニティで参加しましたweb-i2c-element はどうなの? に記述した改良ポイントを使ったchirimen-pianoをアップしました。

変更したポイント:

変更の概要は以下のとおりです。

  • no-web-i2c スロットを指定すると、非対応メッセージを表示できるようになった
  • WebI2cSensorElement を使って、センサー毎の共通実装をなくし、より簡単にセンサーのカスタムエレメントが実装できるようになった
  • Polymer使ってなくても、タグだけを使ってセンサーの値が取得できるようなサンプルに変更した

では、それぞれの変更を詳しく見ていきましょう。

非対応メッセージの表示

chirimen-pianoのリポジトリにあるindex.htmlファイルを見てましょう。

<web-i2c>
  <grove-touch slave-address="0x5a"></grove-touch>
  <grove-gesture slave-address="0x73"></grove-gesture>
  <div slot="no-web-i2c">
    このデバイスはCHIRIMENではありませんが、ピアノ演奏はお楽しみいただけます。
  </div>
</web-i2c>

web-i2c タグの内側に no-web-i2c というslot名のタグを記述することで、 もしCHIRIMEN以外のデバイス(例えばPCなど)から、このページを表示するとメッセージを表示することができるようになります。 試しに、PCからchirimen-pianoのデモページにアクセスしてみてください。

WebI2cSensorElementを利用したカスタムエレメントの記述

最初のバージョンでは、 PolymerElement を使っていたため共通的な実装が多くなっていたのですが、 いくつかのセンサー実装をしてみて共通化できそうな箇所もわかってきたので、I2C用の親クラスを作ることにしました。

WebI2cSensorElement を使ったセンサーのクラスとして、GROVEタッチとGROVEジェスチャーのカスタムエレメントを作りました。

例としてchirimen-pianoのリポジトリにあるgrove-touch.jsファイルを見てましょう。

class GroveTouch extends WebI2cSensorElement {
  async init() {
    this._autoRead = true;
    await this._i2cSlave.write8(0x2b,0x01);
    await this._i2cSlave.write8(0x2c,0x01);
    await this._i2cSlave.write8(0x2d,0x01);
    await this._i2cSlave.write8(0x2e,0x01);
    await this._i2cSlave.write8(0x2f,0x01);
    await this._i2cSlave.write8(0x30,0x01);
    await this._i2cSlave.write8(0x31,0xff);
    await this._i2cSlave.write8(0x32,0x02);
    for(var i=0;i<12*2;i+=2){
      // console.log(i);
      var address = 0x41+i;
      // console.log(address);
      await this._i2cSlave.write8(address,0x0f);
      await this._i2cSlave.write8(address+1,0x0a);
    }
    await this._i2cSlave.write8(0x5d,0x04);
    await this._i2cSlave.write8(0x5e,0x0c);
  }

  async read() {
    const value = await this._i2cSlave.read16(0x00);
    // console.log(value);
    var array = [];
    for(var cnt = 0; cnt < 12; cnt ++){
      array.push(((value & (1 << cnt)) != 0)?true:false);
    }
    if (!this.value || (JSON.stringify(array) != JSON.stringify(this.value))) {
      this._setValue(array);
    }
  }
}

window.customElements.define('grove-touch', GroveTouch);

まず WebI2cSensorElement を継承したクラスを定義します。 このクラスには2つのオーバライド可能なメソッドがあります。

  • init
  • read

どちらも async メソッドにすることで I2Cスレーブデバイスからの応答を待ち合わせる(await)ことができるようになっています。

WebI2cSensorElement は、 init 呼び出し時にメンバー変数として _i2cSlave が利用可能になっています。 これはWeb I2Cのスレーブオブジェクトで、どんなメソッドが使えるかはWeb I2C API Specification4.6 Reading the value4.8 Writing a value を参照ください。 サンプルコードの slaveDevice_i2cSlave と同じものになります。

init を実装する

init ではI2Cデバイスの初期化コードを記述します。 とくに戻り値は必要ありません。

もしセンサーが読み取り可能なら、 this._autoRead = true; のように設定してください。 これを設定すると、インターバル間隔(デフォルトは100ミリ秒)ごとに read メソッドを呼び出してくれます。

read を実装する

init で _autoReadtrue に設定した場合、インターバル間隔ごとに呼び出されます。 センサーの値を読み取って、値が変わった場合は this._setValue(value) を呼び出してください。 エレメントの value プロパティに値が保持されると同時に、 value-changed イベントが発行されます。

普通のHTMLからセンサーを取り扱う

ふたたびchirimen-pianoのリポジトリにあるindex.htmlファイルを見てましょう。

<script>
  document.querySelector('grove-touch').addEventListener('value-changed', e => {
    document.querySelector('chirimen-piano-app').touchChanged(e.detail.value);
  });
  document.querySelector('grove-gesture').addEventListener('value-changed', e => {
    document.querySelector('chirimen-piano-app').gestureChanged(e.detail.value);
  });
</script>

センサー値の変更を受け取るには、センサーエレメントに対して value-changed イベントのリスナーを設定します。 値はイベントオブジェクトの detail.value に入っていて、センサーによってセットされる値(型も含め)が違います。

たとえば grouve-touch エレメントは、タッチセンサーの数分のBoolean配列がセットされ、 grove-gesture エレメントは、発生したジェスチャー名がセットされます。

さいごに

このようにHTMLからセンサーの値をコントロールできたり、センサーのエレメントを簡単に実装できるようになりました。 今後は、それぞれのエレメントやベースクラスを npm install で取得できるようにしたいと思っています。

また、以前作った WebGpioElementPolymer3 で書き換えていきたいなと思っています。

WebComponents と CHIRIMEN の融合は、私がそれぞれのコミュニティに参加して得られたもののアウトプットです。 もしこれらの活動に興味がありましたら、Polymer Japan CHIRIMEN Open Hardware Polymer.co-edo などの活動に参加ください。