14.2.2.1. 스크립트를 펌웨어에 프리징하기¶
frozen 모듈은 빌드 시점에 바이트코드로 컴파일되어 펌웨어 이미지에 링크된 .py 파일입니다. 런타임은 디스크상의 파일시스템을 전혀 들여다보지 않고 frozen 모듈을 플래시에서 곧바로 임포트합니다. 출하되는 제품에서 이것은 애플리케이션 코드를 두기에 가장 알맞은 위치입니다. 최종 사용자가 삭제할 것이 없고, SD 카드에 남아 있는 오래된 .py 파일이 덮어쓸 일도 없으며, 드라이브에 무엇이 있든(혹은 아무것도 없든) 카메라는 매 부팅마다 동일한 코드를 실행합니다.
이 페이지에서는 cam이 따르는 시작 시퀀스를 다룬 다음, manifest.py 와 freeze 디렉티브가 애플리케이션을 빌드에 구워 넣는 방법을 설명합니다.
14.2.2.1.1. 시작 시퀀스¶
리셋에서 막 깨어난 cam에서 무엇이, 언제 실행되는지:
부트로더. 전원이 켜지면 IDE가 펌웨어 업데이트를 푸시하는 데 사용하는 짧은 DFU 윈도우로 진입합니다. 이 윈도우는 몇 초 후에 닫히고 부트로더는 MicroPython에 제어를 넘깁니다. 실행 중인 스크립트는
machine.bootloader()를 호출하여 필요할 때 이 윈도우로 다시 진입할 수 있습니다.프리징된 파일시스템 초기화. 애플리케이션 코드가 실행되기 전에 런타임이 파일시스템을 올립니다. 내부 플래시는
/flash에 마운트됩니다(그리고 그곳에 아무것도 없으면 빈 상태로 포맷됩니다). SD 카드가 있고 또한SKIPSD라는 마커 파일이 내부 플래시에 존재하지 않으면, SD 카드가/sdcard에 마운트됩니다. 빌드에 ROMFS가 포함된 경우 ROMFS는/rom에 자동으로 마운트됩니다. 작업 디렉터리는 부팅 디렉터리로 설정되며(카드가 마운트되었으면/sdcard, 그렇지 않으면/flash),sys.path에는/flash,/flash/lib,/sdcard,/sdcard/lib,/rom,/rom/lib가 채워집니다. 플래시에 상주하는 이 설정은_boot.py라는 프리징된 모듈이 처리합니다. 이는 포트 및 보드 인프라이지 애플리케이션 훅이 아닙니다. 애플리케이션은_boot.py를 커스터마이즈하지 않습니다. 빌드가 그 역할을 합니다. IDE에서SKIPSD파일을 플래시에 떨어뜨리는 것은 cam이 SD 카드 대신 내부 플래시에서 부팅하도록 만드는 지원되는 방법입니다.REPL 이전 설정.
boot.py는 모든 소프트 리셋에서 실행됩니다. 콜드 부트, REPL에서의Ctrl-D, 실행 중인 스크립트의 복귀, 워치독 복구가 모두 해당되며, REPL에 접근 가능해지기 전에 실행됩니다. 그 역할은 시스템의 나머지가 실행될 환경을 준비하는 것입니다. REPL, 애플리케이션, 그리고 모든 복구 도구가 작동하기 위해 갖추어져 있어야 하는 종류의 설정입니다. 여기는 애플리케이션 자체가 사는 곳이 아닙니다.main.py가 애플리케이션의 진입점입니다.메인 루프.
main.py는 애플리케이션의 메인 루프입니다. 콜드 부트 시boot.py직후에 한 번 실행됩니다. 이후의 소프트 리셋에서는 다시 실행되지 않으며 cam은 대신 REPL로 떨어집니다. 그 비대칭성은 개발에는 중요하지만(Ctrl-D는 루프를 다시 실행하지 않고 REPL로 떨어지므로 개발자가 상태를 검사할 수 있습니다) 프로덕션에서는 그렇지 않습니다. 현장에 배치된 cam은 전원 켜기, 워치독, 하드 리셋을 겪는데, 이들은 모두 콜드 부트 경로로 다시 진입하여main.py를 다시 실행하는 하드웨어 리셋입니다.
14.2.2.1.2. 펌웨어로 프리징하기¶
보드의 프리징된 모듈 집합은 펌웨어 트리의 boards/<TARGET>/manifest.py 에 선언됩니다. 매니페스트는 몇 가지 디렉티브를 호출하는 작은 Python 파일입니다:
freeze("$(OMV_LIB_DIR)/", "foo.py")– 단일foo.py를 빌드에 구워 넣습니다.package("mylib", base_path="...")– 여러 파일로 구성된 Python 패키지를 주어진 base path 아래의 디렉터리 구조를 유지한 채 구워 넣습니다.include("...")– 다른 매니페스트 파일을 가져옵니다. 보드 매니페스트는 이를 사용하여 공통 모듈 집합을 공유합니다.require("logging")– 명명된 업스트림micropython-lib모듈을 이름으로 가져옵니다.
최소한의 애플리케이션 매니페스트는 최상위 스크립트당 freeze 줄 하나와, 애플리케이션이 의존하는 패키지당 package 줄 하나를 추가합니다.
14.2.2.1.2.1. 소스가 위치하는 곳¶
애플리케이션 소스는 펌웨어 트리의 scripts/libraries/ 아래, 빌드가 이미 프리징하는 모듈들과 함께 위치합니다. 매니페스트 변수 $(OMV_LIB_DIR) 는 그 경로로 확장되므로 매니페스트 항목이 짧게 유지됩니다. 매니페스트를 편집하는 것은 이미 트리 내부 작업이므로, 소스를 트리 내부에 두면 경로 해석 과정에서 별도의 프로젝트 리포지토리를 다룰 필요가 없습니다.
단일 main.py 와 그것을 지원하는 패키지를 출하하는 애플리케이션의 일반적인 레이아웃:
scripts/libraries/
main.py
my_lib/
__init__.py
helpers.py
그리고 보드의 boards/<TARGET>/manifest.py 에는 스크립트를 위한 freeze 줄 하나와 패키지를 위한 package 줄 하나를 둡니다:
freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")
단일 파일 스크립트 – 여기서는 main.py 이지만 같은 규칙이 boot.py 나 독립형 헬퍼에도 적용됩니다 – 는 freeze 를 사용합니다. 여러 파일로 구성된 패키지는 package 를 사용합니다. 스크립트를 하나 더 추가하는 것은 freeze 줄 하나를 더 추가하는 것이고, 패키지를 하나 더 추가하는 것은 package 줄 하나를 더 추가하는 것입니다.
14.2.2.1.2.2. 빌드 및 플래싱¶
매니페스트가 준비되면, 펌웨어 챕터 에 설명된 그대로 펌웨어를 빌드합니다:
make -j$(nproc) -C lib/micropython/mpy-cross # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET> # builds the firmware
출력물은 build/<TARGET>/bin/ 에 생성됩니다:
build/<TARGET>/bin/
firmware.bin # flash through the IDE
romfs0.img # flash through the IDE in a separate step
.bin 과 .img 를 IDE를 통해 플래싱하면 애플리케이션이 빌드의 일부인 cam이 만들어집니다.
위의 시작 시퀀스가 바로 구워 넣기를 효과적으로 만드는 요소입니다. 런타임은 파일시스템을 확인하기 전에 boot.py 와 main.py 를 프리징된 사본으로 해석하므로, SD 카드에 개발 과정에서 남은 오래된 boot.py 가 있더라도 출하된 cam은 빌드의 코드를 실행합니다.
14.2.2.1.2.3. 조회 순서¶
오버라이드 의미론은 boot.py / main.py 실행 경로와 일반 import 문에서 다릅니다. 어느 것이 어느 것인지 아는 것은 프로덕션과 개발 모두에 중요합니다:
boot.py와main.py의 경우: 런타임은 먼저 프리징된 사본을 찾고, 그다음 파일시스템을 찾습니다. 프리징된boot.py는 SD 카드에 하나를 떨어뜨려도 오버라이드할 수 없습니다 – cam을 손에 쥔 사람은 재플래싱 없이는 진입점을 변경할 수 없습니다.import foo의 경우: 런타임은 먼저sys.path를 검색하며 – 이는/flash,/sdcard,/rom및 그들의lib하위 디렉터리를 포함합니다 – 그다음 프리징된 모듈을 검색합니다. 플래시나 SD에 있는 동일 이름의foo.py는 프리징된foo를 실제로 오버라이드합니다. 이것이 개발상의 편의입니다. 수정한 모듈을 카드에 떨어뜨리고, 소프트 리셋하고, 재플래싱 없이 변경 사항을 확인합니다.
임포트에 대해 파일시스템이 프리징된 것을 오버라이드하는 동작을 억제하고자 하는 출하 제품은 boot.py 초기에 sys.path 를 비울 수 있습니다:
import sys
sys.path.clear()
sys.path 가 비어 있으면 모든 임포트는 프리징된 모듈에서만 해석됩니다. 플래시, SD, ROMFS의 어떤 것도 그것들을 가릴 수 없습니다.
14.2.2.1.2.4. 에셋 문제¶
프리징은 코드에는 훌륭합니다. 하지만 대용량 바이너리 에셋에는 훌륭하지 않습니다: 머신러닝 모델 파일, 레이블 테이블, JSON 구성, 이미지 템플릿. 이것들을 Python 리터럴로 임베딩하면 소스가 부풀고, 재컴파일이 느려지며, 어차피 인터프리터가 raw로 읽을 데이터에 바이트코드 컨테이너를 낭비하게 됩니다. ROMFS 이미지 빌드하기 페이지는 이 공백을 채우는 읽기 전용 플래시 파일시스템을 다룹니다.