IoT見守り監視センタ

[企画・制作] ZEPエンジニアリング

ネットワーク機器のアクティビティ・チェック

  我が家では、固定電話をひかり電話に変えてから宅内の電話機としてWi-Fi接続のIP電話機を使用しています。ところが気付くとコネクションが切れていて、電話の着呼ができない状態になってしまっていることがありました。一旦切れてしまうと自動で復帰しないようで、回線のアクティビティ・チェック(死活監視)をする必要がありました。当初、Raspberry Pi上で簡単なシェル・スクリプトを動作させて、IP電話機にPingを送信し、無応答の場合にスマホへメールを送信するシステムを運用していましたが、昨今の半導体不足の影響によるRaspberry Piの入手難や価格高騰を考えると「もったいない」と思い、同機能をM5Stackで実現してみました。
 リッチなUI(ユーザ・インタフェース)はいらないので、より安価なM5StickC、ATOM Matrixへ移植し使用しています。その中から今回は、一番安価に実現できるATOM Matrix版をご紹介します。※UIを完全に無くしてしまえばESP32単体など、より安価に実現できると思います。

 今回製作したシステムの概念図を以下に示します。
システム図

システムの仕様

 ・アクティビティ・チェック対象機器は、複数登録可能。(最大24)
 ・10分毎(変更可能)に登録した機器に対して、順番にPingを送信。(ATOM内蔵のWi-Fiインターフェースを利用)
  ※Ping応答があれば、LEDを緑に、無応答であれば、LEDを赤に点灯。(Pingテスト中はLEDを黄色に点灯)
  ※無応答の場合は、同時に登録したメールアドレスに対してアラートメールを送信。
 ・アクティビティ・チェックは上記の通り10分間隔で行うが、ボタン(LEDの表面パネル)を押下することによって、
  即時にテストを開始。
 ・本システム自身が正しく動作していることを示すため、右下のLEDを1秒毎に青色で点滅。

構成技術

 今回使用するATOMをはじめ、M5Stackの標準環境には、Ping送信とメール送信の機能が含まれていないため、以下ライブラリを使用しました。これにより、とてもシンプルに、かつ迅速に基本仕様を実現することができました。

 ・Ping送信ライブラリ
  Marian Craciunescu氏作成のESP32用のPingライブラリESP32Pingを使用させていただきました。
  GitHubからダウンロードして、必要なファイルをプロジェクトフォルダにコピーし、一部を変更して使用します。

 ・メール送信ライブラリ
  ArduinoIDEのライブラリマネージャから「ESP Mail Client by Mobizt」をインストールして使用します。

 上記ライブラリは、M5Stack Coreシリーズだけでなく、M5StickC、ATOMシリーズで使うことができます。

 メインのプログラムでは、最初にNTPにより現在時刻を取得した後に、10分毎またはボタン押下時にESP32Pingライブラリを使用して、Ping送信を行い、応答を確認し、LED表示やESP Mail Clientを使用してアラートメール送信を行っています。

 今回は、ATOM Matrixを対象に製作しましたが、本システムは、他のM5StackシリーズやESP32単体にもUI(ユーザ・インターフェース)部分を変更すれば容易に移植可能です。

Pingによるアクティビティ・チェックの限界

 機器のアクティビティ・チェックにPingを使用することによって、ネットワークの疎通(オンライン・オフライン)は、判別できますが、機器そのものが正常に動作しているか否かは厳密には判定できません。機器のネットワーク層が正常に動作していてPingに正常に応答していても「トランスポート層」、「アプリケーション層」が正常動作していない場合があるためです。それらに対応するには、監視対象機器に対するそれぞれに適した個別の対応が必要になります。

本システムの応用

 本製作例では、オフライン時にメールを送信するだけですが、オフラインからオンラインになったとき、逆にオンラインからオフラインになったときの変化点でメールを送信するように改造すれば、Pingの対象を例えば「ネットワーク機能付きのテレビ」にすると、「あっ、テレビ観てるな。あっ、もう寝たな」と遠くに住む家族の在宅確認や見守りなどにも応用できます。ほんの一例ですが、他にも様々な応用が考えられます。本記事をベースにぜひ応用を広げてください。

  動作中写真 
動作中の写真(4機器を監視中。3台目がオフライン、4台目テスト中)

ATOM Matrix
ATOM Matrix
【M5STACK-C008-B】
M5StickC Plus
M5StickC Plus
【M5STACK-K016-P】
M5Stack BASIC
M5Stack Basic V2.6
【M5STACK-K001-V26】

プログラム解説

 ArdionoIDE環境で作成しました。必要なライブラリ等は以下を参考に構築してください。

(1)開発環境

 M5StackのArduino IDE開発環境用を使用しました。その構築方法については、別記事「いろいろなマイコンボードに使えるArduino IDE。環境構築メモ」の「M5StackをArduino IDEで使う」の章を参考にしてください。

 次に「ESP Mail Client by Mobizt」ライブラリをインストールします。
 ・Arduino IDEのメニューで ツール ⇒ ライブラリを管理 を選択し、検索欄に「ESP Mail」と入力し、リストアップされた中から「ESP Mail Client by Mobizt」をインストールしてください。

 続いて、「ESP32Ping」をGitHubからダウンロードします。ZIP形式でダウンロードしたら適当な場所で解凍し、展開されたファイルの中から以下の4つのファイルを本プログラムと同じフォルダにコピーします。
 ・ESP32Ping.cpp
 ・ESP32Ping.h ⇒ 27行目の「#include <ping.h>」を「#include "ping.h"」に書き換えてください。
 ・ping.cpp
 ・ping.h

(2)プログラム

 本プログラムは、こちらよりダウンロードしてください。ESP32Ping関連ファイルは含めていませんので、前項に従ってダウンロードして本プログラムと同じフォルダに配置してください。

プログラムリスト

  1. // M5ATOM Activity Checker (c)2022- @logic_star Allrights reserved.
  2. #include <M5Atom.h>
  3. #include <WiFi.h>
  4. #include <time.h>
  5. // Wifi 関連定義
  6. const char* ssid = "SSID"; //WiFi APのSSID
  7. const char* password = "PASSWORD"; //WiFi APのPassword
  8. WiFiClient client;
  9. // 時計関連
  10. #define NTP_SERVER "ntp.nict.jp"
  11. const char* ntpServer = NTP_SERVER;
  12. const long gmtOffset_sec = 9 * 3600;
  13. const int daylightOffset_sec = 0;
  14. struct tm timeinfo;
  15. uint8_t secLastReport = 0;
  16. // メール関連
  17. #include <ESP_Mail_Client.h>
  18. #define SMTP_HOST "aaa.bbb.ccc.jp" //メールサーバ名
  19. //#define SMTP_PORT esp_mail_smtp_port_25 //SMTP Port 25
  20. //#define SMTP_PORT esp_mail_smtp_port_465 //SMTP port 465
  21. #define SMTP_PORT esp_mail_smtp_port_587 //SMTP port 587
  22. #define AUTHOR_EMAIL "aaa@bbb.ccc.jp" //SMTP認証ID:メール送信者のメールアドレス
  23. #define AUTHOR_PASSWORD "password" //SMTP認証パスワード
  24. #define AUTHOR_DOMAIN "bbb.ccc.jp" //送信者のドメイン
  25. #define AUTHOR_NAME "ACTIVITY CHECKER" //送信者名
  26. #define MESSAGE_SUBJECT "OFFLINE ALART!" //送信メールタイトル
  27. #define MESSAGE_RECIPIENT "xxx@bbb.ccc.jp" //メール送信先アドレス
  28. #define MESSAGE_RECIPIENT_NAME "Administrator" //メール送信先名
  29. #define MESSAGE_ID "Message-ID: <aaa@bbb.ccc.jp>" //メールのメッセージID
  30. SMTPSession smtp;
  31. const char rootCACert[] PROGMEM = "-----BEGIN CERTIFICATE-----\n"
  32.                                   "-----END CERTIFICATE-----\n";
  33. /* Callback function to get the Email sending status */
  34. void smtpCallback(SMTP_Status status)
  35. {
  36.   /* Print the current status */
  37.   Serial.println(status.info());
  38.   /* Print the sending result */
  39.   if (status.success())
  40.   {
  41.     Serial.println("----------------");
  42.     ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
  43.     ESP_MAIL_PRINTF("Message sent failled: %d\n", status.failedCount());
  44.     Serial.println("----------------\n");
  45.     struct tm dt;
  46.     for (size_t i = 0; i < smtp.sendingResult.size(); i++)
  47.     {
  48.       /* Get the result item */
  49.       SMTP_Result result = smtp.sendingResult.getItem(i);
  50.       time_t ts = (time_t)result.timestamp;
  51.       localtime_r(&ts, &dt);
  52.       ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
  53.       ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
  54.       ESP_MAIL_PRINTF("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
  55.       ESP_MAIL_PRINTF("Recipient: %s\n", result.recipients.c_str());
  56.       ESP_MAIL_PRINTF("Subject: %s\n", result.subject.c_str());
  57.     }
  58.     Serial.println("----------------\n");
  59.     //You need to clear sending result as the memory usage will grow up.
  60.     smtp.sendingResult.clear();
  61.   }
  62. }
  63. void send_error_mail(String textMsg){
  64.   smtp.callback(smtpCallback);
  65.   ESP_Mail_Session session;
  66.   session.server.host_name = SMTP_HOST;
  67.   session.server.port = SMTP_PORT;
  68.   session.login.email = AUTHOR_EMAIL;
  69.   session.login.password = AUTHOR_PASSWORD;
  70.   session.login.user_domain = F(AUTHOR_DOMAIN);
  71.   session.time.ntp_server = F(NTP_SERVER);
  72.   session.time.gmt_offset = 9;
  73.   session.time.day_light_offset = 0;
  74.   SMTP_Message message;
  75.   message.sender.name = F(AUTHOR_NAME);
  76.   message.sender.email = AUTHOR_EMAIL;
  77.   message.subject = F(MESSAGE_SUBJECT);
  78.   message.addRecipient(F(MESSAGE_RECIPIENT_NAME), F(MESSAGE_RECIPIENT));
  79.   message.text.content = textMsg;
  80.   message.text.charSet = F("us-ascii");
  81.   message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
  82.   message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low;
  83.   message.addHeader(F(MESSAGE_ID));
  84.   if (!smtp.connect(&session)) return;
  85.   if (!MailClient.sendMail(&smtp, &message))
  86.     Serial.println("Error sending Email, " + smtp.errorReason());
  87.   ESP_MAIL_PRINTF("Free Heap: %d\n", MailClient.getFreeHeap());
  88. }
  89. #include "ESP32Ping.h"
  90. void ping_check(char *name, IPAddress ip, int x){
  91. char text_buffer[200];
  92.       M5.dis.drawpix(x, 0xffff00); //LEDを黄色に
  93.       if(Ping.ping(ip)) {
  94.         M5.dis.drawpix(x, 0x00ff00); //LEDを緑に
  95.       } else {
  96.         M5.dis.drawpix(x, 0xff0000); //LEDを赤に
  97.         sprintf(text_buffer, "%s is offline!", name);
  98.         send_error_mail(text_buffer); //アラートメールの送信
  99.       }
  100. }
  101. // NTPによる時刻取得関数
  102. int ntp(){
  103. uint8_t wifi_retry_cnt;
  104.   WiFi.begin(ssid, password); //WiFi接続開始
  105.   wifi_retry_cnt = 20; //0.5秒×20=最大10秒で接続タイムアウト
  106.   while (WiFi.status() != WL_CONNECTED){
  107.     delay(500);
  108.     if(--wifi_retry_cnt == 0) {
  109.       WiFi.disconnect(true); //タイムアウトでWiFiオフ
  110.       WiFi.mode(WIFI_OFF);
  111.       return(false); //接続失敗でリターン
  112.     }
  113.   }
  114.   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); //NTPによる時刻取得
  115.   if (!getLocalTime(&timeinfo)) {
  116.     WiFi.disconnect(true); //時刻取得失敗でWiFiオフ
  117.     WiFi.mode(WIFI_OFF);
  118.     return (false); //時刻取得失敗でリターン
  119.   }
  120.   return (true); //時刻取得成功でリターン
  121. }
  122. void setup() { //NTPで時刻取得し、もしエラーだった場合は動作停止
  123.   M5.begin(true, false, true);
  124.   if(ntp() == false) while(1);
  125. }
  126. int toggle = 0;
  127. void loop() { //メインプログラム
  128. int target;
  129.   getLocalTime(&timeinfo);
  130.   if((timeinfo.tm_hour == 2)&&(timeinfo.tm_min == 0)&&(timeinfo.tm_sec == 0)) ntp();
  131.   if(secLastReport != timeinfo.tm_sec) {
  132.     secLastReport = timeinfo.tm_sec;
  133.     if(toggle){toggle = 0;M5.dis.drawpix(24, 0);} //青色インジケータの表示
  134.     else{toggle = 1;M5.dis.drawpix(24, 0x0000ff);}
  135.     M5.update();
  136.     if((((timeinfo.tm_min % 10) == 0)&&(timeinfo.tm_sec == 0))||M5.Btn.wasPressed()){ //10分毎または、ボタンが押下時に処理
  137.       //監視サーバ定義
  138.       target = 0;
  139.       ping_check("IP Phone", IPAddress (192, 168, 3, 99), target++); //監視先1 監視対象名とIPアドレスを指定
  140.       ping_check("SVR-X", IPAddress (0, 0, 0, 0), target++); //監視先2
  141.       ping_check("SVR-Y", IPAddress (0, 0, 0, 0), target++); //監視先3
  142.       ping_check("Google DNS", IPAddress (8, 8, 8, 8), target++); //監視先4 適宜24まで拡張可能
  143.     }
  144.   }
  145.   delay(100); //0.1秒ウェイト
  146. }
  • 6~7行目 Wi-Fi関連の設定です。使用環境に合わせて設定してください。
  • 18~35行目 メール関連の設定です。メールアドレスの設定やSMTPサーバー、認証関連の設定をしてください。
  • 71~96行目 アラートメール送信の関数です。
  • 98~110行目 Ping送信テスト関数です。LED制御やアラートメールを送信します。
  • 112~132行目 Wi-Fi接続し、起動時の現在時刻を取得します。
  • 134~137行目 ATOMの初期化です。NTPに失敗した場合は、動作停止します。(Wi-Fi関連設定を見直してください)
  • 141~161行目 メインルーチンです。154~157行目で監視対象を登録しています。監視対象名とIPアドレスを設定してください。

まとめ

 M5Stackの標準ライブラリでは、Ping送信やメール送信が提供されていないため、敷居が少し高いですが、本製作例のように先人の方々が作成されたライブラリを使用することにより、容易に実現することができました。ATOMには、Port.AのGroveコネクタもありますから、単なるアクティビティ・チェックだけでなく、温湿度センサなどを接続すれば、同時に環境モニタなどもでき、応用を広げることができると思います。本記事をベースにいろいろなアイデアを実現してみてはいかがでしょう。ATOM MatrixやPort.Aに接続可能な各種センサはマルツで購入できます。

ATOM Matrix
ATOM Matrix
【M5STACK-C008-B】
M5StickC Plus
M5StickC Plus
【M5STACK-K016-P】
M5Stack BASIC
M5Stack Basic V2.6
【M5STACK-K001-V26】

©2022ー @logic_star All rights reserved.



Page Top