Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: Deploy GitHub Pages
# site. Refreshed on doc edits, on each release publish, and on demand.
on:
push:
branches: [main]
branches: [main, gemini]
paths:
- 'docs/**'
# tools/settings.html is the canonical source; pages.yml copies it
Expand All @@ -15,6 +15,9 @@ on:
# <script src> — deployed next to it (and inlined into the firmware's
# settings_wifi.html at build time).
- 'tools/settings_common.js'
- 'tools/gemini_ui.js'
# Complete English build-time translation catalog and copyeditor rules.
- 'tools/settings_i18n/**'
# The avatar preview module (avatar_module.js) and standalone
# avatar.html are built from these on every deploy; rebuild when the
# source, the VM, the JS DSL compiler or the default DSL changes.
Expand Down Expand Up @@ -93,6 +96,19 @@ jobs:
# Shared settings-page helpers — settings.html references this as
# a sibling <script src="settings_common.js">.
cp tools/settings_common.js _site/settings_common.js
# Gemini customer overlay (Gemini-only fields). The shipped page is
# translated as a complete injected artifact below, not by a partial
# runtime DOM overlay.
cp tools/gemini_ui.js _site/gemini_ui.js
# The canonical sources stay Japanese for upstream. Publish and
# verify English copies of the complete self-contained page and both
# sibling scripts used by the BLE Web Bluetooth settings tool.
python3 tools/settings_i18n/translate.py tools/settings_i18n/catalog.json \
_site/settings.html _site/settings.html \
_site/settings_common.js _site/settings_common.js \
_site/gemini_ui.js _site/gemini_ui.js
python3 tools/settings_i18n/translate.py tools/settings_i18n/catalog.json --check \
_site/settings.html _site/settings_common.js _site/gemini_ui.js
# Avatar preview module embedded by settings.html (built above).
cp build/avatar_web/avatar_module.js _site/avatar_module.js
# Standalone preview page (shell UI + canvas + DSL editor) — same
Expand Down
30 changes: 30 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
cmake_minimum_required(VERSION 3.22)

# stackchan-gemini default board: a plain `idf.py build` (e.g. the ESP-IDF
# VS Code / Cursor extension) builds the Stack-chan CoreS3 configuration.
# `make build BOARD=...` and CI pass SDKCONFIG_DEFAULTS explicitly and are
# unaffected.
if(NOT DEFINED SDKCONFIG_DEFAULTS AND NOT DEFINED ENV{SDKCONFIG_DEFAULTS})
set(SDKCONFIG_DEFAULTS "sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.cores3")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults.local")
list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults.local")
endif()
endif()
if(NOT DEFINED IDF_TARGET AND NOT DEFINED ENV{IDF_TARGET})
set(IDF_TARGET "esp32s3")
endif()
# The avatar DSL is compiled with Node at build time. IDE build tasks often
# don't load ~/.nvm, so fall back to an nvm-installed Node when PATH has none.
if(NOT DEFINED NODE_EXECUTABLE)
find_program(_SC_NODE node)
if(NOT _SC_NODE)
file(GLOB _SC_NVM_NODES "$ENV{HOME}/.nvm/versions/node/*/bin/node")
if(_SC_NVM_NODES)
list(SORT _SC_NVM_NODES COMPARE NATURAL ORDER DESCENDING)
list(GET _SC_NVM_NODES 0 _SC_NODE)
endif()
endif()
if(_SC_NODE)
set(NODE_EXECUTABLE "${_SC_NODE}" CACHE FILEPATH "Node.js used to compile the avatar DSL")
endif()
endif()

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(stackchan_idf)
47 changes: 28 additions & 19 deletions README.en.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
[日本語](README.md)

# stackchan-idf
# stackchan-idf (Gemini edition)

Firmware for Stack-chan running on M5Stack CoreS3 / AtomS3R / AtomS3 / StopWatch (C152),
written against ESP-IDF 5.5 / C++20. Supports AI voice conversation (OpenAI / Gemini /
XiaoZhi), three configuration paths (BLE / Wi-Fi STA / SoftAP), and device-side OTA.
written against ESP-IDF 5.5 / C++20. This fork is a **Google Gemini Live–only**
customer build (OpenAI / XiaoZhi source stays in the tree but is hidden and locked
out at runtime). Default UI and conversation language are English.

## Web Flasher / Settings page

Released firmware can be flashed straight from the browser (Chrome / Edge):

- **Flash**: <https://ciniml.github.io/stackchan-idf/>
- **BLE Settings**: <https://ciniml.github.io/stackchan-idf/settings.html> (Web Bluetooth, desktop Chrome / Edge only)
- **Flash**: <https://m5stack.github.io/stackchan-idf-gemini/>
- **BLE Settings**: <https://m5stack.github.io/stackchan-idf-gemini/settings.html> (Web Bluetooth, desktop Chrome / Edge only)
- **Wi-Fi Settings**: once the device is on Wi-Fi, `http://stackchan-XXXXXX.local/` (mDNS)
- **iOS / SoftAP Settings**: trigger AP mode on-device (per-board, see below), scan the
Wi-Fi QR shown on the LCD with iPhone Camera → join → captive portal pops the
Expand All @@ -36,14 +37,15 @@ appropriately.

## Features

- **AI voice conversation**: WebSocket to one of OpenAI Realtime / Google Gemini
Live / a XiaoZhi server, streaming mic input → reply audio with mouth-sync.
The half-duplex CoreS3 mutes the mic while speaking; interrupt a reply
(barge-in) with an LCD tap or a head touch (Si12T). Turns are detected by
server-side VAD. OpenAI / Gemini drive expression, head pose and robotic
speech via tools (`set_expression` / `set_head_pose` / `speak_katakoto`);
XiaoZhi maps its reply emotion to an expression. Reply text streams into
the balloon.
- **AI voice conversation (Gemini Live only)**: WebSocket to Google Gemini Live.
Auth is an AI Studio API key only (ships empty; enter it from BLE / Wi-Fi
settings). Default model `gemini-3.8-live`, default voice Aoede (both
configurable). Mic input → reply audio with mouth-sync. The half-duplex
CoreS3 mutes the mic while speaking; interrupt a reply (barge-in) with an
LCD tap or a head touch (Si12T). Turns are detected by server-side VAD.
Tools (`set_expression` / `set_head_pose`) drive expression and head pose.
The Japanese-only `speak_katakoto` tool is hidden from the model in English
mode. Set the persona yourself on the settings page system-prompt field.
- **Avatar rendering**: 30 fps with M5GFX. Breath / saccade / blink animators,
six expressions (Neutral / Happy / Sad / Angry / Doubt / Sleepy). The face
layout and animation is driven by an **Avatar DSL** (`.avdsl` source →
Expand All @@ -66,16 +68,22 @@ appropriately.
(info / settings / control / range / conversation / LT)
- AtomS3R / AtomS3 (button only): BtnA short-press toggles the status
overlay, long-press cycles `operation_mode`
- **Speech balloon**: 24 px Japanese-capable Gothic font on a rounded white
panel; long text scrolls right-to-left as a marquee.
- **Boot splash**: Google Cloud logo on the LCD right after board init
(CoreS3 320×240 / Atom 128×128 / StopWatch 466×466, kept inside the
inscribed circle on the round panel). Shown for at least ~2 s until the
face render task takes over.
- **Speech balloon**: 24 px Gothic font on a rounded white panel; long text
scrolls right-to-left as a marquee. Default balloon / on-device UI strings
are English (NVS `lang` can restore Japanese; there is no language switcher).
- **BLE settings service** (NimBLE GATT): configure Wi-Fi, API keys and OTA
from `tools/settings.html` (Web Bluetooth). Bluetooth 4.2+ Just Works
pairing plus an application-layer X25519 + AES-256-GCM session, with
optional password auth. Settings land in NVS; Apply reboots.
- **Wi-Fi settings service**: once Wi-Fi connects, the device serves an HTTP
server (port 80) + a built-in `settings_wifi.html`, advertised over mDNS
(`stackchan-XXXXXX.local`). Covers SSID, provider, API keys, system prompt,
extra HTTP headers, jtts, Avatar DSL, OTA — everything the BLE page covers.
(`stackchan-XXXXXX.local`). Covers SSID, Gemini API key, model, voice, system
prompt, extra HTTP headers, jtts, Avatar DSL, OTA. Pages are in English;
OpenAI / XiaoZhi fields are hidden.
- **SoftAP provisioning (iOS-friendly)**: when STA is unset / failing, trigger
AP mode on-device (`Stackchan-XXXXXX` + WPA2). LCD shows a Wi-Fi QR; the
iPhone Camera scans → joins → **the captive portal automatically opens
Expand All @@ -85,8 +93,9 @@ appropriately.
- **BLE**: settings.html → encrypted chunks
- **Wi-Fi local file**: upload a `.bin` from settings_wifi.html
- **Wi-Fi device-side fetch** (v0.7.4+): `POST /api/ota/release {tag}` —
the device pulls its own per-board binary from GitHub Pages over its
STA link and applies it (STA must be up).
the device pulls its own per-board binary from this repo's GitHub Pages
(`https://m5stack.github.io/stackchan-idf-gemini`) over STA. It will not
fetch upstream ciniml firmware.

## Hardware (CoreS3 + Stack-chan base path)

Expand Down
57 changes: 33 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
[English](README.en.md)

# stackchan-idf
# stackchan-idf (Gemini edition)

M5Stack の CoreS3 / AtomS3R / AtomS3 / StopWatch (C152) で動く Stack-chan ファームウェア。
ESP-IDF 5.5 / C++20。AI 音声対話 (OpenAI / Gemini / XiaoZhi)、BLE / Wi-Fi / SoftAP の 3 経路
設定、デバイス側 OTA をサポートします。
ESP-IDF 5.5 / C++20。このフォークは **Google Gemini Live 専用** の顧客向けビルドです
(上流 ciniml/stackchan-idf の OpenAI / XiaoZhi コードは残していますが、設定画面と実行時
プロバイダは Gemini に固定)。既定の UI / 会話言語は英語です。

## Web Flasher / 設定ページ

GitHub Releases に置かれたファームウェアをブラウザから書き込めます (Chrome / Edge):

- **書き込み**: <https://ciniml.github.io/stackchan-idf/>
- **BLE 設定**: <https://ciniml.github.io/stackchan-idf/settings.html> (Web Bluetooth、デスクトップ Chrome / Edge のみ)
- **書き込み**: <https://m5stack.github.io/stackchan-idf-gemini/>
- **BLE 設定**: <https://m5stack.github.io/stackchan-idf-gemini/settings.html> (Web Bluetooth、デスクトップ Chrome / Edge のみ)
- **Wi-Fi 設定**: デバイスを Wi-Fi に繋いだ後 `http://stackchan-XXXXXX.local/` (mDNS)
- **iOS / SoftAP 設定**: 本体ボタン (boards により方法が異なる、後述) で AP モードに入り、
LCD に表示される Wi-Fi QR を iPhone Camera で読む → captive portal で設定ページが自動表示
Expand All @@ -34,19 +35,20 @@ Pages サイトに反映されます。

## 機能

- **AI 音声対話**: OpenAI Realtime / Google Gemini Live / XiaoZhi サーバーに WebSocket
接続。マイク入力 → 応答音声 → 口パク連動。半二重 CoreS3 では発話中マイクを止め、
応答中は LCD タップ / 頭タッチ (Si12T) で barge-in 可能。サーバー側 VAD で
ターン検出。OpenAI / Gemini ではツール (`set_expression` / `set_head_pose` /
`speak_katakoto`) で表情・首振り・カタコト声を制御 (XiaoZhi では MCP 経由。加えて感情を表情にマップ)。
- **AI 音声対話 (Gemini Live のみ)**: Google Gemini Live に WebSocket 接続。
認証は AI Studio API キーのみ (出荷時は空。BLE / Wi-Fi 設定から入力)。既定モデルは
`gemini-3.8-live`、既定ボイスは Aoede (いずれも設定ページで変更可)。マイク入力 →
応答音声 → 口パク連動。半二重 CoreS3 では発話中マイクを止め、応答中は LCD タップ /
頭タッチ (Si12T) で barge-in 可能。サーバー側 VAD でターン検出。ツール
(`set_expression` / `set_head_pose`) で表情・首振りを制御。日本語専用の
`speak_katakoto` は英語モードではモデルに公開しません。人格 (システム プロンプト)
は設定ページの「システム プロンプト」で変更できます。
システム プロンプト (組み込み、または設定ページの「システム プロンプト」) は OpenAI / Gemini に
加えて XiaoZhi にも接続時に送ります (公式サーバーは無視。自前の XiaoZhi サーバーで受け取る改造を
入れると、サーバー側の人格設定の代わりに機体の人格で話します)。
応答テキストは吹き出しに進行表示。会話の進み具合は画面右上のバッジに出ます
(待受中 → 聞き取り中 → 応答待ち → ツール実行中 / エージェント待ち → 応答中、接続中 / 再接続中 / 接続エラー)。
- **会話中のお手伝いツール** (OpenAI / Gemini / XiaoZhi): 話しかけるだけで次のことができます
(XiaoZhi では機体が MCP サーバーとしてツールを公開し、サーバー側の LLM が呼びます。
サーバーの Intent 設定が function_call である必要があります)。
- **会話中のお手伝いツール** (Gemini Live): 話しかけるだけで次のことができます。
- 「いま何時?」→ 機体の時計 (Wi-Fi 接続時に自動で合う) で答えます
- 「電池どれくらい?」「近くに手ある?」→ 電池残量・近接センサー・稼働時間・LT
タイマー残りを答えます
Expand Down Expand Up @@ -99,14 +101,19 @@ Pages サイトに反映されます。
操作 / 範囲 / 会話 / LT)
- AtomS3R / AtomS3 (button only): BtnA 短押でステータス オーバーレイ、長押で
operation_mode サイクル
- **起動スプラッシュ**: 起動直後に Google Cloud ロゴを LCD に表示 (CoreS3 320×240 /
Atom 128×128 / StopWatch 466×466。円形パネルは内接円の内側に収める)。最短約 2 秒、
顔描画タスクが始まるまで。
- **吹き出し**: 24 px ゴシック フォントの白パネル、長文は marquee スクロール。
既定の吹き出し・本体 UI は英語 (NVS `lang` で日本語に戻せますが、言語切替 UI はありません)。
- **BLE 設定サービス** (NimBLE GATT): `tools/settings.html` (Web Bluetooth) から
Wi-Fi 接続先・API キー・各種設定・OTA 更新。Bluetooth 4.2 以降の Just Works
ペアリング + アプリケーション層 X25519 + AES-256-GCM 暗号化、optional password 認証。
設定は NVS に保存、Apply で再起動して反映。
- **Wi-Fi 設定サービス**: 接続後 HTTP サーバー (port 80) + `settings_wifi.html`、
mDNS (`stackchan-XXXXXX.local`)。SSID / プロバイダ / API キー / システム
プロンプト / 追加 HTTP ヘッダ / jtts / Avatar DSL / OTA 全て対応。
mDNS (`stackchan-XXXXXX.local`)。SSID / Gemini API キー / モデル / ボイス /
システム プロンプト / 追加 HTTP ヘッダ / jtts / Avatar DSL / OTA 全て対応。
設定ページは英語表示。OpenAI / XiaoZhi の入力欄は出しません。
- **SoftAP プロビジョニング (iOS フレンドリー)**: STA が未設定 / 接続失敗時に
本体トリガで AP モード起動 (`Stackchan-XXXXXX` + WPA2)。LCD に Wi-Fi QR 表示、
iPhone Camera スキャン → 接続 → **captive portal が自動で `settings_wifi.html` を開く**
Expand All @@ -116,7 +123,9 @@ Pages サイトに反映されます。
- **BLE 経由**: settings.html → 暗号化された chunk
- **Wi-Fi ローカル ファイル**: settings_wifi.html で `.bin` をアップロード
- **Wi-Fi デバイス側 fetch** (v0.7.4+): `POST /api/ota/release {tag}` で device
自身が GitHub Pages から自分のボード用バイナリをダウンロード → 適用 (STA 必要)
自身がこのリポジトリの GitHub Pages
(`https://m5stack.github.io/stackchan-idf-gemini`) から自分のボード用バイナリを
ダウンロード → 適用 (STA 必要)。上流 ciniml のファームは取得しません。
- **リリース チャンネル** (v0.15+): tag は `vX.Y.Z` (正式) / `vX.Y.Z-rc.N` /
`vX.Y.Z-beta.N` / `vX.Y.Z-alpha.N` (alpha は CoreS3 / AtomS3R のみ)。Web Flasher と
設定ページの「チャンネル」で表示範囲を選ぶ (既定は正式のみ)。`?channel=alpha` で
Expand All @@ -131,9 +140,9 @@ Pages サイトに反映されます。

| 機能 | マイク口パク<br>(MicLipSync) | JTTS ランダム発話<br>(JttsRandom) | 会話応答<br>(Conversation) | ローカル音声認識<br>(AsrLocal) | ESP-NOW リモコン / 姿勢配信<br>(EspNowRemote / Sender) |
|---|:-:|:-:|:-:|:-:|:-:|
| AI 音声対話 (OpenAI / Gemini / XiaoZhi) | × | × | ○ | × | × |
| 会話中のお手伝いツール (時刻 / 状態 / 記憶 / タイマー) | × | × | ○ (OpenAI / Gemini / XiaoZhi=MCP) | × | × |
| エージェントへの依頼 (ask_agent、家の PC の Claude Code) | × | × | ○ (OpenAI / Gemini は機体から直接、XiaoZhi はサーバー側で実行。エージェント サーバー URL 設定時) | × | × |
| AI 音声対話 (Gemini Live) | × | × | ○ | × | × |
| 会話中のお手伝いツール (時刻 / 状態 / 記憶 / タイマー) | × | × | ○ | × | × |
| エージェントへの依頼 (ask_agent、家の PC の Claude Code) | × | × | ○ (エージェント サーバー URL 設定時) | × | × |
| jtts ランダム発話 (独り言) | × | ○ | × | × | × |
| マイク連動口パク (lip sync) | ○ | × | × | × (マイクは ASR が占有) | × |
| ウェイクワード (esp-sr WakeNet) | × | × | × | ○ | × |
Expand Down Expand Up @@ -200,13 +209,13 @@ make monitor BOARD=cores3 PORT=/dev/ttyACM0
`RTC_PowerHub_Class::setAlarmIRQ` で GCC 14 の `-Werror=maybe-uninitialized`
に引っかかる `buf` 初期化を当てるだけです。

OpenAI / Gemini の API キーはビルドに埋め込まず、BLE / Wi-Fi 設定 IF から
実行時に投入します (NVS に保存)。`sdkconfig.defaults.local` (gitignore 済み) で
コンパイル時デフォルトを与えることもできます。
Gemini API キーはビルドに埋め込まず、BLE / Wi-Fi 設定 IF から実行時に投入します
(NVS に保存)。モデル名とボイスも同じ画面で変えられます。
`sdkconfig.defaults.local` (gitignore 済み) でコンパイル時デフォルトを与えることもできます。

## 起動シーケンス (CoreS3 標準パス)

1. M5 / Avatar 初期化、起動音 (C5–E5–G5 アルペジオ、設定で OFF 可)
1. M5 初期化 → Google Cloud 起動ロゴ (最短約 2 秒) → Avatar 初期化、起動音 (C5–E5–G5 アルペジオ、設定で OFF 可)
2. NVS から設定読み込み → BLE 設定サービス起動 (常時 advertising)
3. SSID があれば Wi-Fi STA 接続を非ブロッキング開始
- STA 接続後 → mDNS + HTTP 設定サーバー + SNTP 開始
Expand Down Expand Up @@ -270,4 +279,4 @@ HMM 音声合成に使う **hts_engine API** (Modified BSD / 名古屋工業大
Project Team) をはじめとする第三者コンポーネントの帰属表示は
**[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)** にまとめています。
HTML 版 (Web フラッシャー・設定ページからも参照可):
<https://ciniml.github.io/stackchan-idf/licenses.html>。
<https://m5stack.github.io/stackchan-idf-gemini/licenses.html>。
Loading
Loading