人間に水を飲ませる技術

妻が水を飲まない。健康のために水を飲むと自ら宣言したのに飲まない1。毎日「今日はこれを飲む」と言って 2L のペットボトルを仕事部屋に持っていくのに 500ml も飲んでいない。https://twitter.com/mtkasr/status/1384856756963135488 を見てぴよログで水を飲んだことを記録しはじめたがそれも 3 日で終わった。

f:id:kirikiriyamama:20210518234835p:plain
リマインドするが無力

常に「今日も水を飲めなかった…」とか言ってるのでとにかく記録させるために物理的なボタンを用意することにした。それで機械にリマインドさせたい。


できたもの。

用意したボタン。

www.switch-science.com

仕組み。

  • M5StickC のボタンを押すと GAS の Web Apps にリクエストが飛ぶ
  • Web Apps で Spreadsheets に水を飲んだことを記録する
  • 水を飲んでいなかったら GAS の cron で Slack にリマインドする

あと、一日のおわりにその日飲んだ量を Slack に流している。Spreadsheets で集計しているんだけど、普段あんまり Spreadsheets 使わないからこれが一番むずかしかった2

M5StickC は Arduino (だいたい C 言語) で実装した。久しぶりの C 言語は文字列をフォーマットするだけでわあ大変ってなった。これが初期のコード。

#include <M5StickC.h>

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

const char* ssid = "xxxxx";
const char* passphrase = "xxxxx";

const char* url = "https://example.com/";

void setup() {
  M5.begin();

  Serial.begin(9600);
  delay(1000); // wait for serial initialization
  Serial.print("\n");

  M5.Axp.ScreenBreath(10);
  M5.Lcd.setRotation(1);
  M5.Lcd.setTextSize(1);
  M5.Lcd.fillScreen(BLACK);

  M5.Lcd.print("connecting...");
  WiFi.begin(ssid, passphrase);
  // TODO: timeout
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  M5.Lcd.println("ok");
  Serial.println(WiFi.localIP());
}

void loop() {
  M5.update();

  if (M5.BtnA.wasReleased()) {
    M5.Lcd.print("sending...");

    HTTPClient http;

    http.begin(url);
    http.addHeader("Content-Type", "application/json");
    int code = http.POST("{}");
    Serial.println(http.getString());

    // TODO: follow redirects
    if (code == 302) {
      M5.Lcd.println("ok");
    } else {
      M5.Lcd.println("failed");

      if (code > 0) {
        M5.Lcd.println(code);
      } else {
        M5.Lcd.println(http.errorToString(code).c_str());
      }
    }

    http.end();
  }

  delay(1);
}

周辺の技術がわかっていないのもあり、いまいち一次情報を見つけられなくて苦労した。以下のサイトは日本語で情報がまとまっていてとてもお世話になった。

あと M5StickC で調べるとそんなに情報が出てこないんだけど、中身としては ESP32 という SoC の上で FreeRTOS が動いているので、そのあたりの単語をつかって調べるとよかった。

このあと余計なことをしだして、バッテリー節約のために一定時間操作がなかったら Wi-Fi を切断するとか、送信に失敗したら LED を点滅させる (これを非同期でやりたい、愚直にやると操作をブロックしてしまう) とかやっていたら割り込みや排他処理と向き合わないといけなくなって大変だった。ボタンが手元にないときのために Slack の Slash Commands もつくって、最終的にこうなった3

https://gist.github.com/kirikiriyamama/79c47a74e861e6a16d1b4106c70ec937

これでわりと水を飲むようにはなったんだけど、お昼ごはんのときにいっぱい飲んだり、一日の終わりに駆け込んでむりやり目標を達成したりしていて、そういうことじゃない気がしている。がんばってほしい。


  1. よく知らないけどいっぱい飲むといいらしい

  2. ARRAYFORMULA を使っているせいで GAS で行挿入するコードがむずかしい、もう少しうまいやり方がある気がしている

  3. いま気づいたんだけどラクダとヘビがめちゃくちゃなことになっている