体重計を買ったので、記録する場所を作ることにした
体重計を買いました。オムロンのカラダスキャン KRD-203-W(取扱説明書PDF|オムロン ヘルスケア公式)。体重だけじゃなく体脂肪率・骨格筋率・内臓脂肪レベルまで測ってくれるやつです。
この機種、通信機能は付いていません。承知のうえで買いました。安いしシンプルだし、そもそも「乗るだけで測れる」以上のことを体重計に求めていなかったので。
ちなみにオムロンの体重計は、型番の末尾に「T」が付いているものが通信対応です(KRD-403T、HBF-227T など)。KRD-203 には付いていません。買うときの目安になるので覚えておくと便利です。
根拠は公式の 体重体組成計・体重計 機能比較表|オムロン ヘルスケアストア で、「T」付きの機種にだけ Bluetooth通信機能搭載(「OMRON connect」対応) と書かれています。この表に KRD-203 は載っていません。
で、ここからが本題。測った数字を毎日どこに残すか問題です。
紙のノートは続かない自信があります。市販アプリは山ほどありますが、「体組成4項目を並べて見たい」「健診までのカウントダウンを大きく出したい」「データは自分の手元に置きたい」と欲を出すと、意外と「これ!」がない。
手元にはラズパイが転がっています。作るか。

出た。またそれや。買うより作るんかい
最初に、AIとちょっと揉めた
いつものように相棒のAI(Claude)に相談したところ、開口一番これでした。
BLEで体重計から自動受信して、ラズパイに記録しましょう。まずスキャンから始めます。
いや、うちの体重計それ付いてないって。
指摘したら調べ直して、「KRD-203 は OMRON connect の対応製品リストに載っていない、取説PDFの中身は HBF-236 でこれも非対応」と、根拠つきで訂正してきました。そこはえらい。
でも私が黙っていたら、存在しない機能を前提にコードを書き続けていたはずです。
道具としてはめちゃくちゃ優秀なんですが、「本当に?」と聞くのは人間の仕事だな、と初手から思い知らされました。
というわけで、方針は最初から手入力アプリです。
まあ冷静に考えると、毎日体重計に乗ってる時点ですでに手動作業です。そこから数字を打つ追加コストは10秒。自動化の費用対効果は、実は低い。そう自分に言い聞かせました。
ラズパイ Zero W に載せる(ここが失敗だらけ)
サーバーは Raspberry Pi Zero W(公式製品ページ)。1GHzシングルコア・RAM 512MB という構成で、消費電力が数ワットなので、つけっぱなしでも電気代を気にせずに済みます。
ただこいつ、非力なうえに古いので落とし穴が多い。
64bitのOSは起動しない
Zero W のCPUは ARMv6。Raspberry Pi Imager(公式配布ページ)で 64-bit を焼いてもそもそも起動しません。32-bit の Lite 一択です。
pip install をやってはいけない
これが最大の罠でした。最初は今どきの構成で FastAPI を入れようとしたのですが、ARMv6向けのビルド済みパッケージがほぼ配布されていない。pipがソースからビルドを始め、Rustコンパイラが動き出し、延々待った末にメモリ不足で死にます。
正解は apt。
sudo apt install -y python3-flask
これなら一瞬です。というわけで、このアプリの依存ライブラリは Flask 1個だけという構成になりました。フロントエンドも外部ライブラリゼロ(グラフも自作SVG)です。制約から生まれた設計ですが、結果的にオフラインでも完結する軽いアプリになりました。
SSHを有効にしたのに、繋がらない
Imagerの詳細設定でSSHのチェックはちゃんと入れていました。Wi-Fiもホスト名も設定済み。なのに繋がらない。
ssh: connect to host pi-weight.local port 22: Connection refused
ここで焦らないでほしいのは、Connection refused は悪い知らせばかりではないということです。
- 名前解決には成功している → ラズパイは起動していて、Wi-Fiにも繋がり、mDNSも効いている
- ポート22で能動的に拒否された → SSHサーバーだけが動いていない
つまり本体は元気で、SSHだけが立ち上がっていない状態です。何らかの理由で Imager の設定が効かなかったらしい。
焼き直さなくても直せます。 SDカードをPCに挿して、bootfs パーティションの直下に ssh という空ファイルを置くだけです。
New-Item -ItemType File E:\ssh
エクスプローラーで作るなら、拡張子が付かないよう注意してください(ssh.txt でも動きはしますが)。SDを戻して電源を入れ直したら、あっさり繋がりました。Wi-Fi設定もユーザーアカウントも生きたままです。
scp に C:/... を渡してはいけない
Windowsから転送しようとして怒られました。
# ダメ。scp が「C:」をホスト名だと思い込む
scp -r C:/Users/xxxx/tracker-kit pi@pi-weight.local:~/
# 正解。親フォルダに移動してから相対パスで
cd C:\Users\xxxx
scp -r tracker-kit pi@pi-weight.local:~/

失敗の数が記事の栄養になっとるな
あとは systemd に登録して常時起動にすれば完成です。
できたもの

デザインは「テンションが上がるように」を優先しました。青空と流れる雲の壁紙に、ロケット。おもちゃ箱みたいな空気感です。
画像ファイルは1枚も使っていません。 背景の雲もロケットもアイコンも、全部CSSとSVGで描いています。Zero W に大きな画像を何枚も置きたくなかったのと、オフラインでも完結させたかったので。
毎日の入力欄には前回の数字がヒントで出るようにしました。これが地味に効きます。「昨日より減った」が一瞬でわかるので。
記録するとちょっとだけ紙吹雪が舞います。こういうのは大事です。
「これ、体重以外にも使えるのでは?」
ここまで作って気づきました。
やってることは「毎日数字を入れる」「グラフで眺める」「目標に近づく」の3つだけです。これって体重に限った話じゃない。
- 勉強時間を積み上げて資格試験に向かう
- 走った距離を記録してマラソン大会に向かう
- 家族で歩数を競う
- 血圧を毎朝つけて健診に備える
- 植物の背丈を測って観察日記にする
全部同じ構造です。違うのは「項目名」「単位」「人数」「見た目」だけ。
そこで、汎用ツールに作り直しました。
設計方針:コード生成器にはしない
最初は「質問に答えたらアプリのソースコードを吐き出す generator」を考えました。が、やめました。
生成器にすると、バグを直したときに生成済みのアプリ全部を直して回る羽目になります。10個作ったら10箇所。無理です。
なので、こうしました。
- アプリ本体は1つだけ。全部共通
config.jsonを読んで、中身が変わる- ウィザードは「設定ファイルを書くだけ」
これなら本体を直せば全部直ります。
python wizard.py # 質問に答える → config.json ができる
python app.py # 起動
聞かれるのは5つだけです。
1. アプリの名前 2. 何を記録するか(9種類のプリセットから選ぶ/自分で作る) 3. 誰が記録するか(1〜6人。名前と絵文字) 4. 目標(「健康診断」「大会」「試験日」など呼び方も決められる) 5. 見た目(6種類のテーマ)
答えると設定ファイルが書き出され、テーマに合わせたアイコンまで自動生成されます。

同じ本体から、こういうのが出てきます。


コードは1行も変えていません。 変えたのは config.json だけです。

さっきの体重アプリと同じもんが動いてるとは思えへんな
グラフで盛大にハマった話
ここが一番苦労しました。というか、動いてから間違いに気づいた箇所が多かった。
失敗①:複数人を並べたら、全員まっ平らになった
複数人ぶんの体重を1つのグラフに載せたら、どの線もまっすぐになりました。
当たり前です。70kgの人と50kgの人を同じ縦軸に載せると、軸が45〜75kgに広がります。その中での1〜2kgの変化なんて、線の太さに埋もれます。
かといって縦軸を左右2本にするのは論外。目盛りが違う軸を並べたグラフは、比較しているようで何も比較していません。
解決策は「スタート日からの変化量」に揃えることでした。全員0からスタートして、そこから何kg動いたかを描く。これなら体格差に関係なく「誰が順調か」が読めます。実際の数値はツールチップと表で確認できます。

失敗②:目標が遠いと、グラフが潰れる
目標線を必ず描くようにしたら、目標が現在値から離れている人のグラフが潰れました。目標に軸を合わせにいくせいです。
なので「データの幅の1.5倍を超えて離れている目標線は描かない」というルールにして、そのことをグラフの下に書くようにしました。
失敗③:軸の数字が枠外に消えた
歩数アプリを作ったとき、縦軸の「10000」が左端で切れて表示されませんでした。左の余白を固定値で決めていたからです。
目盛りラベルの文字数から余白を計算するように直しました。体重(4文字)では気づけない、桁数の大きい用途を作って初めて出たバグです。
失敗④:目標線が4本並んで、何も見えない
4人ぶんの目標線を全部引いたら、破線だらけで実データが読めなくなりました。3人以上のときは選択中の人の目標だけを引くようにしました。
色は「目で選ばない」ことにした
これが今回いちばん面白かった部分です。
人ごとに色を割り当てるわけですが、「なんとなくキレイな色」で選ぶと事故ります。
赤と緑って、P型・D型の色覚では、ほとんど同じ色に見えます。先天赤緑色覚異常の頻度は日本人男性の約5%(20人に1人)、女性の約0.2%とされていて(公益社団法人 日本眼科医会)、わりとありふれた特性です。
なので、色を計算で検証するツールを別に作りました。

やっていることはこうです。
- 明度バンド/彩度フロア — 明るすぎ・暗すぎ・灰色に見える色を弾く
- CVD分離 — P型・D型の見え方をシミュレートして、色同士の距離を測る
- 通常視分離 — 色覚正常でも隣り合う色が紛らわしくないか
- 背景との対比 — WCAG 2.1(W3C勧告) のコントラスト比
これがすごく効きました。
テーマごとに「1人目の色が背景に合うように」色の並び順を変えたかったのですが、気分で並べ替えたら赤と緑が隣り合って、D型色覚での距離が ΔE 1.5 になりました。ほぼ同じ色です。目で見てる分にはまったく気づきません。
慌てて全720通りの並び順を総当たりして、6テーマぶんの「合格する並び」を探し直しました。
もちろん色だけに頼らない工夫も入れています。グラフの右端には必ず名前を直接書くし、凡例も出すし、下には全データの表もあります。色が見分けられなくても情報は届きます。

見た目の話やと思とったら、急に理系の話になったな
今回は「家の中だけ」で運用します
さて、ここで悩みどころがあります。外出先からも使えるようにするかです。
結論から言うと、今回はローカル(家庭内LAN)だけにしました。http://pi-weight.local:8080 を家のWi-Fiから開く運用です。
理由は3つあります。
1. 使うのは家の中だけ。体重計は家にあるので、測ったその場で入力すれば足りる 2. Zero W に余計な常駐を増やしたくない。ただでさえ非力なので 3. まず毎日続くかを確かめたい。続かなかったら外部公開する意味がない
ただし、ローカル運用には1つだけ明確な制約があります。
http:// だとオフライン機能が死ぬ
このアプリは PWA なので、スマホのホーム画面に置けます。ホーム画面から開けばアドレスバーなしの全画面になり、見た目はほぼアプリです。
でもオフラインでは開けません。
ブラウザは HTTPS か localhost でしか Service Worker を登録しないという決まり(MDN: 安全なコンテキスト)になっているためです。オフラインキャッシュを担当するのがこの Service Worker なので、http://192.168.x.x や http://pi-weight.local では登録が拒否され、キャッシュが働きません。
家のWi-Fiに繋がっている限りは普通に使えます。困るのは「圏外」「ラズパイの電源が落ちている」ときだけ。今回の使い方なら許容範囲です。
外に出したくなったら Tailscale
将来この制約を外したくなったときのために、調べた内容を残しておきます。
普通に外部公開しようとすると、ルーターのポート開放・DDNS・証明書の取得と更新……と面倒が一気に増えます。しかも家庭内のラズパイをインターネットに直接晒すことになるので、正直やりたくない。
そこで Tailscale です。これは自分のデバイス同士だけをつなぐVPN(メッシュVPN)で、ポート開放が一切要りません。ラズパイ側から外向きに接続を張るので、ルーターの設定を触らずに済みます。
導入はこれだけ。
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# 表示されたURLをブラウザで開いてログイン
スマホ側にも同じアカウントで Tailscale アプリを入れると、その時点でラズパイに繋がります。この状態でも既に外出先から使えます(ただしまだ http:// なのでオフラインは不可)。
うれしいのはここからで、tailscale serve を使うと正規の証明書付きHTTPSがタダで手に入ります。
sudo tailscale serve --bg 8080
# → https://pi-weight.<tailnet名>.ts.net で見られるようになる
管理コンソールで HTTPS(MagicDNS + 証明書)を有効にしておく必要はありますが、証明書の取得も更新も Tailscale がやってくれます。自分でcertbotを回す必要はありません。
これで得られるものを整理すると、
- 外出先からアクセスできる(ポート開放なし)
- 正規のHTTPSになる → Service Worker が登録できる → オフラインでも開ける
- ラズパイをインターネットに晒さない(自分のデバイスからしか見えない)
注意点も書いておきます。
- Zero W には少し重い。常駐で数十MBのRAMと、それなりのCPUを持っていかれます。動作報告は多いですが、もたつくようなら Zero 2 W に載せ替えたほうが早い
- 家族全員のスマホに Tailscale を入れる必要がある。家族で使うアプリだと、ここが地味にハードル
- 無料枠で足りる規模の話です(個人利用なら十分)
つまり「オフラインでも開きたい」「外からも入力したい」が出てきたら Tailscale、それまでは素の http:// で十分、という判断です。

「今はいらん」って言い切れるのも設計やな
まとめ
- オムロンの体重計は型番末尾の「T」が通信対応の目印。KRD-203 は非対応(承知で購入)
- AIは平気で「付いてます」と言う。本当か聞くのは人間の仕事
- Zero W では 64bitは起動しない・pipは地獄・aptが正解
Connection refusedはラズパイが生きている証拠。bootfsにsshを置けば焼き直し不要- 「毎日数字を入れて、グラフで眺めて、目標に近づく」は用途が違っても同じ構造
- なので設定ファイル1つで中身が変わる作りにした
- 色は目で選ばず、計算で検証する。赤と緑は思っているより危ない
- 今回はローカル運用。外に出すなら Tailscale で HTTPS ごと解決できる
次回は「#2 ~外から使えるようにする編~」。実際に Tailscale を Zero W に入れて、どれくらい重くなるのか、オフラインPWAはちゃんと動くのかを試す予定です(そして例によって、どこかで詰まると思います)。
以上、ありがとうございました。
参考リンク(出典)
製品・ハードウェア
- 体重体組成計 KRD-203 取扱説明書(PDF)|オムロン ヘルスケア
- 体重体組成計・体重計 機能比較表|オムロン ヘルスケアストア — 「T」付き機種のみ Bluetooth(OMRON connect)対応
- Raspberry Pi Zero W 製品ページ|Raspberry Pi 公式
- Raspberry Pi OS / Raspberry Pi Imager|Raspberry Pi 公式
ソフトウェア
データ・規格
- 色覚異常は遺伝なの?|公益社団法人 日本眼科医会 — 先天赤緑色覚異常の頻度(男性約5%・女性約0.2%)
- WCAG 2.1 — Contrast (Minimum)|W3C
- 安全なコンテキスト(Secure Contexts)|MDN — Service Worker が HTTPS / localhost を要求する理由

コメント