เรื่องจริงที่บอร์ด ESP32 โดนทิ้งอยู่ในกล่อง “เพราะคิดว่าเสีย” สองปี กลับมามีชีวิตใหม่ในชื่อ “ลาซารัส” — พร้อมเปิดเผยเคล็ดลับ 7-bit ของ ESPrtk
ESP32 นึกว่าเธอตายเสียแล้ว
สองปีกว่าที่แล้ว ผมเป็นมือใหม่สนใจเรื่องทำ RTK Base Station ตัดสินใจลองศึกษาโครงการ ESPrtk ของทีมงานเวียดนาม ในตอนนั้นพบว่าน่าสนใจ เพราะราคาของเฟิร์มแวร์หลักพันสามารถซื้อหาได้ แต่ตอนนั้นติดที่ผมยังไม่ได้ซื้อบอร์ดตัวรับเช่น UBlox Zed F9P เพียงแต่ได้อีเมล์ไปถามวิธีการลองใช้ Trial จากทีมงาน ESPrtk
ผมได้ flash firmware ของ ESPrtk ลงบน ESP32-WROOM-32 ตัวหนึ่งเพื่อทดสอบดูความสามารถ — ESPrtk เป็น firmware เชิงพาณิชย์แบบ closed-source สำหรับงาน RTK GNSS ที่มีชื่อเสียงในวงการ DIY surveyor ทั่วโลก
หลังจากทดสอบเสร็จ ผมพยายาม flash firmware อื่นกลับเข้าไป — Arduino blink ก็ไม่ได้, ESP-IDF ก็ไม่ได้, ลอง erase flash ก็เหมือนได้ผลแต่ ESP32 boot ไม่ขึ้น
ผมก็เลยตัดสินใจ “เอาไว้ก่อน” — เอาปากกาเขียนเหล็กมาจุดไปห้าจุดเพื่อแสดงเครื่องหมายว่าเสียแล้วแล้วโยนใส่กล่องเก็บของพร้อมกับเขียนป้ายไว้ในใจว่า “ตายแล้ว” และซื้อตัวใหม่มาทำงานอื่นต่อ
ผมทิ้งโครงการที่จะทำ DIY ทำ RTKBase ด้วยตัวเองไปนาน จนกระทั่งหลังๆได้ดูคลิปจากยูทูปจากหลายๆท่าน ที่นำเสนอเฟิร์มแวร์ที่ฟรี เลยกลับมาทำอีกครั้งด้วย ESP32 (V1) และ Orange PI Zero 3 (V2) จนโครงการสำเร็จใช้งานได้ดี
เมื่อสัปดาห์ที่แล้ว ระหว่างที่กำลังทำโครงการ SDL30 Collector ผมเห็นกล่องนั้นแล้วคิดว่า “ลองตรวจดูสักครั้งสุดท้ายก่อนทิ้ง” — ด้วยการปรึกษากับคุณคล็อดเอไอ ปรากฏว่ามัน ยังกู้คืนได้! และที่สำคัญกว่านั้น… ผมได้ค้นพบเคล็ดการออกแบบของ ESPrtk ที่น่าทึ่งพอสมควร
บทความนี้จะเล่าทั้งวิธีกู้คืน และเปิดเผยว่าทำไม ESPrtk ถึงจำกัด “Cancel” ได้แค่ 3 ครั้ง — โดยใช้กลไกของ ESP32 ที่ออกแบบมาอย่างแยบยล



ทำไม ESP32 ถึงเหมือนตายแล้ว
ESP32 มีระบบป้องกัน firmware ที่ Espressif ออกแบบมาเรียกว่า eFuses — เป็น bits พิเศษภายในชิปที่ “burn” ได้ครั้งเดียว เปลี่ยนจาก 0 → 1 ได้ แต่เปลี่ยนกลับไม่ได้เลย ประมาณว่าแม่น้ำนี้ไม่มีวันไหลย้อนกลับ
ESPrtk ใช้ eFuse mechanism นี้เพื่อ:
- เปิด Flash Encryption — ข้อมูลใน flash ถูก encrypt ด้วย AES-256
- ปิด UART Download Encrypt/Decrypt — flash firmware ใหม่ผ่าน UART ไม่ได้ในรูปปกติ
- ปิด JTAG — ปิด hardware debug ถาวร
- ปิด Flash Cache ใน Download Mode — เพิ่มความปลอดภัย
ผลคือเมื่อ flash ESPrtk ครั้งแรก ESP32 จะถูก lock — boot ได้แต่ ESPrtk firmware เท่านั้น ผู้ใช้ที่ลอง flash อะไรก็ตามที่ไม่ใช่ ESPrtk จะเจอ boot loop หรือ random characters บน serial port
นี่ไม่ใช่ bug — เป็น feature ที่ผู้พัฒนาตั้งใจป้องกัน intellectual property ของตัวเอง ซึ่งเป็นสิทธิ์ที่เขาทำได้ แต่ผลพลอยได้คือ DIY hobbyist ที่ลองแล้วเปลี่ยนใจ จะรู้สึกว่า ESP32 ของตัวเอง “เสีย” เหมือนในกรณีของผม
ขั้นตอนที่ 1 ตรวจสอบก่อนตัดสินใจทิ้ง
อย่าทิ้ง ESP32 ก่อนตรวจ! ติดตั้ง esptool (ถ้ายังไม่มี):
pip install esptool
ต่อ ESP32 เข้ากับคอม run คำสั่ง:
espefuse.py --port /dev/ttyUSB0 summary
(บน Windows ใช้ --port COM3 หรือ port อื่นตามที่ใช้)
Output จริงจาก ESP32 ของผม
Security fuses:
ABS_DONE_0 Secure boot V1 enabled = False R/W (0b0) ← OK
ABS_DONE_1 Secure boot V2 enabled = False R/W (0b0) ← OK
UART_DOWNLOAD_DIS Disable UART download mode = False R/W (0b0) ← OK
DISABLE_DL_ENCRYPT Disable flash encryption in DL = True R/W (0b1) ← burned
DISABLE_DL_DECRYPT Disable flash decryption in DL = True R/W (0b1) ← burned
DISABLE_DL_CACHE Disable flash cache in DL = True R/W (0b1) ← burned
Flash fuses:
FLASH_CRYPT_CNT Flash encryption counter = 1 R/W (0b0000001) ← burned แต่ R/W
FLASH_CRYPT_CONFIG Flash encryption config = 15 R/W (0xf)
Jtag fuses:
JTAG_DISABLE Disable JTAG = True R/W (0b1) ← เสียถาวร
สัญญาณดี 3 อย่างที่ได้จาก output นี้
ABS_DONE_0และABS_DONE_1= False — Secure Boot ยังไม่ถูกเปิด เรายังสามารถ flash bootloader ใหม่ที่ไม่ได้ sign ได้UART_DOWNLOAD_DIS= False — ยังใช้ UART download mode ได้FLASH_CRYPT_CNTยังเป็น R/W — eFuse นี้ยังเขียนเพิ่มได้ ไม่ได้ถูก write-protect ในกรณีของผมจะสังเกตว่าเลขFLASH_CRYPT_CNTเป็นเลข 1 อยู่
สิ่งที่เสียถาวร: JTAG และการ encrypt/decrypt ผ่าน UART download — แต่สำหรับ DIY ทั่วไปไม่ได้ใช้อยู่แล้ว
สิ่งที่กู้ได้: ทุกอย่างที่เหลือ — WiFi, Bluetooth, GPIOs, UARTs, การ flash firmware ปกติ
ความลับที่ ESPrtk ไม่ได้บอกกันตรงๆกับเคล็ดลับ FLASH_CRYPT_CNT 7-bit
ก่อนจะลงมือกู้คืน ขอเล่าเรื่องที่น่าสนใจที่สุดของบทความนี้
ESP32 ออกแบบ FLASH_CRYPT_CNT เป็น 7-bit field ที่ทำงานตามหลัก parity:
| ค่า binary | bits ที่ตั้ง | Parity | Encryption |
|---|---|---|---|
0b0000000 | 0 | Even | OFF (default) |
0b0000001 | 1 | Odd | ON |
0b0000011 | 2 | Even | OFF |
0b0000111 | 3 | Odd | ON |
0b0001111 | 4 | Even | OFF |
0b0011111 | 5 | Odd | ON |
0b0111111 | 6 | Even | OFF |
0b1111111 | 7 | Odd | ON (saturated, ปิดไม่ได้แล้ว) |
หลักการคือ — ถ้าจำนวน bits ที่ตั้ง = เลขคี่ → encryption เปิด, ถ้า = เลขคู่ → encryption ปิด
เนื่องจาก eFuse bits เปลี่ยนจาก 0 → 1 ได้เท่านั้น (ไม่กลับ) Espressif จึงออกแบบให้ user toggle encryption ได้สูงสุด 7 ครั้ง ก่อนจะ saturated แบบว่าอิ่มแล้วทำอะไรต่อไม่ได้อีกแล้ว
ทำไม ESPrtk ถึงจำกัด “Cancel” ได้แค่ 3 ครั้ง
จากเอกสารของ ESPrtk เอง เขียนไว้ชัดเจนว่า:
“This Register–Cancel cycle is limited to 3 times. The maximum number of Registrations is 4 and Cancel is 3.”
4 Register + 3 Cancel = 7 toggles — ลงตัวพอดีกับ 7 bits ของ FLASH_CRYPT_CNT! 🎯
ลองดูตารางการใช้งานจริงของ ESPrtk:
| Action | FLASH_CRYPT_CNT (Binary) | Encryption | สถานะ | FLASH_CRYPT_CNT (Decimal) |
|---|---|---|---|---|
| ESP32 ใหม่จากโรงงาน | 0b0000000 | OFF | Generic ESP32 | 0 |
| Register #1 | 0b0000001 | ON | ESPrtk | 1 |
| Cancel #1 | 0b0000011 | OFF | Generic ESP32 | 3 |
| Register #2 | 0b0000111 | ON | ESPrtk | 7 |
| Cancel #2 | 0b0001111 | OFF | Generic ESP32 | 15 |
| Register #3 | 0b0011111 | ON | ESPrtk | 31 |
| Cancel #3 | 0b0111111 | OFF | Generic ESP32 | 63 |
| Register #4 | 0b1111111 | ON (saturated) | ESPrtk lock ถาวร | 127 |
ถ้าผู้ใช้อยากจะเปลี่ยนใจทางทีมงาน ESPrtk จะให้ไฟล์เขียนเฟิร์มแวร์ชื่อ Cancel.bin คือ firmware ที่เขาเซ็น (signed) ที่ตอน run จะ burn FLASH_CRYPT_CNT เพิ่ม 1 bit เพื่อ toggle encryption จาก odd กลับเป็น even
จากตาราง ในกรณีของผม FLASH_CRYPT_CNT = 1 ผมจะต้องเขียนเป็นเลข 3 เพื่อให้กลับมาสู่โหมดปกติ
นี่คือการออกแบบที่ฉลาดมาก — ใช้ hardware feature ของ Espressif ให้เป็นทั้ง encryption mechanism และ transaction counter ในตัวเดียว!
แต่ถ้าเราข้าม Cancel.bin ไปเลย?
ถ้าเรา burn FLASH_CRYPT_CNT โดยตรงด้วย espefuse.py — ผลลัพธ์ทาง hardware เหมือนกัน 100% กับ Cancel.bin ของ ESPrtk!
ความแตกต่างเดียวคือ:
- ESPrtk’s Cancel.bin — ทำผ่าน firmware ที่เซ็น และเขาบันทึกสถานะใน secure partition
- วิธี
espefuse.py— ทำตรงๆ ที่ระดับ hardware ไม่ผ่าน firmware ของใครเลย
นั่นคือสิ่งที่ผมจะทำต่อไป
ขั้นตอนที่ 2 คำสั่งกู้คืน 3 บรรทัด
คำสั่งที่ 1 ปิด Flash Encryption
espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT 3
ระวัง: ใน esptool v4.x ต้องระบุค่าเป้าหมายเป็นเลขคี่ ในกรณีของผมต้องใช้เลข 3 (binary 0b11 = 2 bits ที่ตั้ง) ไม่ใช่ 1 — เพราะค่า 1 หมายถึง “ให้มี 1 bit ที่ตั้ง” ซึ่งเป็นค่าปัจจุบันอยู่แล้ว ระบบจะข้ามไป (เสียเวลาผม 2 นาที 555)
Output ที่ได้:
=== Run "burn_efuse" command ===
The efuses to burn:
from BLOCK0
- FLASH_CRYPT_CNT
Burning efuses:
- 'FLASH_CRYPT_CNT' (Flash encryption is enabled if this field has
an odd number of bits set) 0b0000001 -> 0b0000011
This is an irreversible operation!
Type 'BURN' (all capitals) to continue.
BURN
BURN
BLOCK0 - OK (all write block bits are set)
Reading updated efuses...
Successful
ระบบถามให้พิมพ์ BURN (ตัวพิมพ์ใหญ่ทั้งหมด) เพื่อยืนยัน — เพราะ irreversible operation
ตรวจสอบว่าสำเร็จ
espefuse.py --port /dev/ttyUSB0 summary | grep FLASH_CRYPT_CNT
ผลที่ต้องการ:
FLASH_CRYPT_CNT (BLOCK0) Flash encryption counter = 3 R/W (0b0000011)
0b0000011 = 2 bits = even = encryption ปิดแล้ว ✓
ในมุมของ ESPrtk — คุณเพิ่ง “Cancel #1” สำเร็จ! 😄
คำสั่งที่ 2 ลบ ESPrtk firmware ที่ encrypt อยู่
หลัง disable encryption แล้ว ESPrtk firmware ใน flash จะกลายเป็นข้อมูลขยะ (เพราะถูก encrypt แต่ระบบไม่ decrypt อีก) ต้องลบออก:
สำคัญ ถอด USB ออกแล้วเสียบใหม่ก่อน เพื่อให้ ESP32 reset
esptool.py --port /dev/ttyUSB0 erase_flash
Output จริง:
esptool.py v4.11.0
Serial port /dev/ttyUSB0
Connecting.....
Chip is ESP32-D0WD-V3 (revision v3.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse,
Coding Scheme None
Crystal is 40MHz
MAC: 88:13:bf:0b:71:f4
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 0.0 seconds.
Hard resetting via RTS pin...
คำสั่งที่ 3: Flash firmware ใหม่
ตอนนี้ ESP32 พร้อมรับ firmware ใดๆ ก็ได้ ถ้าใช้ Arduino IDE: เลือก board “ESP32 Dev Module” แล้ว upload ตามปกติ ถ้า flash bin file โดยตรง:
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash \
0x1000 bootloader.bin \
0x8000 partitions.bin \
0x10000 firmware.bin
เมื่อไหร่ที่ “กู้ไม่ได้”
ไม่ใช่ทุกกรณีของ ESPrtk lock จะกู้ได้ — กรณีเหล่านี้ ESP32 lock ถาวรจริง:
| สภาพ eFuse | เหตุผล |
|---|---|
ABS_DONE_0 = True | Secure Boot V1 เปิด — flash bootloader ที่ไม่ sign ไม่ได้ |
ABS_DONE_1 = True | Secure Boot V2 เปิด — ต้องใช้ private key |
UART_DOWNLOAD_DIS = True | ปิด UART download — ไม่มีทาง flash อะไรได้อีก |
FLASH_CRYPT_CNT write-protected ที่ค่า odd | Encryption lock ถาวร |
FLASH_CRYPT_CNT = 0b1111111 (saturated) | Toggle ครบโควต้า 7 ครั้งแล้ว |
โดยเฉพาะ saturated state — ถ้าใครใช้ ESPrtk Register #4 ครบแล้ว FLASH_CRYPT_CNT จะเต็ม 0b1111111 และ lock encryption ON ตลอดไป — ในกรณีนี้ผู้ใช้สามารถใช้ ESP32 ตัวนั้นเป็น ESPrtk เท่านั้น (ตามที่ ESPrtk ระบุไว้)
ก่อนเริ่ม recovery ตรวจ summary ทุกครั้ง — ถ้าเข้าเงื่อนไขเหล่านี้ คุณคงต้องซื้อ ESP32 ใหม่ครับ
ESP32 ของผมในวันนี้ “Lazarus”
หลังกู้คืนเสร็จ ผมตั้งชื่อ ESP32 ตัวนี้ว่าคุณ “ลาซารัส” ตามชื่อในตำนานที่ฟื้นจากความตาย มันยังคงเป็น ESP32-D0WD-V3 rev 3.1 ที่สมบูรณ์:
- MAC:
88:13:bf:0b:71:f4 - WiFi + Bluetooth: ใช้งานได้ทั้งหมด
- GPIOs: ครบทุกตัว
- UARTs: ครบ 3 ตัว
- Flash: 4 MB ปกติ
สิ่งเดียวที่หายไปคือ JTAG debug — ซึ่งสำหรับ DIY ทั่วไปไม่ได้ใช้อยู่แล้ว
ในมุมของ ESPrtk: ผมเพิ่ง “Cancel #1” — ยัง register/cancel ได้อีก รวม 6 toggles แต่ในความเป็นจริงผมคงไม่กลับไปใช้ ESPrtk แล้วเพราะ:
- ผมมี V1 RTKBase บนบอร์ด ESP32 ใช้ firmware ของตัวเองที่ผมเขียนเอง เหมาะกับงานของผมมากกว่า
- ผมมี V3 RTKBase รุ่นล่าสุดบนบอร์ด OPZ3 ที่สามารถปรับแต่งได้ไม่แพ้กัน
- ตอนนี้นำ ESP32 มาทำ SDL30 Collector ใช้ ESP32-S3-N16 ที่มี flash ใหญ่กว่า (16 MB) เหมาะกับการ log ข้อมูลสำรวจบนกล้องระดับ SDL30
- ลาซารัสเหมาะที่จะไปต่อชีวิตในโครงการ DIY อื่นๆ ที่ต้องการ ESP32 ธรรมดาๆ
สิ่งที่ได้เรียนรู้
1. อย่าทิ้ง ESP32 ก่อนตรวจ eFuse
ทุกครั้งที่คิดว่า ESP32 “bricked” ให้ run espefuse.py summary ก่อน — 90% ของกรณีมีทางกู้คืน
ผมเสียเวลาสองปี คิดว่า ESP32 ตัวนี้เสีย ทั้งที่ใช้เวลา 2 นาทีตรวจสอบเท่านั้น
2. Engineering ที่ฉลาดของ Espressif และ ESPrtk
FLASH_CRYPT_CNT เป็นการออกแบบที่น่าทึ่ง — ใช้ 7-bit field เป็นทั้ง parity counter และ encryption flag ในตัวเดียว ทำให้ Espressif ให้ผู้ใช้มีโอกาสครั้งที่สองได้แม้ใช้ eFuse one-way ที่ย้อนกลับไม่ได้
ESPrtk ใช้ feature นี้ต่อยอดเป็น license counter (4 register + 3 cancel) ซึ่งเป็น engineering decision ที่ลงตัวพอดี — เป็นการเคารพ design ของ Espressif อย่างเต็มที่
3. Closed-source ไม่ได้แปลว่าไม่ดี
ผมต้องบอกตรงๆ ว่า ESPrtk เป็นผลิตภัณฑ์ที่ดีมาก performance ของเขาดี documentation ละเอียด พัฒนาต่อเนื่องมาตั้งแต่ปี 2017 ทีมงานเขามีความสามารถสูง แต่ closed-source หมายความว่า:
- ผู้ใช้ปรับแต่งไม่ได้ตามต้องการเฉพาะ (เช่น ECEF coordinates เพื่อข้าม geoid model )
- เรียนรู้การทำงานภายในไม่ได้
- แก้ bug เองไม่ได้
- ขึ้นอยู่กับการตัดสินใจของผู้พัฒนาในการ support hardware ใหม่
4. ก่อนหน้าที่ผมรู้จัก open-source
ก่อนผมจะรู้จัก stefal/rtkbase บน GitHub ผมก็เคยเกือบจะซื้อ ESPrtk เพราะมันเป็น solution ที่พร้อมใช้ที่สุดในตลาด แต่ถ้าผมซื้อตอนนั้น — เส้นทางการเรียนรู้ของผมจะถูกปิด ผมคงไม่ได้:
- เข้าใจการทำงานของ RTCM/NMEA ในระดับลึก
- เรียนรู้การ configure UM980 ผ่าน UART config files
- พัฒนาการป้อนพิกัดแบบ ECEF coordinate solution เข้าใน RTKBase
- สร้าง RTKBase V3 บน Linux SBC (Orange Pi Zero 3, Raspberry Pi 2B)
- แบ่งปันความรู้ DIY ผ่านทางบล็อกของผม
5. ยุคของ AI กับ DIY developer
ในอดีต DIY developer ที่ไม่ใช่ professional programmer เผชิญกับเอกสารที่อ่านยาก, forum ที่ไม่ตอบ, Stack Overflow ที่เก่า, community ที่ gatekeeping
ตอนนี้ AI (Claude, ChatGPT, Gemini, Deepseek) ทำหน้าที่เป็น patient teacher ที่ถามได้ไม่จำกัด เป็น pair programmer ที่ช่วย debug — แต่ AI ไม่แทนที่ skill มันเร่ง learning curve ของคนที่ตั้งใจเรียนรู้
ผมทำการกู้คืน ESP32 นี้เสร็จในเวลาเพียง 2 นาที ด้วยการคุยกับ Claude ระหว่างทาง — ถ้าเป็น 5 ปีก่อน ผมคงต้องอ่าน datasheet ESP32 ทั้งเล่ม, คลำหา forum thread เป็นวันๆ, และอาจไม่กล้า burn eFuse เพราะกลัวพังเพิ่ม
สรุป 3 คำสั่งที่ช่วยชีวิต ESP32
# 1. ปิด Flash Encryption (พิมพ์ BURN เพื่อยืนยัน)
espefuse.py --port /dev/ttyUSB0 burn_efuse FLASH_CRYPT_CNT 3
# 2. ลบ firmware เก่า (ถอด USB เสียบใหม่ก่อน)
esptool.py --port /dev/ttyUSB0 erase_flash
# 3. Flash firmware ใหม่ (หรือใช้ Arduino IDE upload ปกติ)
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash \
0x1000 bootloader.bin 0x8000 partitions.bin 0x10000 firmware.bin
หวังว่าบทความนี้จะช่วยนัก DIY และนักประดิษฐ์ที่ประสบปัญหาเดียวกันให้สามารถกู้คืน ESP32 ของตัวเองได้ — ก่อนตัดสินใจซื้อใหม่ ลองตรวจดูก่อนว่ามันยังมีโอกาสฟื้นไหม
และถ้าใครยังไม่เริ่มทำ RTK base station ของตัวเอง — ขอแนะนำให้ลอง stefal/rtkbase หรือ incarvr6/rtkbase บน GitHub ก่อนตัดสินใจซื้อ closed-source solution แล้วคุณจะค้นพบว่าโลกของ open-source RTK กว้างใหญ่กว่าที่คิดมาก
ภาคผนวก: References
- Espressif eFuse documentation
- ESP32 Flash Encryption Programming Guide
- esptool.py GitHub
- ESPrtk Cancel and Re-register Tutorial
บทความนี้เขียนจากประสบการณ์จริงในการกู้ ESP32-WROOM-32 (ESP32-D0WD-V3 rev 3.1, MAC 88:13:bf:0b:71:f4) ที่ถูก lock โดย ESPrtk firmware เมื่อปี 2024 และกู้คืนสำเร็จในเดือนเมษายน 2026 โดยใช้ esptool/espefuse v4.11.0 บน Manjaro Linux
ขอบคุณ Claude จาก Anthropic ที่เป็นเพื่อนคู่คิดในการ debug และค้นหาความลับของ FLASH_CRYPT_CNT ครับ