14.2.2.1. Congelar scripts en el firmware¶
Un módulo congelado es un archivo .py compilado a bytecode y enlazado en la imagen del firmware en tiempo de compilación. El entorno de ejecución importa un módulo congelado directamente desde la memoria flash, sin mirar nunca el sistema de archivos del disco. Para un producto comercializado este es el lugar correcto para el código de la aplicación: nada que el usuario final pueda eliminar, nada que un .py obsoleto en la tarjeta SD pueda sobrescribir, y la cámara ejecuta el mismo código en cada arranque sin importar lo que (si es que hay algo) haya en sus unidades.
Esta página cubre la secuencia de arranque que sigue la cámara, y después cómo manifest.py y la directiva freeze integran una aplicación en la compilación.
14.2.2.1.1. La secuencia de arranque¶
Qué se ejecuta, y cuándo, en una cámara que sale de un reinicio:
El bootloader. El encendido entra en una breve ventana DFU que el IDE utiliza para enviar actualizaciones de firmware. La ventana se cierra después de unos segundos y el bootloader cede el control a MicroPython. Un script en ejecución puede volver a entrar en esta ventana bajo demanda llamando a
machine.bootloader().Inicialización del sistema de archivos congelado. Antes de que se ejecute cualquier código de aplicación, el entorno de ejecución pone en marcha los sistemas de archivos. La memoria flash interna se monta en
/flash(y se formatea vacía si no hay nada allí). Si hay una tarjeta SD presente y un archivo marcador llamadoSKIPSDno existe en la memoria flash interna, la tarjeta SD se monta en/sdcard. ROMFS, cuando la compilación lo incluye, se monta automáticamente en/rom. El directorio de trabajo se establece en el directorio de arranque (/sdcardsi la tarjeta se montó,/flashen caso contrario), ysys.pathse rellena con/flash,/flash/lib,/sdcard,/sdcard/lib,/romy/rom/lib. La configuración residente en la memoria flash la gestiona un módulo congelado llamado_boot.py– infraestructura del puerto y de la placa, no un punto de enganche de la aplicación. Las aplicaciones no personalizan_boot.py; lo hace la compilación. Colocar un archivoSKIPSDen la memoria flash desde el IDE es la forma admitida de hacer que la cámara arranque desde la memoria flash interna en lugar de desde la tarjeta SD.Configuración previa al REPL.
boot.pyse ejecuta en cada reinicio por software – arranque en frío,Ctrl-Ddesde el REPL, el script en ejecución que retorna y la recuperación por watchdog – antes de que el REPL sea accesible. Su trabajo es preparar el entorno en el que se ejecuta el resto del sistema: el tipo de configuración que el REPL, la aplicación y cualquier herramienta de recuperación necesitan tener en su sitio para funcionar. No es donde reside la aplicación en sí.main.pyes el punto de entrada de la aplicación.Bucle principal.
main.pyes el bucle principal de la aplicación. Se ejecuta una vez en el arranque en frío, inmediatamente después deboot.py. No se vuelve a ejecutar en reinicios por software posteriores – en su lugar, la cámara cae al REPL. Esa asimetría importa para el desarrollo (un Ctrl-D cae al REPL sin volver a ejecutar el bucle, de modo que el desarrollador puede inspeccionar el estado) pero no para la producción: una cámara desplegada ve encendidos, watchdog y reinicios por hardware, que son todos reinicios de hardware que vuelven a entrar en la ruta de arranque en frío y ejecutanmain.pyde nuevo.
14.2.2.1.2. Congelar en el firmware¶
El conjunto de módulos congelados de una placa se declara en boards/<TARGET>/manifest.py dentro del árbol del firmware. El manifiesto es un pequeño archivo de Python que llama a un puñado de directivas:
freeze("$(OMV_LIB_DIR)/", "foo.py")– integra un únicofoo.pyen la compilación.package("mylib", base_path="...")– integra un paquete de Python de varios archivos, preservando su disposición de directorios bajo la ruta base indicada.include("...")– incorpora otro archivo de manifiesto. Los manifiestos de las placas usan esto para compartir conjuntos comunes de módulos.require("logging")– incorpora por nombre un módulo demicropython-libascendente.
Un manifiesto de aplicación mínimo añade una línea freeze por cada script de nivel superior y una línea package por cada paquete del que dependa la aplicación.
14.2.2.1.2.1. Dónde reside el código fuente¶
El código fuente de la aplicación reside bajo scripts/libraries/ en el árbol del firmware, junto a los módulos que la compilación ya congela. La variable de manifiesto $(OMV_LIB_DIR) se expande a esa ruta, de modo que las entradas del manifiesto se mantienen cortas. Editar el manifiesto ya es una operación dentro del árbol, por lo que mantener el código fuente dentro del árbol evita tener que hacer malabares con un repositorio de proyecto separado en la resolución de rutas.
Una disposición típica para una aplicación que distribuye un único main.py más un paquete de soporte:
scripts/libraries/
main.py
my_lib/
__init__.py
helpers.py
Y en el boards/<TARGET>/manifest.py de la placa, una línea freeze para el script y una línea package para el paquete:
freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")
Los scripts de un solo archivo – main.py aquí, pero la misma regla se aplica a boot.py o a cualquier ayudante autónomo – usan freeze. Los paquetes de varios archivos usan package. Añadir otro script es una línea freeze más; añadir otro paquete es una línea package más.
14.2.2.1.2.2. Compilar y flashear¶
Una vez que el manifiesto está en su sitio, compila el firmware exactamente como describe el capítulo sobre firmware:
make -j$(nproc) -C lib/micropython/mpy-cross # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET> # builds the firmware
La salida queda en build/<TARGET>/bin/:
build/<TARGET>/bin/
firmware.bin # flash through the IDE
romfs0.img # flash through the IDE in a separate step
Flashear el .bin y el .img a través del IDE deja una cámara cuya aplicación forma parte de la compilación.
La secuencia de arranque anterior es lo que hace eficaz la integración: el entorno de ejecución resuelve boot.py y main.py a las copias congeladas antes de comprobar siquiera el sistema de archivos, de modo que una cámara comercializada ejecuta el código de la compilación incluso si la tarjeta SD contiene un boot.py obsoleto dejado durante el desarrollo.
14.2.2.1.2.3. Orden de búsqueda¶
La semántica de sobrescritura es diferente para la ruta de ejecución de boot.py / main.py y para las sentencias import ordinarias. Saber cuál es cuál importa tanto para la producción como para el desarrollo:
Para
boot.pyymain.py: el entorno de ejecución busca primero una copia congelada, y después el sistema de archivos. Unboot.pycongelado no se puede sobrescribir colocando uno en la tarjeta SD – quien tenga la cámara no puede cambiar el punto de entrada sin reflashear.Para
import foo: el entorno de ejecución busca primero ensys.path– que cubre/flash,/sdcard,/romy sus subdirectorioslib– y después en los módulos congelados. Unfoo.pydel mismo nombre en la memoria flash o en la SD sí sobrescribe a unfoocongelado. Esta es la facilidad para el desarrollo: coloca un módulo corregido en la tarjeta, reinicia por software y ve el cambio sin reflashear.
Un producto comercializado que quiera suprimir el comportamiento de sobrescritura del sistema de archivos sobre lo congelado para las importaciones puede vaciar sys.path al principio de boot.py:
import sys
sys.path.clear()
Con sys.path vacío, todas las importaciones se resuelven únicamente desde los módulos congelados; nada en la memoria flash, la SD o ROMFS puede eclipsarlos.
14.2.2.1.2.4. El problema de los recursos¶
Congelar es estupendo para el código. No es estupendo para recursos binarios grandes: archivos de modelos de aprendizaje automático, tablas de etiquetas, configuración JSON, plantillas de imagen. Incrustar esos elementos como literales de Python hincha el código fuente, recompila lentamente y desperdicia el contenedor de bytecode en datos que el intérprete va a leer en bruto de todos modos. La página Crear una imagen ROMFS cubre el sistema de archivos flash de solo lectura que llena este hueco.