雲端出單系統通訊架構

CPrint3 最終成功方案採用 WT32-ETH01(搭載 ESP32 與 LAN8720 乙太網晶片) 做為雙網口控制橋梁。

💡 方案突破與關鍵改進 (Architecture Insights):
  • 為什麼 CP2102 序列方案會失敗? mC-Print3 出單機的 USB-A 埠為 USB Host (主機模式),但其內部系統僅內建特定認證設備(如專用條碼槍、鍵盤)的驅動程式,並未包含 CP2102, CH340 等常見 USB 轉序列晶片的驅動。因此,印表機無法識別並載入 CP2102,導致序列通訊無法打通。
  • 雙網路橋接方案:WT32-ETH01 透過 Wi-Fi 連接網際網路(取得 IP 192.168.1.106)以訂閱 MQTT Broker 接收指令;同時將其 RJ45 乙太網路孔 設定靜態 IP(192.168.0.100),與出單機(靜態 IP 192.168.0.99)進行實體網路線對接,繞過所有 USB 驅動限制,透過 TCP Port 9100 穩定傳輸列印封包。
WT32-ETH01 控制器
Wi-Fi (外網) 192.168.1.106
LAN (對接) 192.168.0.100
mC-Print3 印表機
RJ45 Port 192.168.0.99
Port 9100 TCP Socket

WT32-ETH01 與出單機雙網口橋接物理連接示意圖

資料傳輸生命週期

1

雲端/本地發送任務

發送端發送列印內容至 Mosquitto MQTT Broker 的主題 cprint3/printer/request

2

WT32-ETH01 Wi-Fi 接收

WT32-ETH01 的 Wi-Fi 介面連上路由器並取得 192.168.1.x IP,訂閱主題並即時接收列印 Payload。

3

區域網 TCP 轉發

控制器提取資料後,透過內部乙太網路實體線,對接出單機的靜態 IP 192.168.0.99:9100 建立 TCP Socket 連線。

4

印表機列印並切紙

出單機經由實體網路線接收 ESC/POS 印表指令,完成列印,中斷連線後自動切紙。

雲端 MQTT (WAN 網際網路) 列印架構

當系統需要跨網際網路進行遠端列印(例如:外送平台接單自動出單、遠端門市出單)時,通訊架構升級為基於公網 MQTT Broker 的分散式訊息拓撲。

🔒 安全防護與網路隔離技術 (Network Security & Isolation):
  • 免開 Port / 免 Port Forwarding:傳統本地列印若要從外網存取,需在路由器上設定虛擬伺服器或 DMZ,這會暴露內網。本架構中,WT32-ETH01 是由內網主動向外發起與雲端 Broker 的 TCP 連線,因此本地端路由器無須開放任何入站 Port,徹底阻斷外網惡意掃描。
  • 網段物理隔離:mC-Print3 出單機設定於封閉網段 192.168.0.x,不設定 Gateway,無網際網路存取權限。WT32-ETH01 核心板扮演安全網閘(Firewall Bridge),僅在收到經雲端驗證的合法 MQTT 列印封包時,才對接印表機 TCP 9100 進行轉發,防範印表機被直接入侵。
  • 連線安全驗證:雲端 Mosquitto Broker 配置有安全使用者驗證密碼(帳號:admin,密碼:27886988),防止未授權的客戶端連線。
網頁/POS發送端
介面 瀏覽器/App
MQTT.js (WSS)
雲端 MQTT Broker
公網主機 AWS EC2
Port 9001 (WS)
Port 1883 (TCP)
WT32-ETH01 控制器
WAN (Wi-Fi) 訂閱連線
LAN 192.168.0.100
mC-Print3 印表機
IP 192.168.0.99
Port 9100

跨網際網路 (WAN) 雲端列印 MQTT 通訊節點拓撲圖

雲端列印通訊生命週期

1

網頁端發送任務

用戶在世界任何地方的網頁端,透過 WebSockets 安全協定 (wss://) 連結雲端 Broker 的 9001 埠,向主題 cprint3/printer/request 發布列印任務。

2

雲端中轉與路由

雲端 MQTT 伺服器 (Mosquitto) 驗證連線後,接收列印任務,並即時廣播發送給所有訂閱了該主題的活動客戶端。

3

WT32-ETH01 接單轉發

WT32-ETH01 使用 TCP 協議(埠 1883)維持與雲端 Broker 的長連接。接收到列印封包後,透過本地網口向 192.168.0.99:9100 建立 TCP Socket 連線並寫入 ESC/POS 位元組。

4

印表機解析輸出

印表機接收到指令流並輸出實體票據。在 WT32-ETH01 關閉 TCP Socket 連線時,觸發印表機切刀切紙,完成一個列印循環。

WT32-ETH01 硬體配置與燒錄指南

⚡ 重要電源與接線提示 (Power & Wiring Warnings):
  • 出單機獨立供電:mC-Print3 熱感印表機列印時需要較大的暫態電流,ESP32 無法也絕對不可為印表機供電。出單機必須連接其專屬變壓器電源。
  • 正常運作免接訊號線:當韌體燒錄完成且網路線對接後,WT32-ETH01 與出單機的通訊完全走乙太網路線,正常運作時必須拔除 TX/RX 等除錯燒錄接線,避免外部雜訊干擾。

一、CP2102 6合1燒錄器開關設定

在使用帶有指撥開關的藍色 CP2102 6合1燒錄器對 WT32-ETH01 進行韌體燒錄前,請務必將開關調至以下位置:

指撥開關 (DIP Switch)

DIP 1 ── ON

DIP 2 ── OFF

工作模式切換開關 (S1)

將開關切至 232-TTL 位置

(確保輸出為安全的 3.3V TTL 電平,非高壓 RS-232)

二、燒錄模式接線對照表 (Flashing Connections)

將 CP2102 燒錄器與 WT32-ETH01 核心板依據下表連線,並將 IO0 短路以進入 Bootloader:

WT32-ETH01 腳位 CP2102 燒錄器腳位 功能說明 接線說明
GND GND 共地線 ⚫ 共地基準線
TXD (GPIO 1) RXD ESP32 TX 發送線 🟢 交叉接線
RXD (GPIO 3) TXD ESP32 RX 接收線 🟣 交叉接線
5V 5V 供電線 (燒錄用) 🔴 提供開發板電源
IO0 (GPIO 0) GND ⚡ 強制進入燒錄模式 在通電前,必須將 IO0 與 GND 短路,再插上 USB 或按下 EN (RST) 按鈕。
👉 燒錄後重啟程序: 燒錄完成後,請務必拔除 IO0 與 GND 的短路跳線,否則 ESP32 將無法正常啟動。拔除後,實體按一下 WT32-ETH01 控制板上的 EN (或 RST) 按鈕,即可重啟並載入正常程式。

三、正常運作對接模式

當程式燒錄成功並運作時,擺脫繁瑣的訊號跳線,僅需連接實體網路線及電源:

🔗 網路直連:使用標準 RJ45 乙太網路線,一端接在 WT32-ETH01 網路孔,另一端接在 mC-Print3 印表機網路孔

🔌 電源供電:透過 5V 與 GND 針腳輸入 DC 5V 電源,或者使用專門的供電適配器。出單機插入其原廠大電流變壓器獨立供電。

ESP32 韌體與環境建置

1. 本地啟動 Mosquitto Broker 指令 (Mac)

Terminal (MacOS)
# 安裝 mosquitto broker
brew install mosquitto

# 啟動 mosquitto 服務並設定開機自啟
brew services start mosquitto

2. WT32-ETH01 核心對接程式碼

請使用 Arduino IDE 並將開發板設定為 ESP32 Dev Module 進行編譯與燒錄。在燒錄前,請修改您的 Wi-Fi SSID、密碼以及您的電腦(MQTT Broker)本地 IP。

CPrint3_ESP32.ino
#include <WiFi.h>
#include <PubSubClient.h>
#include <ETH.h>

// ======= 1. Wi-Fi 與 MQTT 設定 (透過無線連網接收列印任務) =======
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "192.168.1.XXX"; // 您的電腦本地 IP
const char* mqtt_user = "admin";
const char* mqtt_pass = "27886988";

// ======= 2. 實體網路線對接出單機設定 =======
const char* printer_ip_str = "192.168.0.99";  // mC-Print3 目前的靜態 IP
const uint16_t printer_port = 9100;           // 熱感出單機標準 Port

// WT32-ETH01 乙太網口靜態 IP 設定 (必須與出單機在 192.168.0.x 同網域)
IPAddress eth_local_ip(192, 168, 0, 100);
IPAddress eth_gateway(192, 168, 0, 1);
IPAddress eth_subnet(255, 255, 255, 0);

// ======= 3. WT32-ETH01 乙太網晶片 (LAN8720) 腳位定義 =======
#define ETH_PHY_TYPE    ETH_PHY_LAN8720
#define ETH_PHY_ADDR    1
#define ETH_PHY_MDC     23
#define ETH_PHY_MDIO    18
#define ETH_CLK_MODE    ETH_CLOCK_GPIO0_IN
#define ETH_POWER_PIN   16

WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("正在連線至 Wi-Fi: ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  int retry_count = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    retry_count++;
    if (retry_count > 40) { // 20 秒連線逾時
      Serial.println("\nWi-Fi 連線逾時,重新嘗試中...");
      WiFi.begin(ssid, password);
      retry_count = 0;
    }
  }

  Serial.println("");
  Serial.println("Wi-Fi 連線成功!");
  Serial.print("ESP32 Wi-Fi IP 位址: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("收到列印要求 [");
  Serial.print(topic);
  Serial.println("] ");
  
  // 透過實體網路線 (Ethernet) TCP 直接發送給印表機的 Port 9100
  WiFiClient printerClient;
  Serial.print("正在連線至出單機 (");
  Serial.print(printer_ip_str);
  Serial.println(")...");
  
  if (printerClient.connect(printer_ip_str, printer_port)) {
    Serial.println("連線成功!正在傳送列印資料...");
    // 寫入列印資料
    printerClient.write(payload, length);
    // 強制排空快取
    printerClient.flush();
    // 關閉 TCP 連線,出單機收到斷開訊號後會自動輸出與切紙
    printerClient.stop();
    Serial.println("列印資料發送完成,已中斷連線。");
  } else {
    Serial.println("錯誤:無法連線至出單機!請檢查網路線是否對接、印表機是否開機。");
  }
}

void reconnect() {
  // 循環重連 MQTT Broker
  while (!client.connected()) {
    Serial.print("正在連線至 MQTT Broker (");
    Serial.print(mqtt_server);
    Serial.print(")...");
    
    // 建立隨機 Client ID
    String clientId = "CPrint3_WT32_";
    clientId += String(random(0xffff), HEX);
    
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass)) {
      Serial.println("已成功連線!");
      // 訂閱列印請求主題
      client.subscribe("cprint3/printer/request");
      Serial.println("已成功訂閱主題: cprint3/printer/request");
    } else {
      Serial.print("連線失敗,錯誤碼 rc=");
      Serial.print(client.state());
      Serial.println(",將於 5 秒後重試...");
      delay(5000);
    }
  }
}

void setup() {
  // 初始化除錯用序列埠 (USB 連接電腦監控)
  Serial.begin(115200);
  
  // ======= 初始化 WT32-ETH01 實體網路孔 (LAN8720) =======
  Serial.println("正在初始化實體網路孔...");
  pinMode(ETH_POWER_PIN, OUTPUT);
  digitalWrite(ETH_POWER_PIN, HIGH);
  delay(500); // 等待晶片供電穩定
  
  // Arduino Core 3.0+ 需要傳入 6 個參數
  ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_POWER_PIN, ETH_CLK_MODE);
  ETH.config(eth_local_ip, eth_gateway, eth_subnet);
  Serial.println("實體網路孔初始化完成,靜態 IP 設定為 192.168.0.100");

  // ======= 初始化 Wi-Fi 連線 =======
  setup_wifi();
  
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

本地列印任務沙盒模擬器

您可以在此處直接模擬後端發送列印任務。選擇測試模板並填入本地 MQTT Broker IP,即可模擬推送列印命令封包。

cprint3-sandbox-terminal
[System] Sandbox terminal initialized. Ready for print payload simulation.

雲端 MQTT (Mosquitto) 伺服器建置步驟

要在雲端伺服器(例如 AWS EC2、Linode 等 Linux 主機)上搭建專屬於您的 MQTT Broker,以實現跨網際網路的遠端出單列印,請依照下列步驟進行安裝與設定。

1. 安裝 Mosquitto 服務 (Ubuntu / Debian)

Linux Server Terminal
# 更新套件庫並安裝 Mosquitto 伺服器與客戶端工具
sudo apt update
sudo apt install -y mosquitto mosquitto-clients

2. 配置多通訊協定設定檔 (開啟 TCP 與 WebSockets)

由於瀏覽器中的 JavaScript (例如此測試沙盒) 僅支援以 WebSockets 協定與 MQTT Broker 通訊,而 ESP32 則使用標準的 TCP Socket,因此我們必須在 Mosquitto 中同時開通這兩個傳輸埠。

編輯設定檔:/etc/mosquitto/conf.d/default.conf
# =================================================================
# 1. 傳統 TCP 連線埠 (供 ESP32 微控制器連線)
# =================================================================
listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd

# =================================================================
# 2. WebSockets 連線埠 (供此網頁測試沙盒連線)
# =================================================================
listener 9001 0.0.0.0
protocol websockets
allow_anonymous false
password_file /etc/mosquitto/passwd

3. 建立密碼檔案並新增安全性使用者

為了避免您的雲端印表機被未授權的外部人員濫用,請務必建立使用者密碼驗證:

Linux Server Terminal
# 建立密碼檔並新增名為 admin 的使用者 (會提示輸入密碼)
sudo mosquitto_passwd -c /etc/mosquitto/passwd admin

# 如果未來要新增其他使用者 (移除 -c,否則會覆蓋整個檔案):
# sudo mosquitto_passwd /etc/mosquitto/passwd new_user

4. 啟動服務並設定開機自動啟動

Linux Server Terminal
# 重新啟動 Mosquitto 服務以套用新配置
sudo systemctl restart mosquitto

# 設定開機時自動啟動服務
sudo systemctl enable mosquitto

5. 雲端防火牆安全群組設定 (Security Groups)

請登入您的雲端控制台(如 AWS EC2 安全群組、UFW 等),將下列兩個入站規則 (Inbound Rules) 開放:

通訊埠 (Port) 協定 (Protocol) 對象 用途
1883 TCP (MQTT) 任何來源 (0.0.0.0/0) 或您的家裡 IP 供 WT32-ETH01 控制器接收遠端指令
9001 TCP (WS) 任何來源 (0.0.0.0/0) 供本測試網頁建立 WebSockets 連線進行列印

MQTT 雲端列印連線測試沙盒

在此輸入您在雲端建立好的 MQTT Broker 連線資訊。本網頁將使用 WebSockets 直接發送 MQTT 訊息至該伺服器。此時您運作中的 WT32-ETH01 控制器也需設定為連線至該伺服器。

cloud-mqtt-terminal
[System] Cloud terminal initialized. Ready for connection.