M5Atomを使って、遠隔で嫁が筋トレを応援してくれるシステムを作ってみた

#はじめに
　この記事は2020年6月18日にIoTLTでプレゼンしたシステムの一部抜粋記事です。
システムの技術要素ごとに3つに分けて書きます。

1. [M5Atomを使って、遠隔で嫁が筋トレを応援してくれるシステムを作ってみた](https://qiita.com/kmaepu/items/99150db285712dfc68e5)
2. [Node-REDで、MQTTによるデータ送受信（嫁筋トレシステム）](https://qiita.com/kmaepu/items/6711c127c2a0434d5d98)
3. LINE Message

こちらがその時のプレゼンスライドです。（記事書くのに時間かかってしまいました(-_-;)）
https://speakerdeck.com/maepu/yuan-ge-dejia-nijin-torewoying-yuan-sitemorau

#概要

　最近、ストレスのせいか暴飲暴食で太ってしまいました。そこで痩せたいと思い、筋トレを始めました。しかし長続きせずということで、嫁に応援してもらおうと思いました。

　しかしながら、嫁は里帰り出産のため沖縄に...。

　ということで、**遠隔で**嫁が筋トレを応援してくれるシステムを作ってみました！

![簡易的なシステム.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/18682e1f-8e4d-e5b9-d593-ecab76d196a1.jpeg)


こちらがのデモです。
https://www.youtube.com/watch?v=ufq9XtmmxMs

#レシピ
##開発環境
 - M5Atom Lite
 - Arduino IDE ver1.8.12
 - Node-RED  ver1.0.4
 - Node.js v10.15.3

##システム構成
　システム構成は次のようになっています。M5Atomのボタンを押したイベントが発生すると、MQTT通信で[Node-RED](https://nodered.jp/)にデータが送られます。MQTT通信をしている間に「[Shiftr.io](https://shiftr.io/)」というものがあります。これはMQTT通信を管理するサーバーです。無料で理想することができます。

　MQTTで送られてきたデータは、Node-REDで作成したフローによりMQTT通信のメッセージからLINE Messageに変換していてます。
　
　LINE botは使用したいグループLINEに招待して使います。筋トレが開始された時と、10回ごと、終了時にメッセージを送ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183282/09f711d4-1f16-08ac-e4b1-f9062d6de1a7.png)


## M5Atom Lite
 M5Atom　Liteの主な処理は、ボタンのイベントに応じて、データの送信とカウントアップ等を行っています。動作しているプログラムは次の通りです。

```c++:Button.ino

#include "M5Atom.h"
#include <WiFi.h>
#include <PubSubClient.h> // MQTTのライブラリ

WiFiClient client;
PubSubClient mqttClient(client);

// Wi-Fi定義
char *ssid = "SSID";            // Wi-FiのSSID
char *password = "PASS";        // Wi-Fiのパスワード

// MQTT ブローカの設定定義
const char *endpoint = "broker.shiftr.io";    shiftr.ioを利用する場合
const int port = 1883;          // MQTT のポート番号 
char *deviceID = "M5Atom";      // デバイスID
char *userName = "認証名";              // MQTTブローカ認証ユーザ名
char *userPass = "認証パスワード";      // MQTTブローカ認証パスワード
char *pubTopic1 = "/start";          // メッセージを知らせるトピック
char *pubTopic2 = "/end";          // メッセージを知らせるトピック
char *pubTopic3 = "/count";          // メッセージを知らせるトピック
char *subTopic = "/sub";          // メッセージを待つトピック

typedef enum {
  AIDLE = 0,
  SUB_AIDLE,
  RUN,
  SUB_RUN,
}STATE;

char pubMessage[128];

uint8_t DisBuff[2 + 5 * 5 * 3];

char main_state = AIDLE;
unsigned int count = 0;

void setBuff(uint8_t Rdata, uint8_t Gdata, uint8_t Bdata)
{
    DisBuff[0] = 0x05;
    DisBuff[1] = 0x05;
    for (int i = 0; i < 25; i++)
    {
        DisBuff[2 + i * 3 + 0] = Rdata;
        DisBuff[2 + i * 3 + 1] = Gdata;
        DisBuff[2 + i * 3 + 2] = Bdata;
    }
}

// MQTTの接続チェック
void mqttLoop() {
    if (!mqttClient.connected()) {
        connectMQTT();
    }
    mqttClient.loop();
}

// MQTT接続時の処理
void connectMQTT() {
  mqttClient.setServer(endpoint, port);
  mqttClient.setCallback(mqttCallback);
  while (!mqttClient.connected()) {
      if (mqttClient.connect(deviceID,userName,userPass)) {
          Serial.println("MQTT Connected.");
          int qos = 0;
          mqttClient.subscribe(subTopic, qos);
          Serial.println("Subscribed.");
      } else {
          Serial.print("Failed. Error state=");
          Serial.print(mqttClient.state());
          // Wait 5 seconds before retrying
          delay(5000);
      }
  }
}

void setup()
{
    main_state = AIDLE;
    count = 0;
    
    M5.begin(true, false, true);
    Serial.begin(115200);
  
    delay(10);
    setBuff(0x00, 0x00, 0x40);
    M5.dis.displaybuff(DisBuff);

    // Wi-Fi設定
    WiFi.begin(ssid, password);  //  Wi-Fi APに接続
    while (WiFi.status() != WL_CONNECTED) {  //  Wi-Fi AP接続待ち
        delay(500);
        Serial.print(".");
    }
    Serial.print("WiFi connected\r\nIP address: ");
    Serial.println(WiFi.localIP());
    
    // connect MQTT
    mqttClient.setServer(endpoint, port);
    mqttClient.setCallback(mqttCallback);
    connectMQTT();
    Serial.println("MQTT connected");
    
    Serial.println("Start my program");
}

void loop()
{
    // ステートマシン
    // ボタンを2秒長押ししたとき
    if(M5.Btn.pressedFor(2000)){
     // Serial.println("Button press");
      switch(main_state){
        case AIDLE :
          main_state = SUB_AIDLE;
          Serial.println("Change to SUB_AIDLE");
          break;
         case RUN :
          main_state = SUB_RUN;
          Serial.println("Change to SUB_AIDLE");
          break; 
      }
    }
    // ボタンを離したとき
    else if(M5.Btn.wasReleased()){
      switch(main_state){
        case SUB_AIDLE :
          main_state = RUN;
          setBuff(0x00, 0x40, 0x00);
          Serial.println("Change to RUN");
          // MQTT 送信
          sprintf(pubMessage, "start");
          mqttClient.publish(pubTopic1, pubMessage);
          break;
        case RUN :
          count++;
          if((count%10 == 0) && (count > 0)){
            sprintf(pubMessage, "%d",count);
            mqttClient.publish(pubTopic3, pubMessage);
          }
          Serial.printf("count = %d\n",count);     
          break;
        case SUB_RUN :
          main_state = AIDLE;
          setBuff(0x00, 0x00, 0x40);
          Serial.println("Change to AIDLE");

          // MQTT 送信
          sprintf(pubMessage, "%d", count);
          mqttClient.publish(pubTopic2, pubMessage);

          count = 0;
          break;
      }
    }

    mqttLoop();
    
    M5.dis.displaybuff(DisBuff);
    delay(50);
    M5.update();
}

// MQTTで何かメッセージを受け取った場合（今回は未使用）
void mqttCallback (char* topic, byte* payload, unsigned int length) {
    String str = "";

    Serial.print("Received. topic=");
    Serial.println(topic);
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
        str += (char)payload[i];
    }
    Serial.print("\n");
    Serial.println("recive message");
}
```

ソースコードから一部抜粋します。

　次の行では、「**ボタンを2秒長押しした**」のイベント処理です。2秒長押しすると1度この処理に入ります。ここで、筋トレの開始と終了処理をしています。

```c++
if(M5.Btn.pressedFor(2000)){
```

　次の行では、「**ボタンを離した**」のイベントです。ボタンを離したときに1度この処理を行います。ここでカウント処理をしています。

```c++
else if(M5.Btn.wasReleased()){
```

#次回
　Node-REDの説明をしてきます！