設計書 · 2026-09-29
Launch
靴・ベルト・Smart Ring に入れる「アプリ」のしくみです。アプリは開いて使うものではありません。入っていれば、それだけで常に働いています (プラグインに近い考え方)。当てる・通る・届く、という出来事に、入っているアプリが自動で応えます。
公開した画面: Launch (本人のスマホ) ・ 読み取り機 (入り口に置く)
1. しくみ
- 入れる: 本人のスマホの Launch で、アプリの横のスイッチを入れる (顔・指紋で確かめる)。
- 働く: 入り口の読み取り機に靴・ベルト・Smart Ring を当てる → サーバーが「本物のタグか・使い回しでないか・そのアプリが入っているか」を確かめて処理する。
- 知らせる: 結果を本人 (と家族) の iPhone に通知。SIM ありの本体には、光や振動の指示が LTE で届く。
- 本体の決まり: 入れたアプリに合わせた「出来事 → 動作」の表 (最大 12 個・26 バイト) を本体に入れる。SIM なしの本体には画面の点滅で送る (アプリの数で 20〜55 秒)。
| 部分 | どこ |
|---|---|
| サーバー | ~/work/kutsu-belt/site/functions/api/launch/[[route]].js, アプリの一覧 _lib/apps.js |
| 本人のスマホの画面 | /launch/ (ホーム画面に追加して使う) |
| 読み取り機 | /launch/reader.html — Android の Chrome (Web NFC)。iPhone のブラウザは NFC を読めないので読み取り機にはなれない |
| 先生の画面 | /launch/room.html#rid=…&view=… — 見る用の合言葉は # の後ろに置き、サーバーへは本文で送る (URL の記録に残らない)。読み取り機が持つ記録用の合言葉とは別 |
| 同時に来ても壊れないもの | Cloudflare D1 (SQLite) — タグの回数・回数制限・名簿・在室・ロッカー・つけ払い・すれ違い・本体への指示。表は site/schema.sql |
| 本体のプログラム | firmware/launch_rules.c (決まりの表), pass_id.c (すれ違いの ID), optical_rx.c (点滅の長い形式) |
2. アプリ
| アプリ | 使う本体 | できること | 今の状態 |
|---|---|---|---|
| 🚪 入退室 | 靴 (主) ・ Smart Ring ・ ベルト | 教室の入り口の床のマットを歩いて通る (靴) か、読み取り機に当てると入室・退室が交互に記録され、本人と家族のスマホに届く。先生の画面に在室の一覧。生徒は「参加の番号」で教室に参加した時だけ記録される (本人の同意) | 動く (本番で確認) |
| ♨️ 温泉 | Smart Ring ・ 靴 (下足) | 受付でチェックイン → ロッカー番号。下足箱で靴を当てて下足番号。売店でつけ払い。帰りの受付で精算 (合計と下足番号を知らせる)。財布もスマホも持たずに入れる | 動く (手元の試験) |
| 👋 すれ違い | Smart Ring ・ 靴 | 同じアプリを入れた人と近くを通ると、お互いの名刺 (呼び名とひとこと) を交換 | サーバーと ID の計算は動く。Bluetooth の部品が要る (次の版) |
| 💬 S2S | Smart Ring ・ ベルト | S2S にメッセージが届くと Smart Ring が光ってそっと震える | 受け口は動く。S2S 側からの送信はまだつないでいない |
| 🔊 音 | ベルト ・ 靴 | 知らせの音と、体に伝える低音 (8 章) | 部品が要る (次の版) |
3. 入退室は靴で (靴向けの最適化)
入退室の主役は靴です。手に持って当てるのではなく、入り口の床に置いたマットの上を歩くだけで記録されるようにしました。
- 足裏の NFC: 靴底のかかとの真ん中、底から 2.4 mm の高さに、平らに寝かせた NTAG 424 DNA φ25 を左右とも埋め込む。3D プリントを高さ 3.2 mm (くぼみの壁を刷り終えて天井を刷る前)で一時停止してタグを置き、続きを刷る。かかとと外側の壁の中の NFC は縦向きで床からは読みにくいので、別に足した。
- 床の読み取り機 (マット): 250 × 180 × 9 mm。縁は 16 mm のなだらかな坂でつまずかない。上面の厚み 2.5 mm の下に 150 × 140 mm のアンテナ。足裏のタグまで約 6 mm。上面の細い輪 (φ120) が「ここを踏む」の目印。中は空洞 + 40 mm 間隔の骨で、アンテナの下の骨は板が載る高さで止めて上面を支える。印刷用:
floor_reader_mat_PETG.stl,floor_reader_lid_PETG.stl(256 mm 角の造形機に載る)。 - 中のプログラム:
firmware/mat_reader/mat_reader.ino(ESP32 + NFC の読み取り部品)。足が乗ったら NDEF (E104) を読み、URL を取り出してサーバーの/api/launch/tapへ送る。サーバーは手で当てる読み取り機と同じものを使い回す。入室は緑・退室は青・だめなら赤の光と音。 - 両足で 2 回読まれても 1 回: 同じ人の 10 秒以内の読み取りは 1 回にまとめる。左右の靴は別のタグだが、持ち主が同じなのでまとまる。
- 使い回しの URL は受け付けない: 踏むたびにタグの暗号の数字が変わるので、写した URL での記録はできない (前と同じ)。
試していないこと: マットの読み取り距離 (試作の PN532 は出力が小さいので、本番は出力の大きい ST25R3916 と大きなアンテナを推奨)、踏んだ時の強さ、歩く速さで読み切れるか (1 回の読み取りは数十〜百ミリ秒の見込みだが測っていない)。URL を取り出す部分だけは、壊れたデータを含む 5 項目の試験に通した。
4. 代わりに当てられないこと (代返の防止)
- 使い回しの URL は受け付けない: 当てるたびにタグの暗号の回数が増える。サーバーは「前より新しい回数」の時だけ、1 回の操作で確かめて書く (同時に 5 本送っても通るのは 1 本, 試験で確認)。
- 読んだタグの番号と照らす: 読み取り機は NFC で実際に読んだタグの番号 (UID) を一緒に送り、サーバーは URL の暗号の中の UID と一致する時だけ受け付ける。先に読んでおいた URL を、別の場所から貼って送ることはできない。
- 名簿は先生の承認制: 生徒は参加の番号 (8 文字) で申し込み、先生が先生の画面で承認するまで記録しない。同じ教室に同じ名前は 1 人だけ。先生は名前の変更・外すもできる。参加の番号の総当たりは 1 時間に 10 回で止める。
- 合言葉を 2 つに分ける: 読み取り機が持つのは記録用だけ。一覧を見る用と施設の合言葉は、作った時に 1 回だけ見せ、入り口の画面には出さない。
- 防げないこと: 本人の靴そのものを借りて通ること。スマホを NFC のタグのふりをさせて UID まで真似る攻撃は、ふつうのスマホではできない (Android のタグのふりは UID が毎回変わる) が、専用の機器なら可能性がある。
以下は前の版の説明 (大筋は同じ):
タグは当てるたびに暗号の数字が変わる URL を出します (NTAG 424 DNA の SUN)。サーバーは「前より新しい回数か」を確かめるので、URL を写してほかの人のスマホで開いても「使い回しの URL」として記録されません。本番でも、同じ URL を 2 回送ると 2 回目は断られることを確かめました。ただし、本人の靴そのものを借りて当てることは防げません。
5. すれ違いの ID
同じ 2 人の交換は、別々の本体どうしで聞こえても 1 日 1 回。自分の別の本体 (Smart Ring と靴) とは交換しない。上げられるのは今日と前後 1 日だけ。
- 本体は 10 分ごとに変わる 16 バイトの ID を Bluetooth で流し、近くで聞いた ID を記録する。
- ID = HMAC-SHA256(日の鍵, "eph:" + 区切り) の先頭 16 バイト。日の鍵は本体とサーバーだけが知る。ID から持ち主はわからない。
- 本体が上げた「聞いた ID」を、サーバーが参加者全員のその日の ID と照らす。お互いに聞こえた時だけ名刺を交換 (片方だけでは交換しない)。同じ日の同じ相手は 1 回だけ。
- 上げる時は本体の鍵で署名する (偽の上げ方は断る)。
- 照らし合わせはサーバーで行うので、サーバーは「誰と誰がすれ違ったか」を知ります。Bluetooth はこのアプリを入れた時だけ動かす設計です。
6. 本体の決まりの表
1 つの決まり = 2 バイト [出来事 (上 4 ビット) | 動作 (下 4 ビット), 引数]。最大 12 個。光の引数は「色 (上 4 ビット) | 秒 (下 4 ビット)」。転倒の振動は表になくても必ず出る (アプリで消せない)。4 アプリ (入退室・温泉・すれ違い・S2S) を入れた Smart Ring で 10 個・22 バイト。
| 出来事 | 番号 |
|---|---|
| 2 回叩く / 転倒 / すれ違い / 入室 / 退室 / 温泉チェックイン / 温泉チェックアウト / S2S / タイマー / 電池が少ない | 1〜10 |
| 動作 | 番号 |
| 光 / 振動 / 電子ペーパーを次へ / タイマー / 音 | 1〜5 |
7. 確かめたこと
- サーバー: 手元の試験 45 項目 (入退室・温泉・すれ違い・本体への指示・S2S の受け口・Launch の中でのパスキー) がすべて通った。
- 点検 (5 観点 + 指摘ごとに 2 つの目で反証) で本物と判定された 57 件を直し、試験を 60 項目にした (代返の穴・同時の要求・日付をまたぐ入退室・温泉の当て直し・空いているロッカー・すれ違いの自己交換など)。
- 本番: ブラウザで「ベルト → パスキー → このスマホに Launch → 入退室アプリを入れる → 教室に参加 → 靴のタグで入室 → 同じ URL は拒否 → 先生の画面に在室 1 人」が通った (パスキーは開発用の仮想の装置, 通知の購読だけは API を直接呼んで代えた)。
- 本体: 決まりの表の読み込み・壊れた表の拒否、すれ違いの ID が C とサーバーで一致、画面の点滅の長い形式 (18 バイト) が 60 Hz と 120 Hz で 100 回中 100 回受け取れた (模擬)。
8. 音 (ベルト・靴)
Vision Pro は耳のすぐ横に小さなスピーカー (オーディオポッド) を置いて音を届けます。ベルトや靴は耳から 1 m 近く離れているので、同じ置き方はできません。そこで役割を分けます。
- 低音は体で感じる: 振動子 (音の帯域まで動くボイスコイル型) を、ベルトはお腹側・靴は靴底に当てる。音量を上げずに低音を体に伝える (まわりに漏れにくい)。
- 知らせの音は小さなスピーカー: ベルトのベルト通し (金属をやめ樹脂にする) に直径 10 mm 前後のスピーカーを上向きに。声や短い音だけ。
- どの程度聞こえるか・感じるかは試していません。音楽を流すには Bluetooth の部品 (LE Audio) も要ります。
9. S2S を本当につなぐには
Launch 側の受け口 (/api/launch/s2s, 署名つき) はできています。S2S の側で「メッセージが届いた時、つないだ人の番号 (S2S-XXXXXXXX) へ知らせる」処理を足せば、Smart Ring が光ります。s2s.day 本体の変更になるので、まだ手を付けていません。
10. まだのこと
- すれ違いと音の部品 (Bluetooth・振動子・スピーカー) を本体に足す設計と基板。
- 温泉の入浴タイマー (浴室の入口で当てたら N 分後に振動) は、決まりの表には入れたが、サーバーから時間で知らせる仕組みはまだ。
- iPhone の実機で通知が届くか。
- 学校や施設で使う時の、名簿の管理・記録の保存期間・消し方の決まり。