

Firmware ที่อัปเดตใหม่ไม่ได้เริ่มจากอุปกรณ์ว่างเสมอไป บอร์ดที่ใช้งานอยู่มักมีค่าตั้งเก็บไว้แล้ว เช่น ชื่อเครือข่าย ช่วงเวลาอ่านเซนเซอร์ หรือค่าปรับชดเชย เมื่อเปลี่ยนโค้ด เราจึงต้องดูด้วยว่าค่าที่รุ่นก่อนเขียนไว้ยังมีความหมายตรงกับรุ่นใหม่หรือไม่
อย่างค่าช่วงเวลาเดิมเก็บเป็นวินาที แต่โค้ดใหม่เปลี่ยนไปใช้มิลลิวินาที หากอ่านตัวเลขเดิมมาใช้ตรงๆ โปรแกรมอาจยังทำงานได้โดยไม่แสดง error แต่พฤติกรรมที่เกิดกลับต่างจากที่ผู้ใช้ตั้งไว้มาก ปัญหาจึงไม่ได้มีเพียงอ่าน key ไม่พบหรือชนิดข้อมูลผิดเท่านั้น
การเก็บค่าระยะยาวควรมี schema ระดับแอปประกอบครับ เพื่อบอกว่าชุดข้อมูลนี้ถูกเขียนด้วยความหมายแบบใด และรุ่นใหม่จะอ่าน แปลง หรือพักไว้ให้จัดการอย่างไร รวมถึงวันที่ต้องกลับไปใช้ firmware เก่าด้วย
NVS ของ ESP-IDF เป็นการเก็บ key-value ใน flash มีชนิดข้อมูลและ namespace ให้ใช้งานตาม API หากเรียกอ่านด้วยชนิดที่ไม่ตรงกับค่าที่เก็บ สามารถได้รับข้อผิดพลาดเรื่อง type mismatch ได้ แต่ถ้าทั้งรุ่นเก่ากับใหม่ใช้ integer เหมือนกัน เพียงเปลี่ยนหน่วย NVS ก็ไม่มีบริบทที่จะรู้ว่าเราต้องการความหมายคนละแบบ
จึงควรระบุหน่วยในข้อตกลงและชื่อ field เมื่อเหมาะสม เช่น sample_interval_ms แทนชื่อที่ไม่บอกหน่วย และมีเลข schema version สำหรับชุดค่าที่ต้องอ่านร่วมกัน เลขนี้เป็นรุ่นโครงข้อมูล ไม่จำเป็นต้องเพิ่มตามหมายเลข firmware ทุกครั้งหากความหมายไม่ได้เปลี่ยน

อาจใช้ namespace แยกหน้าที่หรือแยกชุดข้อมูลตามการออกแบบ แต่การเปลี่ยน namespace อย่างเดียวก็ไม่ได้แปลงค่ารุ่นเก่าให้ใหม่ ต้องมีโค้ดที่รู้ว่าจะอ่านจากไหน ตรวจอะไร และเขียนผลไปที่ใด โดยรักษาขอบเขตไม่ให้การย้ายค่าตั้งไปกระทบข้อมูลที่ไม่เกี่ยวข้อง
ถ้าเลือกเก็บเป็น blob ก็ต้องมีรูปแบบที่นิยามชัด ไม่เขียน struct ในหน่วยความจำออกไปแล้วสมมติว่าขนาด การเรียง field และ padding จะตรงกันทุก build โดยไม่กำหนดการ serialize การรวมข้อมูลไว้ก้อนเดียวช่วยเรื่องการจัดชุดได้ แต่ยังต้องตรวจขนาดและความเข้ากันได้ตามรูปแบบนั้น
เส้นทางบูตอาจเริ่มจากอ่าน schema version ถ้าเป็นรุ่นที่รองรับก็อ่านและตรวจค่า ถ้าเป็นรุ่นเก่าที่มีวิธีแปลงจึงค่อยทำ migration ส่วนรุ่นที่ใหม่กว่า firmware รู้จักควรมีทางออกชัด ไม่เดาว่าตัวเลข version ที่ไม่ตรงทั้งหมดให้รีเซ็ตทิ้งได้เลย
ในตัวอย่างแปลงวินาทีเป็นมิลลิวินาที ต้องตรวจทั้งช่วงค่าที่อนุญาตและการคูณที่อาจเกินชนิดตัวเลข ค่าที่อ่านได้จาก NVS ยังอาจไม่เหมาะกับงาน เช่น เป็นศูนย์หรือถี่เกินขอบเขตที่ระบบออกแบบไว้ จึงควร validate หลังอ่านและหลังแปลงด้วย
ค่าเริ่มต้นควรใช้กับกรณีที่ตั้งใจรองรับ เช่น อุปกรณ์ใหม่ยังไม่เคยมี field นั้น ไม่แทนทุก error เป็นค่าเริ่มต้นแล้วบันทึกกลับทันที เพราะอาจทำลายหลักฐานของข้อมูลเก่าหรือทำให้อุปกรณ์เปลี่ยนพฤติกรรมโดยผู้ดูแลไม่รู้
หากมีค่าที่เกี่ยวกับการสั่งอุปกรณ์ภายนอก การบูตด้วยข้อมูลไม่แน่ใจควรอยู่ในสถานะที่งานกำหนดไว้สำหรับการจัดการ ไม่รีบใช้ค่าที่ parse ผ่านเพียงอย่างเดียวเป็นคำสั่งทันที ต้องคิดผลต่ออุปกรณ์ปลายทางร่วมกับรูปแบบข้อมูลด้วย

การเขียนหลาย key แล้วเขียน version ใหม่ก่อน อาจทำให้รอบถัดไปเชื่อว่าข้อมูลเป็นรุ่นใหม่ ทั้งที่บาง field ยังไม่ได้ย้ายตามแผน จึงควรวางลำดับให้ตรวจได้ว่าชุดใหม่พร้อมใช้จริงเมื่อใด และถ้าหยุดกลางทางจะกลับมาอ่านสถานะไหน
แนวทางหนึ่งคือเตรียมชุดใหม่แยกจากชุดที่ยังใช้ได้ ตรวจความครบกับความหมายก่อน แล้วเปลี่ยนจุดเลือกชุดตามกลไกที่ออกแบบไว้ แต่รายละเอียดต้องอิงพฤติกรรม storage ที่ใช้ ไม่อ้างว่าการเขียนหลาย key เป็น transaction แบบฐานข้อมูลโดยอัตโนมัติ
สำหรับ NVS ต้องตรวจผลจาก nvs_set และ nvs_commit ตามที่ API กำหนด การปิด handle ไม่ได้แทนการ commit ที่รับรองการเขียนค่า ส่วนความทนต่อไฟดับของ storage ก็ไม่ได้ทำให้ลำดับ migration ระดับแอปถูกต้องขึ้นเอง หากโค้ดประกาศสถานะพร้อมก่อนข้อมูลจริง
ควรเหลือข้อมูลเดิมที่จำเป็นต่อการกู้คืนตามแผน และตรวจพื้นที่สำหรับเก็บชุดใหม่ด้วย เพราะการออกแบบให้มีสองฉบับพร้อมกันเพิ่มความต้องการพื้นที่ การพบว่าพื้นที่ไม่พอควรมีทางจัดการโดยไม่ลบฉบับเดิมก่อนพิสูจน์ว่าชุดใหม่พร้อมใช้
ระบบ OTA อาจมีเส้นทางกลับไปใช้แอปรุ่นก่อนเมื่อของใหม่ไม่ผ่านการตรวจ แต่ข้อมูลตั้งค่าที่อยู่ใน data partition ต้องพิจารณาต่างหาก การกลับไปบูต code เก่าไม่ได้เป็นหลักฐานว่าทุกค่าถูกคืนเป็นฉบับที่ code เก่าเข้าใจแล้ว
ถ้า firmware ใหม่เขียน schema ใหม่ทับไปแล้ว ต้องรู้ว่ารุ่นก่อนจะอ่านอย่างไร ทางเลือกอาจเป็นการรักษารูปแบบที่อ่านร่วมกันได้ช่วงหนึ่ง แยกชุดรุ่นใหม่ หรือมีวิธีคืนค่าที่ออกแบบและตรวจไว้ ทั้งหมดต้องสอดคล้องกับระยะที่ยังอนุญาต rollback
การลบข้อมูลเก่าหลัง migration จึงเป็นอีกการตัดสินใจที่ควรผูกกับวงจรปล่อยรุ่น ไม่ทำทันทีเพียงเพราะบูตครั้งแรกผ่าน เพราะอาจยังต้องใช้ข้อมูลนั้นเมื่อพบปัญหาภายหลัง ขณะเดียวกันก็ต้องมีจังหวะเก็บกวาดที่ชัด ไม่ปล่อยฉบับที่เลิกใช้สะสมจนเต็มพื้นที่
หากเปลี่ยน partition layout พร้อมกัน ต้องตรวจขั้นตอนย้ายและสำรองตามรูปแบบที่ใช้จริง เรื่องนี้มีผลต่อข้อมูลมากกว่าการเพิ่ม field ใน namespace จึงไม่ควรนำคำสั่ง erase จากตัวอย่างเริ่มต้นมาใช้กับอุปกรณ์ที่มีค่าของผู้ใช้อยู่แล้วโดยไม่วางแผนรักษาข้อมูล
เตรียมตัวอย่างค่าตั้งของแต่ละ schema ที่ยังมีอุปกรณ์ใช้งานอยู่ แล้วตรวจการอัปเกรด การบูตซ้ำหลังย้าย และการกลับรุ่นตามเส้นทางที่รองรับ โดยไม่ใช้ข้อมูลลับจริงใน fixture ที่เก็บไว้ใน repo
ควรเพิ่มกรณี field ขาด ชนิดไม่ตรง ค่านอกช่วง และการหยุดกลางขั้นเขียนตามวิธีทดสอบที่ควบคุมได้ จากนั้นตรวจว่าระบบเลือกฉบับใดใช้และบอกสถานะต่อผู้ดูแลอย่างไร ไม่ดูเพียงว่าการเรียก migration คืนค่า success
การเก็บ schema version ที่อ่านพบ ผลการแปลง และเหตุผลที่ไม่ยอมใช้ค่าบางชุดช่วยให้ตามปัญหาภาคสนามได้ โดยไม่ต้องพิมพ์รหัสผ่านหรือค่าลับทั้งหมด บทนี้เสนอแผนความเข้ากันได้ ยังไม่ได้อ้างว่าทดสอบตัดไฟกับอุปกรณ์จริงครบทุกจุดแล้ว
ค่าที่เก็บในบอร์ดต้องมีความหมายที่ข้ามรุ่น firmware ได้อย่างตั้งใจ การกำหนด schema ตรวจค่า วางลำดับ migration และเตรียมทาง rollback ของข้อมูล ช่วยรักษาการตั้งค่าของผู้ใช้ไปพร้อมการอัปเดตโค้ด เมื่อรู้ว่าชุดใดพร้อมใช้และชุดใดยังต้องเก็บไว้ เราก็จะเปลี่ยนรุ่นได้โดยไม่ฝากความเข้ากันได้ทั้งหมดไว้กับการที่ NVS ยังอ่านตัวเลขออกมาได้