protocol — OpenMV-protokollkanaler#

Modulen protocol exponerar OpenMV-värdprotokollet till Python. Den gör det möjligt att initiera och konfigurera protokollstacken på fast programvara-sidan, och låter användarkod registrera anpassade logiska kanaler som backas av ett Python-objekt som implementerar kanalgränssnittet (read, write, size, poll osv.). Det är detta som följeslagarverktyg på skrivbordet kommunicerar med när de strömmar bilddata eller exponerar interaktiva widgetar mot en ansluten kamera.

Exempel#

Strömma en RGB565-bild till ett värdverktyg med en anpassad backend som implementerar det råa kanalgränssnittet (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

Det matchande värdskriptet, som använder Python-paketet openmv (pip install openmv) för att ansluta, skicka skriptet till kameran och hämta varje bildruta:

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

Ersätt /dev/ttyACM0 med kamerans serieport (t.ex. COM3 på Windows). Konstruktorn openmv.camera.Camera accepterar samma protokollparametrar som init (crc / seq / ack / events / max_payload / max_retry / timeout) när stacken på kamerasidan har omkonfigurerats för att matcha.

Funktioner#

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#

Initierar (eller omkonfigurerar) protokollstacken och registrerar de logiska standarddatakanalerna (stdin, stdout, stream och, om den är inkompilerad, profile). Utlöser RuntimeError om initieringen misslyckas. Firmwaren startar med en USB-protokollstack som redan körs som standard, så detta behöver bara anropas för att byta transport eller åsidosätta standardparametrarna för ramning. Anropas den igen initieras stacken om: den tidigare pollningstimern tas bort och kanalerna registreras på nytt.

crc aktiverar CRC-validering på protokollramar.

seq aktiverar spårning av sekvensnummer.

ack aktiverar bekräftelser per ram.

events aktiverar aviseringar om kanalhändelser.

max_payload är den maximala nyttolaststorleken i byte. Om den utelämnas används standardvärdet per kamera nedan; det härleds från varje korts protokollbuffertstorlek som buffer - 10 (header) - 4 (CRC).

Kamera

Buffertstorlek

Max nyttolast

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 är antalet återsändningsförsök. Standard 3.

rtx_timeout_ms är återsändningstimeouten i millisekunder (fördubblas efter varje timeout). Standard 500.

lock_interval_ms är det minsta låsintervallet i millisekunder. Standard 10.

poll_ms är pollningsintervallet i millisekunder. 0 (standard) inaktiverar timerbaserad pollning.

protocol.is_active() → bool#

Returnerar True om en värd är ansluten för närvarande och protokollstacken är aktiv, annars False.

protocol.poll() → int#

Kör en iteration av protokolluppgiften: tömmer transportens mottagningsbuffert, tolkar alla kompletta ramar, skickar vidare kommandon och betjänar kanalhändelser. Normalt gör firmwaren detta åt dig från USB-avbrottet och från poll_ms-timern, så detta behövs bara för anpassade transporter som inte drivs av någotdera – till exempel när poll_ms=0 och transporten pollas från skriptets huvudloop.

Returnerar 0 vid framgång och -1 om ingen aktiv transport är registrerad eller om anropet gjordes från en avbrottskontext.

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

Registrera ett Python-backend-objekt som en ny logisk kanal och returnera ett ProtocolChannel-handtag. De metoder som finns tillgängliga på backend-objektet (se Backend-gränssnitt nedan) bestämmer kanalens kapacitet; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE och protocol.CHANNEL_FLAG_LOCK läggs till i flags automatiskt när motsvarande metoder är implementerade.

name är kanalnamnet som en sträng. Trunkeras till storleken på den fasta programvarans kanalnamnsbuffert. Obligatorisk.

backend är Python-objektet som implementerar backend-gränssnittet. Obligatorisk. Skickas vanligtvis med nyckelord (backend=...).

flags är ytterligare kanalflaggbitar (se konstanterna CHANNEL_FLAG_*). Valfri; standardvärdet är 0.

Höjer RuntimeError om kanalen inte kan registreras (t.ex. inga lediga kanalplatser).

Klasser#

class protocol.ProtocolChannel#

Handtag som returneras av protocol.register. Instanser konstrueras inte direkt.

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

Skicka en avisering om en kanalhändelse till värden.

event är händelseidentifieraren (heltal).

wait_ack om True blockerar tills värden bekräftar händelsen.

Höjer RuntimeError om sändningen av händelsen misslyckas.

Backend-gränssnitt#

Ett backend-objekt som skickas till protocol.register kan implementera valfri delmängd av följande metoder. Endast de metoder som finns på objektet kopplas till C-protokolllagret; metoder som saknas lämnar motsvarande kapacitet inaktiverad.

class protocol.backend#

Kanal-backend-objekt som skickas till protocol.register. Metoderna nedan beskriver det valfria gränssnitt som en Python-backend kan implementera.

init() → object#

Anropas en gång när kanalen initieras. Returnera ett värde som inte är None vid lyckat resultat; ett undantag eller en utebliven retur behandlas som ett fel.

poll() → bool#

Returnerar True om kanalen har data redo att läsas av värden.

lock() → bool#

Förvärva kanalen för en överföring. Returnera True vid lyckat resultat.

unlock() → bool#

Frigör kanalen efter en överföring. Returnera True vid lyckat resultat.

size() → int#

Returnerar antalet byte som för närvarande är läsbara från kanalen.

shape() → tuple#

Returnerar en tupel med upp till fyra heltal som beskriver dataformen (t.ex. bildmått). Upp till fyra element konsumeras av protokolllagret.

flush() → object#

Töm eventuell väntande data. Returnera ett värde som inte är None vid lyckat resultat.

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

Returnerar upp till size byte från och med offset som ett bytes-liknande objekt som stöder buffertprotokollet.

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

Nollkopieringsvariant av read. Returnerar en buffert vars underliggande minne läses direkt av protokolllagret; bufferten måste förbli giltig under hela överföringen.

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

Skriv data vid offset. data är en bytearray som refererar direkt till C-bufferten. Returnera antalet skrivna byte, eller 0 vid standardmässigt lyckat resultat.

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

Hantera en ioctl. arg är None om length är noll, annars en bytearray som refererar till C-bufferten. Returnera 0 eller None vid lyckat resultat, eller ett negativt heltal vid fel.

is_active() → bool#

För transportkanaler, returnera True om den underliggande transporten är ansluten för närvarande.

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

En Python-backend på högre nivå (som tillhandahålls av det frysta protocol-paketet) som serialiserar namngivna fält till CBOR med SenML-kompatibla heltalsnycklar. Stöder visningswidgetar (label, text, depth, waveform) och interaktiva kontroller (toggle, pushbutton, slider, spinbox, select, radio, lineedit) med on_read/on_write-återanrop.

on_read är ett valfritt anropbart objekt on_read(channel) som anropas innan kanalen serialiseras för värden. Använd det för att uppdatera fältvärden.

on_write är ett valfritt anropbart objekt on_write(channel, name, value) som anropas när värden skriver ett nytt värde för ett namngivet fält.

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#

Lägg till ett namngivet fält i kanalen.

name är visningsnamnet; måste vara unikt inom denna kanal.

type är widgettypen:

  • "label" – skrivskyddad numerisk visning med en valfri unit.

  • "text" – skrivskyddad strängvisning.

  • "toggle" – boolesk växlare.

  • "pushbutton" – momentan åtgärd (utlös, kalibrera, …). Värden skriver True vid klick och visar inget tillstånd.

  • "slider" – numerisk kontroll med min/max/step.

  • "spinbox" – exakt stegvis numerisk inmatning; delar skjutreglagets fält och uppdateringsformen med en (min, max, value)-tupel.

  • "select" – rullgardinsmeny med strängarna i options.

  • "radio" – options visade som en grupp radioknappar.

  • "lineedit" – skrivbar stränginmatning.

  • "depth" – 2D-visning av en djupkarta med storleken width x height.

  • "waveform" – 1D-tidsseriediagram över sammanflätade sampel.

value är startvärdet. Standardvärdet beror på type: 0 för label, "" för text/lineedit/select/ radio, False för toggle/pushbutton, min för slider/spinbox.

unit är enhetssträngen för label/slider/spinbox/ waveform (t.ex. "Cel", "%RH").

min är minimivärdet (intervall för slider/spinbox, eller visningsintervall för depth/waveform).

max är maximivärdet (intervall för slider/spinbox, eller visningsintervall för depth/waveform).

step är stegstorleken (slider/spinbox).

options är listan med alternativsträngar (select/radio), eller listan med namn per serie (waveform).

width är pixelbredden (depth).

height är pixelhöjden (depth).

sample_rate är samplingsfrekvensen i sampel per sekund (waveform). Skickas till värden som samplingsperiod.

series är antalet sammanflätade serier i varje sampelblock (waveform), t.ex. 3 för en XYZ-accelerometerkurva.

typecode är array-typkoden för vågformens sampel (t.ex. "H", "h", "f"). Standard är "H" (16-bitars utan tecken).

__getitem__(name: str) → object#

Returnerar det aktuella värdet för det namngivna fältet. För depth- och waveform-fält returneras den binära databufferten, annars skalärvärdet.

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

Sätter värdet för det namngivna fältet. För slider- och spinbox-fält uppdaterar en (min, max, value)-tupel intervallet och det aktuella värdet samtidigt. För depth-fält är value den binära databufferten. För waveform-fält är value ett bytes-liknande block av sammanflätade sampel i fältets typecode; antalet sampel per serie härleds från dess längd och en monoton tidsstämpel (i mikrosekunder, ackumulerad från time.ticks_us()) bifogas så att värden kan placera blocket på sin tidsaxel.

poll() → bool#

Metod i backend-gränssnittet. Returnerar True när serialiserad data är tillgänglig för värden.

size() → int#

Metod i backend-gränssnittet. Anropar on_read (om den är satt) och returnerar storleken på den serialiserade bufferten.

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

Metod i backend-gränssnittet. Returnerar en del av den serialiserade bufferten.

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

Metod i backend-gränssnittet. Avkodar en CBOR-uppdateringslista och tillämpar värden på matchande namngivna fält, varvid on_write anropas för vart och ett.

Konstanter#

Kanalflaggbitar (kombineras bitvis; skickas till protocol.register via flags eller sätts automatiskt baserat på backendens metoder).

protocol.CHANNEL_FLAG_READ: int#

Kanalen stöder läsningar.

protocol.CHANNEL_FLAG_WRITE: int#

Kanalen stöder skrivningar.

protocol.CHANNEL_FLAG_LOCK: int#

Kanalen implementerar lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

Kanalen representerar en fysisk transport (till skillnad från en logisk datakanal).

Inbyggda kanalidentifierare.

protocol.CHANNEL_ID_TRANSPORT: int#

Reserverat kanal-ID för den aktiva transporten.

protocol.CHANNEL_ID_STDIN: int#

Kanal-ID för den inbyggda stdin-kanalen.

protocol.CHANNEL_ID_STDOUT: int#

Kanal-ID för den inbyggda stdout-kanalen.

protocol.CHANNEL_ID_STREAM: int#

Kanal-ID för den inbyggda stream-kanalen.

protocol.CHANNEL_ID_PROFILE: int#

Kanal-ID för den inbyggda profilerarkanalen (finns endast när den fasta programvaran är byggd med profileraren aktiverad).