From 423eb641b19f489b48fb41bb206c552866993b4c Mon Sep 17 00:00:00 2001 From: Atsushi Morimoto <74th.tech@gmail.com> Date: Mon, 6 Apr 2026 20:32:03 +0900 Subject: [PATCH 1/2] docs: update firmware configuration documentation and add detailed servo settings --- docs/firmware_ja.md | 109 ++++++++++++++++++++++++++++- firmware/include/config.template.h | 1 + 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/docs/firmware_ja.md b/docs/firmware_ja.md index dfdfddc..e758ded 100644 --- a/docs/firmware_ja.md +++ b/docs/firmware_ja.md @@ -1,17 +1,120 @@ # ファームウェアの設定とビルド -TODO 詳細を書く +Firmwareに書き込む設定は firmware/include/config.h に記述します。 +[firmware/include/config.template.h](../firmware/include/config.template.h)をコピーして、firmware/include/config.h を作成してください。 -WiFi設定、接続先サーバを[firmware/include/config.h](firmware/include/config.h)に記述します。 +## WiFi設定 + +WiFi設定、接続先サーバをそれぞれ記述します。 ```h +// Wifi #define WIFI_SSID_H "__SSID__" #define WIFI_PASSWORD_H "__PASSWORD__" +``` + +## 接続先サーバ + +サーバを立てるIPアドレスを設定してください。 +今利用しているPCをサーバにする場合、PCのIPアドレスを設定してください。 + +なお、PCのIPアドレスが固定されていない場合、IPアドレスが変わると接続できなくなります。 +ルータの設定などでIPアドレスを固定することをおすすめします。 + +FastAPIのポートはデフォルト値を8000にしています。 +必要に応じて変更してください。 +WebSocketのパスは変更不要です。 -// WebSocket サーバ設定 +```h +// WebSocket Server #define SERVER_HOST_H "192.168.1.179" // 例: サーバのIP #define SERVER_PORT_H 8000 // 例: FastAPIのポート #define SERVER_PATH_H "/ws/stackchan" // WebSocketパス ``` +## サーボ + +サーボモータに合わせて、コメントアウトされている設定の有効化と、ピンの設定を記述してください。 + +### SG90の場合 + +以下の設定と、ピンを設定してください。 + +```h +// -- using SG90 -- +#define USE_SERVO_SG90 1 + +// Pin definitions +#define SERVO_SG90_X_PIN 18 +#define SERVO_SG90_Y_PIN 17 +``` + +M5Pantilt、CoreS3のPortA、PortCの設定例があります。 + +M5Pantiltの場合 + +```h +#define SERVO_SG90_X_PIN 7 +#define SERVO_SG90_Y_PIN 6 +``` + +CoreS3のPortAの場合 + +```h +#define SERVO_SG90_X_PIN 1 +#define SERVO_SG90_Y_PIN 2 +``` +CoreS3のPortCの場合 + +```h +#define SERVO_SG90_X_PIN 18 +#define SERVO_SG90_Y_PIN 17 +``` + +このファームウェアでは90度を中心に動作します。 +90度に位置がずれる場合、オフセット値を設定してください。 + +```h +#define SERVO_SG90_X_CENTER_OFFSET 0 +#define SERVO_SG90_Y_CENTER_OFFSET 0 +``` + +### SCS0009の場合 + +以下の設定と、ピン、サーボIDを設定してください。 + +```h +#define USE_SERVO_SCS0009 1 + +// Pin definitions +#define SCS_SRIAL_RX_PIN 17 +#define SCS_SRIAL_TX_PIN 18 + +// Servo ID +#define SCS0009_X_ID 1 +#define SCS0009_Y_ID 2 +``` + +このファームウェアでは、511を中心に動作します。 +511に位置がずれる場合、オフセット値を設定してください。 + +```h +#define SCS0009_X_CENTER_OFFSET 0 +#define SCS0009_Y_CENTER_OFFSET 0 +``` + +### SCS0009のサーボIDの設定 + +サーボは出荷時はID:1が設定されており、複数のサーボを同時に動かすにはIDの変更が必要です。 + +以下のファームウェアのコードには、ID:1 のサーボを ID:2 にセットするコードが含まれています。 + +https://github.com/74th/stackchan-book-code/blob/main/3.4_scs0009 + +ID:2 にしたいサーボだけを接続してください。 +`setID(1, 2);`がコメントアウトされていますので、コメントアウトを外してビルドして、CoreS3に書き込んでください。 +実行すると、ID:1 のサーボが ID:2 に変更されます。 + +## ビルドする + StackChanのファームウェアをPlatformIOでビルドして、CoreS3に書き込みます。 diff --git a/firmware/include/config.template.h b/firmware/include/config.template.h index a51e7e3..9b2d869 100644 --- a/firmware/include/config.template.h +++ b/firmware/include/config.template.h @@ -1,3 +1,4 @@ +// Wifi #define WIFI_SSID_H "__SSID__" #define WIFI_PASSWORD_H "__PASSWORD__" From 0b2081a0319217959139c11f887dcbc498afa0e1 Mon Sep 17 00:00:00 2001 From: Atsushi Morimoto <74th.tech@gmail.com> Date: Tue, 7 Apr 2026 19:43:30 +0900 Subject: [PATCH 2/2] feat: load environment variables using dotenv in multiple example apps --- .../claude_agent_sdk/claude_agent_sdk.py | 28 +++++++++++-------- example_apps/echo.py | 6 +++- example_apps/echo_with_move.py | 4 +++ example_apps/gemini.py | 9 ++++-- pyproject.toml | 1 + uv.lock | 2 ++ 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/example_apps/claude_agent_sdk/claude_agent_sdk.py b/example_apps/claude_agent_sdk/claude_agent_sdk.py index 61f8473..2b0975d 100644 --- a/example_apps/claude_agent_sdk/claude_agent_sdk.py +++ b/example_apps/claude_agent_sdk/claude_agent_sdk.py @@ -12,6 +12,7 @@ create_sdk_mcp_server, tool, ) +from dotenv import load_dotenv from pydantic import BaseModel from stackchan_server.app import StackChanApp @@ -24,6 +25,8 @@ WsProxy, ) +load_dotenv() + logger = getLogger(__name__) logger.addHandler(StreamHandler()) logger.setLevel("DEBUG") @@ -42,6 +45,7 @@ def _create_app() -> StackChanApp: ) return StackChanApp() + app = _create_app() model = "claude-haiku-4-5-20251001" @@ -74,17 +78,16 @@ async def aircon_remote(dict_args: dict[str, Any]): tools=[aircon_remote], ) + def setup_claude_agent_sdk() -> ClaudeSDKClient: option = ClaudeAgentOptions( model=model, system_prompt="あなたは音声AIアシスタントのスタックチャンです。ユーザの質問に対して、3文程度の言葉で答えてください。音声案内であるため、マークダウンや絵文字等は用いずに、文字列だけで回答してください", cwd=str(WORKSPACE_DIR), setting_sources=["project"], - # MCPサーバを登録 mcp_servers={"home-remote": home_remote_mcp}, # tools=["mcp__home-remote__aircon-control"], - # 全て許可 permission_mode="bypassPermissions", ) @@ -117,15 +120,17 @@ async def talk_session(proxy: WsProxy): logger.info("Human: %s", text) - await proxy.move_servo([ - (ServoMoveType.MOVE_Y, 100, 100), - (ServoWaitType.SLEEP, 200), - (ServoMoveType.MOVE_Y, 90, 100), - (ServoWaitType.SLEEP, 200), - (ServoMoveType.MOVE_Y, 100, 100), - (ServoWaitType.SLEEP, 200), - (ServoMoveType.MOVE_Y, 90, 100), - ]) + await proxy.move_servo( + [ + (ServoMoveType.MOVE_Y, 100, 100), + (ServoWaitType.SLEEP, 200), + (ServoMoveType.MOVE_Y, 90, 100), + (ServoWaitType.SLEEP, 200), + (ServoMoveType.MOVE_Y, 100, 100), + (ServoWaitType.SLEEP, 200), + (ServoMoveType.MOVE_Y, 90, 100), + ] + ) # AI応答の取得 await client.query(text) @@ -133,7 +138,6 @@ async def talk_session(proxy: WsProxy): logger.info(message) if isinstance(message, ResultMessage): - # 発話 logger.info("AI: %s", message.result) if message.result: diff --git a/example_apps/echo.py b/example_apps/echo.py index 7218540..098c456 100644 --- a/example_apps/echo.py +++ b/example_apps/echo.py @@ -4,6 +4,8 @@ import os from logging import getLogger +from dotenv import load_dotenv + from stackchan_server.app import StackChanApp from stackchan_server.speech_recognition import ( WhisperCppSpeechToText, @@ -18,6 +20,9 @@ datefmt="%H:%M:%S", ) +load_dotenv() + + def _create_app() -> StackChanApp: whisper_model = os.getenv("STACKCHAN_WHISPER_MODEL") # if os.getenv("STACKCHAN_WHISPER_SERVER_URL") or os.getenv("STACKCHAN_WHISPER_SERVER_PORT"): @@ -56,7 +61,6 @@ async def talk_session(proxy: WsProxy): await proxy.speak(text) - if __name__ == "__main__": import uvicorn diff --git a/example_apps/echo_with_move.py b/example_apps/echo_with_move.py index 104b91e..196a165 100644 --- a/example_apps/echo_with_move.py +++ b/example_apps/echo_with_move.py @@ -4,6 +4,8 @@ import os from logging import getLogger +from dotenv import load_dotenv + from stackchan_server.app import StackChanApp from stackchan_server.speech_recognition import ( WhisperCppSpeechToText, @@ -16,6 +18,8 @@ WsProxy, ) +load_dotenv() + logger = getLogger(__name__) logging.basicConfig( level=os.getenv("STACKCHAN_LOG_LEVEL", "INFO"), diff --git a/example_apps/gemini.py b/example_apps/gemini.py index dcf83da..f541130 100644 --- a/example_apps/gemini.py +++ b/example_apps/gemini.py @@ -2,6 +2,7 @@ from logging import StreamHandler, getLogger +from dotenv import load_dotenv from google import genai from google.genai import types @@ -12,15 +13,18 @@ logger.addHandler(StreamHandler()) logger.setLevel("DEBUG") +load_dotenv() app = StackChanApp() client = genai.Client(vertexai=True).aio + @app.setup async def setup(proxy: WsProxy): logger.info("WebSocket connected") + @app.talk_session async def talk_session(proxy: WsProxy): chat = client.chats.create( @@ -45,8 +49,9 @@ async def talk_session(proxy: WsProxy): await proxy.speak(resp.text) - if __name__ == "__main__": import uvicorn - uvicorn.run("example_apps.gemini:app.fastapi", host="0.0.0.0", port=8000, reload=True) + uvicorn.run( + "example_apps.gemini:app.fastapi", host="0.0.0.0", port=8000, reload=True + ) diff --git a/pyproject.toml b/pyproject.toml index 7458d32..95caadb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "google-cloud-speech>=2.35.0", "uvicorn[standard]>=0.40.0", "voicevox-client>=1.1.0", + "python-dotenv>=1.2.1", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 5e62090..785ce3c 100644 --- a/uv.lock +++ b/uv.lock @@ -1412,6 +1412,7 @@ dependencies = [ { name = "fastapi" }, { name = "google-cloud-speech" }, { name = "google-genai" }, + { name = "python-dotenv" }, { name = "uvicorn", extra = ["standard"] }, { name = "voicevox-client" }, ] @@ -1430,6 +1431,7 @@ requires-dist = [ { name = "fastapi", specifier = ">=0.128.0" }, { name = "google-cloud-speech", specifier = ">=2.35.0" }, { name = "google-genai", specifier = ">=1.59.0" }, + { name = "python-dotenv", specifier = ">=1.2.1" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.40.0" }, { name = "voicevox-client", specifier = ">=1.1.0" }, ]