star-cloud/.agents/skills/mqtt-communication-specs/SKILL.md
sky121113 23225ffe3f [DOCS] 更新 MQTT 即時通訊規範,新增 App 端 WSS 連線標準
1. 補齊各連線通道 (TCP/SSL/WS/WSS) 的埠號建議表。
2. 新增 App 端 (Android) WSS 連線專節,包含 URL 參數與密碼雜湊要點。
3. 提供 HiveMQ Client 初始化範例程式碼。
4. 定義 MQTT 優先、HTTP 兜底的 Fallback 通訊機制與時間窗口。
2026-04-17 17:25:20 +08:00

8.1 KiB
Raw Blame History

name description
MQTT 即時通訊與 Topic 規範 定義 Star Cloud 與終端機台 (IoT) 之間的 MQTT 全域通訊拓撲、主題 (Topic) 結構、資料載體 (Payload) 格式與安全性認證機制。

MQTT 即時通訊與 Topic 規範 (MQTT Protocol Specs)

本文件定義機台與 Star Cloud 之間的高併發即時通訊標準。MQTT 主要用於處理高頻率且對即時性要求高的訊息(如心跳、遠端指令),與 HTTP REST API 形成雙軌互補架構。

1. 連線基礎設定 (Connection Basics)

1.1 Broker 資訊

  • 協議版本MQTT v3.1.1 (相容性最高)
  • Keep-Alive:建議設定為 30 ~ 60 秒。

各連線通道埠號:

通道 埠號 用途 使用者
TCP 1883 Docker 內部 / 區網測試 Go Gateway、開發除錯
SSL/TLS 8883 加密 TCP (正式環境) Go Gateway (選配)
WebSocket 8083 EMQX 原生 WS 監聽 NPM 反向代理目標
WSS (公網) 443 App 端透過 NPM 反代連線 Android / iOS App

1.2 身份認證 (Authentication)

機台連線時必須提供以下憑據:

  • Username:機台編號 (serial_no),例如 SN00001
  • Password:機台 api_token 的 SHA256 雜湊值,由 B014 API 取得。
  • Client ID:建議格式為 SC_{serial_no}_{random_suffix},確保唯一性。

EMQX 認證機制

  • 資料結構Redis Hash (HSET)。Key 格式為 {prefix}machine_auth:{username},包含 password 欄位。
  • 查詢指令EMQX 使用 HMGET {prefix}machine_auth:${username} password 進行驗證。
  • Laravel 同步:透過 MachineService::syncMqttAuth() 或批量指令 php artisan mqtt:sync-auth 維護。

Caution

嚴禁使用 Redis String (SET) 儲存認證資料。EMQX 5.x 的 Redis Auth Plugin 僅支援 HMGET 讀取 Hash 結構。若誤用 String認證將永遠失敗。

1.3 Go Gateway 認證

  • Usernamestar-cloud-gateway (固定值)。
  • Password:與 Redis Hash machine_auth:star-cloud-gateway 中的 password 欄位對應。
  • Go Gateway 的認證資料須透過 Laravel 側寫入 Redis。

1-A. App 端 WSS 連線規範 (WebSocket Secure)

App (Android) 透過 WSS 穿越公網連線 Broker。路徑App → Cloudflare → NPM (port 8083 反代) → EMQX

連線參數

參數
URL wss://mqtt.setdomains.work:443/mqtt
Username {machineID} (即 serial_no)
Password {api_token} 原始值B014 取得,不做 SHA256
Client ID SC_{machineID}_{random}
Clean Session true

Caution

App 傳入原始 TokenEMQX 端的 password_hash_algorithm 須設為 sha256,由 Broker 自動雜湊比對 Redis 中的值。

Android 範例 (HiveMQ)

Mqtt3Client client = MqttClient.builder()
    .useMqttVersion3()
    .identifier("SC_" + machineID + "_" + randomSuffix)
    .serverHost("mqtt.setdomains.work")
    .serverPort(443)
    .sslWithDefaultConfig()
    .webSocketConfig().serverPath("/mqtt").applyWebSocketConfig()
    .simpleAuth().username(machineID).password(apiToken.getBytes()).applySimpleAuth()
    .willPublish()
        .topic("machine/" + machineID + "/heartbeat")
        .payload("{\"status\":\"offline\"}".getBytes())
        .applyWillPublish()
    .buildBlocking();

Fallback 策略

  • 指數退避重連1s → 2s → 4s → ... → 最大 60s。
  • WSS 斷線超過 3 分鐘 → 退回 HTTP 輪詢 (B010),背景每 60 秒嘗試重連 WSS。
  • WSS 恢復 → 立即切回 MQTT停止 HTTP 輪詢。

2. 主題架構 (Topic Topology)

我們採用目錄式的層級結構,方便未來進行萬台設備的管理與 ACL 權限切分。

主題名稱 (Topic) 方向 QoS 用途說明
machine/{serial_no}/heartbeat 設備 ➜ 雲端 0 每 10 秒上報心跳、溫度、目前的頁面碼。
machine/{serial_no}/error 設備 ➜ 雲端 1 發生硬體故障、卡貨或門未關時立即上報。
machine/{serial_no}/transaction 設備 ➜ 雲端 1 交易完成、出貨結果的回報。
machine/{serial_no}/command 雲端 ➜ 設備 1 雲端下發的即時指令(出貨、更新、重啟)。

3. 資料載體規範 (Payload Definitions)

所有 Payload 統一採用 JSON 格式,字母一律為 snake_case

3.1 心跳上報 (Heartbeat) - machine/{id}/heartbeat

比照原 B010 邏輯,但去除不必要的 HTTP Header 開銷。

{
  "current_page": 1,
  "firmware_version": "1.0.5",
  "temperature": 25.5,
  "door_status": 0,
  "timestamp": "2026-04-14T09:00:00+08:00"
}

3.2 異常上報 (Error/Event) - machine/{id}/error

比照原 B013 邏輯。

{
  "tid": 12,
  "error_code": "0403",
  "log": "Slot jammed at slot 12",
  "timestamp": "2026-04-14T09:05:00+08:00"
}

3.3 雲端指令 (Downstream Commands) - machine/{id}/command

這是雲端主動下發給機台的訊息,取代原本 B010 Response 的輪詢等待。

{
  "command": "dispense",
  "payload": {
    "slot_no": 5,
    "transaction_id": "T202604140001"
  },
  "message_id": "MSG_123456789"
}

常用指令集:

  • reboot: 機台重啟。
  • reload_config: 重新下載參數 (B014)。
  • reload_products: 重新同步商品 (B012)。
  • dispense: 遠端出貨指令 (B055)。

4. 安全與 QOS 規範

  1. 存取控制 (ACL)EMQX Broker 必須設定 ACL禁止機台 A 訂閱機台 B 的 Topic。機台僅能訂閱與發布包含自身 serial_no 的路徑。
  2. QoS 策略
    • QoS 0:適用於高頻率心跳,即使掉一兩次包也不影響系統判斷。
    • QoS 1適用於交易與指令確保「至少送達一次」。App 端收到指令後應回覆回執。
  3. 遺囑訊息 (Last Will and Testament) 機台 Connect 時應設定 Last Will 於 machine/{serial_no}/heartbeatPayload 為 {"status": "offline"}。當連線異常中斷時,雲端能立刻得知。
  4. 傳輸加密
    • App 端 (公網):強制使用 WSS (wss://)TLS 由 NPM / Cloudflare 終止。
    • Go Gateway (內網):預設走 TCP (tcp://emqx:1883),區網環境可免 TLS。正式環境建議升級至 ssl://emqx:8883

5. Redis 橋接注意事項

5.1 Key 前綴一致性

Laravel 的 Redis 驅動會自動在 Key 前加上前綴 (預設 starcloud_database_)。Go Gateway 不使用 Laravel Redis 客戶端,因此必須在程式碼中手動加上此前綴

// main.go
prefixedIncomingKey := "starcloud_database_" + cfg.IncomingQueueKey
pusher := bridge.NewRedisPusher(rdb, prefixedIncomingKey)

Caution

若 Go 與 Laravel 操作不同前綴的 Key訊息將永遠無法傳達。修改 Laravel 的 config/database.php 中 Redis prefix 後,必須同步修改 Go 端的前綴字串。

5.2 長連線穩定性

Laravel mqtt:listen 使用 BLPOP 長時間阻塞等待。為防止 PHP 端主動關閉連線,必須在 config/database.php 設定:

'redis' => [
    'default' => [
        'read_timeout' => -1,  // 永不超時
        'timeout' => 10.0,     // 初始連線超時
    ],
],

6. 與 REST API 的同步關係

App 端的通訊策略為 MQTT 優先、HTTP 兜底

狀態 心跳 指令接收 交易回報
WSS 連線正常 MQTT 發布 (10s) MQTT 訂閱 MQTT 發布
WSS 斷線 < 3 分鐘 暫停,等待重連 不可用 暫存本地,重連後補發
WSS 斷線 ≥ 3 分鐘 HTTP B010 輪詢 不可用 (降級) HTTP B600 回報
WSS 恢復 切回 MQTT 恢復 MQTT 訂閱 切回 MQTT

7. 常用維運指令

指令 用途
sail artisan mqtt:sync-auth 將所有機台認證資料從 MySQL 同步至 Redis Hash
sail artisan mqtt:listen 啟動 MQTT 訊息消費者 (常駐執行)
docker compose build mqtt-gateway 重新編譯 Go Gateway
docker restart star-cloud-mqtt-gateway 重啟 Gateway 容器
docker restart star-cloud-queue 重啟 Queue Worker (程式碼變更後必須執行)