2026年8月22日 星期六

Wi-Fi連上了, 為何資料還是送不到?

封包旅行記 EP03|分層找故障

Wi-Fi連上了,
為何資料還是送不到?

ESP32 顯示 WL_CONNECTED,只代表它已經加入無線基地台,不代表資料已抵達 Django。真正的連線,要連續通過「Wi-Fi → IP → Gateway → DNS → TCP/TLS → HTTP → 應用程式」七道關卡。

一、先破解最常見的誤會

Wi-Fi connected ≠ Internet available ≠ Server reachable ≠ Data accepted
「連上 Wi-Fi」是第一關成功;只要後面任何一層失敗,Django 儀表板仍然看不到資料。
1Wi-Fi連上 AP
2IP取得位址
3Gateway走出區網
4DNS找到伺服器
5TCP/TLS建立通道
6HTTP/App接受資料

診斷原則:不要一看到「資料沒上傳」就立刻改程式。由下往上逐層驗證,先找到第一個失敗點。

二、Wi-Fi連線只證明了什麼?

已經證明

SSID、密碼與無線訊號大致可用;ESP32 已與基地台完成關聯。

還沒證明

是否取得正確 IP、Subnet、Gateway、DNS,網路是否能通往外部。

更沒證明

Django 網址、Port、憑證、API 路徑、權杖、JSON 格式與資料庫都正確。

先把 ESP32 的網路身分印出來

#include <WiFi.h>

void printNetworkInfo() {
  Serial.println("=== NETWORK INFO ===");
  Serial.print("SSID: ");    Serial.println(WiFi.SSID());
  Serial.print("IP: ");      Serial.println(WiFi.localIP());
  Serial.print("Subnet: ");  Serial.println(WiFi.subnetMask());
  Serial.print("Gateway: "); Serial.println(WiFi.gatewayIP());
  Serial.print("DNS: ");     Serial.println(WiFi.dnsIP());
  Serial.print("RSSI: ");    Serial.println(WiFi.RSSI());
}

若 IP 是 0.0.0.0,表示尚未完成網路配置;若是 169.254.x.x,通常代表 DHCP 沒有成功提供位址。RSSI 約 -30 dBm 很強、-67 dBm 尚可,低於 -75 dBm 時傳輸容易不穩。

三、從區域網路到 Django:封包實際走哪裡?

ESP32Wi-Fi AP/RouterNAT/InternetWeb ServerDjango URLView/Database
關卡封包需要知道常見失敗
Wi-FiSSID、密碼、訊號密碼錯、2.4/5 GHz 不相容、訊號弱
IP/Subnet本機 IP 與同網段判斷DHCP 失敗、固定 IP 衝突、Subnet 設錯
Gateway/NAT跨網段的下一跳Gateway 錯、訪客網路隔離、路由器無外網
DNS網域名稱對應的 IPDNS 未配置、解析失敗、網址拼錯
TCP/TLS目標 Port 與安全連線Port 關閉、防火牆阻擋、時間或憑證錯誤
HTTPMethod、URL、Header、BodyPOST/GET 用錯、路徑錯、JSON 或 Content-Type 錯
Django路由、驗證、資料模型404、403、CSRF、500、欄位驗證失敗

四、七層診斷法:先找第一個失敗點

① 無線層

狀態WiFi.status() == WL_CONNECTED
訊號觀察 RSSI 是否持續過低或大幅波動。

② IP 配置層

確認 IP、Subnet、Gateway、DNS 不是空值;檢查固定 IP 是否與其他設備重複。

③ 本地網路層

用同一 Wi-Fi 的電腦或手機測試 Router 與區網伺服器。若訪客 Wi-Fi 開啟 Client Isolation,設備彼此可能不能連線。

④ DNS 層

先用 IP 測,再用網域測。IP 可通、網域不通,問題多半在 DNS;不要把 https:// 一起當作主機名稱解析。

⑤ TCP/TLS 層

確認 Port:HTTP 常用 80,HTTPS 常用 443。HTTPS 還要檢查 ESP32 時間、CA 憑證與 TLS 相容性。

⑥ HTTP 層

序列埠必須印出完整 URL、HTTP 回應碼與回應內容。只有「send failed」不足以定位問題。

⑦ Django 應用層

同步查看伺服器 log。請求有進來但回 4xx/5xx,代表網路大致已通,問題已經移到 API 或程式端。

⑧ 儀表板與資料庫

API 回 200/201 仍未顯示時,檢查資料是否真正寫入、查詢條件、時區、快取與前端刷新。

五、HTTP回應碼就是伺服器留下的線索

回應代表意義優先檢查
200 OK201 Created請求成功或資料已建立若畫面仍沒資料,檢查資料庫與前端查詢
301/302網址被重新導向HTTP→HTTPS、尾端斜線、網域設定
400 Bad Request請求內容無法解析JSON、必填欄位、資料型別
401/403身分驗證或權限失敗API Key、Token、CSRF、權限
404 Not Found找不到 API 路徑Django urls.py、版本路徑、尾斜線
405 Method Not AllowedHTTP 方法不被接受伺服器要 POST,裝置卻送 GET
415 Unsupported Media Type資料格式宣告不符Content-Type: application/json
500502503應用程式或伺服器端異常Django log、反向代理、服務狀態
-1 或連線逾時常是 HTTP 之前就失敗DNS、TCP、TLS、Port、防火牆

六、ESP32上傳時,至少留下這些診斷資訊

#include <HTTPClient.h>

void postEvent(const String& json) {
  const char* url = "https://example.org/api/events/";
  HTTPClient http;

  Serial.print("POST URL: "); Serial.println(url);
  Serial.print("WiFi RSSI: "); Serial.println(WiFi.RSSI());

  if (!http.begin(url)) {
    Serial.println("HTTP begin failed");
    return;
  }

  http.addHeader("Content-Type", "application/json");
  int code = http.POST(json);
  Serial.print("HTTP code: "); Serial.println(code);

  if (code > 0) {
    Serial.print("Response: "); Serial.println(http.getString());
  } else {
    Serial.print("Transport error: ");
    Serial.println(http.errorToString(code));
  }
  http.end();
}

上例用 example.org 作教材占位網址。實際部署 HTTPS 時,應依函式庫版本正確設定 CA 憑證與系統時間;不要為了省事而長期停用憑證驗證。

離線佇列很重要:IoT 裝置不應假設網路永遠在線。每筆事件加入唯一 event_uuid,失敗時先保存,恢復連線後再重送;伺服器依 UUID 去除重複資料。

七、最有效率的交叉測試

測試結果可推論的範圍
手機使用同一 Wi-Fi 也打不開 API先查 Router、DNS、伺服器或 API,不急著改 ESP32
手機可用,ESP32 不行查 ESP32 的 DNS、TLS、時間、憑證、記憶體與程式
用 IP 可連,用網域不行DNS 解析問題
HTTP 可用,HTTPS 不行TLS、CA 憑證、系統時間或 SNI
API 收得到,但回 400/403/404網路已走到應用層,查 Header、Token、路徑與資料格式
API 回 201,但儀表板沒顯示查資料庫、查詢條件、時區與前端刷新

八、場域版故障排除順序

  1. 記錄現象:時間、裝置 ID、IP、RSSI、目標網址、HTTP code。
  2. 確認影響範圍:一台裝置、同一基地台,還是所有場域都失敗?
  3. 由下往上測:Wi-Fi → DHCP → Gateway → DNS → TCP/TLS → HTTP → Django。
  4. 做單一變因測試:不要同時換 Wi-Fi、改網址又重寫程式。
  5. 恢復後驗證:即時資料與離線佇列是否都完成補送,是否產生重複事件。

好系統不只是「正常時會動」:還要在斷網時保留資料、恢復時自動重送、出錯時留下足以診斷的證據。

九、學生實作:同一問題,三種學習層次

No-AI|人工診斷

抄錄 ESP32 的 IP、Subnet、Gateway、DNS、RSSI與 HTTP code;依七層表判斷第一個失敗點,畫出封包停在哪裡。

AI Pair|協作除錯

把「去識別化後」的序列埠紀錄與 Django log 交給 AI,要求它提出三個假設、每個假設的驗證方法,以及不可直接下結論的原因。

Challenge AI|場域韌性

設計斷網佇列、指數退避重試、event_uuid 去重與狀態儀表板;實測關閉 Wi-Fi 5 分鐘後能否完整補送。

十、學習檢核

我能做到完成證據
說明 Wi-Fi connected 與資料送達的差異能指出封包必須通過的七個關卡
讀懂 ESP32 的網路配置能解釋 IP、Subnet、Gateway、DNS與RSSI
利用 HTTP code 定位問題能區分傳輸錯誤、用戶端錯誤與伺服器錯誤
完成可重現的故障報告包含時間、環境、步驟、log、假設、測試與結果
設計斷線不漏資料的 IoT 裝置具備佇列、重試、去重與恢復驗證
封包旅行記 EP03|「連上」只是狀態;能逐層驗證、找到第一個失敗點,才是真正的網路能力。

沒有留言:

張貼留言