Zuruzuru Blog
🍜< ズルズル

May 08, 2020

Generic Sensor API

#Generic Sensor API
#Android
#WebAPI
#Chrome

top

ブラウザからデバイス上のセンサの値を取得する API である Generic Sensor API で遊んでみたくなったので使えるかどうか確認がてらブログ記事にしました。

作るもの

簡単なデモとして、腕立て伏せの回数を測定することを目標にします。

最終的な完成物

まず完成したものをみてみましょう。 腕立てしている様子を自分で撮影するのは難しくてできなかったので、代わりにスマホに手を近づけると画面のカウンターが増える様子です。

環境について

前提として、この API は Safari で対応されていないため、2020/5 現在、iPhone で使用することはできません。ただ、PC ではなくセンサの内蔵されているスマートフォンから行いたいため、今回は Android デバイスを用意しました。

最終的な環境

  • デバイス:Huawei P30 lite
  • ブラウザ:Chrome

PC の Chrome から Android の Chrome をデバッグする

まず、Android の Chrome のデバッグ機能が使えないと不便なのでその設定をします。 できる人は読み飛ばしてください。

方法

Android 側で開発者モードを On にして、USB デバックを許可し、Chrome を開いておきます。その後、PC と USB ケーブルで繋ぎます。 その状態で、PC の Chrome でchrome://inspect/#devicesにアクセスすると以下のような画面が出てくると思います。

chrome-debug

その後 inspect を押すとそのページのデバッグを PC 上の Chrome で行うことができます。

flag の設定

2020/5 現在の Chrome では、Generic Sensor API を使用するのに必要な enable-generic-sensor-extra-classes という flag がデフォルトで disabled になっているためこれをまず enabled にします。

方法

センサを使用したい端末でchrome://flagsにアクセスして、generic-sensor-extra-classes を検索しその flag を enabled に変更します。

chrome-flag

https

http 経由ではこの Generic Sensor API を使えないようなので,自分でローカル環境への SSL 証明書設定を行うか、ngrokを使うか、デプロイするなどして https 経由で行ってください。

実装

以上で準備は終わったので実装に入っていきます。 近接センサがまだ実装されていないので今回は、環境光センサを使用します。 環境光が少なくなったらデバイスに頭が近いているとみなしてと腕立て伏せの回数をカウントしたいと思います。

センサから値の取得

MDN で推奨されている方法通りに、PermissionAPI を用いて、使用するセンサータイプにあらかじめ許可を与えます。今回はambient-light-sensorです。

その後、使用したいセンサを扱うオブジェクトを初期化します。 この時に引数でセンサのサンプリング周波数を設定することができます。 サンプリング周波数は、1 秒間に取得するサンプル数のことで Hz で表します。 今回は、腕立て伏せの回数を測るだけなので 1 秒間に 5 回(5Hz)もあれば十分だと思います。

センサの値を読み込んだ際のイベントリスナを登録します。 この例では、1 秒間に 5 回センサの出力がコンソールに表示されるように設定しました。

最後に start()でセンサの読み取りを開始します。

main.js

navigator.permissions.query({ name: 'ambient-light-sensor' })
.then(result => {
  if (result.state === 'denied') {
    console.log('Permission to use this sensor is denied.');
    return;
  }else if(result.state === 'granted') {
    console.log('Permission to use this sensor is granted.');
  }

  //Sensorを扱う(サンプリング周波数:5Hz)
  const sensor = new AmbientLightSensor({frequency: 5});

  //センサの値を読み取った際のイベントリスナ
  sensor.addEventListener('reading', e => {
    const value = sensor.illuminance;
    console.log(value);
  })

  sensor.addEventListener('error', event => {
    console.log(event.error.name, event.error.message);
  })

  sensor.start()
});

腕立てカウントの実装

センサの絶対的な値が毎回変わる可能性があるので最初にキャリブレーションしておきます。

単純に暗いかどうかで腕立て伏せをカウントしています。

sample.js

let sensor;

const params = {
  start: true,        //腕立てカウント中か
  counter: 0,         //腕立てカウンタ
  before: 0           //前の状態
}
const calibration = { //センサの値のキャリブレーション
  flag: true,
  value: 0
}

navigator.permissions.query({ name: 'ambient-light-sensor' })
.then(result => {
  if (result.state === 'denied') {
    console.log('Permission to use this sensor is denied.');
    return;
  }else if(result.state === 'granted') {
    console.log('Permission to use this sensor is granted.');
  }

  sensor = new AmbientLightSensor({frequency: 5});

  //値を読み取った際
  sensor.addEventListener('reading', e => {
    const value = sensor.illuminance;
    //キャリブレーション(最初だけ)
    if(calibration.flag){
      calibration.value=value;
      calibration.flag=false;
    }else{//通常時
      if((calibration.value/2>=value)&&(params.before>value)){
        //カウンタの更新
        params.counter++;
        document.getElementsByTagName("h1")[0].innerText = params.counter;
      }
    }
    //前の状態を保存
    params.before=value;
  })

  sensor.addEventListener('error', event => {
    console.log(event.error.name, event.error.message);
  })
});

//ボタン押下時
function pressed() {
  params.start=!params.start;
  //センサの読み取りの開始/終了
  document.getElementsByTagName("button")[0].innerText = (params.start)?"開始":"終了";
  (params.start)?sensor.stop():sensor.start();

  //カウンタの更新
  params.counter=0;
  document.getElementsByTagName("h1")[0].innerText = params.counter;
}

index.html

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Sensor</title>
    <meta name="description" content="Sensor">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="./main.css">
  </head>
  <body>
    <div class="parent">
      <h1>0</h1>
      <button class="button-sensor" onclick="pressed()">開始</button>
    </div>
    <script src="./main.js" async defer></script>
  </body>
</html>

終わりに

以上の手順で作成できました。

他にも加速度センサなどがあるので信号処理の勉強がてら遊び倒してみたいと思います。 また時間があったらブログに書こうと思います。

参考文献

MDN:Generic Sensor API

Google developers:Sensors-for-the-web