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