miyoshitan’s blog

岐阜発。自動車サービス現場とPython・Flask・Raspberry Pi・M5Stackの実践記録。

AI・IoT・個人開発の実験ログと考察をまとめる技術ブログ

ラズパイとM5Stack Cardputerで「猫の水交換リマインダー」を作った話

 


ラズパイとM5Stack Cardputerで「猫の水交換リマインダー」を作った話

自宅の猫の水交換をよく忘れるので、「前回交換日から4日後に、M5Stack Cardputerがビープ音&メッセージで教えてくれる仕組み」を、Raspberry PiとCardputerの連携で作りました。

やりたいこと

  • 前回の水交換日:2026/03/13

  • 次回交換日:4日後の 2026/03/17 07:00

  • その日時になったら

    • Cardputerがビープ音で知らせる

    • 画面に「猫の水を交換して」というメッセージと日時を表示

  • 飼い主が「交換したよボタン(BtnA)」を押した瞬間に、次の4日後を自動で再設定していく

Raspberry Piは24時間稼働の前提ですが、「毎回4日後を計算して送り続ける」のではなく、最初の1回だけターゲット日時を送るだけにして、あとはCardputer側だけで4日ごとに回す構成にしました。


全体構成

役割分担

  • Raspberry Pi

    • 正確な時刻を持っているので、「前回交換日から4日後」の日時を計算。

    • CardputerにHTTP GETで「ターゲットUNIX時刻(ts)」を1回送る。

    • テスト用に「今から60秒後」を送るスクリプトも用意。

  • M5Stack Cardputer(無印)

    • Wi‑Fiで自宅ルーターに接続。

    • NTPで現在時刻を取得し、UNIX時刻として保持。

    • HTTPサーバを立てて /set?ts=... を受信し、次回アラームのUNIX時刻 targetTs を保存。

    • 現在時刻と targetTs を比較して、時間になったらビープ&メッセージ表示。

    • BtnAが押された瞬間に「交換完了」とみなし、その時刻から+4日を計算して次回 targetTs に設定。

    • その次回日時も画面に表示。


Cardputer側:Arduinoスケッチ概要

1. Wi‑Fi接続とIP確認

  • ssidpassword に自宅Wi‑Fiの情報を直書き。

  • WiFi.begin() して、WiFi.status()WL_CONNECTED になるまで待機。

  • 接続完了したら

    • シリアルモニタに WiFi connectedIP: 192.168.0.x を出力

    • 画面にも同じIPを表示

このIPアドレスを、あとでラズパイ側PythonのURLに使います。

2. NTPで現在時刻取得

  • configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); でNTP設定(JSTならUTC+9時間)。

  • getLocalTime()struct tm を取得し、mktime() でUNIX時刻(秒)に変換。

  • この nowTstargetTs を比較してアラーム判定を行います。

3. HTTPサーバで /set?ts=... を受け取る

  • WebServer server(80); でHTTPサーバを起動。

  • /set ハンドラで server.arg("ts") を取得し、targetTstoInt() で代入。

  • targetSet = true にして、画面を「Next change at: …」表示に更新。

4. アラームとボタン処理

  • ループ内で

    • if (targetSet && !alarmActive && nowTs >= targetTs) → アラーム状態に移行。

    • アラーム状態では

      • 画面に「!! ALERT !! 猫の水交換」などを大きく表示。

      • 数秒おきにビープ音(Speaker.tone() の簡単な2音パターン)。

    • BtnAが押されたら

      • アラーム解除。

      • nextTs = nowTs + FOUR_DAYS_SEC で4日後を計算。

      • targetTs = nextTs に保存。

      • 画面に「Done! 次回は yyyy/mm/dd hh:mm」と表示。

5. 4日後の秒数定義

cpp
const long FOUR_DAYS_SEC = 4L * 24L * 60L * 60L; // 4日 = 345600秒

テスト時はここを 60 に変えて、BtnA押下後に「次の1分後」を確認することもできます。


Raspberry Pi側:Pythonスクリプト

1. 本番用(前回交換日から4日後を送る)

python
#!/usr/bin/env python3 from datetime import datetime, timedelta import requests import sys # Cardputer のURL(IPを指定) CARDPUTER_URL = "http://192.168.0.8/set" # CardputerのIPに合わせる # 前回交換日時 LAST_CHANGE = datetime(2026, 3, 13, 7, 0, 0) def main(): # 4日後の 2026/03/17 07:00 を計算 target_dt = LAST_CHANGE + timedelta(days=4) target_ts = int(target_dt.timestamp()) print("Last change :", LAST_CHANGE) print("Next change :", target_dt, "UNIX:", target_ts) try: r = requests.get(CARDPUTER_URL, params={"ts": target_ts}, timeout=5) print("Status:", r.status_code) print("Response:", r.text) except Exception as e: print("Error sending to Cardputer:", e, file=sys.stderr) if __name__ == "__main__": main()
  • これを set_next_change.py として保存。

  • Cardputer起動後にラズパイから python3 set_next_change.py を1回実行すると、Card側の表示が「Next change at: 2026/03/17 07:00」に変わります。

2. テスト用(今から60秒後を送る)

python
#!/usr/bin/env python3 from datetime import datetime, timedelta import requests CARDPUTER_URL = "http://192.168.0.8/set" def main(): now = datetime.now() target_dt = now + timedelta(seconds=60) # 今から60秒後 target_ts = int(target_dt.timestamp()) print("Now :", now) print("Next change:", target_dt, "UNIX:", target_ts) r = requests.get(CARDPUTER_URL, params={"ts": target_ts}, timeout=5) print("Status:", r.status_code) print("Response:", r.text) if __name__ == "__main__": main()

テストの流れ:

  1. Cardputerを起動してWi‑Fi接続&NTP完了。

  2. ラズパイでこのテスト用スクリプトを実行。

  3. Card側の表示が「Next change at: (1分後)」に変わる。

  4. 約1分後にビープ&アラート画面。

  5. BtnAを押すと「Done! 次回は(今+4日)」が表示される。


実際に動かしてみた感想

  • 「前回交換日から4日後」の計算はラズパイ側に任せつつ、4日ごとのループはCardputerだけで完結する設計にしたおかげで、ラズパイ側はほぼ放置で済む構成になった。

  • Cardputerをデスクに置いておけば、時間になったときに音と画面でしっかり存在感を出してくれるので、「あ、猫の水換えなきゃ」がかなり防げそう。

  • テスト用に「今から60秒後」を送るスクリプトを用意しておくと、スケッチやメッセージを変更したときの動作確認がすごく楽だった。


これからやりたい拡張

  • 画面メッセージを日本語にして、もっとわかりやすくする。

  • WAV音声を再生して「猫の水交換の時間です」と喋らせる。

  • ラズパイ側で「前回交換日」をWeb UIから変更できるようにして、記録も残す。