Trabalhar com ROMFS

Visão Geral

ROMFS (Sistema de Ficheiros em Memória Apenas de Leitura) é um sistema de ficheiros leve e somente de leitura concebido para dispositivos MicroPython. É optimizado para microcontroladores e sistemas embebidos onde o código e os dados precisam de ser armazenados em memória flash e acedidos de forma eficiente sem serem copiados para a RAM.

As principais vantagens do ROMFS são:

  • Importações sem cópia: os ficheiros de bytecode .mpy armazenados num ROMFS podem ser executados directamente a partir da memória flash (mapeada em memória) em vez de serem primeiro copiados para a RAM. Isto é semelhante ao funcionamento dos módulos congelados, mas não requer a re-escrita de todo o firmware.

  • Baixa sobrecarga de RAM: os objectos constantes (strings, bytes, etc.) nos ficheiros .mpy carregados a partir de ROMFS são referenciados directamente a partir da flash, sem duplicação na RAM.

  • Implementação flexível: uma imagem ROMFS pode ser construída num PC anfitrião e implementada no dispositivo utilizando mpremote, sem necessidade de reconstruir o firmware.

  • Interface de sistema de ficheiros padrão: um ROMFS é montado no VFS e acedido através de operações normais de ficheiros Python (open, os.listdir, import, etc.).

O ROMFS é complementar tanto aos sistemas de ficheiros FAT/LittleFS de leitura-escrita (que residem noutras partições flash) como aos módulos congelados (que são compilados no próprio firmware).

Suporte de placas

O ROMFS está activado no firmware OpenMV em todas as placas de câmara que têm uma partição ROMFS reservada no seu esquema de flash. Nestas placas, a partição ROMFS é detectada automaticamente no arranque e montada em /rom; tanto /rom como /rom/lib são adicionados ao sys.path para que os módulos aí armazenados possam ser importados directamente.

Placa

Suporte ROMFS

OpenMV Cam N6

Sim

OpenMV AE3

Sim

OpenMV Cam RT1062

Sim

OpenMV Cam Pure Thermal

Sim

OpenMV Cam M4 / M7 / H7 / H7 Plus

Sim

Arduino Giga

Sim

Arduino Portenta H7

Sim

Arduino Nicla Vision

Sim

Arduino Nano 33 BLE Sense

Não (sem partição ROMFS)

Arduino Nano RP2040 Connect

Não (sem partição ROMFS)

Consulte romfs para um utilitário específico do OpenMV que inspeciona o ROMFS montado em /rom.

Fluxo de Trabalho

O fluxo de trabalho típico para utilizar o ROMFS é:

  1. Crie um directório no seu PC com os ficheiros Python (ou ficheiros .mpy) que pretende implementar.

  2. Utilize mpremote romfs deploy <directory> para construir e implementar a imagem ROMFS no dispositivo.

  3. O ROMFS será montado em /rom no próximo arranque (ou pode ser montado imediatamente se o dispositivo for reiniciado).

  4. O código Python no dispositivo pode então usar import para importar módulos do ROMFS tal como de qualquer outro sistema de ficheiros.

Por exemplo:

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

Após uma reinicialização suave, o dispositivo terá /rom/app.py (ou /rom/app.mpy se o mpy_cross estiver instalado) disponível para importação.

Consulte a secção sub-comandos romfs do mpremote abaixo para detalhes completos dos sub-comandos do mpremote.

API Python

A API Python do ROMFS é fornecida através do módulo vfs.

class vfs.VfsRom(buffer)

Cria um objecto de sistema de ficheiros ROMFS a partir de buffer, que deve ser um objecto que suporte o protocolo de buffer (p.ex. um objecto bytes, bytearray ou memoryview) contendo uma imagem ROMFS válida.

O construtor valida que buffer começa com os bytes mágicos do ROMFS (b"\xd2\xcd\x31"). Se o buffer for demasiado pequeno ou não for um ROMFS válido, é lançado OSError(ENODEV).

Os objectos criados por este construtor podem ser montados utilizando vfs.mount().

Exemplo:

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')

Ou, para montar uma imagem ROMFS armazenada num ficheiro:

import vfs

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

Os seguintes métodos estão disponíveis num objecto VfsRom:

VfsRom.open(path, mode)

Abre um ficheiro do ROMFS. Apenas os modos de leitura ('', 'r', 'rt', 'rb') são suportados. Tentar abrir um ficheiro para escrita lançará OSError(EROFS).

O objecto de ficheiro retornado suporta read(), seek(), tell() e close(). Para ficheiros binários abertos em modo de leitura, o objecto retornado também suporta o protocolo de buffer, pelo que se pode obter uma memoryview dos dados do ficheiro, que referencia directamente a memória do ROMFS (sem cópia).

VfsRom.ilistdir(path)

Retorna um iterador sobre as entradas no directório path. Cada entrada é um tuplo (name, type, inode, size) onde type é 0x8000 para um ficheiro ou 0x4000 para um directório.

VfsRom.stat(path)

Retorna um tuplo de 10 elementos semelhante a os.stat para path. Lança OSError(ENOENT) se o caminho não existir.

VfsRom.statvfs(path)

Retorna estatísticas do sistema de ficheiros. O tamanho do bloco é indicado como 1 e a contagem de blocos representa o tamanho total da imagem ROMFS em bytes. Os blocos livres e os ficheiros livres são sempre 0 (sistema de ficheiros somente de leitura).

VfsRom.chdir(path)

Muda de directório dentro do ROMFS. Apenas a raiz ('/') é suportada; mudar para qualquer subdirectório lança OSError(EOPNOTSUPP).

VfsRom.getcwd()

Retorna o directório de trabalho actual dentro do ROMFS. Retorna sempre '/'.

vfs.rom_ioctl(op, ...)

Interface de baixo nível para aceder às partições de memória somente de leitura (ROM) do dispositivo.

As operações suportadas são:

  • vfs.rom_ioctl(1) – Retorna o número de partições ROM disponíveis.

  • vfs.rom_ioctl(2, id) – Retorna a partição ROM com o índice id como um objecto memoryview. A memória pode ser lida mas não escrita directamente.

  • vfs.rom_ioctl(3, id, length) – Prepara uma partição ROM para escrita. Apaga os primeiros length bytes da partição com índice id. Retorna o tamanho mínimo de escrita em bytes (o alinhamento necessário para escritas subsequentes).

  • vfs.rom_ioctl(4, id, offset, buf) – Escreve buf (um objecto tipo bytes) na partição ROM com índice id no byte offset.

  • vfs.rom_ioctl(5, id) – Conclui uma sequência de escrita na partição id (realiza qualquer finalização necessária após a escrita, como a limpeza de cache).

Estas operações são utilizadas internamente pelo mpremote para implementar imagens ROMFS. A maioria dos utilizadores não precisa de chamar vfs.rom_ioctl() directamente.

Exemplo (consultar partições disponíveis):

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")

Montagem automática no arranque

Quando o suporte ROMFS está activado no firmware, o MicroPython tentará automaticamente montar a primeira partição ROM em /rom durante a inicialização. Se a partição contiver uma imagem ROMFS válida, esta é montada e tanto /rom como /rom/lib são adicionados ao sys.path automaticamente.

Isto significa que após implementar uma imagem ROMFS com mpremote, uma reinicialização suave é suficiente para tornar os novos módulos importáveis.

Se não for encontrada uma imagem ROMFS válida na partição (p.ex. numa placa recém-programada), a montagem é silenciosamente ignorada.

Utilizar o mpremote para gerir ROMFS

A ferramenta mpremote fornece três sub-comandos para gerir imagens ROMFS num dispositivo ligado.

romfs query

$ mpremote romfs query

Lista todas as partições ROMFS disponíveis no dispositivo e os seus tamanhos. Mostra também os primeiros 12 bytes de cada partição em hexadecimal e indica se está presente uma imagem ROMFS válida.

Exemplo de saída:

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>

Constrói uma imagem ROMFS a partir do directório source no PC anfitrião. A imagem é escrita em output (predefinição: <source>.romfs).

Opções:

  • -o <output>, --output <output>: Especifica o caminho do ficheiro de saída.

  • -m, --mpy (predefinição): Compila automaticamente ficheiros .py para .mpy utilizando mpy_cross antes de os adicionar à imagem. Requer o pacote Python mpy_cross (pip install mpy_cross).

  • --no-mpy: Desactiva a compilação automática de ficheiros .py.

Exemplo:

$ 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>

Implementa uma imagem ROMFS no dispositivo. source pode ser:

  • Um directório no anfitrião: a imagem ROMFS é construída em memória e implementada directamente.

  • Um ficheiro .romfs ou .img: a imagem é lida do disco e implementada.

Opções:

  • -p <partition>, --partition <partition>: Especifica o índice da partição de destino (predefinição: 0).

  • -m, --mpy (predefinição): Compila .py para .mpy quando source é um directório.

  • --no-mpy: Desactiva a compilação automática de ficheiros .py.

Após a implementação, o dispositivo deve ser reiniciado de forma suave para que o novo ROMFS seja montado em /rom.

Exemplo:

$ 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

Exemplos

Implementar uma aplicação simples

Suponha que tem um directório de projecto myapp/ com a seguinte estrutura:

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

Para implementá-lo no ROMFS do dispositivo:

$ mpremote romfs deploy myapp/

Após uma reinicialização suave, os módulos são importáveis a partir do ROMFS:

import main
import utils
from lib import helper

Listar conteúdos do ROMFS a partir de Python

Após a montagem, os conteúdos do ROMFS podem ser explorados como qualquer outro sistema de ficheiros:

import os

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

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

O OpenMV também inclui um pequeno utilitário romfs que imprime uma listagem formatada incluindo o endereço mapeado em memória e o alinhamento de cada ficheiro:

from omv import romfs
romfs.ls_romfs()

Aninhar um ROMFS dentro de um ROMFS

Uma imagem ROMFS armazenada como ficheiro dentro de um ROMFS externo pode ser montada como um sistema de ficheiros aninhado. Por exemplo, se /rom/inner.romfs existir. Como /rom é um ROMFS, os objectos de ficheiro abertos a partir dele suportam o protocolo de buffer, pelo que se pode obter directamente uma memoryview sem cópia:

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 imagem ROMFS

O formato de imagem ROMFS é um formato binário compacto concebido para acesso mapeado em memória em microcontroladores. Uma breve descrição:

  • A imagem começa com os bytes mágicos 0xd2 0xcd 0x31 (codificados como "RM1" com os bits mais significativos dos dois primeiros bytes definidos).

  • O resto da imagem é composto por registos, cada um com uma etiqueta de tipo (varuint), um comprimento (varuint) e uma carga útil.

  • Os tipos de registo incluem: preenchimento, dados literais, ponteiro de dados indirecto, directório, ficheiro.

  • Os nomes de directório e de ficheiro são armazenados como strings de bytes prefixadas com comprimento.

  • Os dados de ficheiro podem ser armazenados literalmente (inline) ou através de um ponteiro indirecto para outro local na imagem, o que permite o alinhamento para acesso mapeado em memória.

  • Os tipos de registo desconhecidos são silenciosamente ignorados, garantindo compatibilidade futura.

Este formato está definido em extmod/vfs_rom.c no código-fonte do MicroPython. A implementação Python utilizada pelo mpremote para construir imagens encontra-se em tools/mpremote/mpremote/romfs.py.

Veja também

Trabalhar com sistemas de ficheiros – Visão geral do VFS do MicroPython e dos tipos de sistemas de ficheiros disponíveis.

Ficheiros de manifesto MicroPython – Como congelar módulos Python no firmware.

Ficheiros .mpy do MicroPython – Formato de ficheiro binário .mpy do MicroPython.

Controlo remoto do MicroPython: mpremote – A referência completa do comando mpremote.

romfs – Utilitário OpenMV para inspecionar o sistema de ficheiros /rom montado.