honeylab's blog

各種ハードウェアの改造、主にファミコンミニなどをやってます(ました)

ウォーターサーバーを導入したので、ジャンクの体重計をばらして合体させてスマートウォーターサーバーにした

タイトルで全部言っちゃったので、流れを書きます。

 

先日から、嫁がウォーターサーバーを導入したい、ただし、ボトルのやつはダメ。
水道水入れるやつがいい。と言っていて、私はそれなら給茶機でいいやん、水道は俺がつなぐし、と言っていたのですが、なかったことにされ、ぼちぼち契約したいな、という話をしているタイミングでコーナンに行ったところ、運よくウォーターサーバーの代理店に捕獲され、ネットよりも若干安い価格で契約しました。

 

俺の欲しかったもの

 

届いたウォーターサーバ

画像

このウォーターサーバー、月3000円のレンタルで、年に一回のフィルター交換代が含まれ、あとは水道から汲んだ水をサーバーの頭の上から流し込んでおけば、フィルターを経由した冷水(6~10度)、温水(95度・再沸騰可能)、常温水が出せるというものです。なんだか最近息子の友達がよく遊びに来たり、娘も小学校にあがったり、ということもあり、水分補給を自発的にしてもらった方が楽だし、私も血圧の関係で、水分はしっかりとるようにと言われていたり、嫁も、体をよくするために水をたくさん飲んだ方がいいのだ、しかしペットボトルではたいへんだし、実際そんなに飲めないのだ、というので、個人の水分の補給量が観測できる機能があったら便利です。

が、そんなものは当然存在していないので作ることにします。

知り合いが夜逃げするときに回収してきた残置物の体脂肪計を分解します。この体重計、なんかスマホと連動して体重を送ったりする機能があるんですが、これはそのままでは使えないんです。

 

ウォーターサーバの下にこいつを置いて、水の増減を計測すればいいですね。体重計としての動作としては、最初の電源投入時に0点調整して、体重を測り終わったらそれで電源が切れてしまいます。

余談ですが、分解している最中に、体脂肪計に見せかけて体脂肪率を測ることができない仕組みだったことを発見してしまいましたw

 

しかし、体重を測るための部品、ロードセルはきっちりついています。

先述の通り、マイコンはそのままでは使えません。もったいないので外して、
amazonで売っている、ロードセルを繋いで重量を測定するための基板を購入します。



基板にロードセルが4つついてきてしまいますが、気にしないことにします。
っていうか、このロードセルで、きっちり重さをはかる仕組みを作るのは結構難しいのです。
体重計はその仕組みが完全に実装されていますから、これを分解するのが手っ取り早いのです。

 

配線がごちゃごちゃしています。元の基板では、直接CoB(Chip on board)、基板にマイコンのダイが直接実装されて樹脂で封入されているため、ロードセルからの配線を読み取ることができませんので、例のごとくChatGPTに聞いてみます。

自信満々で答えてくれました。

が、全然反応しません。

ちょっと疑いながら確かめてるんですが、自信満々です。

やっぱり反応しません。

改めて調べてみましたが、全力で間違っていたようで自分で調べました。

重量計_HX711-ハーフブリッジロードセル4個 | ものづくりとプログラミング日記

こちらのWebサイトに載っていた配線で接続したらさっくり動作しました。

通常のロードセルを体重計として使うライブラリでは、電源投入時にtare()関数でゼロ点を補正し、そこからの差分と、事前に構成した係数をかけて体重として取り出すようになっています。

しかし、今回はウォーターサーバを乗せっぱなしにしますので、少し使い方を変えます。
ライブラリの内部で使用する係数などは使用せず、単純に生値を保管し、差分で水が飲まれたのか、給水されたかを判定することにします。

ロードセルADC基板HX711とESP32を接続し、連続計測させます。



Webサーバを立ち上げ、グラフ化してみると、水の出し入れやコップを置いたことなど、しっかり値が取れていることが判りました。

画像

これをもとにアルゴリズムを考えながらCursor AIに実装してもらいます。

 

実装が終わった状態で、サマリーをまとめてもらいました

実装されているアルゴリズムの要点だけを整理します。

1. 計測と換算

  • HX711を 1 秒ごとに平均し、1 秒 1 点の時系列にする(バケット集計)。
  • 内部は raw のまま保持し、表示・Slack・CSV の g は 2 点校正kRawAt0g / kRawAt600g)で線形変換 rawToGrams
  • 履歴: UI 用に直近 60 秒、解析用に最大 3600 秒のリングバッファ。

2. 安定/変動の検出(ライブと /analyze で同じ考え方)

  • 安定の定義: 直近 win 秒(デフォルト 5)の max − min ≤ thr(デフォルト raw 500)なら「その秒は安定」。
  • 定区間の開始: 上記が hold 秒連(デフォルト 2)で初めて安定に入る。
  • 状態機械SeekingStable → InStable →(揺れたら)InUnstable → 再び連続安定で InStable へ。
  • 不安定中はその区間の peak の max/min を追跡。再安定時、前の安定平均との差が mthr(デフォルト 200 raw)より大きけれ本当の遷移として peak と新 stable_start差が mthr 以下なら同じ安定の揺れとみなし stable_resume(ピーク扱いしない)。
ライブ側は kLiveThr/kLiveWin/kLiveHold/kLiveMthr で上記と揃え/analyze はクエリで thr, mthr, win, hold, sec を変えられる。

3. /analyze 専用の後処理

  • 上記で切り出した安定区間の列に対し、隣り合う区間の平均の差が mthr 以下なら 1 つの安定区間に結合(間のピークは捨てるイメージ)。結合後の平均は 秒数で重み付け
  • ピーク一覧は、結合後の隣接安定区間のあいだの区間について max/min と位置を出す。

4. イベントログ(LittleFS CSV)

  • stable_start / stable_end / stable_resume / peak などを logEvent で記録。
  • ファイルは 日付 events_YYYYMMDD.csv、NTP 未同期時は events_unsynced.csv。行は epoch, local, type, raw, g, extra

5. Slack 通知(安定が変わったとき)

  • stable_start のたびに maybeTweetOnStableChange(安定時の g) を呼ぶ(起動後最初の安定は baseline 確保のみで return)
  • クールダウン 10 分(成功 POST 後に更新)。
  • 変化量 dg = 現在の安定 g − 前回通知用 baseline g
  • |dg| < 50 g → 通知しない。
  • 0 < dg ≤ 500 g → 増加なし扱い(Slack なし、baseline は更新しない)。次の減少は「増える前」の重さからの差分になる。
  • それ以外で POST 成功時だけ baseline とクールダウンを更新。
  • 文面は 減少=汲み取り ml増加=補充 mlり容量 ≈ (g − 20000) / 1000 ℓ(下限 0)。

6. 起動時

  • 可能なら 起動計測(tare 後の平均)で残量付きの Slack /startup。重量計未準備なら文言のみ。
  • 中継は Windows 側 POST /startup と POST /tweet、任意 X-Auth でトークン一致。

7. その他インフラ

  • NTP(JST)、OTAHTTP//data/series/analyze/time/ntp/resync, CSV 系)で状態確認・解析・ファイル取得。
以上が、いまのファームウェア側の「アルゴリズムの骨格」です
 

抽象的な、水の増減に対する認識の部分をまとめて

 
 

水の増減をどう「認識しているか」(抽象)

1. 見ているのは「瞬間値」ではなく「落ち着いた水位」

連続する重さのなかから、ばらく揺れが小さい状態を「今の水量の代表」とみなす。
揺れが大きくなったあと、また落ち着いたときに 前の落ち着きとの差 で「増えた/減った」を考えるイメージに近い。

2. 「同じ水量のブレ」と「本当に変わった」の切り分け

前の落ち着きと次の落ち着きの重さが ぼ同じなら、間の揺れは 汲み・補充ではなくノイズや振動 とみなし、イベントにしない
逆に、落ち着き同士の差が十分大きいときだけ、一度だけ「変化があった」と認識る。

3. 減った=汲み、増えた=補充(の語り)

落ち着き同士を比べて 軽くなったを「誰かが汲んだ量(ml 表現)」として報告し、重くなった分「水が増えた(補充など)」として報告する。
ずれも 1 g ≒ 1 ml として語る

4. 小さな変化は「無視」か「なかったこと」

  • とても小さい変化(閾値未満)は、増減があったとは扱わない。
  • さな増加だけは、計算上の「基準となる水量」を更新せず 増えなかったのと同じにする。
その結果、あとから本当に減ったときの 汲んだ量は、あの小さな増の前の基準からの差になる。

5. 残量の言い方

表示・通知の「残り容量」は、計測した重さから あらかじめ決めた空器相当(例: 20 kg)を引いたあと、リットルに換算した およその目安として語る(負にならないよう下限だけかける)。

 

このような実装で、ウォーターサーバへの水の出し入れが、見事に可視化できました。

あとは個人の識別ですね。

水を出した後、個人登録と称して自分でボタンを押す、という実装が確実ですがさすがにそれはカッコ悪いので、できればカメラを設置し、その映像から個人を識別したいと思っています。何せ、ウォーターサーバの前に立つので、顔を見ることは容易なはずです。ちょっと忙しいので、それはまた仕事が煮詰まったときにやっていきたいと思います。

とりあえず、水の出し入れが可視化できて、

Slackに投稿できるようになったのでかなり満足ですw