protocol — OpenMV-Protokollkanäle

Das Modul protocol stellt das OpenMV-Host-Protokoll für Python bereit. Es erlaubt die Initialisierung und Konfiguration des firmwareseitigen Protokoll-Stacks und ermöglicht es Benutzercode, eigene logische Kanäle zu registrieren, die von einem Python-Objekt unterstützt werden, das die Kanalschnittstelle implementiert (read, write, size, poll usw.). Mit diesem kommunizieren Desktop-Begleittools, wenn sie Bilddaten streamen oder interaktive Widgets an einer verbundenen Kamera bereitstellen.

Beispiele

Streamt ein RGB565-Bild an ein Host-Tool über ein eigenes Backend, das die Roh-Kanalschnittstelle implementiert (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

Das passende hostseitige Skript, das das openmv-Python-Paket (pip install openmv) verwendet, um eine Verbindung herzustellen, das kameraseitige Skript zu übertragen und jedes Einzelbild abzurufen:

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

Ersetzen Sie /dev/ttyACM0 durch den seriellen Port der Kamera (z. B. COM3 unter Windows). Der Konstruktor openmv.camera.Camera akzeptiert dieselben Protokollparameter wie init (crc / seq / ack / events / max_payload / max_retry / timeout), wenn der kameraseitige Stack passend neu konfiguriert wurde.

Funktionen

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

Initialisiert (oder rekonfiguriert) den Protokoll-Stack und registriert die logischen Standard-Datenkanäle (stdin, stdout, stream und, falls einkompiliert, profile). Löst RuntimeError aus, wenn die Initialisierung fehlschlägt. Die Firmware startet mit einem bereits laufenden USB-Standard-Protokoll-Stack, sodass dieser Aufruf nur erforderlich ist, um den Transport zu ändern oder die Standard-Framing-Parameter zu überschreiben.

crc aktiviert die CRC-Validierung von Protokoll-Frames.

seq aktiviert die Verfolgung von Sequenznummern.

ack aktiviert Bestätigungen pro Frame.

events aktiviert Kanal-Ereignisbenachrichtigungen.

max_payload ist die maximale Nutzlastgröße in Bytes. Wird sie weggelassen, wird der untenstehende kameraspezifische Standardwert verwendet; er wird aus der Protokoll-Puffergröße der jeweiligen Platine als buffer - 10 (header) - 4 (CRC) abgeleitet.

Kamera

Puffergröße

Maximale Nutzlast

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 ist die Anzahl der erneuten Übertragungsversuche. Standardwert 3.

rtx_timeout_ms ist das Zeitlimit für die erneute Übertragung in Millisekunden (verdoppelt sich nach jedem Zeitüberschreiten). Standardwert 500.

lock_interval_ms ist das minimale Sperrintervall in Millisekunden. Standardwert 10.

poll_ms ist das Abfrageintervall in Millisekunden. 0 (der Standardwert) deaktiviert das Timer-Polling.

protocol.is_active() bool

Gibt True zurück, wenn aktuell ein Host verbunden und der Protokoll-Stack aktiv ist, andernfalls False.

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

Registriert ein Python-backend-Objekt als neuen logischen Kanal und gibt ein ProtocolChannel-Handle zurück. Die verfügbaren Methoden des backend-Objekts (siehe Backend-Schnittstelle unten) bestimmen die Fähigkeiten des Kanals; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE und protocol.CHANNEL_FLAG_LOCK werden automatisch zu flags hinzugefügt, wenn die entsprechenden Methoden implementiert sind.

name ist der Kanalname als Zeichenkette. Wird auf die Größe des Kanalnamen-Puffers der Firmware gekürzt. Erforderlich.

backend ist das Python-Objekt, das die Backend-Schnittstelle implementiert. Erforderlich. Wird typischerweise als Schlüsselwort übergeben (backend=...).

flags sind zusätzliche Kanal-Flag-Bits (siehe die CHANNEL_FLAG_*-Konstanten). Optional; Standardwert 0.

Löst RuntimeError aus, wenn der Kanal nicht registriert werden kann (z. B. wenn keine freien Kanalplätze vorhanden sind).

Klassen

class protocol.ProtocolChannel

Handle, das von protocol.register zurückgegeben wird. Instanzen werden nicht direkt erstellt.

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

Sendet eine Kanal-Ereignisbenachrichtigung an den Host.

event ist die Ereigniskennung (Ganzzahl).

wait_ack blockiert bei True, bis der Host das Ereignis bestätigt.

Löst RuntimeError aus, wenn das Senden des Ereignisses fehlschlägt.

Backend-Schnittstelle

Ein an protocol.register übergebenes Backend-Objekt kann eine beliebige Teilmenge der folgenden Methoden implementieren. Nur die auf dem Objekt vorhandenen Methoden werden mit der C-Protokollschicht verdrahtet; fehlende Methoden lassen die entsprechende Fähigkeit deaktiviert.

class protocol.backend

Kanal-Backend-Objekt, das an protocol.register übergeben wird. Die folgenden Methoden beschreiben die optionale Schnittstelle, die ein Python-Backend implementieren kann.

init() object

Wird einmal aufgerufen, wenn der Kanal initialisiert wird. Gibt bei Erfolg einen beliebigen Wert ungleich None zurück; eine Ausnahme oder ein fehlender Rückgabewert wird als Fehler behandelt.

poll() bool

Gibt True zurück, wenn der Kanal Daten bereit hat, die vom Host gelesen werden können.

lock() bool

Belegt den Kanal für eine Übertragung. Gibt bei Erfolg True zurück.

unlock() bool

Gibt den Kanal nach einer Übertragung wieder frei. Gibt bei Erfolg True zurück.

size() int

Gibt die Anzahl der aktuell aus dem Kanal lesbaren Bytes zurück.

shape() tuple

Gibt ein Tupel aus bis zu vier Ganzzahlen zurück, das die Datenform beschreibt (z. B. Bildabmessungen). Bis zu vier Elemente werden von der Protokollschicht verbraucht.

flush() object

Schreibt alle ausstehenden Daten aus. Gibt bei Erfolg einen beliebigen Wert ungleich None zurück.

read(offset: int, size: int) bytes

Gibt bis zu size Bytes ab offset als bytes-ähnliches Objekt zurück, das das Buffer-Protokoll unterstützt.

readp(offset: int, size: int) bytes

Zero-Copy-Variante von read. Gibt einen Puffer zurück, dessen zugrunde liegender Speicher direkt von der Protokollschicht gelesen wird; der Puffer muss für die Dauer der Übertragung gültig bleiben.

write(offset: int, data: bytearray) int

Schreibt data an offset. data ist ein bytearray, das direkt auf den C-Puffer verweist. Gibt die Anzahl der geschriebenen Bytes zurück oder 0 bei standardmäßigem Erfolg.

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

Verarbeitet einen ioctl. arg ist None, wenn length null ist, andernfalls ein bytearray, das auf den C-Puffer verweist. Gibt bei Erfolg 0 oder None zurück, bei einem Fehler eine negative Ganzzahl.

is_active() bool

Gibt bei Transportkanälen True zurück, wenn der zugrunde liegende Transport aktuell verbunden ist.

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

Ein übergeordnetes Python-Backend (bereitgestellt durch das eingefrorene Paket protocol), das benannte Felder mit SenML-kompatiblen Ganzzahl-Schlüsseln in CBOR serialisiert. Unterstützt Anzeige-Widgets (label, depth) und interaktive Steuerelemente (toggle, slider, select) mit on_read/on_write-Callbacks.

on_read ist ein optionales aufrufbares Objekt on_read(channel), das aufgerufen wird, bevor der Kanal für den Host serialisiert wird. Verwenden Sie es, um Feldwerte zu aktualisieren.

on_write ist ein optionales aufrufbares Objekt on_write(channel, name, value), das aufgerufen wird, wenn der Host einen neuen Wert für ein benanntes Feld schreibt.

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

Fügt dem Kanal ein benanntes Feld hinzu.

name ist der Anzeigename; muss innerhalb dieses Kanals eindeutig sein.

type ist der Widget-Typ: "label", "toggle", "slider", "select" oder "depth".

value ist der Anfangswert. Der Standardwert hängt von type ab.

unit ist die Einheitenzeichenkette für label/slider (z. B. "Cel", "%RH").

min ist der Minimalwert (Schieberegler-Bereich oder Tiefenbereich).

max ist der Maximalwert (Schieberegler-Bereich oder Tiefenbereich).

step ist die Schrittweite (Schieberegler).

options ist die Liste der Optionszeichenketten (Auswahl).

width ist die Breite in Pixeln (Tiefe).

height ist die Höhe in Pixeln (Tiefe).

__getitem__(name: str) object

Gibt den aktuellen Wert des benannten Feldes zurück. Bei depth-Feldern wird der Binärdatenpuffer zurückgegeben, andernfalls der Skalarwert.

__setitem__(name: str, value: Any) None

Setzt den Wert des benannten Feldes. Bei slider-Feldern aktualisiert ein (min, max, value)-Tupel den Bereich und den aktuellen Wert gleichzeitig. Bei depth-Feldern ist value der Binärdatenpuffer.

poll() bool

Methode der Backend-Schnittstelle. Gibt True zurück, wenn serialisierte Daten für den Host verfügbar sind.

size() int

Methode der Backend-Schnittstelle. Ruft on_read auf (falls gesetzt) und gibt die Größe des serialisierten Puffers zurück.

read(offset: int, size: int) bytes

Methode der Backend-Schnittstelle. Gibt einen Ausschnitt des serialisierten Puffers zurück.

write(offset: int, data: bytearray) int

Methode der Backend-Schnittstelle. Decodiert eine CBOR-Aktualisierungsliste und wendet die Werte auf die entsprechenden benannten Felder an, wobei für jedes on_write aufgerufen wird.

Konstanten

Kanal-Flag-Bits (bitweise kombiniert; an protocol.register über flags übergeben oder automatisch anhand der Methoden des Backends gesetzt).

protocol.CHANNEL_FLAG_READ: int

Der Kanal unterstützt Lesevorgänge.

protocol.CHANNEL_FLAG_WRITE: int

Der Kanal unterstützt Schreibvorgänge.

protocol.CHANNEL_FLAG_LOCK: int

Der Kanal implementiert lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int

Der Kanal stellt einen physischen Transport dar (im Gegensatz zu einem logischen Datenkanal).

Integrierte Kanalkennungen.

protocol.CHANNEL_ID_TRANSPORT: int

Reservierte Kanal-ID für den aktiven Transport.

protocol.CHANNEL_ID_STDIN: int

Kanal-ID des integrierten stdin-Kanals.

protocol.CHANNEL_ID_STDOUT: int

Kanal-ID des integrierten stdout-Kanals.

protocol.CHANNEL_ID_STREAM: int

Kanal-ID des integrierten stream-Kanals.

protocol.CHANNEL_ID_PROFILE: int

Kanal-ID des integrierten Profiler-Kanals (nur vorhanden, wenn die Firmware mit aktiviertem Profiler erstellt wurde).