雲端出單系統通訊架構
CPrint3 最終成功方案採用 WT32-ETH01(搭載 ESP32 與 LAN8720 乙太網晶片) 做為雙網口控制橋梁。
- 為什麼 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),與出單機(靜態 IP192.168.0.99)進行實體網路線對接,繞過所有 USB 驅動限制,透過 TCP Port 9100 穩定傳輸列印封包。
WT32-ETH01 與出單機雙網口橋接物理連接示意圖
資料傳輸生命週期
雲端/本地發送任務
發送端發送列印內容至 Mosquitto MQTT Broker 的主題 cprint3/printer/request。
WT32-ETH01 Wi-Fi 接收
WT32-ETH01 的 Wi-Fi 介面連上路由器並取得 192.168.1.x IP,訂閱主題並即時接收列印 Payload。
區域網 TCP 轉發
控制器提取資料後,透過內部乙太網路實體線,對接出單機的靜態 IP 192.168.0.99:9100 建立 TCP Socket 連線。
印表機列印並切紙
出單機經由實體網路線接收 ESC/POS 印表指令,完成列印,中斷連線後自動切紙。
雲端 MQTT (WAN 網際網路) 列印架構
當系統需要跨網際網路進行遠端列印(例如:外送平台接單自動出單、遠端門市出單)時,通訊架構升級為基於公網 MQTT Broker 的分散式訊息拓撲。
- 免開 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),防止未授權的客戶端連線。
跨網際網路 (WAN) 雲端列印 MQTT 通訊節點拓撲圖
雲端列印通訊生命週期
網頁端發送任務
用戶在世界任何地方的網頁端,透過 WebSockets 安全協定 (wss://) 連結雲端 Broker 的 9001 埠,向主題 cprint3/printer/request 發布列印任務。
雲端中轉與路由
雲端 MQTT 伺服器 (Mosquitto) 驗證連線後,接收列印任務,並即時廣播發送給所有訂閱了該主題的活動客戶端。
WT32-ETH01 接單轉發
WT32-ETH01 使用 TCP 協議(埠 1883)維持與雲端 Broker 的長連接。接收到列印封包後,透過本地網口向 192.168.0.99:9100 建立 TCP Socket 連線並寫入 ESC/POS 位元組。
印表機解析輸出
印表機接收到指令流並輸出實體票據。在 WT32-ETH01 關閉 TCP Socket 連線時,觸發印表機切刀切紙,完成一個列印循環。
WT32-ETH01 硬體配置與燒錄指南
- 出單機獨立供電: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) 按鈕。 |
三、正常運作對接模式
當程式燒錄成功並運作時,擺脫繁瑣的訊號跳線,僅需連接實體網路線及電源:
🔗 網路直連:使用標準 RJ45 乙太網路線,一端接在 WT32-ETH01 網路孔,另一端接在 mC-Print3 印表機網路孔。
🔌 電源供電:透過 5V 與 GND 針腳輸入 DC 5V 電源,或者使用專門的供電適配器。出單機插入其原廠大電流變壓器獨立供電。
ESP32 韌體與環境建置
1. 本地啟動 Mosquitto Broker 指令 (Mac)
# 安裝 mosquitto broker
brew install mosquitto
# 啟動 mosquitto 服務並設定開機自啟
brew services start mosquitto
2. WT32-ETH01 核心對接程式碼
請使用 Arduino IDE 並將開發板設定為 ESP32 Dev Module 進行編譯與燒錄。在燒錄前,請修改您的 Wi-Fi SSID、密碼以及您的電腦(MQTT Broker)本地 IP。
#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,即可模擬推送列印命令封包。
雲端 MQTT (Mosquitto) 伺服器建置步驟
要在雲端伺服器(例如 AWS EC2、Linode 等 Linux 主機)上搭建專屬於您的 MQTT Broker,以實現跨網際網路的遠端出單列印,請依照下列步驟進行安裝與設定。
1. 安裝 Mosquitto 服務 (Ubuntu / Debian)
# 更新套件庫並安裝 Mosquitto 伺服器與客戶端工具
sudo apt update
sudo apt install -y mosquitto mosquitto-clients
2. 配置多通訊協定設定檔 (開啟 TCP 與 WebSockets)
由於瀏覽器中的 JavaScript (例如此測試沙盒) 僅支援以 WebSockets 協定與 MQTT Broker 通訊,而 ESP32 則使用標準的 TCP Socket,因此我們必須在 Mosquitto 中同時開通這兩個傳輸埠。
# =================================================================
# 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. 建立密碼檔案並新增安全性使用者
為了避免您的雲端印表機被未授權的外部人員濫用,請務必建立使用者密碼驗證:
# 建立密碼檔並新增名為 admin 的使用者 (會提示輸入密碼)
sudo mosquitto_passwd -c /etc/mosquitto/passwd admin
# 如果未來要新增其他使用者 (移除 -c,否則會覆蓋整個檔案):
# sudo mosquitto_passwd /etc/mosquitto/passwd new_user
4. 啟動服務並設定開機自動啟動
# 重新啟動 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 控制器也需設定為連線至該伺服器。