14.2.2.1. Đóng băng tập lệnh vào firmware

Một module đóng băng là tệp .py được biên dịch sang bytecode và liên kết vào ảnh firmware tại thời điểm build. Khi chạy, runtime nhập module đóng băng trực tiếp từ bộ nhớ flash, không cần kiểm tra hệ thống tệp trên đĩa. Đối với sản phẩm xuất xưởng, đây là nơi thích hợp để đặt mã ứng dụng: người dùng cuối không thể xóa, tệp .py cũ trên thẻ SD không thể ghi đè, và camera luôn chạy cùng một mã mỗi lần khởi động bất kể ổ đĩa có gì (hay không có gì).

Trang này trình bày trình tự khởi động mà camera thực hiện, sau đó hướng dẫn cách manifest.py và chỉ thị freeze đưa ứng dụng vào quá trình build.

14.2.2.1.1. Trình tự khởi động

Những gì chạy, và khi nào, trên camera khi thoát khỏi trạng thái reset:

  • Bootloader. Khi bật nguồn, hệ thống vào một khoảng thời gian DFU ngắn mà IDE sử dụng để đẩy cập nhật firmware. Cửa sổ này đóng sau vài giây và bootloader chuyển điều khiển sang MicroPython. Một tập lệnh đang chạy có thể vào lại cửa sổ này theo yêu cầu bằng cách gọi machine.bootloader().

  • Khởi tạo hệ thống tệp đóng băng. Trước khi bất kỳ mã ứng dụng nào chạy, runtime khởi tạo các hệ thống tệp. Flash nội bộ được gắn kết tại /flash (và được định dạng trống nếu không có gì ở đó). Nếu có thẻ SD tệp đánh dấu SKIPSD không tồn tại trên flash nội bộ, thẻ SD sẽ được gắn kết tại /sdcard. ROMFS, khi build có bao gồm, được tự động gắn kết tại /rom. Thư mục làm việc được đặt thành thư mục boot (/sdcard nếu thẻ đã gắn kết, /flash nếu không), và sys.path được điền với /flash, /flash/lib, /sdcard, /sdcard/lib, /rom, và /rom/lib. Quá trình thiết lập trên flash được xử lý bởi một module đóng băng gọi là _boot.py -- cơ sở hạ tầng cổng và board, không phải hook ứng dụng. Ứng dụng không tùy chỉnh _boot.py; quá trình build mới làm vậy. Đặt tệp SKIPSD lên flash từ IDE là cách được hỗ trợ để làm camera khởi động từ flash nội bộ thay vì thẻ SD.

  • Thiết lập trước REPL. boot.py chạy mỗi lần soft reset -- khởi động lạnh, Ctrl-D từ REPL, tập lệnh đang chạy thoát ra, và phục hồi watchdog -- trước khi REPL trở nên có thể truy cập. Nhiệm vụ của nó là chuẩn bị môi trường mà phần còn lại của hệ thống chạy trong đó: loại thiết lập mà REPL, ứng dụng, và bất kỳ công cụ phục hồi nào đều cần để hoạt động. Đây không phải là nơi đặt bản thân ứng dụng. main.py là điểm vào của ứng dụng.

  • Vòng lặp chính. main.py là vòng lặp chính của ứng dụng. Chạy một lần khi khởi động lạnh, ngay sau boot.py. Không chạy lại trong các lần soft reset tiếp theo -- camera chuyển xuống REPL thay vào đó. Sự bất đối xứng đó quan trọng cho quá trình phát triển (Ctrl-D chuyển xuống REPL mà không chạy lại vòng lặp, để nhà phát triển có thể kiểm tra trạng thái) nhưng không quan trọng cho sản xuất: camera triển khai thực tế chỉ gặp reset khi bật nguồn, watchdog, và hard reset, tất cả đều là hardware reset đi vào lại con đường khởi động lạnh và chạy main.py lại.

14.2.2.1.2. Đóng băng vào firmware

Tập hợp module đóng băng của một board được khai báo trong boards/<TARGET>/manifest.py trong cây firmware. Manifest là một tệp Python nhỏ gọi một số chỉ thị:

  • freeze("$(OMV_LIB_DIR)/", "foo.py") -- đưa một foo.py vào build.

  • package("mylib", base_path="...") -- đưa một gói Python nhiều tệp vào, giữ nguyên cấu trúc thư mục của nó dưới base path đã cho.

  • include("...") -- kéo vào một tệp manifest khác. Các manifest của board sử dụng điều này để chia sẻ tập module chung.

  • require("logging") -- kéo vào một module micropython-lib upstream đã đặt tên.

Một manifest ứng dụng tối giản thêm một dòng freeze cho mỗi tập lệnh cấp cao nhất và một dòng package cho mỗi gói mà ứng dụng phụ thuộc vào.

14.2.2.1.2.1. Nơi lưu trữ mã nguồn

Mã nguồn ứng dụng nằm trong scripts/libraries/ trong cây firmware, bên cạnh các module mà build đã đóng băng. Biến manifest $(OMV_LIB_DIR) mở rộng đến đường dẫn đó, nên các mục manifest vẫn ngắn gọn. Chỉnh sửa manifest đã là thao tác trong cây, nên giữ nguồn trong cây tránh việc phải xoay sở với repo dự án riêng biệt trên đường dẫn phân giải.

Cấu trúc điển hình cho một ứng dụng xuất xưởng với một main.py duy nhất cùng một gói hỗ trợ:

scripts/libraries/
    main.py
    my_lib/
        __init__.py
        helpers.py

Và trong boards/<TARGET>/manifest.py của board, một dòng freeze cho tập lệnh và một dòng package cho gói:

freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")

Tập lệnh đơn tệp -- main.py ở đây, nhưng quy tắc tương tự áp dụng cho boot.py hay bất kỳ trình trợ giúp độc lập nào -- sử dụng freeze. Gói nhiều tệp sử dụng package. Thêm một tập lệnh nữa là thêm một dòng freeze; thêm một gói nữa là thêm một dòng package.

14.2.2.1.2.2. Build và nạp firmware

Khi manifest đã sẵn sàng, build firmware hoàn toàn như chương firmware mô tả:

make -j$(nproc) -C lib/micropython/mpy-cross   # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET>                # builds the firmware

Đầu ra nằm trong build/<TARGET>/bin/

build/<TARGET>/bin/
    firmware.bin     # flash through the IDE
    romfs0.img       # flash through the IDE in a separate step

Nạp .bin.img qua IDE để có camera mà ứng dụng là một phần của build.

Trình tự khởi động ở trên là điều làm cho việc đưa vào build có hiệu quả: runtime phân giải boot.pymain.py thành các bản sao đóng băng trước khi kiểm tra hệ thống tệp, nên camera xuất xưởng chạy mã của build ngay cả khi thẻ SD có boot.py cũ từ quá trình phát triển.

14.2.2.1.2.3. Thứ tự tra cứu

Ngữ nghĩa ghi đè khác nhau đối với con đường thực thi boot.py / main.py và đối với các câu lệnh import thông thường. Biết cái nào là cái gì quan trọng cho cả sản xuất lẫn phát triển:

  • Đối với boot.pymain.py: runtime tìm kiếm bản sao đóng băng trước, rồi mới đến hệ thống tệp. Một boot.py đóng băng không thể bị ghi đè bằng cách đặt một tệp lên thẻ SD -- bất kỳ ai giữ camera cũng không thể thay đổi điểm vào mà không reflash.

  • Đối với import foo: runtime tìm kiếm sys.path trước -- bao gồm /flash, /sdcard, /rom, và các thư mục con lib của chúng -- rồi mới đến module đóng băng. Một foo.py cùng tên trên flash hay SD sẽ ghi đè foo đóng băng. Đây là tiện ích phát triển: đặt module đã sửa lên thẻ, soft-reset, xem thay đổi mà không cần reflash.

Sản phẩm xuất xưởng muốn ngăn hành vi filesystem-ghi đè-đóng băng đối với import có thể xóa sys.path sớm trong boot.py

import sys

sys.path.clear()

Khi sys.path rỗng, tất cả import đều phân giải từ các module đóng băng; không có gì trên flash, SD, hay ROMFS có thể che khuất chúng.

14.2.2.1.2.4. Vấn đề tài nguyên

Đóng băng rất tốt cho mã. Nó không tốt cho các tài nguyên nhị phân lớn: tệp mô hình (ML), bảng nhãn, cấu hình JSON, mẫu ảnh. Nhúng những thứ đó dưới dạng Python literals làm phình mã nguồn, biên dịch lại chậm, và lãng phí container bytecode cho dữ liệu mà interpreter sẽ đọc thô anyway. Trang Tạo ảnh ROMFS đề cập đến hệ thống tệp flash chỉ đọc lấp đầy khoảng trống này.