Working with ROMFS¶
Overview¶
ROMFS (Read-Only Memory Filesystem) is a lightweight, read-only filesystem designed for MicroPython devices. It is optimised for microcontrollers and embedded systems where code and data need to be stored in flash memory and accessed efficiently without being copied into RAM.
The key benefits of ROMFS are:
Zero-copy imports:
.mpybytecode files stored in a ROMFS can be executed directly from flash memory (memory-mapped) rather than being copied into RAM first. This is similar to how frozen modules work, but does not require reflashing the entire firmware.Low RAM overhead: Constant objects (strings, bytes, etc.) in
.mpyfiles loaded from ROMFS are referenced directly from flash, not duplicated in RAM.Flexible deployment: A ROMFS image can be built on a host PC and deployed to the device using mpremote, without rebuilding the firmware.
Standard filesystem interface: A ROMFS is mounted in the VFS and accessed via normal Python file operations (
open,os.listdir,import, etc.).
ROMFS is complementary to both the read-write FAT/LittleFS filesystems (which live in other flash partitions) and to frozen modules (which are compiled into the firmware itself).
Board support¶
ROMFS is enabled in the OpenMV firmware on every camera board that has a
ROMFS partition reserved in its flash layout. On these boards the ROMFS
partition is auto-detected at boot and mounted at /rom; both /rom
and /rom/lib are added to sys.path so modules stored there can be
imported directly.
Board |
ROMFS support |
|---|---|
OpenMV Cam N6 |
Yes |
OpenMV AE3 |
Yes |
OpenMV Cam RT1062 |
Yes |
OpenMV Cam Pure Thermal |
Yes |
OpenMV Cam M4 / M7 / H7 / H7 Plus |
Yes |
Arduino Giga |
Yes |
Arduino Portenta H7 |
Yes |
Arduino Nicla Vision |
Yes |
Arduino Nano 33 BLE Sense |
No (no ROMFS partition) |
Arduino Nano RP2040 Connect |
No (no ROMFS partition) |
See romfs for an OpenMV-specific helper that inspects the
mounted ROMFS at /rom.
Workflow¶
The typical workflow for using ROMFS is:
Create a directory on your PC with the Python files (or
.mpyfiles) you want to deploy.Use
mpremote romfs deploy <directory>to build and deploy the ROMFS image to the device.The ROMFS will be mounted at
/romon next boot (or can be mounted immediately if the device is rebooted).Python code on the device can then
importmodules from the ROMFS just like from any other filesystem.
For example:
# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/
After a soft-reset, the device will have /rom/app.py (or /rom/app.mpy
if mpy_cross is installed) available for import.
See the mpremote romfs sub-commands
section below for full details of the mpremote sub-commands.
Python API¶
The ROMFS Python API is provided via the vfs module.
- class vfs.VfsRom(buffer)
Create a ROMFS filesystem object from buffer, which must be an object supporting the buffer protocol (e.g. a
bytes,bytearray, ormemoryviewobject) that contains a valid ROMFS image.The constructor validates that buffer begins with the ROMFS magic bytes (
b"\xd2\xcd\x31"). If the buffer is too small or not a valid ROMFS thenOSError(ENODEV)is raised.Objects created by this constructor can be mounted using
vfs.mount().Example:
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')
Or, to mount a ROMFS image stored in a file:
import vfs with open('/flash/app.romfs', 'rb') as f: romfs_data = f.read() fs = vfs.VfsRom(romfs_data) vfs.mount(fs, '/rom2')
The following methods are available on a
VfsRomobject:- VfsRom.open(path, mode)
Open a file from the ROMFS. Only read modes (
'','r','rt','rb') are supported. Attempting to open a file for writing will raiseOSError(EROFS).The returned file object supports
read(),seek(),tell(), andclose(). For binary files opened in read mode, the returned object also supports the buffer protocol so that amemoryviewof the file data can be obtained, which refers directly into the ROMFS memory (zero-copy).
- VfsRom.ilistdir(path)
Return an iterator over the entries in the directory path. Each entry is a tuple
(name, type, inode, size)where type is0x8000for a file or0x4000for a directory.
- VfsRom.stat(path)
Return an
os.stat-like 10-tuple for path. RaisesOSError(ENOENT)if the path does not exist.
- VfsRom.statvfs(path)
Return filesystem statistics. The block size is reported as 1 and the block count represents the total size of the ROMFS image in bytes. Free blocks and free files are always 0 (read-only filesystem).
- VfsRom.chdir(path)
Change directory within the ROMFS. Only the root (
'/') is supported; changing to any subdirectory raisesOSError(EOPNOTSUPP).
- VfsRom.getcwd()
Return the current working directory within the ROMFS. Always returns
'/'.
- vfs.rom_ioctl(op, ...)
Low-level interface for accessing the read-only memory (ROM) partition(s) of the device.
The supported operations are:
vfs.rom_ioctl(1)– Return the number of available ROM partitions.vfs.rom_ioctl(2, id)– Return the ROM partition with index id as amemoryviewobject. The memory can be read but not written directly.vfs.rom_ioctl(3, id, length)– Prepare a ROM partition for writing. Erases the first length bytes of the partition with index id. Returns the minimum write size in bytes (the alignment required for subsequent writes).vfs.rom_ioctl(4, id, offset, buf)– Write buf (a bytes-like object) to the ROM partition with index id at byte offset.vfs.rom_ioctl(5, id)– Complete a write sequence to partition id (performs any finalisation needed after writing, such as cache flushing).
These operations are used internally by
mpremoteto deploy ROMFS images. Most users do not need to callvfs.rom_ioctl()directly.Example (querying available partitions):
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")
Automatic mounting at boot¶
When ROMFS support is enabled in the firmware, MicroPython will automatically
attempt to mount the first ROM partition at /rom during initialisation.
If the partition contains a valid ROMFS image, it is mounted and both
/rom and /rom/lib are added to sys.path automatically.
This means that after deploying a ROMFS image with mpremote, a soft-reset
is sufficient to make the new modules importable.
If no valid ROMFS image is found in the partition (e.g. on a freshly-programmed board), the mount is silently skipped.
Using mpremote to manage ROMFS¶
The mpremote tool provides three sub-commands for managing ROMFS images on a connected device.
romfs query¶
$ mpremote romfs query
Lists all available ROMFS partitions on the device and their sizes. Also shows the first 12 bytes of each partition in hex and reports whether a valid ROMFS image is present.
Example output:
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>
Build a ROMFS image from the directory source on the host PC. The image
is written to output (default: <source>.romfs).
Options:
-o <output>,--output <output>: Specify the output file path.-m,--mpy(default): Automatically compile.pyfiles to.mpyusingmpy_crossbefore adding them to the image. Requires thempy_crossPython package (pip install mpy_cross).--no-mpy: Disable automatic compilation of.pyfiles.
Example:
$ 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>
Deploy a ROMFS image to the device. source can be either:
A directory on the host: the ROMFS image is built in memory and deployed directly.
A
.romfsor.imgfile: the image is read from disk and deployed.
Options:
-p <partition>,--partition <partition>: Specify the target partition index (default:0).-m,--mpy(default): Compile.pyto.mpywhen source is a directory.--no-mpy: Disable automatic compilation of.pyfiles.
After deployment, the device must be soft-reset for the new ROMFS to be
mounted at /rom.
Example:
$ 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
Examples¶
Deploying a simple application¶
Suppose you have a project directory myapp/ with the following structure:
myapp/
main.py
utils.py
lib/
helper.py
To deploy it to the device’s ROMFS:
$ mpremote romfs deploy myapp/
After a soft-reset, the modules are importable from the ROMFS:
import main
import utils
from lib import helper
Listing ROMFS contents from Python¶
After mounting, the ROMFS contents can be explored like any other filesystem:
import os
for entry in os.ilistdir('/rom'):
print(entry)
# Or simply:
print(os.listdir('/rom'))
OpenMV also ships a small romfs helper that prints a formatted
listing including each file’s memory-mapped address and alignment:
from omv import romfs
romfs.ls_romfs()
Nesting a ROMFS within a ROMFS¶
A ROMFS image stored as a file within an outer ROMFS can be mounted as a
nested filesystem. For example, if /rom/inner.romfs exists. Because
/rom is a ROMFS, file objects opened from it support the buffer protocol,
so a zero-copy memoryview can be obtained directly:
import vfs
with open('/rom/inner.romfs', 'rb') as f:
inner = vfs.VfsRom(memoryview(f))
vfs.mount(inner, '/inner')
print(os.listdir('/inner'))
ROMFS image format¶
The ROMFS image format is a compact binary format designed for memory-mapped access on microcontrollers. A brief overview:
The image starts with the magic bytes
0xd2 0xcd 0x31(encoded as"RM1"with the high bits of the first two bytes set).The remainder of the image is composed of records, each with a type tag (varuint), a length (varuint), and a payload.
Record types include: padding, verbatim data, indirect data pointer, directory, file.
Directory and file names are stored as length-prefixed byte strings.
File data can be stored verbatim (inline) or via an indirect pointer to elsewhere in the image, which enables alignment for memory-mapped access.
Unknown record types are silently skipped, providing forwards compatibility.
This format is defined in extmod/vfs_rom.c in the MicroPython source.
The Python implementation used by mpremote to build images is in
tools/mpremote/mpremote/romfs.py.
See also
Working with filesystems – Overview of the MicroPython VFS and available filesystem types.
MicroPython manifest files – How to freeze Python modules into firmware.
MicroPython .mpy files – MicroPython .mpy binary file format.
MicroPython remote control: mpremote – The full mpremote command reference.
romfs – OpenMV helper for inspecting the mounted /rom
filesystem.