Skip to content

About

An ESP-IDF based Stack-chan firmware.

Resources

Stars

45 stars

Watchers

0 watching

Forks

Latest commit

 

History

415 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English

stackchan-idf

M5Stack の CoreS3 / AtomS3R / AtomS3 / StopWatch (C152) で動く Stack-chan ファームウェア。 ESP-IDF 5.5 / C++20。AI 音声対話 (OpenAI / Gemini / XiaoZhi)、BLE / Wi-Fi / SoftAP の 3 経路 設定、デバイス側 OTA をサポートします。

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 のみ)
  • Wi-Fi 設定: デバイスを Wi-Fi に繋いだ後 http://stackchan-XXXXXX.local/ (mDNS)
  • iOS / SoftAP 設定: 本体ボタン (boards により方法が異なる、後述) で AP モードに入り、 LCD に表示される Wi-Fi QR を iPhone Camera で読む → captive portal で設定ページが自動表示

タグ vX.Y.Z を push すると CI が 4 ボード分ビルドして Release を作り、 Pages サイトに反映されます。

対応ボード

ボード 略号 表示 主な特徴
CoreS3 + Stack-chan ベース cores3 320×240 IPS + touch 標準。サーボ 2 軸、頭タッチ センサー、INA226 電池計
CoreS3 + Takao Base cores3 同上 Port A 半二重サーボ、サーボ電源/電池計なし
AtomS3R + Atomic ECHO BASE (アトムニャン) atoms3r 128×128 LCD サーボなし、ES8311 音声、BtnA で UI / AP
AtomS3 (PSRAM なし) + ECHO BASE atoms3 128×128 LCD 軽量プロファイル、会話/RTP 非対応
M5 StopWatch (C152) stopwatch 466×466 円形 AMOLED + touch サーボなし、touch で視線追従、ES8311 音声

ビルドは make build BOARD=<略号> (デフォルト cores3)。 ボードは起動時に検出され、set_board_kind() で UI / 機能のグレーアウトに反映されます。

機能

  • 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 経由。加えて感情を表情にマップ)。 システム プロンプト (組み込み、または設定ページの「システム プロンプト」) は OpenAI / Gemini に 加えて XiaoZhi にも接続時に送ります (公式サーバーは無視。自前の XiaoZhi サーバーで受け取る改造を 入れると、サーバー側の人格設定の代わりに機体の人格で話します)。 応答テキストは吹き出しに進行表示。会話の進み具合は画面右上のバッジに出ます (待受中 → 聞き取り中 → 応答待ち → ツール実行中 / エージェント待ち → 応答中、接続中 / 再接続中 / 接続エラー)。
  • 会話中のお手伝いツール (OpenAI / Gemini / XiaoZhi): 話しかけるだけで次のことができます (XiaoZhi では機体が MCP サーバーとしてツールを公開し、サーバー側の LLM が呼びます。 サーバーの Intent 設定が function_call である必要があります)。
    • 「いま何時?」→ 機体の時計 (Wi-Fi 接続時に自動で合う) で答えます
    • 「電池どれくらい?」「近くに手ある?」→ 電池残量・近接センサー・稼働時間・LT タイマー残りを答えます
    • 「〇〇を覚えておいて」「〇〇って何だった?」「〇〇は忘れて」→ 機体内に保存 (32 件・各 500 文字まで、再起動しても残ります)
    • 「N 分後に教えて」→ 時間が来たら吹き出しと声でお知らせ (1 件まで。会話セッション中は 吹き出しのみ)
    • 「3 分のタイマーをセット」「タイマー止めて」「あと何分?」→ LT タイマー (本体の LT タブと同じ。 画面に残り時間、残り 1 分・終了・超過をお知らせ。会話中はスタックチャンが自分の声で知らせます)
    • 「音量上げて / 下げて」「ミュート」「ミュート解除」「音量 50 にして」→ スピーカー音量 (設定ページのスライダーと同じ値で保存。ミュートは本体の角タップと同じで再起動すると解除)
    • 「〇〇を調べて」「〇〇に聞いておいて」→ 家の PC で動くエージェント サーバー (tools/stackchan-agent、Claude Code を裏で動かす) に依頼し、数十秒後に届いた答えをスタックチャンの口調で話します (右上バッジは 「エージェント待ち」)。設定ページ (Wi-Fi) の「会話」タブで「エージェント サーバー URL」と「トークン」を入れて再起動すると有効になります (URL が空なら無効)。 LAN / Tailscale 内だけで使う前提の平文 HTTP です。
  • アバター描画: M5GFX で 30 fps。呼吸 / saccade / blink、6 表情 (Neutral / Happy / Sad / Angry / Doubt / Sleepy)。Avatar DSL (.avdsl ソース → .avbc バイトコード) で顔のレイアウトとアニメーションを差し替え可能 (BLE / Wi-Fi 経由でライブ更新)。
  • マイク連動口パク (lip sync): FFT + 帯域 log + spectral flux で開口度推定、 雑音床 EWMA AGC で環境ノイズに追従。
  • サーボ: SCS0009 yaw + pitch を UART1 (1 Mbps) で制御。台形速度 プロファイル PathGenerator、駆動時のみトルク有効化。ボード別レンジ キャリブレーション (ServoLimits) を NVS 保存。
  • スピーカー / オーディオ: 起動音 (C5–E5–G5、設定で OFF 可)、jtts ランダム babble、AAC 録音再生、BLE オーディオ ストリーム、Wi-Fi RTP (L16 / μ-law / AAC) 受信。 音量は 0..200% をライブ制御 (BLE / Wi-Fi / 本体 UI)。
  • Wi-Fi RTP 音声受信: 機体の UDP 6970 に L16 (48 kHz mono) / μ-law、6972 に AAC を送ると、約 200 ms 遅れで再生します (受信中は Wi-Fi のパワーセーブを切り、送受の クロック差は再生側で ±0.5 % 以内のレート補正で吸収)。送信側はクロック駆動で送る GStreamer を推奨:
    gst-launch-1.0 pulsesrc ! audioconvert ! audioresample \
      ! "audio/x-raw,rate=48000,channels=1,format=S16BE" \
      ! rtpL16pay pt=97 mtu=1400 ! udpsink host=stackchan-XXXXXX.local port=6970
    ffmpeg の -re はペーシングが粗く (実測で 2 % 速い)、開始時のバーストと合わせて たまに音が飛ぶことがあります。μ-law (PT 0、8 kHz) は帯域が小さいので無線が弱い環境向け。
  • NeoPixel: nekomimi 用 LED ストリップ アニメーション (虹 / 単色 / リップシンク レベルメーター モード)。会話モードでは画面右上のステータスに合わせて光り方が変わります (既定: 聞き取り中 = 緑で点灯、応答待ち / ツール実行中 / エージェント待ち = 青が回転、 応答中 = オレンジが口の動きに連動。待受中は通常の設定どおり)。各状態のパターン (通常どおり / 点灯 / 呼吸 / 回転 / 点滅 / 口連動) と色は Wi-Fi 設定ページの「ネコミミ LED」 の下で変更でき、即時反映です。
  • LT タイマー: 発表時間アシスト。残り N 秒で予告 → 時刻ぴったり → 超過繰返し アナウンス (jtts)。
  • オンデバイス UI:
    • CoreS3 / StopWatch (touch panel): 画面右上タップで 5 タブ UI (情報 / 設定 / 操作 / 範囲 / 会話 / LT)
    • AtomS3R / AtomS3 (button only): BtnA 短押でステータス オーバーレイ、長押で operation_mode サイクル
  • 吹き出し: 24 px ゴシック フォントの白パネル、長文は marquee スクロール。
  • 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 全て対応。
  • SoftAP プロビジョニング (iOS フレンドリー): STA が未設定 / 接続失敗時に 本体トリガで AP モード起動 (Stackchan-XXXXXX + WPA2)。LCD に Wi-Fi QR 表示、 iPhone Camera スキャン → 接続 → captive portal が自動で settings_wifi.html を開く (DNS hijack + HTTP 404 catch-all)。AP モード中は require_auth が bypass され、 設定が即触れる。
  • OTA 更新: デュアル OTA パーティション、起動検証 + ロールバック対応。
    • 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 必要)
    • リリース チャンネル (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 で Web Flasher を開くとアルファが選べる。仕様は docs/adr-002-release-channels.md

動作モードと機能の対応

動作モード (operation_mode、設定ページ / 本体 UI で選択・再起動で反映) によって、 起動する機能が変わります。マイク・スピーカー (I2S) と Wi-Fi を取り合う機能は排他です。 ○ = 動く、△ = 条件付き、× = 起動しない。

機能 マイク口パク
(MicLipSync)
JTTS ランダム発話
(JttsRandom)
会話応答
(Conversation)
ローカル音声認識
(AsrLocal)
ESP-NOW リモコン / 姿勢配信
(EspNowRemote / Sender)
AI 音声対話 (OpenAI / Gemini / XiaoZhi) × × ○ × ×
会話中のお手伝いツール (時刻 / 状態 / 記憶 / タイマー) × × ○ (OpenAI / Gemini / XiaoZhi=MCP) × ×
エージェントへの依頼 (ask_agent、家の PC の Claude Code) × × ○ (OpenAI / Gemini は機体から直接、XiaoZhi はサーバー側で実行。エージェント サーバー URL 設定時) × ×
jtts ランダム発話 (独り言) × ○ × × ×
マイク連動口パク (lip sync) ○ × × × (マイクは ASR が占有) ×
ウェイクワード (esp-sr WakeNet) × × × ○ ×
Wi-Fi RTP 音声受信 (UDP 6970 / 6972) ○ ○ × (会話と排他) ○ (想定外・未検証) ×
BLE オーディオ ストリーム ○ ○ × (会話と排他) ○ ○
吹き出し / MCP say (jtts 発話) ○ ○ △ 会話セッション中は会話側が優先 ○ ○
HMM / sanoTTS ボイスのロード ○ ○ ○ △ HMM のみ (単位ボイスは RAM 節約で省略) ×
表情サイクル・近接センサー反応 ○ ○ △ セッション接続前のみ (接続後は停止) ○ ○
ランダム ポーズ・なでなで・振動反応 ○ ○ △ 会話の待機中のみ ○ △ 頭部は ESP-NOW が駆動 (ポーズ系は停止、表情・吹き出しは動く)
ダンス ○ ○ ○ × ×
LT タイマー ○ ○ ○ ○ ○
カメラ / QR (CoreS3) ○ ○ ○ × (RAM を ASR に回す) ○
BLE 設定 ○ ○ ○ ○ ○
Wi-Fi STA 接続・HTTP 設定ページ・mDNS・MCP ○ ○ ○ ○ × (Wi-Fi は固定チャネル、AP 非接続)
OTA: BLE 転送 ○ ○ ○ ○ ○
OTA: リリース取得 (機体が Wi-Fi で取得) ○ ○ ○ ○ ×
ESP-NOW 受信 / 配信 × × × × ○

補足:

  • RTP 受信・BLE オーディオ ストリームは、設定「RTP 受信」が ON で、かつ会話応答モードでない ときに動きます (会話バックエンドとスピーカー / I2S を共有できないため)。atoms3 (PSRAM なし) は RTP / 会話非対応のスリム ビルドです。
  • 会話応答モードでは、サーバーとのセッションが確立すると独り言・表情サイクル・近接反応が止まり、 待機 (聞き取り) 中だけランダム ポーズとなでなでが動きます。Wi-Fi 未接続などでセッションが 無い間は他モードと同じように動きます。
  • ESP-NOW の 2 モードは Wi-Fi を固定チャネルで使うため、AP に接続する機能 (HTTP 設定、 リリース取得、MCP、RTP) はすべて起動しません。設定変更は BLE から行ってください。
  • 各機能のボード対応は「対応ボード」を参照 (例: 近接センサーは CoreS3、ウェイクワードは CoreS3 内蔵マイクのみ)。

ハードウェア (CoreS3 + Stack-chan ベースの場合)

  • M5Stack CoreS3 (ESP32-S3、8 MB Quad-SPI PSRAM、16 MB Flash)
  • Stack-chan ベース (PY32 IO Expander @ 0x6F、SCS0009 ×2)
    • 内部 I²C: AXP2101 (0x34) / Touch (0x38) ほか M5Unified が管理
    • PY32 Pin 0: サーボ VM 電源 EN (ON 後 200 ms 待ってバス使用)
    • サーボ バス (SCS0009): UART1, TX GPIO 6 / RX GPIO 7, 1 Mbps, 8 N 1
      • Yaw ID = 1, zero_pos = 460
      • Pitch ID = 2, zero_pos = 620
    • 1 step ≈ 0.3125° (deg = (raw - zero) * 5 / 16)
    • 頭タッチ センサー Si12T @ 0x68 (なでなで / barge-in、3 ゾーン)
  • 他ボードのピン配置は各 sdkconfig.defaults.<board> と components/board/board.cpp を参照

セットアップ

ESP-IDF 5.5 (本リポジトリは 5.5.5 で検証。CI も espressif/idf:v5.5.5 に固定) を導入済みの環境で:

git clone <this repo>
cd stackchan-idf
git submodule update --init --recursive
tools/apply-m5-patches.sh                    # M5Unified の 1 行修正を適用
make set-target BOARD=cores3                 # 初回のみ (BOARD 別に build dir が分かれる)
make build     BOARD=cores3
make flash     BOARD=cores3 PORT=/dev/ttyACM0
make monitor   BOARD=cores3 PORT=/dev/ttyACM0

BOARD= を atoms3r / atoms3 / stopwatch に置き換えると該当ボード用の ビルドが build-<board>/ 配下に作られます (各 build dir は独立)。

tools/apply-m5-patches.sh は upstream M5Unified の RTC_PowerHub_Class::setAlarmIRQ で GCC 14 の -Werror=maybe-uninitialized に引っかかる buf 初期化を当てるだけです。

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

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

  1. M5 / Avatar 初期化、起動音 (C5–E5–G5 アルペジオ、設定で OFF 可)
  2. NVS から設定読み込み → BLE 設定サービス起動 (常時 advertising)
  3. SSID があれば Wi-Fi STA 接続を非ブロッキング開始
    • STA 接続後 → mDNS + HTTP 設定サーバー + SNTP 開始
  4. マイク loopback (2 秒録音 → 再生) で動作確認
  5. サーボ電源 ON → ping (Yaw / Pitch) → 1.5 s 待機
  6. Render Task (顔描画 30 fps, core 1) + Servo Task (20 ms 周期, core 0) を起動。 会話が有効なら Conversation Task が Wi-Fi 接続を待って AI 対話を開始
  7. demo_loop 開始 — 会話アイドル時はランダム babble + ランダム ポーズ + なでなで反応、会話中は AI 対話タスクがアバターと音声を制御
  8. 画面右上タップでオンデバイス UI、応答中の画面タップで barge-in、 操作タブの「AP モード」で SoftAP プロビジョニング

リポジトリ構成

.
├── components/
│   ├── avatar/             顔描画 + アニメーション (Breath / Saccade / Blink、6 表情)
│   ├── avatar_vm/          Avatar DSL バイトコード VM + ストレージ
│   ├── board/              CoreS3 / AtomS3R / StopWatch HW 初期化 (ボード自動検出)
│   ├── scs_servo/          SCS0009 ドライバ + PathGenerator (台形速度)
│   ├── jtts/               日本語カタコト TTS (babble / speak_katakoto / LT 通知)
│   ├── conversation/       AI 音声対話クライアント (OpenAI / Gemini / XiaoZhi)
│   ├── config_service/     BLE GATT 設定サービス + NVS + OTA + X25519/AES-GCM
│   ├── wifi_config_service/ Wi-Fi HTTP 設定 + 内蔵 Web ページ + release OTA
│   ├── telegram/           Telegram Bot API (TLS) 通知クライアント (oss)
│   ├── M5Unified/          submodule (upstream + 1 patch)
│   └── tl_expected/        tl::expected backport (submodule)
├── third_party/
│   └── M5GFX/              submodule (upstream; main/idf_component.yml の override_path で
│                           m5stack/m5gfx として 1 コピーだけ使う)
├── main/                   app_main, render/servo task, demo_loop, ap_screen,
│                           captive_portal, device_ui, atom_status, wifi_sta
├── patches/                upstream-targeted patches
├── tools/                  apply-m5-patches.sh, monitor_log.py, settings.html,
│                           avatar_dsl/ (コンパイラ + WASM 連携),
│                           stackchan-agent/ (ask_agent の委譲先サーバー, Bun + Claude Code)
├── assets/                 .avdsl ソース (default_face, omega_mouth, aokko_face)
├── partitions.csv          OTA 配置 (ota_0 / ota_1 / nvs / storage)
├── sdkconfig.defaults*     共通 + ボード別 (.cores3 / .atoms3r / .atoms3 / .stopwatch)
└── Makefile                idf.py の薄いラッパ (BOARD= 切替)

ライセンス

このリポジトリの自前ソース (components/board, components/scs_servo, components/avatar, components/avatar_vm, components/jtts, components/conversation, components/config_service, components/wifi_config_service, components/telegram, main, tools) は Boost Software License 1.0 (LICENSE) の下で配布されます。

Submodule (third_party/m5gfx / components/M5Unified / components/tl_expected/expected) と managed_components (espressif/esp_audio_codec / espressif/esp_websocket_client / espressif/mdns / espressif/esp_jpeg / espressif/esp32-camera 等) はそれぞれの upstream ライセンスに従います。

第三者ソフトウェア・音声データの帰属表示

HMM 音声合成に使う hts_engine API (Modified BSD / 名古屋工業大学・東京工業大学) と、同梱・配布する HMM ボイス "Mei" (CC BY 3.0 / 名古屋工業大学・MMDAgent Project Team) をはじめとする第三者コンポーネントの帰属表示は THIRD_PARTY_NOTICES.md にまとめています。 HTML 版 (Web フラッシャー・設定ページからも参照可): https://ciniml.github.io/stackchan-idf/licenses.html。

About

An ESP-IDF based Stack-chan firmware.

Resources

Stars

45 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages