Lavorare con ROMFS¶
Panoramica¶
ROMFS (Read-Only Memory Filesystem) è un filesystem leggero e di sola lettura progettato per i dispositivi MicroPython. È ottimizzato per microcontrollori e sistemi embedded in cui il codice e i dati devono essere memorizzati nella memoria flash e accessibili in modo efficiente senza essere copiati nella RAM.
I principali vantaggi di ROMFS sono:
Import zero-copy: i file di bytecode
.mpymemorizzati in un ROMFS possono essere eseguiti direttamente dalla memoria flash (memory-mapped) anziché essere prima copiati nella RAM. Questo è simile al funzionamento dei moduli congelati, ma non richiede di riprogrammare l’intero firmware.Basso consumo di RAM: gli oggetti costanti (stringhe, byte, ecc.) nei file
.mpycaricati da ROMFS sono referenziati direttamente dalla flash, non duplicati nella RAM.Distribuzione flessibile: un’immagine ROMFS può essere costruita su un PC host e distribuita al dispositivo usando mpremote, senza ricompilare il firmware.
Interfaccia filesystem standard: un ROMFS viene montato nel VFS ed è accessibile tramite le normali operazioni sui file di Python (
open,os.listdir,import, ecc.).
ROMFS è complementare sia ai filesystem FAT/LittleFS in lettura-scrittura (che risiedono in altre partizioni della flash) sia ai moduli congelati (che sono compilati nel firmware stesso).
Supporto delle schede¶
ROMFS è abilitato nel firmware OpenMV su ogni scheda camera che dispone di una partizione ROMFS riservata nel proprio layout della flash. Su queste schede la partizione ROMFS viene rilevata automaticamente all’avvio e montata su /rom; sia /rom che /rom/lib vengono aggiunti a sys.path in modo che i moduli memorizzati lì possano essere importati direttamente.
Scheda |
Supporto ROMFS |
|---|---|
OpenMV Cam N6 |
Sì |
OpenMV AE3 |
Sì |
OpenMV Cam RT1062 |
Sì |
OpenMV Cam Pure Thermal |
Sì |
OpenMV Cam M4 / M7 / H7 / H7 Plus |
Sì |
Arduino Giga |
Sì |
Arduino Portenta H7 |
Sì |
Arduino Nicla Vision |
Sì |
Arduino Nano 33 BLE Sense |
No (nessuna partizione ROMFS) |
Arduino Nano RP2040 Connect |
No (nessuna partizione ROMFS) |
Vedi romfs per un helper specifico di OpenMV che ispeziona il ROMFS montato su /rom.
Flusso di lavoro¶
Il tipico flusso di lavoro per usare ROMFS è:
Crea una directory sul tuo PC con i file Python (o i file
.mpy) che vuoi distribuire.Usa
mpremote romfs deploy <directory>per costruire e distribuire l’immagine ROMFS al dispositivo.Il ROMFS verrà montato su
/romal successivo avvio (o può essere montato immediatamente se il dispositivo viene riavviato).Il codice Python sul dispositivo può quindi usare
importsui moduli dal ROMFS proprio come da qualsiasi altro filesystem.
Ad esempio:
# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/
Dopo un soft-reset, il dispositivo avrà /rom/app.py (o /rom/app.mpy se mpy_cross è installato) disponibile per l’import.
Vedi la sezione sotto-comandi mpremote romfs qui sotto per tutti i dettagli sui sotto-comandi di mpremote.
API Python¶
L’API Python di ROMFS è fornita tramite il modulo vfs.
- class vfs.VfsRom(buffer)
Crea un oggetto filesystem ROMFS da buffer, che deve essere un oggetto che supporta il protocollo buffer (ad es. un oggetto
bytes,bytearrayomemoryview) contenente un’immagine ROMFS valida.Il costruttore verifica che buffer inizi con i byte magici di ROMFS (
b"\xd2\xcd\x31"). Se il buffer è troppo piccolo o non è un ROMFS valido, viene sollevatoOSError(ENODEV).Gli oggetti creati da questo costruttore possono essere montati usando
vfs.mount().Esempio:
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')
Oppure, per montare un’immagine ROMFS memorizzata in un file:
import vfs with open('/flash/app.romfs', 'rb') as f: romfs_data = f.read() fs = vfs.VfsRom(romfs_data) vfs.mount(fs, '/rom2')
I seguenti metodi sono disponibili su un oggetto
VfsRom:- VfsRom.open(path, mode)
Apre un file dal ROMFS. Sono supportate solo le modalità di lettura (
'','r','rt','rb'). Tentare di aprire un file in scrittura solleveràOSError(EROFS).L’oggetto file restituito supporta
read(),seek(),tell()eclose(). Per i file binari aperti in modalità di lettura, l’oggetto restituito supporta anche il protocollo buffer in modo da poter ottenere unamemoryviewdei dati del file, che fa riferimento direttamente alla memoria del ROMFS (zero-copy).
- VfsRom.ilistdir(path)
Restituisce un iteratore sulle voci nella directory path. Ogni voce è una tupla
(name, type, inode, size)dove type è0x8000per un file o0x4000per una directory.
- VfsRom.stat(path)
Restituisce una 10-tupla simile a
os.statper path. SollevaOSError(ENOENT)se il percorso non esiste.
- VfsRom.statvfs(path)
Restituisce le statistiche del filesystem. La dimensione del blocco è riportata come 1 e il numero di blocchi rappresenta la dimensione totale dell’immagine ROMFS in byte. I blocchi liberi e i file liberi sono sempre 0 (filesystem di sola lettura).
- VfsRom.chdir(path)
Cambia directory all’interno del ROMFS. È supportata solo la radice (
'/'); il passaggio a qualsiasi sottodirectory sollevaOSError(EOPNOTSUPP).
- VfsRom.getcwd()
Restituisce la directory di lavoro corrente all’interno del ROMFS. Restituisce sempre
'/'.
- vfs.rom_ioctl(op, ...)
Interfaccia di basso livello per accedere alle partizioni di memoria di sola lettura (ROM) del dispositivo.
Le operazioni supportate sono:
vfs.rom_ioctl(1)– Restituisce il numero di partizioni ROM disponibili.vfs.rom_ioctl(2, id)– Restituisce la partizione ROM con indice id come oggettomemoryview. La memoria può essere letta ma non scritta direttamente.vfs.rom_ioctl(3, id, length)– Prepara una partizione ROM per la scrittura. Cancella i primi length byte della partizione con indice id. Restituisce la dimensione minima di scrittura in byte (l’allineamento richiesto per le scritture successive).vfs.rom_ioctl(4, id, offset, buf)– Scrive buf (un oggetto bytes-like) nella partizione ROM con indice id all’offset in byte offset.vfs.rom_ioctl(5, id)– Completa una sequenza di scrittura nella partizione id (esegue qualsiasi finalizzazione necessaria dopo la scrittura, come lo svuotamento della cache).
Queste operazioni sono usate internamente da
mpremoteper distribuire le immagini ROMFS. La maggior parte degli utenti non ha bisogno di chiamare direttamentevfs.rom_ioctl().Esempio (interrogazione delle partizioni disponibili):
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")
Montaggio automatico all’avvio¶
Quando il supporto ROMFS è abilitato nel firmware, MicroPython tenterà automaticamente di montare la prima partizione ROM su /rom durante l’inizializzazione. Se la partizione contiene un’immagine ROMFS valida, viene montata e sia /rom che /rom/lib vengono aggiunti automaticamente a sys.path.
Questo significa che dopo aver distribuito un’immagine ROMFS con mpremote, un soft-reset è sufficiente per rendere importabili i nuovi moduli.
Se nella partizione non viene trovata alcuna immagine ROMFS valida (ad es. su una scheda appena programmata), il montaggio viene saltato silenziosamente.
Usare mpremote per gestire ROMFS¶
Lo strumento mpremote fornisce tre sotto-comandi per gestire le immagini ROMFS su un dispositivo connesso.
romfs query¶
$ mpremote romfs query
Elenca tutte le partizioni ROMFS disponibili sul dispositivo e le loro dimensioni. Mostra anche i primi 12 byte di ogni partizione in esadecimale e indica se è presente un’immagine ROMFS valida.
Output di esempio:
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>
Costruisce un’immagine ROMFS dalla directory source sul PC host. L’immagine viene scritta in output (predefinito: <source>.romfs).
Opzioni:
-o <output>,--output <output>: specifica il percorso del file di output.-m,--mpy(predefinito): compila automaticamente i file.pyin.mpyusandompy_crossprima di aggiungerli all’immagine. Richiede il pacchetto Pythonmpy_cross(pip install mpy_cross).--no-mpy: disabilita la compilazione automatica dei file.py.
Esempio:
$ 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>
Distribuisce un’immagine ROMFS al dispositivo. source può essere:
Una directory sull’host: l’immagine ROMFS viene costruita in memoria e distribuita direttamente.
Un file
.romfso.img: l’immagine viene letta dal disco e distribuita.
Opzioni:
-p <partition>,--partition <partition>: specifica l’indice della partizione di destinazione (predefinito:0).-m,--mpy(predefinito): compila.pyin.mpyquando source è una directory.--no-mpy: disabilita la compilazione automatica dei file.py.
Dopo la distribuzione, il dispositivo deve essere sottoposto a soft-reset affinché il nuovo ROMFS venga montato su /rom.
Esempio:
$ 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
Esempi¶
Distribuire una semplice applicazione¶
Supponiamo di avere una directory di progetto myapp/ con la seguente struttura:
myapp/
main.py
utils.py
lib/
helper.py
Per distribuirla nel ROMFS del dispositivo:
$ mpremote romfs deploy myapp/
Dopo un soft-reset, i moduli sono importabili dal ROMFS:
import main
import utils
from lib import helper
Elencare il contenuto di ROMFS da Python¶
Dopo il montaggio, il contenuto del ROMFS può essere esplorato come qualsiasi altro filesystem:
import os
for entry in os.ilistdir('/rom'):
print(entry)
# Or simply:
print(os.listdir('/rom'))
OpenMV include anche un piccolo helper romfs che stampa un elenco formattato comprendente l’indirizzo memory-mapped e l’allineamento di ogni file:
from omv import romfs
romfs.ls_romfs()
Annidare un ROMFS all’interno di un ROMFS¶
Un’immagine ROMFS memorizzata come file all’interno di un ROMFS esterno può essere montata come filesystem annidato. Ad esempio, se /rom/inner.romfs esiste. Poiché /rom è un ROMFS, gli oggetti file aperti da esso supportano il protocollo buffer, quindi è possibile ottenere direttamente una memoryview zero-copy:
import vfs
with open('/rom/inner.romfs', 'rb') as f:
inner = vfs.VfsRom(memoryview(f))
vfs.mount(inner, '/inner')
print(os.listdir('/inner'))
Formato dell’immagine ROMFS¶
Il formato dell’immagine ROMFS è un formato binario compatto progettato per l’accesso memory-mapped sui microcontrollori. Una breve panoramica:
L’immagine inizia con i byte magici
0xd2 0xcd 0x31(codificati come"RM1"con i bit alti dei primi due byte impostati).Il resto dell’immagine è composto da record, ciascuno con un tag di tipo (varuint), una lunghezza (varuint) e un payload.
I tipi di record includono: padding, dati verbatim, puntatore a dati indiretto, directory, file.
I nomi di directory e file sono memorizzati come stringhe di byte con prefisso di lunghezza.
I dati dei file possono essere memorizzati verbatim (inline) o tramite un puntatore indiretto a un’altra posizione nell’immagine, il che consente l’allineamento per l’accesso memory-mapped.
I tipi di record sconosciuti vengono saltati silenziosamente, garantendo la compatibilità in avanti.
Questo formato è definito in extmod/vfs_rom.c nel codice sorgente di MicroPython. L’implementazione Python usata da mpremote per costruire le immagini si trova in tools/mpremote/mpremote/romfs.py.
Vedi anche
Lavorare con i filesystem – Panoramica del VFS di MicroPython e dei tipi di filesystem disponibili.
File manifest di MicroPython – Come congelare i moduli Python nel firmware.
File .mpy di MicroPython – Formato dei file binari .mpy di MicroPython.
Controllo remoto di MicroPython: mpremote – Il riferimento completo dei comandi di mpremote.
romfs – Helper OpenMV per ispezionare il filesystem /rom montato.