boot.py và main.py trong MicroPython: chương trình tự chạy khi cấp nguồn, chế độ an toàn, ghi lỗi
boot.py và main.py trong MicroPython: thứ tự chạy khi khởi động, nên đặt gì vào mỗi file, chế độ an toàn bằng nút BOOT, bắt lỗi và ghi lỗi ra file, watchdog, chép file bằng Thonny, mpremote.
Trong MicroPython, chương trình chỉ tự chạy khi cấp nguồn nếu được lưu trên bo với đúng tên main.py; còn boot.py chạy trước nó để cấu hình. Bài này giải thích thứ tự MicroPython chạy các file khi khởi động, nên đặt gì vào boot.py và main.py, cách thoát khi main.py làm bo bị treo, chế độ an toàn bằng nút BOOT, bắt lỗi và ghi lỗi ra file, watchdog, chép file bằng Thonny và mpremote, kèm boot.py, main.py mẫu cho ESP32.
1. MicroPython chạy những file nào khi khởi động

- Firmware chạy
_boot.pynằm sẵn trong firmware: gắn hệ thống file trên flash (lần đầu thì tạo mới). - Chạy
boot.pynếu có trên bo. - Chạy
main.pynếu có trên bo. - Khi main.py kết thúc (hết lệnh, hoặc lỗi, hoặc bấm Ctrl+C), bo về dấu nhắc REPL
>>>.
Bấm Ctrl+D trong REPL là khởi động mềm: chạy lại boot.py và main.py nhưng không khởi động lại phần cứng. Chạy file trong Thonny bằng nút Run không cần lưu thành main.py; chỉ khi muốn bo tự chạy lúc cắm điện (không cắm máy tính) mới cần lưu thành main.py trên bo.
2. Nên đặt gì vào boot.py, gì vào main.py

boot.py phải ngắn và không bao giờ chờ vô hạn: một vòng while not wlan.isconnected() không giới hạn thời gian sẽ treo bo mãi khi router tắt. main.py chứa chương trình chính và nên bắt mọi lỗi để tự phục hồi. Hàm, driver cảm biến dài đặt thành module riêng (ví dụ cam_bien.py) rồi import trong main.py: dễ thử từng phần trong REPL.
3. boot.py mẫu: chế độ an toàn và Wi-Fi có giới hạn thời gian
# PYWORLD.VN - boot.py cho ESP32 chạy MicroPython: chạy trước main.py mỗi lần khởi động, chỉ làm việc ngắn và an toàn
# PYWORLD.VN - Trong 2 giây đầu, bấm nút BOOT (GPIO0) để vào chế độ an toàn: main.py dừng ngay, REPL trống để sửa file
# PYWORLD.VN - Kết nối Wi-Fi có giới hạn thời gian; thay TEN_WIFI, MAT_KHAU_WIFI
import time
import machine
import network
TEN_WIFI = "TEN_WIFI"
MAT_KHAU_WIFI = "MAT_KHAU_WIFI"
CHO_WIFI_GIAY = 10
CHO_NUT_MS = 2000
FILE_AN_TOAN = "an_toan.co"
# Không giữ nút BOOT lúc bấm EN: ESP32 sẽ vào chế độ nạp firmware. Bấm sau khi đã khởi động.
nut_boot = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
print("boot.py: bam nut BOOT trong 2 giay de vao che do an toan")
an_toan = False
bat_dau = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), bat_dau) < CHO_NUT_MS:
if nut_boot.value() == 0:
an_toan = True
break
time.sleep_ms(50)
if an_toan:
open(FILE_AN_TOAN, "w").close() # main.py thấy file này thì dừng
print("boot.py: che do an toan")
else:
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
wlan.connect(TEN_WIFI, MAT_KHAU_WIFI)
bat_dau = time.ticks_ms()
while not wlan.isconnected() and time.ticks_diff(time.ticks_ms(), bat_dau) < CHO_WIFI_GIAY * 1000:
time.sleep_ms(200)
if wlan.isconnected():
print("boot.py: Wi-Fi", wlan.ifconfig()[0])
else:
print("boot.py: chua ket noi duoc Wi-Fi, main.py van chay")

Nút BOOT trên bo ESP32 nối với GPIO0. Lưu ý quan trọng: không giữ BOOT trong lúc bấm EN, vì khi đó ESP32 vào chế độ chờ nạp firmware chứ không chạy MicroPython. Cách đúng: bấm EN, thả ra, rồi bấm BOOT trong 2 giây đầu. boot.py tạo một file đánh dấu, main.py thấy file đó thì không chạy chương trình chính.
4. main.py mẫu: bắt lỗi, ghi lỗi ra file, watchdog
# PYWORLD.VN - main.py cho ESP32 chạy MicroPython: chương trình chính tự chạy sau boot.py, có watchdog và ghi lỗi ra file
# PYWORLD.VN - Nháy LED GPIO2, in thời gian chạy mỗi 5 giây; lỗi bất ngờ được ghi vào loi.txt rồi khởi động lại sau 10 giây
# PYWORLD.VN - Dừng bằng Ctrl+C thì watchdog vẫn chạy và sẽ khởi động lại chip sau 30 giây: muốn sửa file lâu, dùng chế độ an toàn
import os
import sys
import time
import machine
def la_che_do_an_toan():
try:
os.stat("an_toan.co") # boot.py tạo file này khi người dùng bấm BOOT
except OSError:
return False
os.remove("an_toan.co")
return True
def ghi_loi(loi):
with open("loi.txt", "a") as f:
f.write("ticks {}: ".format(time.ticks_ms()))
sys.print_exception(loi, f)
def vong_lap(led, cho):
lan_in = time.ticks_ms()
while True:
led.value(not led.value())
if time.ticks_diff(time.ticks_ms(), lan_in) >= 5000:
lan_in = time.ticks_ms()
print("main.py dang chay, {} giay".format(time.ticks_ms() // 1000))
cho.feed()
time.sleep_ms(500)
# Không dùng raise SystemExit ở đây: MicroPython coi đó là lệnh khởi động mềm lại, chế độ an toàn sẽ mất
if la_che_do_an_toan():
print("main.py: che do an toan, khong chay chuong trinh, ve REPL")
else:
led = machine.Pin(2, machine.Pin.OUT)
cho = machine.WDT(timeout=30000) # quá 30 giây không gọi feed() thì chip tự khởi động lại
try:
vong_lap(led, cho)
except KeyboardInterrupt:
print("Dung boi Ctrl+C; watchdog se khoi dong lai chip sau 30 giay")
except Exception as loi:
ghi_loi(loi)
print("Loi, da ghi vao loi.txt; khoi dong lai sau 10 giay")
time.sleep(10)
machine.reset()
- Bắt lỗi: lỗi bất ngờ (cảm biến rút dây, mất mạng) được ghi vào
loi.txtkèm dòng gây lỗi bằngsys.print_exception, rồi bo khởi động lại sau 10 giây thay vì đứng ở REPL. - Watchdog (
machine.WDT): nếu chương trình bị kẹt quá 30 giây không gọifeed(), chip tự khởi động lại. Watchdog đã bật thì không tắt được: dừng chương trình bằng Ctrl+C thì sau 30 giây bo vẫn tự khởi động lại. Muốn sửa file lâu, vào chế độ an toàn để watchdog không bị bật. - Không dùng
raise SystemExitđể dừng main.py: trên ESP32, MicroPython coi đó là lệnh khởi động mềm lại, chạy lại boot.py và main.py.
5. Chép file lên bo

Trong Thonny: mở file, File → Save as → MicroPython device, đặt tên boot.py hoặc main.py. Dùng dòng lệnh: cài pip install mpremote rồi chạy mpremote cp boot.py :boot.py, mpremote cp main.py :main.py, mpremote reset. Bo bị main.py làm treo: chạy mpremote rm :main.py (mpremote tự ngắt chương trình đang chạy trước khi xóa), hoặc vào chế độ an toàn.
6. Kết quả mong đợi

7. Lỗi hay gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Cắm điện ngoài thì chương trình không chạy | file chưa lưu trên bo, sai tên (Main.py, main.py.py) | lưu đúng tên main.py lên MicroPython device |
| Thonny không kết nối, REPL không hiện | main.py vòng lặp dày đặc, in liên tục | bấm Stop nhiều lần, hoặc chế độ an toàn, hoặc mpremote rm |
| Bo khởi động lại sau khi bấm Ctrl+C | watchdog đã bật vẫn chạy | dùng chế độ an toàn khi cần sửa lâu |
| Treo ở boot.py khi router tắt | chờ Wi-Fi không giới hạn | giới hạn thời gian như boot.py mẫu |
| Giữ BOOT rồi bấm EN thì không thấy REPL | ESP32 vào chế độ chờ nạp firmware | bấm EN trước, thả, rồi mới bấm BOOT |
8. Xem thêm
- Watchdog trên ESP32: vì sao mạch tự khởi động lại, đọc lý do reset
- LittleFS trên ESP32: lưu file trong flash, ghi log
- Kết nối Wi-Fi trên ESP32: quét mạng, tự kết nối lại, IP tĩnh
- ESP32 DevKit V1 30 chân, Type-C, CP2102
MicroPython là gì và cách nạp firmware MicroPython cho ESP32 có ở các bài cùng cụm chủ đề.
Xem thêm