Skip to content

feat: Stream Deck Studio TCP emulator (Cora protocol)#43

Closed
FlowingSPDG wants to merge 10 commits into
mainfrom
feat/tcp-emulator
Closed

feat: Stream Deck Studio TCP emulator (Cora protocol)#43
FlowingSPDG wants to merge 10 commits into
mainfrom
feat/tcp-emulator

Conversation

@FlowingSPDG

@FlowingSPDG FlowingSPDG commented Mar 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Cargo workspace 化: 既存の RP2040 ファームウェアを firmware/ crate に移動し、ルートを [workspace] 定義に変更。ファームウェア固有のビルド設定(thumbv6m-none-eabi ターゲット)は firmware/.cargo/config.toml に閉じ込め、tcp-emulator には影響しない構造にした。
  • tcp-emulator/ crate を新規追加: Stream Deck Studio (VID=0x0fd9 / PID=0x00aa) を Cora プロトコルで TCP エミュレートする std/tokio バイナリ。
  • node-elgato-stream-deck の TCP クライアントから完全動作確認済み

実装内容

Cora プロトコル (cora.rs)

  • 16バイトヘッダ (magic / flags / hidOp / messageId / payloadLen) + ペイロードのフレーム構造を実装
  • tokio_util::codec::Framed で TCP ストリームのバッファリング・フレーム境界検出を処理
  • magic バイト同期(受信バッファ内を検索してパケット先頭を特定)

TCP サーバ / セッション管理 (server.rs, session.rs)

  • tokio::net::TcpListener で複数クライアントを並行受付・セッション spawn
  • tokio::select! で keepalive タイマー / 受信メッセージ / CLI コマンドを並行処理

キープアライブ (session.rs)

  • 接続直後と以降 2秒間隔で keepalive Cora メッセージ (payload[0..1] = [0x01, 0x0a]) を送信
  • クライアントからの ACK_NAK フラグ付きレスポンスを受け取り接続維持

Feature Report 応答 (feature_reports.rs)

Report ID 内容
0x80 デバイス情報 (VID/PID)
0x83 ファームウェアバージョン
0x84 シリアル番号
0x85 MACアドレス
0x86, 0x8a エンコーダファームウェア

ボタン入力イベント (input.rs)

  • Gen2 Studio 形式のペイロード: [0x01, 0x00, 0x00, 0x00, key0..key31]
  • KEY_DATA_OFFSET=3 に準拠 (node-elgato-stream-deck/core の Gen2 入力サービスと互換)

mDNS サービス広告 (discovery.rs)

  • _elg._tcp.local. でサービスを登録
  • TXT レコード: vid, pid, sn, dt

CLI (main.rs)

productiondeck-tcp-emulator [OPTIONS]
  --port <PORT>        (default: 5343)
  --serial <SERIAL>    (default: EMULATOR001)
  --mac <MAC>          (default: 00:11:22:33:44:55)
  --no-mdns

# 起動後の対話コマンド
> press <key>    # ボタン押下 (0-31)
> release <key>  # ボタン解放
> tap <key>      # 押して離す
> status         # 接続中クライアント一覧
> quit

Test plan

  • cargo build -p productiondeck-tcp-emulator がエラー・警告なしで通ること
  • StreamDeckTcpConnectionManager.connectTo('127.0.0.1', port)connected イベント発火
  • getSerialNumber() / getFirmwareVersion() / getMacAddress() が正常な値を返すこと
  • tap <key> コマンドで node 側に KEY_DOWN / KEY_UP イベントが届くこと
  • mDNS 広告: StreamDeckTcpDiscoveryService が LAN 上でデバイスを検出できること (実機 LAN 環境で確認)

Made with Cursor

- Convert repository to Cargo workspace; move existing RP2040 firmware
  into firmware/ crate with its own .cargo/config.toml (thumbv6m target)
- Add tcp-emulator/ crate: a std/tokio binary that emulates Stream Deck
  Studio (VID 0x0fd9 / PID 0x00aa) over TCP using the Cora protocol

tcp-emulator features:
- Cora message framing with tokio_util Codec (magic sync, LE header)
- TCP server accepting multiple concurrent clients
- Periodic keepalive (2 s interval) with ACK_NAK handling
- Feature Report responses: 0x80 device info, 0x83 firmware, 0x84 serial,
  0x85 MAC address, 0x86/0x8a encoder firmware
- Gen2 button input events (KEY_DATA_OFFSET=3, 32-button payload)
- mDNS advertisement via mdns-sd (_elg._tcp, vid/pid/sn/dt TXT records)
- Interactive CLI: press/release/tap/status/quit commands

Verified with node-elgato-stream-deck TCP example:
- StreamDeckTcpConnectionManager detects device as Stream Deck Studio
- Serial, firmware, MAC feature reports return correctly
- KEY_DOWN / KEY_UP events fire on tap commands

Made-with: Cursor
- Add --firmware CLI argument (default: "6.06.001") for feature report 0x83
- Remove hardcoded "1.00.000" from main.rs; firmware version now flows from CLI
- Update DeviceConfig::default() to use "6.06.001" for consistent defaults
- Print serial/firmware/mac on startup for easy verification
- Real Stream Deck Studio firmware is ~6.x; "1.00.000" was causing
  "Device firmware is not supported" with the official Elgato software

Made-with: Cursor
Log every received/sent message at INFO level with:
- flags (hex), hid_op (hex), message_id (hex), payload_len
- payload[:64] as hex bytes

This makes it possible to diagnose "Device firmware is not supported"
by capturing the exact bytes the official Elgato software sends.

Made-with: Cursor
…t requests

The official Elgato Stream Deck software uses hid_op=WRITE (0x00) with
flags=NONE and payload=[0x03, reportId, 0x00...] to request feature reports,
instead of hid_op=GET_REPORT (0x02) as used by node-elgato-stream-deck.

Previous behaviour: WRITE messages were only logged, no response was sent,
causing the official software to time out and disconnect (~600ms).

Fix: In the WRITE handler, detect payload[0]==0x03 + !VERBATIM as a
feature report request (Studio port pattern), look up the report, and
respond with WRITE+flags=NONE carrying the report payload.

VERBATIM WRITEs (image data / child device passthrough) are still only
logged as before.

Made-with: Cursor
@FlowingSPDG

Copy link
Copy Markdown
Member Author

一応TCPの接続確率は出来るようになったっぽい?
デバイス一覧には表示されないので、まだカバーできていないコマンドがある

@FlowingSPDG

Copy link
Copy Markdown
Member Author

Studio実機を使ったテストが必要

@FlowingSPDG

FlowingSPDG commented Mar 16, 2026

Copy link
Copy Markdown
Member Author

@FlowingSPDG

Copy link
Copy Markdown
Member Author

Network dockとして認識
image

@FlowingSPDG

Copy link
Copy Markdown
Member Author

Studioは実機がないのでテストが難しそう
Network Dockとして認識されるのを逆手にとって、XLやPlus扱いで接続できないか試してみる

@FlowingSPDG

Copy link
Copy Markdown
Member Author

Suspend due to lacking of equipment

@FlowingSPDG FlowingSPDG deleted the feat/tcp-emulator branch April 22, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant