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
109 changes: 106 additions & 3 deletions docs/firmware_ja.md
Original file line number Diff line number Diff line change
@@ -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に書き込みます。
28 changes: 16 additions & 12 deletions example_apps/claude_agent_sdk/claude_agent_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
create_sdk_mcp_server,
tool,
)
from dotenv import load_dotenv
from pydantic import BaseModel

from stackchan_server.app import StackChanApp
Expand All @@ -24,6 +25,8 @@
WsProxy,
)

load_dotenv()

logger = getLogger(__name__)
logger.addHandler(StreamHandler())
logger.setLevel("DEBUG")
Expand All @@ -42,6 +45,7 @@ def _create_app() -> StackChanApp:
)
return StackChanApp()


app = _create_app()

model = "claude-haiku-4-5-20251001"
Expand Down Expand Up @@ -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",
)
Expand Down Expand Up @@ -117,23 +120,24 @@ 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)
async for message in client.receive_response():
logger.info(message)

if isinstance(message, ResultMessage):

# 発話
logger.info("AI: %s", message.result)
if message.result:
Expand Down
6 changes: 5 additions & 1 deletion example_apps/echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"):
Expand Down Expand Up @@ -56,7 +61,6 @@ async def talk_session(proxy: WsProxy):
await proxy.speak(text)



if __name__ == "__main__":
import uvicorn

Expand Down
4 changes: 4 additions & 0 deletions example_apps/echo_with_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -16,6 +18,8 @@
WsProxy,
)

load_dotenv()

logger = getLogger(__name__)
logging.basicConfig(
level=os.getenv("STACKCHAN_LOG_LEVEL", "INFO"),
Expand Down
9 changes: 7 additions & 2 deletions example_apps/gemini.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from logging import StreamHandler, getLogger

from dotenv import load_dotenv
from google import genai
from google.genai import types

Expand All @@ -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(
Expand All @@ -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
)
1 change: 1 addition & 0 deletions firmware/include/config.template.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Wifi
#define WIFI_SSID_H "__SSID__"
#define WIFI_PASSWORD_H "__PASSWORD__"

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading