Nâng cao8 phút đọc03/10/2026

Wi-Fi provisioning và giữ credential: state machine đúng

AP-mode, captive portal, khi nào được xoá credential, trạng thái mất mạng vs sai mật khẩu, và dấu hiệu để chẩn đoán từ xa.

Wi-Fi provisioning và giữ credential: state machine đúng
Mục lục bài viết9 ▼
  1. Mở bài: Vì sao state machine Wi-Fi provisioning hay sai
  2. AP-mode và captive portal: thiết kế đúng vai trò
  3. Phân biệt mất mạng vs sai mật khẩu: tín hiệu nào tin được
  4. Khi nào được xoá credential: nguyên tắc an toàn
  5. Thiết kế bộ đếm và timeout chống rơi nhầm trạng thái
  6. Dấu hiệu để chẩn đoán từ xa
  7. Chúng tôi đã gặp gì trên thiết bị thật
  8. Checklist khi thiết kế
  9. Đọc tiếp

Thiết bị IoT ngoài field mất Wi-Fi không hiếm — router khởi động lại, đổi mật khẩu, nhiễu tạm thời. Vấn đề là firmware thường phản ứng sai: coi mọi mất kết nối là "credential hỏng" rồi xoá, hoặc kẹt vĩnh viễn trong AP-mode chờ người dùng không bao giờ xuất hiện. Bài này dành cho kỹ sư firmware đang thiết kế state machine Wi-Fi provisioning, cần phân biệt rõ ba nhánh lỗi và biết khi nào thực sự nên xoá credential.

#Mở bài: Vì sao state machine Wi-Fi provisioning hay sai

Hậu quả thường gặp nhất trên thiết bị thật là hai cực: thiết bị tự xoá credential đúng khi lẽ ra nó vẫn còn hợp lệ, hoặc rơi vào vòng lặp AP-mode vô hạn vì không có đường thoát. Cả hai đều xuất phát từ cùng một lỗi gốc — gộp chung "mất mạng tạm thời", "sai mật khẩu", và "router đổi cấu hình" vào một bộ đếm lỗi duy nhất.

Ba tình huống này cần ba nhánh xử lý khác nhau. Mất mạng tạm thời (router reboot, nhiễu RF, mất điện khu vực) là lỗi tự phục hồi — không nên động vào credential. Sai mật khẩu là lỗi auth-fail xác định bởi driver Wi-Fi, lặp lại ổn định ở mọi lần thử. Router đổi cấu hình (đổi SSID, đổi băng tần, đổi kênh) tạo ra lỗi "AP không tìm thấy" — khác hẳn hai loại trên.

#AP-mode và captive portal: thiết kế đúng vai trò

AP-mode nên chỉ là lối thoát cho hai tình huống: provisioning lần đầu (thiết bị chưa từng có credential) hoặc người dùng chủ động giữ nút reset. Nó không nên là phản ứng tự động mỗi khi kết nối STA thất bại — nếu vậy, một lần mất mạng ngắn cũng đủ đẩy thiết bị ra khỏi mạng vận hành và biến nó thành một AP cô lập giữa field.

Captive portal — trang cấu hình Wi-Fi hiện ra khi người dùng kết nối vào AP của thiết bị — cần timeout rõ ràng. Nếu không ai truy cập portal trong một khoảng thời gian xác định, thiết bị phải tự quay lại thử STA-mode với credential cũ (nếu có), không được giữ AP-mode vô thời hạn. Thiếu cơ chế này, thiết bị kẹt AP vĩnh viễn ngay khi không có ai ở gần để cấu hình lại.

#Phân biệt mất mạng vs sai mật khẩu: tín hiệu nào tin được

Driver Wi-Fi thường trả về mã lỗi khác nhau cho từng nguyên nhân — ví dụ auth-fail (sai mật khẩu/khoá), AP-not-found (SSID không tồn tại trong vùng phủ), và connection-timeout (có thấy AP nhưng không hoàn tất handshake) theo định nghĩa thường gặp của SDK Wi-Fi stack đang dùng — tên và giá trị mã lỗi cụ thể cần tra lại trong tài liệu API/Error Code của SDK đó. Ba mã này phải map vào ba nhánh xử lý khác nhau trong state machine, không được gộp chung thành một bộ đếm "lỗi kết nối".

Nguyên tắc quan trọng nhất: mất mạng tạm thời không được coi là tín hiệu "credential sai". Router reboot (do mất điện, do cập nhật firmware router) hay nhiễu kênh 2.4 GHz tạm thời đều tạo ra connection-timeout hoặc AP-not-found, không phải auth-fail. Nếu state machine không phân biệt, một lần router khởi động lại bình thường cũng đủ kích hoạt xoá credential.

Mã lỗi (khái quát)Nguyên nhân khả dĩHành động đúng
Auth-failMật khẩu/khoá sai, hoặc router đổi mật khẩuTăng bộ đếm auth-fail riêng, không reset bởi timeout mạng
AP-not-foundSSID đổi tên, router tắt, ngoài vùng phủThử lại theo backoff, không xoá credential ngay
Connection-timeoutNhiễu, router đang khởi động lại, nghẽn kênhThử lại theo backoff, coi là lỗi tạm thời

#Khi nào được xoá credential: nguyên tắc an toàn

Chỉ xoá credential khi có xác nhận auth-fail lặp lại nhiều lần liên tiếp với cùng SSID — không xoá vì mất mạng kéo dài, dù kéo dài bao lâu. Mất mạng là trạng thái mạng, auth-fail là trạng thái credential; hai thứ này không được dùng chung một ngưỡng.

Ngưỡng số lần thử và thời gian trước khi xoá phải lớn hơn thời gian khởi động lại thực tế của router mà thiết bị kết nối tới. Nếu ngưỡng quá ngắn, một lần router restart bình thường (vài chục giây) đủ bị hiểu lầm thành auth-fail dồn dập nếu logic không tách bạch rõ loại lỗi.

#Thiết kế bộ đếm và timeout chống rơi nhầm trạng thái

Dùng backoff tăng dần cho số lần kết nối lại (ví dụ thử lại sau khoảng thời gian tăng dần, có giới hạn trần) để tránh retry dồn dập làm nghẽn thêm mạng đang yếu. Quan trọng hơn: tách biệt hoàn toàn hai bộ đếm — bộ đếm "mất mạng/timeout" dùng để điều khiển backoff, và bộ đếm "auth-fail" dùng để điều khiển quyết định xoá credential. Hai bộ đếm này không reset lẫn nhau.

Trạng thái cuối (last known good — SSID, thời điểm kết nối thành công gần nhất) nên được lưu vào bộ nhớ không bay hơi (NVS/flash) để không mất ngữ cảnh khi mất điện giữa lúc đang retry.

C
// Minh hoạ, chưa biên dịch — pseudocode bộ đếm backoff tách biệt
typedef struct {
    uint32_t network_retry_count;   // tăng khi timeout/AP-not-found
    uint32_t auth_fail_count;       // tăng khi auth-fail, SSID không đổi
    uint32_t last_connect_ts;       // lưu NVS: timestamp kết nối OK gần nhất
    char last_known_ssid[32];       // lưu NVS: SSID của credential hiện tại
} wifi_state_t;

void on_connect_error(wifi_state_t *s, wifi_err_t err) {
    if (err == WIFI_ERR_AUTH_FAIL) {
        s->auth_fail_count++;
        if (s->auth_fail_count >= AUTH_FAIL_THRESHOLD) {
            // chỉ xoá khi vượt ngưỡng, không do mất mạng
            clear_credential_and_enter_ap_mode();
        }
    } else { // timeout, AP-not-found
        s->network_retry_count++;
        uint32_t backoff = min(BASE_BACKOFF_MS << s->network_retry_count, MAX_BACKOFF_MS);
        schedule_retry(backoff); // không chạm auth_fail_count
    }
}

#Dấu hiệu để chẩn đoán từ xa

Trường lastFailedSSID giúp phân biệt hai tình huống dễ nhầm: thiết bị đang kẹt thử lại một SSID cũ không còn tồn tại (môi trường mạng đã đổi, ví dụ đổi router) hay vẫn thử đúng SSID hiện tại nhưng auth liên tục thất bại (có khả năng mật khẩu đã đổi ở router nhưng thiết bị chưa được cấu hình lại).

Nên log kèm timestamp của lần chuyển vào AP-mode gần nhất và nguyên nhân cụ thể (hết timeout captive portal / vượt ngưỡng auth-fail / người dùng reset tay). Thiếu nguyên nhân, log chỉ cho biết "thiết bị đang ở AP-mode" mà không biết vì sao, gây khó khăn khi debug từ xa.

#Chúng tôi đã gặp gì trên thiết bị thật

Một đơn vị cảm biến trong field đã tự xoá credential sau khoảng 50 s mất mạng, vì logic cũ coi mọi lỗi kết nối (bao gồm cả connection-timeout) là tín hiệu để tăng cùng một bộ đếm dẫn tới xoá — không có nhánh riêng cho auth-fail. Kết quả: một lần mất mạng tạm thời đủ để thiết bị rơi vào AP-mode và cần người đến cấu hình lại thủ công.

#Checklist khi thiết kế

  • Tách riêng bộ đếm mất mạng (timeout/AP-not-found) và bộ đếm auth-fail — không dùng chung ngưỡng
  • Ngưỡng xoá credential (số lần auth-fail + thời gian) lớn hơn thời gian khởi động lại router thực tế đo được
  • AP-mode luôn có timeout thoát về STA-mode, không phụ thuộc hành động người dùng
  • Captive portal có cơ chế hết hạn rõ ràng, không giữ AP-mode vô thời hạn
  • Lưu last known good (SSID, timestamp kết nối OK) vào NVS/flash để không mất ngữ cảnh khi mất điện
  • Log đủ trường (lastFailedSSID, mã lỗi, timestamp, trigger chuyển AP-mode) để chẩn đoán từ xa
  • Backoff tăng dần cho retry mạng, có trần tối đa, không áp dụng backoff cho đếm auth-fail

#Đọc tiếp

Bài tiếp theo trong series: "MQTT cho thiết bị: envelope versioned, msgpack, HMAC, và bẫy retained".