14.2.2.2. Een ROMFS-image bouwen

Een ROMFS-image is een in flash residerend, alleen-lezen bestandssysteem dat de runtime automatisch koppelt op /rom. Het lost het assetprobleem op waar de vorige pagina mee afsloot: machine-learning-modelbestanden, labeltabellen, JSON-configuratie, afbeeldingssjablonen – alles wat de applicatie opent en leest maar nooit schrijft – gaat mee in de build zonder de kosten van inbedding als Python-literals te betalen.

Drie dingen maken ROMFS de juiste tool voor verzonden assets:

  • Het bestandssysteem is onderdeel van de firmware-image. Eindgebruikers kunnen geen bestand uit /rom verwijderen, er een bewerken of er een vervangen door een eigen exemplaar.

  • Bestanden in /rom zijn ter plaatse toegankelijk. Consumenten zoals de ml-module die een modelbestand laadt, krijgen een directe weergave in het flashgeheugen zonder RAM-kopie – een model van meerdere megabytes op /rom “laadt” in wezen gratis, terwijl hetzelfde bestand op /sdcard bij het laden in RAM wordt ingelezen en daar blijft gedurende de levensduur van de referentie. Gewone open() + read kopieert op aanvraag: elke read(n)-aanroep kopieert n bytes van flash naar RAM op het moment van de aanroep, terwijl een kale read() om het hele bestand vraagt.

  • /rom en /rom/lib worden bij het booten aan sys.path toegevoegd. Python-pakketten die in de image worden geplaatst zijn op naam importeerbaar; niets bijzonders op de aanroepplek.

14.2.2.2.1. Een image bouwen

ROMFS-images worden via de IDE gemaakt, bewerkt en geflasht. Gebruik die als de bron van waarheid voor de inhoud van elke verzonden ROMFS-partitie.

De reden dat dit van belang is: modelbestanden komen met uitlijningseisen die de loader tijdens runtime afdwingt. .tflite-bestanden moeten worden opgevuld tot 16-byte-grenzen, en de NPU van de N6 vereist 32-byte-uitlijning voor gecompileerde modellen. De IDE past die opvulling automatisch toe wanneer hij de image schrijft. Tools die de bronboom doorlopen zonder de opvulling toe te passen – mpremote romfs in het bijzonder – produceren een image die netjes koppelt maar waarvan de modellen falen bij de eerste inferentie-aanroep.

De ROMFS-editor van de IDE is een interactieve weergave van de inhoud van de image. Bestanden en mappen kunnen in het geheugen worden toegevoegd, hernoemd en verwijderd; bij het opslaan wordt het resultaat weggeschreven als een .img-bestand dat klaar is om te flashen. Een typische structuur voor een applicatie die een model verzendt naast wat assets en een Python-pakket ziet er als volgt uit:

model.tflite
labels.txt
config.json
templates/
    calibration.jpg
lib/
    mylib/
        __init__.py
        helpers.py

Tip

Zowel de IDE als mpremote cross-compileren .py-bestanden naar .mpy-bytecode op weg naar een ROMFS-image, zodat de cam ze importeert zonder bij het laden de parse-kosten te betalen. Bronbestanden in de editor blijven .py; de image bevat .mpy.

Zodra de image is geflasht, is de boom zichtbaar vanuit MicroPython op /rom/

>>> import os
>>> os.listdir('/rom')
['model.tflite', 'labels.txt', 'config.json', 'templates', 'lib']
>>> import mylib
>>> mylib.helpers
<module 'mylib.helpers' from '/rom/lib/mylib/helpers.mpy'>

14.2.2.2.2. Het grootste deel van de applicatie woont in ROMFS

ROMFS is de juiste thuisbasis voor bijna alles wat een applicatie verzendt: de bibliotheken die het importeert, de modelbestanden die het laadt, de configuratie die het leest, elke asset waarvan de uitvoer afkomstig is van een buildtool die een bestandsboom produceert (modelconverters, afbeeldingspijplijnen, asset-packers), en – belangrijk – de applicatiecode zelf.

De frozen-modules-kant moet klein blijven: boot.py voor pre-REPL-setup, main.py als een dun startpunt, en alleen de bibliotheken waarzonder de cam echt niet kan booten. Al het andere gaat in ROMFS, waar erop itereren neerkomt op een nieuwe .img die uit de IDE wordt opgeslagen en opnieuw geflasht – geen firmware-herbouw vereist, geen toolchain bij de hand nodig om het te doen.

Het patroon dat hieruit voortvloeit is een main.py die niets anders doet dan delegeren naar de in ROMFS residerende applicatie:

# main.py (frozen)
import app
app.run()

# /rom/app/__init__.py (in ROMFS)
def run():
    ...

Een wijziging aan app is een ROMFS-bewerking en een herflash. De firmware-build blijft onveranderd gedurende de levensduur van het product, tenzij er aan de frozen kant daadwerkelijk iets moet veranderen.