尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
電気通信大学のU☆POCに向けたデバイスの開発
翻译准备中
正在加载完整 README。
查看完整 README ↗電気通信大学 U☆PoC(ユーザ志向アイディア実証コンテスト)応募作品。
生成AIで人格を持つIoTプランター。土壌湿度・温度・照度・空気湿度のセンサーデータをもとに、植物が擬人化キャラクターとして LINE で自発的に話しかけ、こちらの返信にも応える育成支援システム。単なる数値の可視化ではなく「感情の可視化」を目指す。
docs/proposal/PlanterAI.tex → PlanterAI.pdf6ページ構成の公式サイトは site/
にある。Viteでローカル起動と静的ビルドを行い、ルートの vercel.json
からVercelへデプロイする。
公開URL:https://planterai.vercel.app/
cd site
npm ci
npm run dev
本番用の静的ファイルは npm run build で site/dist/
に生成される。node_modules/ と dist/ は生成物のためGitでは管理しない。
[土壌湿度/温度/照度/湿度センサー]
│
[ESP32] --HTTP POST (x-device-key)--> [Edge Function: ingest-sensor]
│
┌──────────────────────────────────┼───────────────┐
▼ ▼ ▼
[Supabase DB] [Gemini Flash] 状態遷移時のみ
(センサー/感情/会話ログ) (セリフ生成) │
▼
[LINE] ──ユーザーの返信──> [Edge Function: line-webhook] ──> [LINE Messaging API] ──> ユーザー
▲
[Edge Function: weekly-summary]
(pg_cron で週次の関係性サマリー生成)
サーバーサイドは Supabase Edge Functions(Deno / TypeScript) で完結している。
センサー値からセリフ生成までを3層に分け、LLM にはセリフ生成のみを担わせることでハルシネーションを抑える。
normalize.ts)—
直近5サンプルの中央値フィルタでノイズを除去し、植物プロファイルの快適レンジに照らして
0〜100 の快適スコアへ変換。emotionEngine.ts / emotionTable.ts)—
スコアの緊急度と継続時間から、ルールベースで感情(満足/軽い不満/不満/不安/苛立ち)と主訴を判定。日照不足は瞬時値では誤発火するため、積算光量による日次判定(dailyLight.ts
/ dailyLightJob.ts、1日最大2回)に分離。llm.ts)— 状態が遷移したときだけ Gemini Flash
でキャラ口調のセリフを生成し LINE
通知。生成失敗時はテンプレ(fallback.ts)にフォールバックして通知を落とさない。通知の抑制:境界付近での状態の往復(チャタリング)による LINE
乱発を防ぐため、直近通知から
NOTIFY_COOLDOWN_MINUTES(既定60分)は原則再通知しない。不調を伝えたあとの別の不調(悪化していないもの)は3倍の180分あける。満足に戻ったときと危険域(urgency=high)はクールダウンを無視して即通知する(shouldNotify・cooldownFor)。
site/ Vite製の公式Webサイト
public/images/ Webサイトで使う画像
src/ JavaScriptとCSS
*.html 6ページのHTMLエントリ
supabase/ Edge FunctionsとDBマイグレーション
functions/_shared/ 状態判定・LLM・LINEなどの共有処理
functions/*/ ingest-sensor、line-webhook、weekly-summary
migrations/ PostgreSQLスキーマ
tests/ 状態判定・通知・日照判定・入力検証・LLMのテスト
docs/
proposal/ U☆PoC提案書と図版
slides/ ポスターとプレゼン資料
qr/ 公開サイトのQRコード
hardware/models/ 3Dプリント用モデル
archive/legacy-site/ 旧シングルページLP
_shared/config.ts):植物種ごとの快適/注意/危険レンジ。現在は
pothos(ポトス)と calathea(カラテア)を定義。devices.plant_profile
で切り替える。照度は非対称に扱い、瞬時値 lux で「日照過剰(葉焼け)」を、積算
lux·h で「日照不足」を判定する。_shared/weather.ts・_shared/dailyWeather.ts):WEATHER_LATITUDE・WEATHER_LONGITUDE
を入れると、Open-Meteo(鍵は要らない)で今日の天気を見る。日が差した時間が日の長さの半分に届かない日(曇りや雨)は、14時台の予告を出さず、20時も置き場所を責めずに「明日は晴れるといいね」と言う。予報は0時と12時に取って
daily_weather
に覚え、取りそこねたら前の予報を使う。今日の分が1度も取れていなければ送るのを次の回に回す(締めは21時まで)。入れなければ天気を見ずに判定する。家の場所は細かく入れず、市くらいの粗さ(小数1〜2桁)で足りる_shared/llm.ts):amaenbo(甘えん坊)/tsundere(ツンデレ)/keigo(執事風)。devices.character_id
で切り替える。_shared/config.ts):SOIL_CALIBRATION に
SEN0193 の実測 ADC 値(空気中/水中)を設定。| レイヤー | 技術 |
|---|---|
| マイコン | ESP32 + 土壌湿度・温度・照度・湿度センサー |
| サーバー | Supabase Edge Functions(Deno / TypeScript) |
| LLM | Gemini Flash |
| 通知 | LINE Messaging API(プッシュ通知 + Webhook双方向チャット) |
| DB | Supabase(PostgreSQL、RLS全拒否 + service_roleでアクセス) |
| バッチ | pg_cron(週次サマリー) |
前提:Supabase CLI、Node.js(テスト用)、LINE公式アカウント、Gemini API キー。
# 1. DBスキーマを反映
supabase db push
# 2. Edge Functions をデプロイ
# ingest-sensor は ESP32 が JWT を持たないため --no-verify-jwt で公開し、
# x-device-key による自前認証を使う
supabase functions deploy ingest-sensor --no-verify-jwt
supabase functions deploy line-webhook --no-verify-jwt
supabase functions deploy weekly-summary --no-verify-jwt
supabase functions deploy daily-diary --no-verify-jwt
# 3. シークレットを登録
supabase secrets set \
GEMINI_API_KEY=... \
LINE_CHANNEL_ACCESS_TOKEN=... \
LINE_CHANNEL_SECRET=... \
CRON_SECRET=... WEATHER_LATITUDE=35.66 WEATHER_LONGITUDE=139.54
# WEATHER_* は日照不足を天気のせいにするかの判断に使う(なくても動く)
# (SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY は自動で注入される)
_sharedを変更したら、それを import している function を全部再デプロイする。 Supabase は_sharedを function ごとにバンドルするため、共有ライブラリのように自動追随しない。 片方だけ古い_sharedを抱えたままになり、同じコードのはずなのに挙動が割れる (実例は「PlanterAI 定型文フォールバック多発 修正記録(2026-09-02)」)。
Supabase ダッシュボードの SQL Editor で実行する(値は書き換える)。
insert into devices (device_key, plant_name, character_id, line_user_id)
values ('ここに長いランダム文字列', 'ポト助', 'amaenbo', 'あなたのLINE User ID');
LINE User ID は LINE Developers
の「チャネル基本設定」→「あなたのユーザーID」(U で始まる文字列)。
LINE公式アカウントを自分の LINE で友だち追加しておかないと通知が届かない。
鉢の line_user_id には、1対1のユーザー ID のかわりに、LINE グループの ID (C
で始まる)も入れられる。グループに入れると、通知も週の日記もそのグループに届き、家族みんなが見られる(ADR
0012)。
line-webhook)に出る「LINE のグループに招待されました:
C…」から、グループの ID を読む(無料プランのログは1日で消える)update devices set line_user_id = 'グループの ID' where id = '鉢の id';
グループでは、植物の名前を呼んだときか、公式アカウントにメンションしたときだけ返事をする(「カラ美、元気?」「@PlanterAI 元気?」)。家族どうしの会話に毎回割り込まないため。1つのトークに複数の鉢を結ぶと、名前を呼ばれた鉢が答える。
以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗