2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5AtomUで見守りデバイス作成

2
Posted at

はじめに

遠方に住む祖母の見守りとして、「テレビをつけたらLINE通知が届く」IoTシステムをM5AtomUで作ってみました。
ただし、通知が毎回届くと迷惑なので、「1日1回だけ通知する」ように制御しています。

実現したいこと

  • M5AtomUが起動したらLINE通知を送る
    • ただし、同じ日に複数回通知しない
  • 通知履歴はEEPROMに保存
  • Wi-Fi接続が失敗したら再接続を試みる

実装のポイント

1. EEPROMに年月日を保存(3バイト)

int today = (timeinfo.tm_year * 10000) + (timeinfo.tm_mon * 100) + timeinfo.tm_mday;
EEPROM.write(0, today & 0xFF);
EEPROM.write(1, (today >> 8) & 0xFF);
EEPROM.write(2, (today >> 16) & 0xFF);
EEPROM.commit();

2. 保存された日付と比較して通知制御

int lastSent = EEPROM.read(0) + (EEPROM.read(1) << 8) + (EEPROM.read(2) << 16);
if (today == lastSent) {
  Serial.println("今日はすでに通知済みです。スキップします。");
  return;
}

3. Wi-Fi接続失敗時に再接続処理(最大3回)

int wifiAttempts = 0;
bool wifiConnected = false;

while (wifiAttempts < 3) {
  WiFi.begin(ssid, password);
  int retry = 0;
  while (WiFi.status() != WL_CONNECTED && retry < 20) {
    delay(500);
    retry++;
  }
  if (WiFi.status() == WL_CONNECTED) {
    wifiConnected = true;
    break;
  }
  wifiAttempts++;
}
if (!wifiConnected) {
  Serial.println("WiFi接続に3回失敗しました。通知は送信されません。");
  return;
}

学びと工夫

  • tm_yearは1900年からの年数なので、2025年なら125となる
  • EEPROMは1バイトずつしか保存できないので、3バイトに分割して保存
  • M5AtomUは2.5GHz接続のみ対応
  • LINE Messaging APIは月200回までメッセージ送信が無料(回数=メッセージ数×送信先数)

おわりに

 家庭用としての作成なため、できるだけコストをかけない「無料枠に収まる範囲」での実装にこだわりました。
 以前まではIFTTTを使ってメールを送信させようとしていましたが、近年有償化していました。(確か、webhookの部分だった気がする。)

 また、筆者はプログラミング超初心者なため、Windows標準搭載のCopilotさんに教えてもらいながら作成しました。この投稿自体も、大体Copilotさんに作ってもらっています。便利になりましたね。
 実生活でも役立つものが作れたので満足です。また何か思いつけば作りたいと思います。

 作ってみたい人に向けて、次の項目でコードを全文載せているので、良かったら使ってください。
*LINE Messaging APIでのAPIトークン作成は別途必要です。
*手練れの方は良きように修正してみてください。

コード全文

#include <EEPROM.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <time.h>

void setup() {
  Serial.begin(115200);
  EEPROM.begin(10);
  
  //Wi-Fi接続情報の設定
  const char* ssid = "SSIDを入力";
  const char* password = "パスワードを入力";

  //LINE Messaging APIのトークン設定
  const char* token = "APIトークンを入力";

  //通知を送信
  int wifiAttempts = 0;
  bool wifiConnected = false;
  while (wifiAttempts < 3) {

   Serial.println("WiFi接続開始" + String(wifiAttempts + 1) + " 回目");
   WiFi.begin(ssid, password);
  
   int retry = 0;
   while (WiFi.status() != WL_CONNECTED && retry < 20)
   {
     delay(500);
     Serial.print(".");
     retry++;
   }

   if (WiFi.status() == WL_CONNECTED)
   {
     wifiConnected = true;
     break;
   }

   Serial.println("\nWiFi接続失敗。再試行します。"); 
   wifiAttempts++;
  }

 if (!wifiConnected) {
   Serial.println("WiFi接続に3回失敗しました。通知は送信されません。"); 
   return;
 }

 Serial.println("\nWiFi接続成功。");

  // NTPで現在の日付を取得
  configTime(9 * 3600, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("時刻取得失敗。通知は送信されません。");
    return;
  }

  int today = (timeinfo.tm_year * 10000) + (timeinfo.tm_mon * 100) + timeinfo.tm_mday;
  int lastSent = EEPROM.read(0) + (EEPROM.read(1) << 8) + (EEPROM.read(2) << 16);
  
  Serial.println("今日の日付コード: " + String(today)); 
  Serial.println("前回通知コード: " + String(lastSent));

  if (today == lastSent) {
    Serial.println("今日はすでに通知済みです。スキップします。");
    return;
  }

  HTTPClient http;
  http.begin("https://api.line.me/v2/bot/message/broadcast");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", String("Bearer ") + token);

  int year = timeinfo.tm_year + 1900;
  int month = timeinfo.tm_mon + 1;
  int day = timeinfo.tm_mday;

  String message = String(year) + "年" + String(month) + "月" + String(day) + "日にテレビをつけました!";
  String payload = "{\"messages\":[{\"type\":\"text\",\"text\":\"" + message + "\"}]}";

  int attempts = 0;
  int httpCode = 0;

  while (attempts < 3){
    httpCode = http.POST(payload);
    Serial.println("通知試行 " + String(attempts + 1) + " 回目 → HTTPコード: " + String(httpCode));

    if (httpCode == 200) {
      Serial.println("通知成功!");
      EEPROM.write(0, today & 0xFF);         // 下位バイト
      EEPROM.write(1, (today >> 8) & 0xFF);  // 中位バイト
      EEPROM.write(2, (today >> 16) & 0xFF);   // 上位バイト
      EEPROM.commit(); //保存確定
      break;
    } else {
      Serial.println("通知失敗。再送します。");
      delay(1000);
    }

    attempts++;
  }
  
if (httpCode != 200) {
    Serial.println("通知は3回試行しましたが失敗しました。");
  }

  http.end();
}

void loop() {

}

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?