โค้ดต่อ WiFi ที่เราคุ้นกันดีมักหน้าตาประมาณนี้:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void setupWiFi() {
  WiFi.begin(SSID, PASSWORD);
  int retry = 0;
  while (WiFi.status() != WL_CONNECTED && retry < 30) {
    delay(500);
    Serial.print(".");
    retry++;
  }
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("ต่อ WiFi ไม่ได้ รีบูต...");
    ESP.restart();        // ← ตรงนี้แหละคือปัญหา
  }
}

บนโต๊ะทำงานมันดูสมเหตุสมผลมาก — ต่อไม่ได้ก็เริ่มใหม่ เดี๋ยวก็ติดเอง

แต่พอเอาไปติดตั้งจริงในตู้ไฟฟ้าที่สัญญาณอ่อน มันจะกลายเป็นกับดักทันที

เกิดอะไรขึ้นในสนามจริง

ผมเจอกับบอร์ดที่ติดตั้งในตู้เหล็กใกล้หม้อแปลง สัญญาณ WiFi วัดได้ประมาณ −78 dBm ซึ่งอ่อนแต่ยังพอใช้ได้

ปัญหาคือที่ความแรงระดับนั้น การเชื่อมต่อบางครั้งใช้เวลา 20–40 วินาที กว่าจะสำเร็จ ซึ่งนานกว่าเวลารอในโค้ด ผลที่ตามมาคือ:

1
2
3
บูต → รอ 15 วินาที → ยังไม่ติด → รีบูต
  → บูต → รอ 15 วินาที → ยังไม่ติด → รีบูต
    → วนแบบนี้ไปเรื่อย ๆ ไม่มีวันจบ

และเพราะโค้ดค้างอยู่ใน setup() ไม่เคยเข้า loop() เลย ผลกระทบจึงลามไปทั้งหมด:

  • ❌ ไม่อ่านค่าจากเซนเซอร์/มิเตอร์
  • ❌ เว็บตั้งค่าบนบอร์ดเข้าไม่ได้
  • OTA ใช้ไม่ได้ — แก้ไขจากระยะไกลไม่ได้เลย ต้องเดินไปถอดบอร์ดมา

ข้อสุดท้ายเจ็บที่สุด เพราะเป็นสถานการณ์ที่ต้องปีนขึ้นไปหน้างานพร้อมสายและโน้ตบุ๊ก

หลักคิดที่ควรใช้แทน

WiFi เป็นแค่ช่องทางส่งข้อมูล ไม่ใช่เงื่อนไขของการทำงาน

เครื่องวัดที่ต่อเน็ตไม่ได้ ก็ยังควรวัดค่าต่อไปได้ ยังควรแสดงผลที่หน้าจอได้ และที่สำคัญที่สุดคือ ยังต้องเปิดรับการแก้ไขจากระยะไกลได้ทันทีที่เน็ตกลับมา

เขียนแบบนี้แทน

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void setupWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);       // ให้ stack ต่อกลับเองเบื้องหลัง
  WiFi.setSleep(false);              // ปิดโหมดประหยัดไฟ สัญญาณนิ่งกว่า
  WiFi.setTxPower(WIFI_POWER_19_5dBm);  // ดันกำลังส่งสูงสุด ช่วยได้จริงตอนสัญญาณอ่อน

  WiFi.begin(SSID, PASSWORD);

  unsigned long t0 = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) {
    delay(250);
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.printf("WiFi ต่อแล้ว: %s (%d dBm)\n",
                  WiFi.localIP().toString().c_str(), WiFi.RSSI());
  } else {
    Serial.println("ยังต่อ WiFi ไม่ได้ — ทำงานต่อไปก่อน เดี๋ยวลองใหม่เอง");
    // ไม่รีบูต ปล่อยให้ setup() จบแล้วเข้า loop() ตามปกติ
  }
}

แล้วใน loop() ทำตัวเฝ้าดูแบบไม่บล็อก:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const unsigned long WIFI_CHECK_MS = 15000;   // ตรวจทุก 15 วินาที
const unsigned long WIFI_HARD_MS  = 120000;  // หลุดเกิน 2 นาที ค่อยรีเซ็ตสแตก

unsigned long lastCheck = 0, downSince = 0;

void manageWiFi() {
  if (millis() - lastCheck < WIFI_CHECK_MS) return;
  lastCheck = millis();

  if (WiFi.status() == WL_CONNECTED) {
    downSince = 0;
    return;
  }

  if (downSince == 0) downSince = millis();

  if (millis() - downSince > WIFI_HARD_MS) {
    // หลุดนานผิดปกติ = WiFi stack อาจค้าง เริ่มใหม่ทั้งชุด (แต่ไม่รีบูตบอร์ด)
    Serial.println("WiFi หลุดนาน — เริ่ม stack ใหม่");
    WiFi.disconnect(true);
    delay(100);
    WiFi.begin(SSID, PASSWORD);
    downSince = millis();
  } else {
    WiFi.reconnect();   // ลองเบา ๆ ก่อน
  }
}

void loop() {
  manageWiFi();        // ไม่บล็อก คืนค่าทันทีถ้ายังไม่ถึงเวลา
  readSensors();       // ทำงานหลักต่อได้เสมอ
  handleWebServer();
  handleOTA();
}

จุดสำคัญคือ ทุกฟังก์ชันต้องไม่บล็อกmanageWiFi() เช็คเวลาแล้วคืนค่าทันทีถ้ายังไม่ถึงรอบ ทำให้งานหลักเดินได้เต็มความเร็ว

ทำไมต้องมีสองระดับ

ในทางปฏิบัติผมเจอ WiFi หลุดสองแบบที่ต้องแก้ต่างกัน:

อาการสาเหตุวิธีแก้
หลุดชั่วคราวAP รีบูต, สัญญาณตกชั่วขณะ, คนเดินบังWiFi.reconnect() พอ
หลุดยาวผิดปกติWiFi stack ภายในค้างต้อง disconnect(true) แล้ว begin() ใหม่

แบบแรกเกิดบ่อยกว่ามากและหายเองใน 15–30 วินาที ถ้ารีเซ็ตสแตกทุกครั้งจะยิ่งช้ากว่าเดิม ส่วนแบบที่สองนาน ๆ เกิดที แต่ถ้าไม่มีตัวจัดการก็ค้างยาวจนกว่าจะมีคนไปกดรีเซ็ต

แล้วถ้าอยากรู้ว่าบอร์ดหลุดตอนไหน

ปัญหาของอุปกรณ์ที่เน็ตหลุดคือ มันบอกใครไม่ได้ว่าตัวเองหลุด วิธีที่ใช้ได้ผลคือให้ฝั่งเซิร์ฟเวอร์เป็นคนตัดสินแทน:

  • ใช้ MQTT Last Will (LWT) ให้ broker ประกาศ offline แทนบอร์ดเมื่อการเชื่อมต่อขาด
  • หรือฝั่งเซิร์ฟเวอร์นับเวลาว่า “ถ้าไม่มีข้อมูลเข้ามาเกิน X นาที ให้ถือว่าออฟไลน์”

ข้อควรรู้: MQTT ตั้ง LWT ได้แค่หนึ่ง topic ต่อหนึ่งการเชื่อมต่อ ถ้าบอร์ดหนึ่งส่งข้อมูลหลายหมวดและอยากรู้สถานะแยกกัน จะเจอปัญหาว่าอีกหมวดค้างสถานะ online ตลอด — ต้องใช้วิธีนับเวลาเงียบเสริมเข้าไป

สรุป

  • อย่ารีบูตเพราะ WiFi ต่อไม่ติด — ที่จุดสัญญาณอ่อนมันจะวนไม่จบและปิดทางแก้จากระยะไกลไปด้วย
  • ให้ setup() ผ่านไปให้ได้เสมอ แล้วจัดการ WiFi แบบไม่บล็อกใน loop()
  • แยกการแก้เป็นสองระดับ: reconnect เบา ๆ ก่อน แล้วค่อยรีเซ็ตสแตกถ้าหลุดยาว
  • เปิด setAutoReconnect(true) + setSleep(false) + กำลังส่งสูงสุด ตั้งแต่แรก

ถ้าจำได้ข้อเดียว ให้จำข้อนี้ครับ: บอร์ดที่ต่อเน็ตไม่ได้ยังมีประโยชน์ แต่บอร์ดที่วนรีบูตไม่มีประโยชน์อะไรเลย