LINEを使用してLチカしてみた

#####始めに
勢いでアドベントカレンダーに手を挙げたので、何をやったらいいのか思いつきませんでした。ということで、初心に戻ってLチカをやってみることにしました。

Lチカとは、初めてのプログラミング言語を試すとき最初に「Hello world」と表示させて動作の確認をすることが多いように、電子工作を始めるときに最初に踏む手順のことです。LEDをちかちかと点灯させることで動作の確認を行います。

今回実際にどのようなことをやるのかというと、LINEからスイッチをオンするとLEDが点灯して、LINEからスイッチをオフするとLEDが消灯するというものを作ってみようと思います。

#####使用器具
windowsPC M5stack(ESP32)
#####開発環境
ArduinoIDE node-red ngrok

#####実際の手順
①まずはLINE Developperに登録します。
[こちら](https://developers.line.biz/ja/services/messaging-api/)のページから登録します。
わかりにくければ[こちら](https://qiita.com/shinfrom/items/4e4917152222666a1af3)のページを参考にしてください。

②次に開発環境を用意していきます。
今回はnode-redとngrokを使用して開発環境を構築しました。
開発環境の構築の方法については[こちら](https://qiita.com/shinfrom/items/e5ecf1ac68f5f0b7118f)に別途まとめていますので、参考にしてください。開発環境を用意するのが面倒な場合は、Herokuやenebularを使用するとよいと思います。

用意した開発環境でLINEからの送信を受信できるようにします。
LINE Developperのページでmessaging apiでプロバイダとチャンネルを作成します。このあたりのことは良い資料がたくさんありますので、今回は割愛します。
用意したnode-redの環境にhttpinノードを置いて、メソッドをpostにして、適当なパスを追加します（今回は/sublineにしました）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/e23350e4-84fa-3fbf-df8f-9692d8294330.png)


②の手順を実行してngrokで作成したhttpsのurlをLine Messaging APIの設定でwebhook urlに貼り付けます。その時に追加したパスを付け足すのを忘れないでください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/b9c89b03-3770-2445-5d61-d6db2a8d3c0b.png)
検証ボタンを押して接続が成功すればnode-redとLINEの連携は完了です。

webhookの検証まで終わったら、応答メッセージの編集を行います。
応答モードをBot、挨拶メッセージをオフ、応答メッセージをオフ、webhookをオンにします。
![応答設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/f4133a88-3af9-057c-607b-0632fe973948.png)
応答メッセージの編集まで終わったら、QRコードで友達追加します。

ここからはnode-redでの作業がメインになります。
LINEからのメッセージを受信したら、メッセージの内容に応じた処理をマイコンに投げるフローを作成します。
![noderedフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/7004b0e5-089a-b4a1-465c-39975283a7ba.png)
スイッチノードを利用して、LINEからのメッセージの内容を判別します。
具体的には、オン、オフ等の簡単な言葉を利用して場合分けします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/f6b9fea7-3d3e-2ac8-045e-7042bb3efaaa.png)
マイコンとはMQTTで通信するようにしますので、命令に応じたpayloadをfunctionノードで作成します。LEDONの場合の処理の例です。

```json
var sw_flag = 1;
msg.payload = {
    sw_flag
}
return msg;
```
MQTTノードの設定を行います。今回は開発環境は用意しましたが、MQTTブローカーは用意していませんので[mqtt.uko.jp](https://mqtt.uko.jp/)の環境を使用させてもらうことにします。個人での使用であれば、登録不要で使用することができます。
[mqtt.uko.jp](https://mqtt.uko.jp/)を開くとmqttのパスワードは毎日変わるようになっており、そのパスワードはページを開いたときに表示されたものを使用するようになっています。
実際のMQTTノードの設定画面です。
![mqtt設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/88cbe98f-a468-1629-1a48-2bde93dec5df.png)
サーバーにはhttps://mqtt.uko.jp/ を入力します。
セキュリティタブを開いて、ユーザー名はicecream、パスワードは[mqtt.uko.jp](https://mqtt.uko.jp/) のワンデイパスワードを入力します。
このノードを繋いでdeployしたらクラウド側の準備は完了です。

次はマイコン側のプログラムを準備していきます。
マイコン側のプログラムは、こちらに用意しましたので、参考にしてください。

```c++
/* LINEからメッセージを受信してメッセージに応じた動作をするサンプルプログラム
 *  
 * ESP32とM5stack grayで動作を確認しました
 * Wi-FiでMqttに接続してLINEからのメッセージを受信します
 * メッセージに応じて処理を振り分けます
 * その処理に応じた動作（今回はLEDのオンオフ）をします。
 * 
 * 参考にしたプログラム　https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino
 * jsonのライブラリはインストールしておく必要がありますhttps://arduinojson.org/?utm_source=meta&utm_medium=library.properties
 * 
 * M5stackでの動作確認は行いましたがM5stackのライブラリを使用しての確認は行っていませんので
 * M5stackで使用するときはボードマネージャをesp32Dev Moduleにしてそのまま書き込んでください
 * 
 * @author S.Nakamura 2021-12-10
*/


#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>  

#define LED_PIN 22  //LEDピン番号

volatile int sw_status = 0; //スイッチの状態を把握する

//ネットワークの設定-----------------------------------------------
//SSIDの設定
const char ssid[] = " your_ssid";                     //ssidの設定（変更）
const char passwd[] = "your_passwd";                  //パスワードの設定（変更）

// Pub/Subの設定
const char* mqttHost = "mqtt.uko.jp";                 // Mqttのサーバーを指定（固定）
const int mqttPort = 1883;                            // MQTTのポートを指定(固定)
const char* mqtt_username = "icecream";               //ユーザーネーム（固定）
const char* mqtt_password = "Today's password";       //mqttのワンデイパスワード（変更）
const char* topic_pub = "publish_topic";              // 送信するトピック名（変更）
const char* topic_sub = "subscribe_topic";            // 受信するトピック名 （変更）

char* payload;                                        // 送信するデータ（未使用）
//--------------------------------------------------------------


//インスタンスの作成
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

//プロトタイプ宣言
//mqttのセットアップ
void MqttSet();
//Wi-Fiの接続
void connectWiFi();
//mqttの接続
void connectMqtt();
//mqttの送信
void MqttPublish(char *PayloadMemory);
// Macアドレスを文字列で取得する
String getMacAddr();
//コールバック関数
void callback(char* TOPIC, byte* payload, unsigned int length); 

void setup(){
  Serial.begin(115200);
  Serial.println("Line subscribe start!"); 
  pinMode(LED_PIN, OUTPUT);       //LEDピンの準備
  digitalWrite(LED_PIN, LOW);  
  connectWiFi();  //Wi-Fiの接続
  connectMqtt();  //mqttの接続
}

void loop(){
  mqttClient.loop();
}


//mqtt受信時の動作(callback関数)
void callback(char* TOPIC, byte* payload, unsigned int length) {
  Serial.print("Message arrived [ topic: "); //受信メッセージの確認
  Serial.print(TOPIC); 
  Serial.println("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println("");
  StaticJsonDocument<256> doc;    //jsonとして扱う
  deserializeJson(doc, payload, length);
  int sw_flag = doc["sw_flag"];
  Serial.print("sw_flag is ");
  Serial.println(sw_flag);
  
  //フラグに応じて処理を振り分ける
  if (sw_flag == 0 && sw_status != 0){
    sw_status = 0;
    Serial.println("switch off");     //LEDをオフにする
    digitalWrite(LED_PIN, LOW);
  } else if(sw_flag == 1 && sw_status != 1){
    sw_status = 1;
    Serial.println("switch on !");    //LEDをオンにする
    digitalWrite(LED_PIN, HIGH);
  }
}

//mqttを送信する関数（今回未使用ですので使用する際には調整が必要です）
void MqttPublish(char *PayloadMemory){
  payload = PayloadMemory;    //送信データの作成
  mqttClient.publish(topic_pub, payload); //mqttの送信
  Serial.println("published");
}

//Wi-Fiを接続する関数
void connectWiFi(){
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");

  int i = 0 ;   
  //Wi-Fiの接続確認　30秒間接続できないときは接続やりなおし
  while(WiFi.status() != WL_CONNECTED) {
  i += 1;
  Serial.print("...");
  delay(5000);
  if (i == 6){
    Serial.println("wi-fi reset");
    connectWiFi();
      }
  }
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());
}

//mqttサーバーに接続
void connectMqtt(){  
  while(WiFi.status() != WL_CONNECTED) {      //Wi-Fiの接続確認
  Serial.print("WiFi is not connect");
  connectWiFi();
  }
  mqttClient.setServer(mqttHost, mqttPort);   
  mqttClient.setCallback(callback);
  while( ! mqttClient.connected() ) {   //brokerサーバーに接続する
    Serial.println("Connecting to MQTT...");
    String clientId = "ESP32-" +  getMacAddr();
    Serial.print("clientID : "); 
    Serial.println(clientId.c_str()); 
    
    //接続の確認
    if ( mqttClient.connect(clientId.c_str(),mqtt_username, mqtt_password) ) {  //ユーザー認証を行う時はこちらを利用する
//    if ( mqttClient.connect(clientId.c_str())) { //ユーザー認証を使用しないときはこちらを利用する
      Serial.println("connected"); 

    }
  }
  mqttClient.subscribe(topic_sub);
}

// Macアドレスを文字列で取得する
String getMacAddr()
{
  byte mac[6];
  char buf[50];
  WiFi.macAddress(mac);
  sprintf(buf, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}
```
こちらのソースコードのWi-Fiの接続情報、MQTTのトピックなどを変更するとそのまま使用できるようになっています。トピックに関しては、node-redのMQTTノードのトピックと合わせることを忘れないように注意してください。

これでLINEからのLEDのオン、オフが制御できるようになりました。
![LEDblynk.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/f8a3c8d0-e920-f748-5dd6-ec073b44c64f.jpeg)
※写真が汚いので、近日中に差し替えます。

#####結果
マイコンのプログラムを多少書く必要がありますが、LINEからLチカすることに成功しました。

#####まとめ
LINEの機能とローコードツールの開発環境を併用することで、そこまで時間をかけなくても開発ができるようになっています。今回は挑戦できなかったのですが、UIflowやobnizを使用することによって、ノンコードでLINEからLチカができるのではないかと考えられます。

#####おまけ
オン、オフはできるようになったのですがそれだけでは味気ないので、クイックリプライやスタンプ等を併用してデコってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/4c5e14b5-086f-ff39-e673-f0880b1a499d.png)

#####参考情報
[Ubuntuに最新のNode.jsを難なくインストールする](https://qiita.com/seibe/items/36cef7df85fe2cefa3ea)
![noderedフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/0eb7c5cf-266f-14aa-3636-b46773f18b8d.png)