今日、水、どれだけ飲んだっけ？に答える仕組みをつくったよー。

## はじめに

この記事はHameeアドベントカレンダー2021の18日目の記事です。
M5Stackとかを使って困っていたことを解決する方法を考えた記事となっています。

さて、ここ最近は、リモートワークで仕事している人も多いのではないでしょうか。
私もほぼほぼリモートワークになり、ほとんど外出しない日も増えました。

出社していた頃は、通勤やランチ、ミーティングでの部屋の移動などなどで、
結構、運動していたんだなぁ、と思い知らされている日々です。

運動不足になると何が起きるか、言うまでもないですね、体重の増加や体力の低下など健康状態に黄色信号がともります。
すべてをリモートワークのせいにする気はありませんが、この私にも、確実に体重の増加は起こりました。
私の場合は、お医者様にもっと運動して痩せなさい、と言われるほどにまで育ててしまったのもあって、
今年の1月から、食生活を変えたり、睡眠の質を監視したり、運動を習慣づけることにチャレンジしてきました。


そんな中で、[Fitbit](https://www.fitbit.com/global/jp/products)というスマートウォッチにはだいぶお世話になりました。
今では、睡眠状況、どれだけ運動したか、ストレス兆候は出てないか、などをほぼ全てこれに頼っています。
で、このFitbit、食べた食事の記録や飲んだ水の記録も専用のスマホアプリでできるのですが、ここら辺の機能はほとんど使えていません。

特に水に関しては、厚生労働省が、[健康のため水を飲もう](https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/topics/bukyoku/kenkou/suido/nomou/index.html)
という推進運動をされているほど重要なもののようですが、正直水を飲まない日が結構あります。
寝ている間にも汗として水分が出て行ってしまったり、健康リスクの低減のためや新陳代謝のためにも適量の水を適度な回数飲むことが有効なようです。

今年の10月くらいから水を定期的に飲むということを意識し始めてはいるのですが、アプリへの記録を疎かにしがちです。
そこで、飲んだ水の量を自動で記録できる方法はないかなーって考え、仕組みを作ったので共有します。

## 課題(再掲)
毎日の飲んでいる水の量を、気軽に記録できるようにしたい

## 基本方針
* 水は必ずタンブラーで飲んでいるので、タンブラーに入っている水の量を計量して、それを自動で記録する仕組みがあれば良さそう。
* 水の量というか、重さは、1000円程度で購入できる電子キッチンスケール(歪ゲージ)を用いて簡単に計測できそう(センサー)
* その計測には、手元にあるだけで全然使ってなかった、[M5Stack(GRAY)](https://shop.m5stack.com/products/grey-development-core?_pos=9&_sid=5a0d23295&_ss=r&variant=16804796006490)を使うことにする。
* FitbitのAPI経由で、飲んだ水の量を記録する。[Create Water Log](https://dev.fitbit.com/build/reference/web-api/nutrition/create-water-log/)
* M5Stackから直接 fitbit APIを叩くのは、実装がごちゃごちゃしそう。
    * IoTで用いられているMQTTを採用して、M5Stackはあくまで計測された値を外部に送ることに専念してもらう(publisher)。
    * fitbitへのAPIリクエストはRaspberryPiにお願いする(broker,subscriber)。

## システムの連携図
全体のイメージとしてはこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71289/e5a17b7e-a7db-4c2c-6249-e329a8d203fc.png)

M5Stack・RaspberryPi間の連携は、同一のWiFiネットワークに接続することで実現します。
ここでの通信プロトコルは、IoT機器同士で通信することを想定している、MQTTを採用しています。

### MQTT(Message Queueing Telemetry Transport) 
[こちらの記事](https://kfep.jp/solution/iot-mqtt/mqtt)によると、Message Queuingに関しては、以下のように説明されています。
>「Message Queueing」とは、送信側が送るデータをデータ領域に一旦保持しながら、受信側の処理が完了するのを待たずに次の処理へ移る方式です。
>

要するに、データを送る側は、とりあえずデータを送ることに専念、受け取る側も自分のペースで処理できる的な仕組みのようです。
MQTTでは、データを送る存在(Publisher)とデータを受け取る存在(Subscriber)が存在します。この中間に、broker(仲介人)が存在します。
まず、Subscriberは、brokerにこういうメッセージ(topic)が来たら通知をください、と伝えます。
Publisherは、brokerにメッセージを送りつけます。
そのメッセージ(topic)が事前に登録されているものだったら、そのメッセージ(topic)の購読を登録したSubscriberにメッセージを送信します。

イメージとしては、メルマガ配信システムとほとんど同じで、メルマガを購読したい人がシステムに登録することで、そのメルマガが必要なユーザーのもとに届くようなものです。

誰に届けるか、という観点を意識せずに、情報を送るということに集中できるのがこの仕組みのメリットです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71289/c10fa29a-a4aa-d801-991a-bb6c3616d4f1.png)
https://www.advanet.co.jp/2020/10/14/mqtt-introduction/

そのほかにも、途中で通信状況が悪化して通信が途切れても、メッセージを伝えやすくしたりなどIoTデバイスの通信に特化したプロトコルになっています。
詳細は [パブリッシュ/サブスクライブ](https://www.ibm.com/docs/ja/wip-mg/2.0.0?topic=concepts-publishsubscribe)や[MQTT入門ガイド](https://www.advanet.co.jp/2020/10/14/mqtt-introduction/)などをご覧ください。

## 実装
実際に書いたコードはこちらをご確認ください。
https://github.com/kuroneko913/send_water_amount_to_fitbit

M5Stackでは、

* WiFiに接続する
* MQTTサーバーに接続する
* hx711と呼ばれるコンバータで、電子キッチンスケールの値を取得し、画面に表示する
* MQTTサーバーに取得した重さをPublisherとして送信する

といった実装を行なっています。

以下の記事を主に参考にしています。
[M5STACKとHX711を使って、重量を測定してみた](https://eguchi.jp/blog/?p=1268)
[ラズパイを使って、重さをはかるよ](https://zenn.dev/kotaproj/articles/f0e0337c68a0057ec21c#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E3%82%AD%E3%83%A3%E3%83%AA%E3%83%96%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%80%A4(referenceunit)%E3%82%92%E7%AE%97%E5%87%BA)
[M5Stack を WiFiに接続する](https://kotarohattori.com/m5stack-wifi/)
[M5Stack公式の実装例](https://github.com/m5stack/M5StickC/blob/master/examples/Unit/WEIGHT/WEIGHT.ino)


RaspberryPi側には、

* MQTTサーバーである、[mosquiito](https://mosquitto.org/download/) の導入
* PythonでMQTT通信をするライブラリである、paho-mqttを用いて、メッセージを受信し、fitbitにAPI連携する実装

といったことが行われています。

[Mosquitto(MQTT Broker)のインストール](https://qiita.com/n-yamanaka/items/73e4d3022186732b4e88)
[pythonでMQTT送受信](https://qiita.com/hsgucci/items/6461d8555ea1245ef6c2)
などを参考にさせていただいています。


## 全体的に工夫した点
- 予めタンブラーの重さを差し引いておくことで、タンブラーの重さの変化を取得する必要がなくなりシンプルになった。
- 画面を複数のspriteで分割したことで、変更が楽になった。
    - ここはタイトルのスペース
    - ここは、タンブラーの重さ用のスペース
    - ここはMQTTで通信する際に使う
- RaspberryPiを間に挟んだMQTTでの通信にすることで、M5Stack側は値を計測して送ることに専念できた。
    - RaspberryPi3側で受け取った値を送ること・Fitbit APIのトークンの更新などをやってる。
    - M5Stackだけでやってたらコードがちょっと複雑になるかも。

### スプライトについて
M5Stackの画面実装においてspriteを用いているのですが、このサイトを参考にさせていただいています。
[【M5STACK】スプライト技術入門(静止画編)](https://haratta-tech-lab.com/m5stack_sprite_image/)  

詳細は上記記事に委ねるとして、ひとことで説明すると、予め表示する画面を作っておいて、必要なタイミングで表示する技術という認識です。
複数のスプライトを用いて、一つの画面を構成することで、
変化させたい部分だけ変化させて、それ以外の変化させないでいい部分はそのまま表示し続けるということができます。

なんとなく、reactなどで使われている仮想DOMに近いのかな、なんて印象を抱きました。

今回は、

* タイトルブロック
* タンブラーの重さを表示する部分
* MQTTでPublishする部分の3つのスプライトを出し分ける
実装にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71289/0ae4adf1-551b-5d17-1f0d-e2d0ebd1a888.png)

これによって、画面遷移時のチラつきを低減させられるそうです。

## 完成品

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">載せられたタンブラーに入っている水の量をfitbitに連携するだけのシステム。<br><br>これで家の中で飲んだ水の量を記録できるようになった。 <a href="https://t.co/RUaXke6hDU">pic.twitter.com/RUaXke6hDU</a></p>&mdash; まさき。🌥️ (@myblackcat7112) <a href="https://twitter.com/myblackcat7112/status/1471494791716556802?ref_src=twsrc%5Etfw">December 16, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### fitbitに148mlが連携されているの図。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71289/b15e3ce4-7310-a280-7a8b-eb20992eb4b5.png)


## まとめ

今回は、健康を少し意識して、毎日水を飲むという習慣をつけるために、
自動で飲んだ水の量を保存できる仕組みを、M5StackとRaspberryPiとfitbitAPIを連携することで作りました。

実は一番時間がかかった部分が、M5Stackへいい感じに文字列を出すところと、画面遷移のところでした。
重さを測るところやMQTTに関しては先人の知恵のおかげでさくっと実装できました。

手を動かして動くものを作るのはやっぱ楽しいですね、ソフトウェアであっても、ハードウェアであっても、と思いました。