[M5StickC] 子どもの歯磨き応援ツール

![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140921/1a355b53-efb5-ea52-2d95-7b2087d6502c.png)


# TL;DR(まとめ)
- M5StickC, Raspberry Pi Zero W, Google Home Mini を用いて歯磨き応援ツールを開発しました。
- M5StickCは加速度センサー、内蔵バッテリー、Wi-Fi、モニタ、スピーカーHAT（後付け）ありでPoCに最適なデバイスでした。
- PoCによって作り手がこだわった機能と、ユーザーに刺さる機能は必ずしも一致しないことを学びました。

# きっかけ
- M5StickC　×　加速度センサーで何か面白いものを作る、というお題が出た。
- 最近子供が歯磨きを面倒くさがっている。

# システム構成
![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140921/faa9db0d-05ef-2398-71ef-e3101fc7df90.png)



1. 歯ブラシを 1 往復したら（ M5StickC の加速度センサーで Y 軸方向に一定以上の差分が出たら） 1 回カウントする。
2. カウントごとに SPK HAT から音を鳴らす。（プッ！プッ！）
3. 10 回ごとに少し高めの音を鳴らす。（ピッ！）
3. 一定回数ごとに LCD モニターに "Over XX times!" と表示させる。
4. 規定回数を超えたら Google Home に激励（あるいはお疲れ様）のメッセージを喋ってもらう。


# 構築手順（ブラッシュアップのアドバイス大募集中です！）
### M5StickC スケッチ
（2019/12/12追記）HTTP / POST した後に http.end(); する1行を追加

```c:cheer_brushing.ino
#include <M5StickC.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "config.h"
#include <HTTPClient.h>

// Speaker用
const int servo_pin = 26;
int freq = 50;
int ledChannel = 0;
int resolution = 10;

// getAccelData用
float accX = 0;
float accY = 0;
float accZ = 0;
float previous_accY = 0;
float current_accY = 0;

// ブラッシングカウント用
int brushing_count = 0;
// カウントフラグ
int count_flag = 0;
// Google Homeが喋るセリフ
char *nodered_json = "";
// HTTP Response
int status_code = 0;

WiFiClientSecure wifiClient;
HTTPClient http;

void setup_wifi() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.print("connecting to ");
    Serial.print(ssid);
    Serial.println("...");
    WiFi.begin(ssid, password);

    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
      // Wi-Fiアクスポイントへの接続に失敗したら5秒間待ってリトライ
      Serial.println("failed to connect");
      delay(5000);
      return;
    } else {
      Serial.print("WiFi connected: ");
      Serial.println(WiFi.localIP());
      //M5.Lcd.setTextSize(1);
      //M5.Lcd.println(WiFi.localIP());
    }

    //wifiClient.setCACert(test_ca_cert);
    randomSeed(micros());
  }
}

// 初期設定
void setup() {
  M5.begin();
  M5.Axp.ScreenBreath(8);     // 画面の輝度（MIN:7～MAX:15）
  M5.Lcd.setRotation(3);      // 左を上にする
  M5.Lcd.setTextSize(2);      // 文字サイズを2にする
  M5.Lcd.fillScreen(BLACK);   // 背景を黒にする

  //シリアル接続速度指定
  Serial.begin(9600);

  //Wi-Fi接続
  setup_wifi();

  // 6軸センサー
  M5.MPU6886.Init();

  // Speaker用
  ledcSetup(ledChannel, freq, resolution);
  ledcAttachPin(servo_pin, ledChannel);
}

// Node-REDにWebhooks
void nodered_post(int counts) {
  Serial.println(counts);
  //const char *nodered_json = "{\"message\":\"" + msg + "\"}";
  if ( counts == 50) {
    nodered_json = "{\"message\":\"まだまだがんばれ\"}";
  } else if ( counts == 100) {
    nodered_json = "{\"message\":\"もう少しだよ\"}";
  } else if ( counts == 150) {
    nodered_json = "{\"message\":\"はい、お疲れ様でした\"}";
  }
  Serial.println(nodered_json);
  http.begin( nodered_server, 1880, "/hamigakimsg");
  http.addHeader("Content-Type", "application/json" );
  status_code = http.POST((uint8_t*)nodered_json, strlen(nodered_json));
  Serial.println(status_code);
  http.end();
}

// ループ処理
void loop() {
  
  // 加速度取得（変化前）
  M5.MPU6886.getAccelData(&accX,&accY,&accZ);
  previous_accY = accY * 1000;

  // 加速度表示
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println(previous_accY);

  // 100ミリ秒待つ
  delay(100);

  // 加速度取得（変化後）
  M5.MPU6886.getAccelData(&accX,&accY,&accZ);
  current_accY = accY * 1000;

  // Y軸加速度の差分が400以上の場合
  if ( current_accY - previous_accY > 400) {
    // Beep音発行(10階ごとにちょい高めの音鳴らす)
    if ( brushing_count % 10 == 0) {
      ledcWriteTone(ledChannel, 1500);
    } else {
      ledcWriteTone(ledChannel, 1000);
    }
    delay(10);
    ledcWriteTone(ledChannel, 0);

    // ブラッシング回数インクリメント
    brushing_count++;
  }

  // ブラッシング回数表示
  M5.Lcd.setCursor(0, 16);
  M5.Lcd.println(brushing_count);
  
  // ToDo 一定回数超えたらWebhooksしてGoogle Homeしゃべらす。
  if ( brushing_count % 50 == 0 && brushing_count !=0 && count_flag == 0) {
    // 一定回数超えたことを表示する。
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 32);
    M5.Lcd.println("over " + String(brushing_count) + " times!!");

    // Google Home しゃべらす。
    nodered_post(brushing_count);

    // ループ抑止フラグを立てる
    count_flag = 1;
  }

  // 51,101,151回目の時にフラグを初期化する。
  if ( brushing_count % 50 == 1) {
    count_flag = 0;
  }

  // ボタン全体の状態更新
  M5.update();
  // ボタンA(M5)が押されたらカウンタリセット＆画面リセット
  if ( M5.BtnA.wasPressed() ) {
    brushing_count = 0;
    M5.Lcd.fillScreen(BLACK);
  }
}
```

```c:config.h
// Wi-Fiアクセスポイント
const char *ssid = "XXX";
const char *password = "XXX";

// Raspberry pi Node-RED
const char *nodered_server = "192.168.XXX.XXX";
```


### Google Home周り（技術要素：Node-RED, google-home-notifier）

Raspberry Pi Zero W にNode-RED, google-home-notifier をインストールし、
Node-REDを以下の通り設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140921/2180bfad-efd3-6aef-174e-045a0a07fa8f.png)

1. M5StickCからのHTTPリクエストを受け付ける http in ノードを作成する。
2. Function ノードにて、 google-home-notifier を呼びだす。 
3. http request ノードで状態コード 200 を返す。 

function ノードで google-home-notifier を呼べるよう、メインの settings.js 内で、 functionGlobalContext を定義します。

```js:~/.node-red/settings.js
functionGlobalContext: {
    googlehomenotifier:require('[google-home-notifierパス]/google-home-notifier'),
},
```

function ノードは以下の記述だけで msg.payload.message に渡されたメッセージを Google Home が喋ってくれます。

```js:functionノード
const googlehome = context.global.googlehomenotifier;
const language = 'ja';

googlehome.device("GoogleHome", language);
googlehome.ip("XXX.XXX.XXX.XXX");
googlehome.notify(msg.payload.message, function(res) {
  console.log(res);
});

return msg;
```

# 所感
- 子どもに使ってもらったところ、想像以上に楽しそうに歯磨きしてくれてます。
- 作った本人としては、歯磨きの回数に応じて Google Home が喋るところがポイントでした。
- 使う側としては、音が出たり、歯磨き回数が表示される方が気に入ったようです。