![見出し画像](https://assets.st-note.com/production/uploads/images/122141193/rectangle_large_type_2_4dcfccb5d35658426e22f5e841754745.png?width=1200)
プログラムと電子工作・LINEに通知
M5StickC Plus は IoT と整合性が非常に高いです。何らかの事象が発生したときスマートフォンに通知できれば、様々な応用が考えられます。
例えば、人が入室したときに通知する、温室の気温が高くなりすぎたら通知するなど、様々なリモートセンシングに活用できます。
今回は、M5StickC Plus に Web クライアントを実装します。M5StickC Plus のボタンを押したことを、Web クライアントで LINE に通知します。LINE が提供する LINE Notify API を利用します。
目標
M5StickC Plus で Web クライアントを動作させます。
M5StickC Plus のボタンを押すと、LINE にメッセージを通知します。
部品・機材
使用する部品は次のとおりです。「Hello, World!」と同じですが、Wi-Fi ルータとスマートフォンが必要です。
電子部品
M5StickC Plus 1台
開発用機材
PC(Windows10 または 11)、開発環境 Arduino-IDE 導入ずみ
USB-A・USB-C ケーブル
Wi-Fi ルータ
スマートフォン(LINE アプリをインストールずみ)
LINE の準備
(1) LINE グループを作成
スマートフォンで LINE を開く。
トーク →トークルームを作成→グループ
「友だちを選択」しないで、「次へ」
・グループ名:LINE Notify テスト
・自分しかメンバーがいないトークルームができる。
(2) LINE Notify にユーザ登録
PC のブラウザで LINE Notify https://notify-bot.line.me/ja/ にアクセスする。
下の方の「サービスを登録する>」をクリックする。
LINE のユーザ名とパスワードでログインする。
・スマートフォンの LINE に本人確認の通知が届く。右上のユーザ名をクリック →マイページ
[トークンを発行する]
・トークン名:LINE Notify テスト ※(1)3.と同じもの
・[発行する]
※トークンは一度しか表示されない、保存しておくこと。
(3) LINEグループに LINE Notify を追加
スマートフォンで LINE アプリを開く。
トーク →「LINE Notify テスト」を開く。
[≡]→招待
「LINE Notify」を選択し、[招待]
開発手順
PC と M5StickC Plus を USBケーブルで接続する。
Arduino-IDE でスケッチ line_notify.ino を開く。
検証・コンパイルする。
M5StickC Plus に書き込む。
WiFi ルータに接続できたら、上ボタン(押しボタンA)あるいは横ボタン(押しボタンB)を押して LINE 通知する。
スマートフォンの LINE アプリに通知が届くことを確認する。
スケッチ
line_notify.ino
#include <M5StickCPlus.h>
#include <WiFiClientSecure.h>
#include <ssl_client.h>
#include <UrlEncode.h>
#define SECURE /*安全な通信*/
#define LHEIGHT 25 /*1行の高さ(px)M5StickCPlus:25、M5StickC:15*/
// それぞれのWi-Fi環境を設定する。
const char* ssid = "xxxxxxxx"; /*SSID*/
const char* password = "xxxxxxxx"; /*PASSWORD*/
// LINE設定
const char* host = "notify-api.line.me"; // LINE NOTIFY ホスト
const char* token = "*******************************************"; // LINE トークン
#if defined(SECURE)
// GlobalSign は notify-api.line.me のルート認証局。
const char* root_ca =
"-----BEGIN CERTIFICATE-----\n"
"MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G\n"
"A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp\n"
"Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4\n"
"MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG\n"
"A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\n"
"hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8\n"
"RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT\n"
"gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm\n"
"KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\n"
"QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ\n"
"XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw\n"
"DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o\n"
"LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU\n"
"RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp\n"
"jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK\n"
"6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX\n"
"mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs\n"
"Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\n"
"WD9f\n"
"-----END CERTIFICATE-----\n"; /*GlobalSign*/
#endif
void line_notify(String);
//------------------------------------------------------------------------------
// setup()
void setup() {
// 電源ON時に 1回だけ実行する処理をここに書く。
M5.begin(); /*M5を初期化する*/
M5.Lcd.setTextSize(2); /*文字サイズはちょっと大きめ*/
M5.Lcd.setRotation(3); /*上スイッチが左になる向き*/
M5.Lcd.print("line_notify");
Serial.begin(115200); /*デバッグ用のシリアル通信を初期化する*/
// WiFiアクセスポイントに接続する。
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { /*Wi-Fi AP接続待ち*/
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.fillScreen(BLACK); /*背景を黒にする*/
M5.Lcd.setCursor(0, LHEIGHT*0); /*1行目*/
M5.Lcd.print("WiFi OK");
Serial.print("WiFi connected\r\nIP address: ");
Serial.println(WiFi.localIP());
}
//------------------------------------------------------------------------------
// loop()
void loop() {
// 自動的に繰り返し実行する処理をここに書く。
// ボタンの状態を更新する。
M5.update();
// 上ボタン(ボタンA)をチェックする。
if (M5.BtnA.wasPressed()) {
Serial.println("Pushed A (Top)");
line_notify("Pushed A (Top)");
}
else if (M5.BtnB.wasPressed()) {
Serial.println("Pushed B (Side)");
line_notify("Pushed B (Side)");
}
delay(1);
}
//------------------------------------------------------------------------------
// LINE Notify
// in: String msg 通知する文
void line_notify(String msg) {
WiFiClientSecure client;
#if defined(SECURE)
client.setCACert(root_ca);
#else
client.setInsecure(); /*サーバの検証をスキップする*/
#endif
if (!client.connect(host, 443)) {
Serial.println("cannot connect LINE Notify API.");
return; /*FIN*/
}
String query = String("message=") + urlEncode(msg);
String request = String() +
"POST /api/notify HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Authorization: Bearer " + token + "\r\n" +
"Content-Length: " + query.length() + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" +
query + "\r\n";
Serial.println(request);
// HTTPS リクエストを送信する。
client.print(request);
// HTTPS レスポンス・ヘッダを受信する。
while (client.connected()) {
String line = client.readStringUntil('\n');
Serial.println(line);
if (line == "\r") {
break;
}
}
// HTTPS レスポンス・ボディを受信する。
// {"status":200,"message":"ok"} だったら、LINE NOTIFY が成功である。
while (client.available()) {
String line = client.readStringUntil('\n');
Serial.println(line);
}
client.stop();
}
WiFiClientSecure クラス
WiFiClientSecure クラスは TLS(SSL)を使用して安全に接続します。これはブラウザが HTTPS(URL に https://… を指定するアクセス)で通信する仕組みと同等です。
安全な通信のためには、setCACert() 関数で、通信先サーバの証明書を指定する必要があります。
通信先サーバの証明書
通信先サーバのルート証明書は、次の方法によって取得できます。Google Chrome で notify-bot.line.me の証明書の取得方法を記します。
PC の Chrome で https://notify-bot.line.me/ へアクセスする。
URL 表示左の鍵マークをクリックする。
(鍵マーク)「この接続は保護されています」をクリックする。
「証明書は有効です」をクリックする。
「詳細」タブをクリックする。
・証明書の階層が表示される。
▽GlobalSign
▽GlobalSign RSA OV SSL CA 2018
*.line.me一番上の GlobalSign を選んで、[エクスポート]をクリックする。
・GlobalSign.crt という証明書ファイルが得られる。安全なブラウザによって得られたサーバ証明書は正当であると言える。
・スケッチではテキストとして、定数 root_ca にセットしている。
安全でない通信
setCACert() 関数でサーバ証明書を指定する代わりに、setInsecure() 関数でサーバ証明書の検証をスキップすることができます。通信先のサーバ証明書を無視するので、DNS が詐称されるなどの脅威には対応できません。
結果
押しボタンを押すたびに、スマートフォンの LINE アプリに通知が届きます。(図1)
![](https://assets.st-note.com/img/1700391804657-pqLqndIxaE.png?width=1200)
参考
サンプルスケッチ
Arduino-IDE メニュー→ファイル→スケッチ例→WiFiClientSecure→WiFiClientSecure あるいは WiFiClientInsecuregithub espressif/arduino-esp32/arduino-esp32/libraries/WiFiClientSecure/
https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFiClientSecureLINE Notify API Document
https://notify-bot.line.me/doc/ja/
LINE 通知に関する説明は、見出し「通知系」の箇所
所感
setCACert() 関数を使えば安全で、setInsecure() 関数を使うと安全ではないと書きましたが、実際のところそう思っているだけで確認できていません。セキュリティというものは実際に確認できないことがあります。
スケッチに埋め込まれる root_ca の 1文字を別の文字に改ざんしてみると、LINE 通知が失敗("cannot connect LINE Notify API.")になる場合もあるし、成功する場合もあります。本当に機能しているのかと疑いたくなります。
ライセンス
このページのソースコードは、複製・改変・配布が自由です。営利目的にも使用してかまいませんが、何ら責任を負いません。