วันหนึ่งเฟิร์มแวร์ที่เคยอัปเดตผ่าน OTA ได้ดีมาตลอด จู่ ๆ ก็ขึ้น fail begin ทุกครั้ง ทั้งที่ตอนคอมไพล์ Arduino IDE บอกชัดเจนว่า:
| |
ใช้ไปแค่ 69% เอง แล้วทำไมถึงเขียนไม่ลง?
คำตอบ: ตัวเลขนั้นไม่ใช่ของบอร์ดคุณ
เปอร์เซ็นต์ที่ IDE แสดงคือขนาดของตารางพาร์ทิชันที่คุณเลือกไว้ในเมนู Tools ตอนนั้น ไม่ใช่ตารางที่อยู่ในชิปจริง ๆ
และนี่คือหัวใจของปัญหา — ตารางพาร์ทิชันเปลี่ยนผ่าน OTA ไม่ได้
ตารางพาร์ทิชันอยู่ที่แอดเดรส 0x8000 ในแฟลช ส่วน OTA เขียนได้เฉพาะช่อง app เท่านั้น เวลาอัปเดตผ่าน OTA ตารางจึงยังเป็นของเดิมที่ถูกเขียนไว้ตอน flash ผ่านสาย USB ครั้งแรก ตลอดไป
เพราะฉะนั้นถ้าตอนแรกคุณ flash ด้วย USB โดยเลือก partition แบบ default แล้ววันหลังเปลี่ยนเมนูเป็น min_spiffs เพื่อให้โค้ดใหญ่ขึ้นได้ — บอร์ดจะยังใช้ตาราง default อยู่ IDE บอกว่าพอ แต่บอร์ดบอกว่าไม่พอ
ตัวเลขที่ต้องจำ
| Partition Scheme | ช่อง app (OTA เขียนได้) |
|---|---|
Default 4MB with spiffs | 1,310,720 ไบต์ |
Minimal SPIFFS (Large APPS) | 1,966,080 ไบต์ |
ถ้าไฟล์เฟิร์มแวร์ของคุณอยู่ระหว่างสองค่านี้แล้ว OTA ล้ม แปลว่าบอร์ดถูก flash ครั้งแรกด้วย default เกือบแน่นอน
เคสที่ผมเจอ: เฟิร์มแวร์เดิม 1,267,168 ไบต์ อัปเดต OTA ได้ปกติ (ต่ำกว่า 1,310,720) พอเพิ่มการเชื่อมต่อแบบเข้ารหัสเข้าไป โตเป็น 1,368,848 ไบต์ ซึ่งเกินเพดานเดิมไป 58 KB — จบเห่ทันที
ทางแก้
ต่อสาย USB flash หนึ่งครั้ง โดยเลือก partition scheme ที่ต้องการ การ flash ผ่าน USB จะเขียน bootloader + ตารางพาร์ทิชัน + แอปพร้อมกัน หลังจากนั้น OTA จะใช้พื้นที่ได้เต็มขนาดใหม่
ข่าวดีคือค่าที่ตั้งไว้ไม่หาย — ช่อง NVS อยู่ที่ 0x9000 และมีขนาดเท่ากันทั้งสอง scheme ค่า WiFi, รหัสอุปกรณ์, ค่าตั้งต่าง ๆ ที่เก็บด้วย Preferences.h ยังอยู่ครบหลัง flash
ทำให้ครั้งหน้าไม่ต้องเดา
ปัญหาจริง ๆ ของเรื่องนี้คือคำว่า fail begin มันไม่ได้บอกอะไรเลย ให้แก้ตัวรับ OTA ฝั่งบอร์ดให้รายงานเหตุผลกลับมาด้วย:
| |
ESP.getFreeSketchSpace() คือตัวเลขที่บอกความจริง — มันอ่านจากตารางพาร์ทิชันที่อยู่ในบอร์ดจริง ไม่ใช่จากเมนูตอนคอมไพล์ เอาสองตัวเลขนี้มาเทียบกันก็รู้ทันทีว่าปัญหาคืออะไร
ถ้าระบบ OTA ของคุณส่งผลกลับผ่าน MQTT หรือ HTTP อยู่แล้ว ส่งข้อความนี้กลับไปด้วยจะประหยัดเวลาไล่หาสาเหตุได้มาก
เกร็ดเพิ่ม: ระบบกันอัปเดตถอยหลังที่อาจล็อกตัวเอง
ถ้าระบบ OTA ของคุณมีกลไกกันอัปเดตย้อนเวอร์ชัน (เทียบ timestamp หรือเลขเวอร์ชันก่อนยอมเขียน) ให้ระวังกรณีนี้:
พอ flash บอร์ดผ่าน USB ด้วยไฟล์ที่ไม่เคยอัปขึ้นเซิร์ฟเวอร์ บอร์ดจะรันเฟิร์มแวร์ที่เซิร์ฟเวอร์ไม่รู้จัก กลไกกันถอยหลังจะมองว่าทุกอย่างบนเซิร์ฟเวอร์ “เก่ากว่าตัวเอง” แล้ว ปฏิเสธ OTA ทุกเวอร์ชันถาวร — ปลดล็อกจากฝั่งเซิร์ฟเวอร์ไม่ได้เลย ต้องเดินไปต่อสาย USB อย่างเดียว
วิธีกัน: อัปไฟล์ .bin ขึ้นเซิร์ฟเวอร์ก่อน แล้วค่อย flash USB ด้วยไฟล์เดียวกันนั้น พอบอร์ดบูตขึ้นมามันจะเห็นว่าตรงกับที่เซิร์ฟเวอร์มี แล้วรับค่าฐานเวลาไปใช้ต่อได้ตามปกติ
ทำเป็นนิสัยไว้เลยครับ: อัปขึ้นก่อน แล้วค่อย flash ไม่ว่าจะเร่งแค่ไหน