protocol — OpenMV-protocolkanalen#

De protocol module stelt het OpenMV-hostprotocol beschikbaar aan Python. Het maakt het mogelijk om de protocolstack aan de firmwarezijde te initialiseren en te configureren, en laat gebruikerscode aangepaste logische kanalen registreren die worden ondersteund door een Python-object dat de kanaalinterface implementeert (read, write, size, poll, enz.). Dit is waar desktop-begeleidingstools mee communiceren wanneer ze afbeeldingsgegevens streamen of interactieve widgets beschikbaar maken aan een verbonden camera.

Voorbeelden#

Stream een RGB565-afbeelding naar een hosttool met behulp van een aangepaste backend die de raw-kanaalinterface implementeert (backend.size(), backend.shape(), backend.poll(), backend.read()):

import csi
import protocol

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.HD)

img = csi0.snapshot()
img_mv = memoryview(img.bytearray())
frame_ready = True


class FrameChannel:
    def size(self):
        return len(img_mv)

    def shape(self):
        return (img.height(), img.width(), len(img_mv))

    def poll(self):
        return frame_ready

    def read(self, offset, size):
        global frame_ready
        end = offset + size
        chunk = img_mv[offset:end]
        if end >= len(img_mv):
            frame_ready = False
        return chunk


protocol.register(name="frame", backend=FrameChannel())

while True:
    if not frame_ready:
        img = csi0.snapshot()
        img_mv = memoryview(img.bytearray())
        frame_ready = True

Het bijbehorende script aan de hostzijde, dat het openmv Python-pakket (pip install openmv) gebruikt om verbinding te maken, het script op de camera te pushen en elk frame op te halen:

import cv2
import numpy as np
from openmv.camera import Camera

# The on-cam script above, stored as a string (or read from a file).
SCRIPT = open("frame_streamer_on_cam.py").read()

with Camera("/dev/ttyACM0", baudrate=921600) as cam:
    cam.stop()
    cam.exec(SCRIPT)

    while True:
        status = cam.read_status()
        if not cam.has_channel("frame") or not status.get("frame"):
            continue

        h, w, size = cam._channel_shape(cam.get_channel(name="frame"))
        if cam.channel_size("frame") < size:
            continue

        data = cam.channel_read("frame", size)
        rgb565 = np.frombuffer(data, dtype="<u2").reshape(h, w)

        # Unpack RGB565 to an HxWx3 uint8 RGB image.
        r = ((rgb565 >> 11) & 0x1F) << 3
        g = ((rgb565 >>  5) & 0x3F) << 2
        b = ( rgb565        & 0x1F) << 3
        frame = np.dstack([r, g, b]).astype(np.uint8)

        # Display with OpenCV (cv2 expects BGR, not RGB).
        cv2.imshow("OpenMV", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
        if cv2.waitKey(1) == ord("q"):
            break

cv2.destroyAllWindows()

Vervang /dev/ttyACM0 door de seriële poort van de camera (bijv. COM3 op Windows). De openmv.camera.Camera constructor accepteert dezelfde protocolparameters als init (crc / seq / ack / events / max_payload / max_retry / timeout) wanneer de stack aan de camerazijde overeenkomstig is geherconfigureerd.

Functies#

protocol.init(crc: bool = True, seq: bool = True, ack: bool = True, events: bool = True, max_payload: int = ..., rtx_retries: int = 3, rtx_timeout_ms: int = 500, lock_interval_ms: int = 10, poll_ms: int = 0) → None#

Initialiseert (of herconfigureert) de protocolstack en registreert de standaard logische datakanalen (stdin, stdout, stream en, indien meegecompileerd, profile). Genereert RuntimeError als de initialisatie mislukt. De firmware start op met een standaard USB-protocolstack die al draait, dus deze aanroep is alleen nodig om het transport te wijzigen of de standaard framing-parameters te overschrijven. Opnieuw aanroepen herinitialiseert de stack: de vorige polling-timer wordt verwijderd en de kanalen worden opnieuw geregistreerd.

crc schakelt CRC-validatie van protocolframes in.

seq schakelt het bijhouden van volgnummers in.

ack schakelt bevestigingen per frame in.

events schakelt kanaalgebeurtenis-notificaties in.

max_payload is de maximale payloadgrootte in bytes. Indien weggelaten wordt de onderstaande standaardwaarde per camera gebruikt; deze is afgeleid van de protocolbuffergrootte van elk board als buffer - 10 (header) - 4 (CRC).

Camera

Buffergrootte

Max. payload

OpenMV Cam M4 (OPENMV2)

512

498

OpenMV Cam M7 (OPENMV3)

512

498

OpenMV Cam H7 (OPENMV4)

512

498

OpenMV Cam H7 Plus (OPENMV4P)

4096

4082

OpenMV Pure Thermal (OPENMVPT)

4096

4082

OpenMV Cam RT1062 (OPENMV_RT1060)

4096

4082

OpenMV Cam N6 (OPENMV_N6)

8192

8178

OpenMV AE3 (OPENMV_AE3)

8192

8178

Arduino Portenta H7 (ARDUINO_PORTENTA_H7)

4096

4082

Arduino Giga (ARDUINO_GIGA)

4096

4082

Arduino Nicla Vision (ARDUINO_NICLA_VISION)

4096

4082

rtx_retries is het aantal hertransmissiepogingen. Standaard 3.

rtx_timeout_ms is de hertransmissie-timeout in milliseconden (verdubbeld na elke timeout). Standaard 500.

lock_interval_ms is het minimale vergrendelingsinterval in milliseconden. Standaard 10.

poll_ms is het polling-interval in milliseconden. 0 (de standaardwaarde) schakelt timer-polling uit.

protocol.is_active() → bool#

Retourneert True als er momenteel een host verbonden is en de protocolstack actief is, anders False.

protocol.poll() → int#

Voert één iteratie van de protocoltaak uit: leegt de ontvangstbuffer van het transport, parseert eventuele complete frames, verwerkt commando’s en handelt kanaalgebeurtenissen af. Normaal doet de firmware dit voor je vanuit de USB-interrupt en vanuit de poll_ms-timer, dus dit is alleen nodig voor aangepaste transporten die door geen van beide worden aangestuurd – bijvoorbeeld wanneer poll_ms=0 en het transport vanuit de hoofdlus van het script wordt gepold.

Retourneert 0 bij succes en -1 als er geen actief transport is geregistreerd of de aanroep vanuit een interrupt-context is gedaan.

protocol.register(name: str, *, backend: object, flags: int = 0) → ProtocolChannel#

Registreert een Python backend object als een nieuw logisch kanaal en retourneert een ProtocolChannel handle. De beschikbare methoden van het backend object (zie Backend-interface hieronder) bepalen de mogelijkheden van het kanaal; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE en protocol.CHANNEL_FLAG_LOCK worden automatisch aan flags toegevoegd wanneer de bijbehorende methoden zijn geïmplementeerd.

name is de kanaalnaam als string. Afgekapt tot de buffergrootte van de kanaalnaam in de firmware. Vereist.

backend is het Python-object dat de backend-interface implementeert. Vereist. Wordt doorgaans via een keyword doorgegeven (backend=...).

flags zijn aanvullende kanaalvlagbits (zie de CHANNEL_FLAG_* constanten). Optioneel; standaard 0.

Genereert een RuntimeError als het kanaal niet kan worden geregistreerd (bijv. geen vrije kanaalslots).

Klassen#

class protocol.ProtocolChannel#

Handle die wordt geretourneerd door protocol.register. Instanties worden niet rechtstreeks geconstrueerd.

send_event(event: int, wait_ack: bool = False) → None#

Stuurt een kanaalgebeurtenis-notificatie naar de host.

event is de gebeurtenis-identifier (geheel getal).

wait_ack indien True blokkeert totdat de host de gebeurtenis bevestigt.

Genereert een RuntimeError als het verzenden van de gebeurtenis mislukt.

Backend-interface#

Een backend-object dat aan protocol.register wordt doorgegeven mag elke subset van de volgende methoden implementeren. Alleen de methoden die op het object aanwezig zijn worden gekoppeld aan de C-protocollaag; ontbrekende methoden laten de bijbehorende mogelijkheid uitgeschakeld.

class protocol.backend#

Kanaal-backendobject dat aan protocol.register wordt doorgegeven. De onderstaande methoden beschrijven de optionele interface die een Python-backend mag implementeren.

init() → object#

Wordt eenmalig aangeroepen wanneer het kanaal wordt geïnitialiseerd. Retourneert bij succes een waarde die niet None is; een uitzondering of ontbrekende retourwaarde wordt als een fout behandeld.

poll() → bool#

Retourneert True als het kanaal gegevens klaar heeft om door de host te worden gelezen.

lock() → bool#

Vergrendelt het kanaal voor een overdracht. Retourneert True bij succes.

unlock() → bool#

Geeft het kanaal vrij na een overdracht. Retourneert True bij succes.

size() → int#

Retourneert het aantal bytes dat momenteel uit het kanaal kan worden gelezen.

shape() → tuple#

Retourneert een tuple van maximaal vier gehele getallen die de gegevensvorm beschrijven (bijv. afbeeldingsafmetingen). Maximaal vier elementen worden door de protocollaag verbruikt.

flush() → object#

Spoelt eventuele in behandeling zijnde gegevens door. Retourneert bij succes een waarde die niet None is.

read(offset: int, size: int) → bytes#

Retourneert maximaal size bytes vanaf offset als een bytes-achtig object dat het bufferprotocol ondersteunt.

readp(offset: int, size: int) → bytes#

Zero-copy-variant van read. Retourneert een buffer waarvan het onderliggende geheugen rechtstreeks door de protocollaag wordt gelezen; de buffer moet geldig blijven gedurende de overdracht.

write(offset: int, data: bytearray) → int#

Schrijft data op offset. data is een bytearray die rechtstreeks naar de C-buffer verwijst. Retourneert het aantal geschreven bytes, of 0 bij standaard succes.

ioctl(cmd: int, length: int, arg: bytearray | None) → int#

Handelt een ioctl af. arg is None als length nul is, anders een bytearray die naar de C-buffer verwijst. Retourneert 0 of None bij succes, of een negatief geheel getal bij een fout.

is_active() → bool#

Retourneert voor transportkanalen True als het onderliggende transport momenteel verbonden is.

class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)#

Een Python-backend op hoger niveau (geleverd door het bevroren protocol-pakket) die benoemde velden naar CBOR serialiseert met SenML-compatibele integer-sleutels. Ondersteunt weergavewidgets (label, text, depth, waveform) en interactieve bedieningselementen (toggle, pushbutton, slider, spinbox, select, radio, lineedit) met on_read/on_write-callbacks.

on_read is een optionele aanroepbare on_read(channel) die wordt aangeroepen voordat het kanaal voor de host wordt geserialiseerd. Gebruik het om veldwaarden te vernieuwen.

on_write is een optionele aanroepbare on_write(channel, name, value) die wordt aangeroepen wanneer de host een nieuwe waarde voor een benoemd veld schrijft.

add(name: str, type: str, value: Any = None, unit: str | None = None, min: int | float | None = None, max: int | float | None = None, step: int | float | None = None, options: list | None = None, width: int | None = None, height: int | None = None, sample_rate: float | None = None, series: int = 1, typecode: str | None = None) → None#

Voegt een benoemd veld toe aan het kanaal.

name is de weergavenaam; moet uniek zijn binnen dit kanaal.

type is het widgettype:

  • "label" – alleen-lezen numerieke weergave met een optionele unit.

  • "text" – alleen-lezen stringweergave.

  • "toggle" – booleaanse schakelaar.

  • "pushbutton" – kortstondige actie (trigger, kalibreren, …). De host schrijft True bij een klik en toont geen toestand.

  • "slider" – numeriek bedieningselement met min/max/step.

  • "spinbox" – nauwkeurige numerieke invoer in stappen; deelt de slider-velden en de update-vorm met een (min, max, value)-tuple.

  • "select" – keuzelijst (drop-down) van options-strings.

  • "radio" – options weergegeven als een groep keuzerondjes.

  • "lineedit" – schrijfbare stringinvoer.

  • "depth" – 2D-dieptekaartweergave van width x height.

  • "waveform" – 1D-tijdreeksplot van interleaved samples.

value is de beginwaarde. De standaardwaarde hangt af van type: 0 voor label, "" voor text/lineedit/select/ radio, False voor toggle/pushbutton, min voor slider/spinbox.

unit is de eenheidsstring voor label/slider/spinbox/ waveform (bijv. "Cel", "%RH").

min is de minimumwaarde (bereik van slider/spinbox, of weergavebereik van depth/waveform).

max is de maximumwaarde (bereik van slider/spinbox, of weergavebereik van depth/waveform).

step is de stapgrootte (slider/spinbox).

options is de lijst met optiestrings (select/radio), of de lijst met namen per reeks (waveform).

width is de breedte in pixels (depth).

height is de hoogte in pixels (depth).

sample_rate is de bemonsteringsfrequentie in samples per seconde (waveform). Wordt naar de host gestuurd als de sampleperiode.

series is het aantal interleaved reeksen in elk sampleblok (waveform), bijv. 3 voor een XYZ-accelerometerspoor.

typecode is de array-typecode van de waveform-samples (bijv. "H", "h", "f"). Standaard "H" (unsigned 16-bit).

__getitem__(name: str) → object#

Retourneert de huidige waarde van het benoemde veld. Voor depth- en waveform-velden wordt de binaire databuffer geretourneerd, anders de scalaire waarde.

__setitem__(name: str, value: Any) → None#

Stelt de waarde van het benoemde veld in. Voor slider- en spinbox-velden werkt een (min, max, value)-tuple het bereik en de huidige waarde tegelijk bij. Voor depth-velden is value de binaire databuffer. Voor waveform-velden is value een bytes-achtig blok interleaved samples in de typecode van het veld; het aantal samples per reeks wordt uit de lengte afgeleid en er wordt een monotone tijdstempel (in microseconden, opgebouwd uit time.ticks_us()) aan gekoppeld zodat de host het blok op zijn tijdas kan plaatsen.

poll() → bool#

Methode van de backend-interface. Retourneert True wanneer er geserialiseerde gegevens beschikbaar zijn voor de host.

size() → int#

Methode van de backend-interface. Roept on_read aan (indien ingesteld) en retourneert de grootte van de geserialiseerde buffer.

read(offset: int, size: int) → bytes#

Methode van de backend-interface. Retourneert een segment van de geserialiseerde buffer.

write(offset: int, data: bytearray) → int#

Methode van de backend-interface. Decodeert een CBOR-updatelijst en past waarden toe op overeenkomende benoemde velden, waarbij voor elke on_write wordt aangeroepen.

Constanten#

Kanaalvlagbits (bitsgewijs gecombineerd; doorgegeven aan protocol.register via flags of automatisch ingesteld op basis van de methoden van de backend).

protocol.CHANNEL_FLAG_READ: int#

Het kanaal ondersteunt leesbewerkingen.

protocol.CHANNEL_FLAG_WRITE: int#

Het kanaal ondersteunt schrijfbewerkingen.

protocol.CHANNEL_FLAG_LOCK: int#

Het kanaal implementeert lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

Het kanaal vertegenwoordigt een fysiek transport (in tegenstelling tot een logisch datakanaal).

Ingebouwde kanaal-identifiers.

protocol.CHANNEL_ID_TRANSPORT: int#

Gereserveerde kanaal-ID voor het actieve transport.

protocol.CHANNEL_ID_STDIN: int#

Kanaal-ID van het ingebouwde stdin kanaal.

protocol.CHANNEL_ID_STDOUT: int#

Kanaal-ID van het ingebouwde stdout kanaal.

protocol.CHANNEL_ID_STREAM: int#

Kanaal-ID van het ingebouwde stream kanaal.

protocol.CHANNEL_ID_PROFILE: int#

Kanaal-ID van het ingebouwde profiler-kanaal (alleen aanwezig wanneer de firmware met de profiler ingeschakeld is gebouwd).