obnizOSとM5StickCで体温計を作ってみた

#はじめに
新しい「obnizOS for M5StickC」を搭載したM5StickCと、それに直接接続できる環境センサモジュールENV Hatを使って、現在品薄で入手困難な**電子体温計**の制作にチャレンジしてみました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318815/361ec5ef-09c7-97ef-29f8-26ed11e4b6de.jpeg" width=75%>


#使い方
ENV Hatを付けた**M5StickCを脇の下にそのままパクっと挟み**、obnizのコンソール画面からプログラムを実行するだけです。
早ければ2~3分、最長の場合は10分後に計測が完了します。

なお検温時は、M5StickC自身の発熱が計測に影響するため、検温直前まで電源を切っておきます。
そしてM5StickCを脇の下に挟む時は、WiFiの電波を出来るだけ阻害しない様、オレンジの本体側をなるべく体外の方に露出するようにします。

[M5StickC](https://www.switch-science.com/catalog/5517/)はご存じの通り、小型のプラスチック筐体にスマートに収まっており、ENV Hatを付けた際も全体としての一体感が担保されていますので、そのまま脇の下に挟んでも（生のPCB基板と違って）チクチクすることなく安心して利用することができます（爆


#今回使ったもの
* [**M5StickC（obniz無期限ライセンス付属版）**](https://obniz.io/ja/products/m5stickc/)
新しいobnizOSが乗ったM5StickCは、当然ながら従来のobnizの使い心地と何ら変わりませんでした。非常に快適です。
ちなみに私の場合は、[こちらのキャンペーンを利用して](https://blog.obniz.io/ja/news/m5stickc-os-migration/)obnizOSのアップグレードを行いました。

* [**M5StickC ENV Hat（DHT12）**](https://www.switch-science.com/catalog/5755/)
M5StickCにそのまま接続できる環境センサモジュールです。温度だけではなく、湿度や気圧、磁界まで測ることができる優れモノです。


#プログラム
このプログラム自身、[obzizが用意するWebブラウザ上のエディタ](https://obniz.io/ja/console/program)でそのまま編集して動かすことが可能です。
開発から実行までの全ての工程が、ブラウザ上に用意された開発環境だけで完結しますので、サクッと開発したい場合などに大変便利です。

```html:this_sample_is_on_obniz-instant-HTML
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.6.2/math.min.js" type="text/javascript"></script>
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/obniz@3.4.0-beta.0/obniz.js" crossorigin="anonymous"></script>
  </head>
  <body>
    <div id="obniz-debug"></div>
    <h3>体温計</h3>
    <div id="recentStatus"></div>
    <div id="result"></div>
    <script>
      let array = [];
      let obniz = new Obniz.M5StickC("OBNIZ_ID_HERE");
      let timer=0;
      let finished=false;
      
      obniz.onconnect = async function () {
        let device = obniz.wired("DHT12",{scl:26,sda:0});
        // メインループ
        obniz.repeat(async function () {
          timer++; // 10分経過判定用
          let degrees = await device.getTempWait();
          recentStatus.innerHTML = "温度: " + degrees;

          // 平温のレンジ(35度以上)に入ってから処理を始める
          if (degrees>35 && finished==false) {
            result.innerHTML = "検温中...";
            array.unshift(degrees); // 先頭に追加
            // 30秒(15秒x2セット)のデータが満ちているか
            if (array.length<60){
              result.innerHTML += " (基礎データ収集フェーズ)";
              return;
            }
            // 15秒x2セットの中央値を算出
            let newMedian = math.median(array.slice(0, 30)); // 比較先30個セット
            let oldMedian = math.median(array.slice(30));    // 比較元30個セット
            array.pop(); // 末尾を削除
            // 前後を比較した結果の温度差が±0.1度の範囲内、または計測後10分が経過の場合、最新の中央値の方を表示
            let difference = newMedian-oldMedian;
            if ((difference<0.1 && difference>-0.1) || timer>1200){
              result.innerHTML = "検温値: " + Math.round(newMedian*10)/10 + " 【完了】";
              finished=true;
            }
          }

        },500) // 0.5秒間隔で検温
      }
    </script>
  </body>
</html>
```


#解説
###体温計の仕組み(アルゴリズム)を調べる

[体温を脇の下に挟んで計測する場合は、基本は10分の間じっとして](https://www.terumo-taion.jp/terumo/report/18.html)、体温の漏れ(揺らぎ)を極力抑えなければならないそうです。
つまり、この10分という時間を確保しなければ安定した体温を測ることは難しいため、電子体温計の登場以前は10分間の計測が必須でした。

しかしながら、現在では数分、早ければ15~20秒で検温を終える体温計が主流です。実はこれらは、**実測値に基づいた予測値**を算出しています。
この予測値の算出方法については、[各社各様のアルゴリズムが開発されています](https://www.terumo-taion.jp/terumo/report/08_5.html)が、ググってもそれらの具体的な資料は見つかりませんでした。どうも企業秘密扱いとなっている模様です。

電子体温計の電池の持ち具合といい、その小ささや価格といい、とても高スペックのマイコンチップが中に入っているとは思えませんので、先人のエンジニア達の苦労が伺い知れます。


###本作品の方針(アルゴリズム)を決める

[平衡温に達するまでの体温上昇カーブ](http://www.keikoren.or.jp/reading/ken17.html)の考え方に則り、平衡温に達したと判定できたら検温値を表示する方針を考えました。
具体的なロジックは次の通りです。

　**①**　平温の閾値(35℃)を超えるまで待つ
　**②**　直近から15秒間の、体温の中央値を算出する
　**③**　項目**②**以前の15秒間の、体温の中央値を算出する
　**④**　項目**②**と**③**を比較した差が±0.1℃に収まっている場合は、**②**の結果を以って検温終了とする
　**⑤**　項目**④**の比較結果が安定しないまま10分が経過した場合、**②**の結果を以って検温終了とする

検温のサイクルは0.5秒に一回としています。
項目**②**と**③**の中央値の算出には、[JavaScript用の数学ライブラリ「Math.js」](https://mathjs.org/)を用いました。


#まとめ

実際にオムロン社の体温計と比較したのが、以下の写真です。
双方の差はわずかに0.1度。いい感じです！
　　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318815/9435c481-7125-fa94-3601-33f36e9086f4.jpeg" width=75%>
尚、本作例は体温計としての動作を保証するものではありません。ご理解いただければ幸いです。
（ツイッターフォローは<a href="https://twitter.com/SAMonodera?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">**こちら**</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>）

###2020/4/13 追記
**#コロナ対策つくろか**
急遽、こちらのハッカソンに本作で参加させていただく事となりました！
[緊急企画！コロナ対策つくろか！ハッカソン](https://tsukuroka.org/2020/04/03/post-1686/)

###2020/5/2 追記
**#MFKyoto2020 #作品発表**
こちらのオンラインイベントにも、本作で参加させていただきました！
[Maker Faire Kyoto Online](https://makezine.jp/blog/2020/04/mfkonline_show_and_tell.html)


#参考
obniz 公式：
　[M5StickC　obniz無期限ライセンス付属](https://obniz.io/ja/products/m5stickc/)
　[Temperature Sensor - DHT12](https://obniz.io/ja/sdk/parts/DHT12/README.md)

テルモ 公式：
　[知っておきたい体温の話](https://www.terumo-taion.jp/terumo/report/18.html)
　[予想式電子体温計について](https://www.terumo-taion.jp/terumo/report/08_5.html)

その他：
　[平衡温に達するまでの体温上昇カーブ](http://www.keikoren.or.jp/reading/ken17.html)
　[体温計測温部のモデル化による予測アルゴリズムの検証](https://www.jstage.jst.go.jp/article/jceeek/2014/0/2014_128/_pdf/-char/ja)