Trabajar con ROMFS

Descripción general

ROMFS (Read-Only Memory Filesystem, sistema de archivos de memoria de solo lectura) es un sistema de archivos ligero y de solo lectura diseñado para dispositivos MicroPython. Está optimizado para microcontroladores y sistemas embebidos donde el código y los datos deben almacenarse en memoria flash y accederse de forma eficiente sin copiarse en la RAM.

Las principales ventajas de ROMFS son:

  • Importaciones sin copia (zero-copy): los archivos de bytecode .mpy almacenados en un ROMFS pueden ejecutarse directamente desde la memoria flash (mapeada en memoria) en lugar de copiarse primero en la RAM. Esto es similar al funcionamiento de los módulos congelados, pero no requiere reflashear todo el firmware.

  • Bajo consumo de RAM: los objetos constantes (cadenas, bytes, etc.) de los archivos .mpy cargados desde ROMFS se referencian directamente desde la memoria flash, sin duplicarse en la RAM.

  • Despliegue flexible: una imagen ROMFS puede compilarse en un PC anfitrión y desplegarse en el dispositivo mediante mpremote, sin recompilar el firmware.

  • Interfaz de sistema de archivos estándar: un ROMFS se monta en el VFS y se accede a él mediante operaciones de archivo normales de Python (open, os.listdir, import, etc.).

ROMFS es complementario tanto a los sistemas de archivos de lectura-escritura FAT/LittleFS (que residen en otras particiones de la memoria flash) como a los módulos congelados (que se compilan dentro del propio firmware).

Compatibilidad de placas

ROMFS está habilitado en el firmware de OpenMV en todas las placas de cámara que tienen una partición ROMFS reservada en su disposición de memoria flash. En estas placas, la partición ROMFS se detecta automáticamente al arrancar y se monta en /rom; tanto /rom como /rom/lib se añaden a sys.path para que los módulos almacenados allí puedan importarse directamente.

Placa

Compatibilidad con ROMFS

OpenMV Cam N6

OpenMV AE3

OpenMV Cam RT1062

OpenMV Cam Pure Thermal

OpenMV Cam M4 / M7 / H7 / H7 Plus

Arduino Giga

Arduino Portenta H7

Arduino Nicla Vision

Arduino Nano 33 BLE Sense

No (sin partición ROMFS)

Arduino Nano RP2040 Connect

No (sin partición ROMFS)

Consulte romfs para conocer un asistente específico de OpenMV que inspecciona el ROMFS montado en /rom.

Flujo de trabajo

El flujo de trabajo habitual para usar ROMFS es:

  1. Cree un directorio en su PC con los archivos de Python (o archivos .mpy) que desee desplegar.

  2. Use mpremote romfs deploy <directory> para compilar y desplegar la imagen ROMFS en el dispositivo.

  3. El ROMFS se montará en /rom en el siguiente arranque (o puede montarse de inmediato si se reinicia el dispositivo).

  4. El código Python del dispositivo puede entonces hacer import de los módulos del ROMFS igual que desde cualquier otro sistema de archivos.

Por ejemplo:

# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/

Tras un reinicio por software, el dispositivo tendrá /rom/app.py (o /rom/app.mpy si mpy_cross está instalado) disponible para su importación.

Consulte la sección subcomandos de mpremote romfs más abajo para conocer todos los detalles de los subcomandos de mpremote.

API de Python

La API de Python de ROMFS se proporciona a través del módulo vfs.

class vfs.VfsRom(buffer)

Crea un objeto de sistema de archivos ROMFS a partir de buffer, que debe ser un objeto compatible con el protocolo de búfer (por ejemplo, un objeto bytes, bytearray o memoryview) que contenga una imagen ROMFS válida.

El constructor valida que buffer comience con los bytes mágicos de ROMFS (b"\xd2\xcd\x31"). Si el búfer es demasiado pequeño o no es un ROMFS válido, se lanza OSError(ENODEV).

Los objetos creados por este constructor pueden montarse mediante vfs.mount().

Ejemplo:

import vfs

# Load a ROMFS image from flash into a memoryview.
dev = vfs.rom_ioctl(2, 0)   # get partition 0 as a memoryview
fs = vfs.VfsRom(dev)
vfs.mount(fs, '/rom')

O bien, para montar una imagen ROMFS almacenada en un archivo:

import vfs

with open('/flash/app.romfs', 'rb') as f:
    romfs_data = f.read()
fs = vfs.VfsRom(romfs_data)
vfs.mount(fs, '/rom2')

Los siguientes métodos están disponibles en un objeto VfsRom:

VfsRom.open(path, mode)

Abre un archivo del ROMFS. Solo se admiten los modos de lectura ('', 'r', 'rt', 'rb'). Intentar abrir un archivo para escritura lanzará OSError(EROFS).

El objeto de archivo devuelto admite read(), seek(), tell() y close(). Para archivos binarios abiertos en modo lectura, el objeto devuelto también admite el protocolo de búfer, de modo que puede obtenerse un memoryview de los datos del archivo, que apunta directamente a la memoria del ROMFS (sin copia).

VfsRom.ilistdir(path)

Devuelve un iterador sobre las entradas del directorio path. Cada entrada es una tupla (name, type, inode, size) donde type es 0x8000 para un archivo o 0x4000 para un directorio.

VfsRom.stat(path)

Devuelve una 10-tupla similar a la de os.stat para path. Lanza OSError(ENOENT) si la ruta no existe.

VfsRom.statvfs(path)

Devuelve estadísticas del sistema de archivos. El tamaño de bloque se informa como 1 y el recuento de bloques representa el tamaño total de la imagen ROMFS en bytes. Los bloques libres y los archivos libres son siempre 0 (sistema de archivos de solo lectura).

VfsRom.chdir(path)

Cambia de directorio dentro del ROMFS. Solo se admite la raíz ('/'); cambiar a cualquier subdirectorio lanza OSError(EOPNOTSUPP).

VfsRom.getcwd()

Devuelve el directorio de trabajo actual dentro del ROMFS. Siempre devuelve '/'.

vfs.rom_ioctl(op, ...)

Interfaz de bajo nivel para acceder a la(s) partición(es) de memoria de solo lectura (ROM) del dispositivo.

Las operaciones admitidas son:

  • vfs.rom_ioctl(1) – Devuelve el número de particiones ROM disponibles.

  • vfs.rom_ioctl(2, id) – Devuelve la partición ROM con índice id como un objeto memoryview. La memoria puede leerse pero no escribirse directamente.

  • vfs.rom_ioctl(3, id, length) – Prepara una partición ROM para escritura. Borra los primeros length bytes de la partición con índice id. Devuelve el tamaño mínimo de escritura en bytes (la alineación requerida para las escrituras posteriores).

  • vfs.rom_ioctl(4, id, offset, buf) – Escribe buf (un objeto de tipo bytes) en la partición ROM con índice id en el byte offset.

  • vfs.rom_ioctl(5, id) – Completa una secuencia de escritura en la partición id (realiza cualquier finalización necesaria tras la escritura, como el vaciado de la caché).

Estas operaciones las usa internamente mpremote para desplegar imágenes ROMFS. La mayoría de los usuarios no necesitan llamar a vfs.rom_ioctl() directamente.

Ejemplo (consultando las particiones disponibles):

import vfs

n = vfs.rom_ioctl(1)
print("Number of ROM partitions:", n)
for i in range(n):
    dev = vfs.rom_ioctl(2, i)
    print(f"  Partition {i}: {len(dev)} bytes")

Montaje automático al arrancar

Cuando la compatibilidad con ROMFS está habilitada en el firmware, MicroPython intentará montar automáticamente la primera partición ROM en /rom durante la inicialización. Si la partición contiene una imagen ROMFS válida, se monta y tanto /rom como /rom/lib se añaden automáticamente a sys.path.

Esto significa que, tras desplegar una imagen ROMFS con mpremote, basta con un reinicio por software para que los nuevos módulos sean importables.

Si no se encuentra ninguna imagen ROMFS válida en la partición (por ejemplo, en una placa recién programada), el montaje se omite de forma silenciosa.

Usar mpremote para gestionar ROMFS

La herramienta mpremote proporciona tres subcomandos para gestionar imágenes ROMFS en un dispositivo conectado.

romfs query

$ mpremote romfs query

Lista todas las particiones ROMFS disponibles en el dispositivo y sus tamaños. También muestra los primeros 12 bytes de cada partición en hexadecimal e informa de si hay presente una imagen ROMFS válida.

Salida de ejemplo:

ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
  Raw contents: d2:cd:31:XX:XX:XX:XX:XX:XX:XX:XX:XX ...
  ROMFS image size: 1234

romfs build

$ mpremote romfs [-o <output>] build <source>

Compila una imagen ROMFS a partir del directorio source en el PC anfitrión. La imagen se escribe en output (predeterminado: <source>.romfs).

Opciones:

  • -o <output>, --output <output>: especifica la ruta del archivo de salida.

  • -m, --mpy (predeterminado): compila automáticamente los archivos .py a .mpy usando mpy_cross antes de añadirlos a la imagen. Requiere el paquete de Python mpy_cross (pip install mpy_cross).

  • --no-mpy: desactiva la compilación automática de los archivos .py.

Ejemplo:

$ mpremote romfs build myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
    \-- helper.py -> .mpy
Writing 2048 bytes to output file myapp.romfs

romfs deploy

$ mpremote romfs [-p <partition>] deploy <source>

Despliega una imagen ROMFS en el dispositivo. source puede ser:

  • Un directorio en el anfitrión: la imagen ROMFS se compila en memoria y se despliega directamente.

  • Un archivo .romfs o .img: la imagen se lee del disco y se despliega.

Opciones:

  • -p <partition>, --partition <partition>: especifica el índice de la partición de destino (predeterminado: 0).

  • -m, --mpy (predeterminado): compila los archivos .py a .mpy cuando source es un directorio.

  • --no-mpy: desactiva la compilación automática de los archivos .py.

Tras el despliegue, el dispositivo debe reiniciarse por software para que el nuevo ROMFS se monte en /rom.

Ejemplo:

$ mpremote romfs deploy myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
    \-- helper.py -> .mpy
Image size is 2048 bytes
ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
Preparing ROMFS0 partition for writing
Deploying ROMFS to ROMFS0 partition
ROMFS image deployed

$ mpremote soft-reset

Ejemplos

Desplegar una aplicación sencilla

Supongamos que tiene un directorio de proyecto myapp/ con la siguiente estructura:

myapp/
    main.py
    utils.py
    lib/
        helper.py

Para desplegarlo en el ROMFS del dispositivo:

$ mpremote romfs deploy myapp/

Tras un reinicio por software, los módulos pueden importarse desde el ROMFS:

import main
import utils
from lib import helper

Listar el contenido del ROMFS desde Python

Tras el montaje, el contenido del ROMFS puede explorarse como cualquier otro sistema de archivos:

import os

for entry in os.ilistdir('/rom'):
    print(entry)

# Or simply:
print(os.listdir('/rom'))

OpenMV también incluye un pequeño asistente romfs que imprime un listado con formato que incluye la dirección mapeada en memoria y la alineación de cada archivo:

from omv import romfs
romfs.ls_romfs()

Anidar un ROMFS dentro de un ROMFS

Una imagen ROMFS almacenada como archivo dentro de un ROMFS exterior puede montarse como un sistema de archivos anidado. Por ejemplo, si /rom/inner.romfs existe. Como /rom es un ROMFS, los objetos de archivo abiertos desde él admiten el protocolo de búfer, por lo que puede obtenerse directamente un memoryview sin copia:

import vfs

with open('/rom/inner.romfs', 'rb') as f:
    inner = vfs.VfsRom(memoryview(f))
vfs.mount(inner, '/inner')

print(os.listdir('/inner'))

Formato de imagen de ROMFS

El formato de imagen de ROMFS es un formato binario compacto diseñado para el acceso mapeado en memoria en microcontroladores. Una breve descripción general:

  • La imagen comienza con los bytes mágicos 0xd2 0xcd 0x31 (codificados como "RM1" con los bits altos de los dos primeros bytes activados).

  • El resto de la imagen se compone de registros, cada uno con una etiqueta de tipo (varuint), una longitud (varuint) y una carga útil.

  • Los tipos de registro incluyen: relleno, datos literales, puntero indirecto a datos, directorio y archivo.

  • Los nombres de directorios y archivos se almacenan como cadenas de bytes con prefijo de longitud.

  • Los datos de los archivos pueden almacenarse de forma literal (en línea) o mediante un puntero indirecto a otra parte de la imagen, lo que permite la alineación para el acceso mapeado en memoria.

  • Los tipos de registro desconocidos se omiten de forma silenciosa, lo que proporciona compatibilidad hacia adelante.

Este formato está definido en extmod/vfs_rom.c en el código fuente de MicroPython. La implementación de Python que usa mpremote para compilar imágenes se encuentra en tools/mpremote/mpremote/romfs.py.

Ver también

Trabajar con sistemas de archivos – Descripción general del VFS de MicroPython y de los tipos de sistema de archivos disponibles.

Archivos de manifiesto de MicroPython – Cómo congelar módulos de Python en el firmware.

Archivos .mpy de MicroPython – Formato de archivo binario .mpy de MicroPython.

Control remoto de MicroPython: mpremote – La referencia completa de comandos de mpremote.

romfs – Asistente de OpenMV para inspeccionar el sistema de archivos /rom montado.