protocol — Canaux du protocole OpenMV#

Le module protocol expose le protocole hôte OpenMV à Python. Il permet d’initialiser et de configurer la pile de protocole côté micrologiciel, et permet au code utilisateur d’enregistrer des canaux logiques personnalisés adossés à un objet Python qui implémente l’interface de canal (read, write, size, poll, etc.). C’est ce avec quoi les outils compagnons de bureau communiquent lorsqu’ils diffusent des données d’image ou exposent des widgets interactifs à une caméra connectée.

Exemples#

Diffusez une image RGB565 vers un outil hôte à l’aide d’un backend personnalisé qui implémente l’interface de canal brute (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

Le script côté hôte correspondant, utilisant le paquet Python openmv (pip install openmv) pour se connecter, envoyer le script embarqué sur la caméra et récupérer chaque trame

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

Remplacez /dev/ttyACM0 par le port série de la caméra (par ex. COM3 sous Windows). Le constructeur openmv.camera.Camera accepte les mêmes paramètres de protocole que init (crc / seq / ack / events / max_payload / max_retry / timeout) lorsque la pile côté caméra a été reconfigurée en conséquence.

Fonctions#

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#

Initialise (ou reconfigure) la pile de protocole et enregistre les canaux de données logiques par défaut (stdin, stdout, stream et, s’il est compilé, profile). Lève RuntimeError si l’initialisation échoue. Le micrologiciel démarre avec une pile de protocole USB par défaut déjà active, l’appel de cette fonction n’est donc nécessaire que pour changer de transport ou remplacer les paramètres de tramage par défaut.

crc active la validation CRC sur les trames du protocole.

seq active le suivi des numéros de séquence.

ack active les accusés de réception par trame.

events active les notifications d’événements de canal.

max_payload est la taille maximale de la charge utile en octets. S’il est omis, la valeur par défaut propre à chaque caméra ci-dessous est utilisée ; elle est dérivée de la taille du tampon de protocole de chaque carte selon buffer - 10 (header) - 4 (CRC).

Caméra

Taille du tampon

Charge utile max

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 est le nombre de tentatives de retransmission. Par défaut 3.

rtx_timeout_ms est le délai d’expiration de retransmission en millisecondes (doublé après chaque expiration). Par défaut 500.

lock_interval_ms est l’intervalle de verrouillage minimal en millisecondes. Par défaut 10.

poll_ms est l’intervalle d’interrogation en millisecondes. 0 (la valeur par défaut) désactive l’interrogation par minuteur.

protocol.is_active() bool#

Renvoie True si un hôte est actuellement connecté et que la pile de protocole est active, sinon False.

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

Enregistre un objet Python backend comme nouveau canal logique et renvoie un descripteur ProtocolChannel. Les méthodes disponibles de l’objet backend (voir Interface du backend ci-dessous) déterminent les capacités du canal ; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE et protocol.CHANNEL_FLAG_LOCK sont ajoutés automatiquement à flags lorsque les méthodes correspondantes sont implémentées.

name est le nom du canal sous forme de chaîne. Tronqué à la taille du tampon de nom de canal du micrologiciel. Obligatoire.

backend est l’objet Python qui implémente l’interface du backend. Obligatoire. Généralement passé par mot-clé (backend=...).

flags correspond à des bits d’indicateur de canal supplémentaires (voir les constantes CHANNEL_FLAG_*). Facultatif ; vaut 0 par défaut.

Lève RuntimeError si le canal ne peut pas être enregistré (par ex. aucun emplacement de canal libre).

Classes#

class protocol.ProtocolChannel#

Descripteur renvoyé par protocol.register. Les instances ne sont pas construites directement.

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

Envoie une notification d’événement de canal à l’hôte.

event est l’identifiant de l’événement (entier).

wait_ack si True bloque jusqu’à ce que l’hôte accuse réception de l’événement.

Lève RuntimeError si l’envoi de l’événement échoue.

Interface du backend#

Un objet backend passé à protocol.register peut implémenter n’importe quel sous-ensemble des méthodes suivantes. Seules les méthodes présentes sur l’objet sont reliées à la couche de protocole C ; les méthodes manquantes laissent la capacité correspondante désactivée.

class protocol.backend#

Objet backend de canal passé à protocol.register. Les méthodes ci-dessous décrivent l’interface optionnelle qu’un backend Python peut implémenter.

init() object#

Appelée une seule fois lorsque le canal est initialisé. Renvoie toute valeur autre que None en cas de succès ; une exception ou l’absence de valeur de retour est considérée comme une erreur.

poll() bool#

Renvoie True si le canal a des données prêtes à être lues par l’hôte.

lock() bool#

Acquiert le canal pour un transfert. Renvoie True en cas de succès.

unlock() bool#

Libère le canal après un transfert. Renvoie True en cas de succès.

size() int#

Renvoie le nombre d’octets actuellement lisibles depuis le canal.

shape() tuple#

Renvoie un tuple d’au plus quatre entiers décrivant la forme des données (par ex. les dimensions d’une image). Jusqu’à quatre éléments sont consommés par la couche de protocole.

flush() object#

Vide toutes les données en attente. Renvoie toute valeur autre que None en cas de succès.

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

Renvoie jusqu’à size octets à partir de offset sous forme d’objet de type bytes prenant en charge le protocole tampon.

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

Variante sans copie de read. Renvoie un tampon dont la mémoire sous-jacente est lue directement par la couche de protocole ; le tampon doit rester valide pendant toute la durée du transfert.

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

Écrit data à offset. data est un bytearray référençant directement le tampon C. Renvoie le nombre d’octets écrits, ou 0 en cas de succès par défaut.

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

Traite un ioctl. arg vaut None si length est nul, sinon un bytearray référençant le tampon C. Renvoie 0 ou None en cas de succès, ou un entier négatif en cas d’erreur.

is_active() bool#

Pour les canaux de transport, renvoie True si le transport sous-jacent est actuellement connecté.

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

Un backend Python de plus haut niveau (fourni par le paquet figé protocol) qui sérialise des champs nommés en CBOR à l’aide de clés entières compatibles SenML. Prend en charge les widgets d’affichage (label, depth) et les contrôles interactifs (toggle, slider, select) avec des fonctions de rappel on_read/on_write.

on_read est un appelable optionnel on_read(channel) invoqué avant que le canal ne soit sérialisé pour l’hôte. Utilisez-le pour rafraîchir les valeurs des champs.

on_write est un appelable optionnel on_write(channel, name, value) invoqué lorsque l’hôte écrit une nouvelle valeur pour un champ nommé.

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#

Ajoute un champ nommé au canal.

name est le nom d’affichage ; il doit être unique au sein de ce canal.

type est le type de widget : "label", "toggle", "slider", "select" ou "depth".

value est la valeur initiale. La valeur par défaut dépend de type.

unit est la chaîne d’unité pour label/slider (par ex. "Cel", "%RH").

min est la valeur minimale (plage du slider ou plage de depth).

max est la valeur maximale (plage du slider ou plage de depth).

step est le pas (slider).

options est la liste des chaînes d’options (select).

width est la largeur en pixels (depth).

height est la hauteur en pixels (depth).

__getitem__(name: str) object#

Renvoie la valeur actuelle du champ nommé. Pour les champs depth, le tampon de données binaires est renvoyé, sinon la valeur scalaire.

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

Définit la valeur du champ nommé. Pour les champs slider, un tuple (min, max, value) met à jour simultanément la plage et la valeur actuelle. Pour les champs depth, value est le tampon de données binaires.

poll() bool#

Méthode de l’interface du backend. Renvoie True lorsque des données sérialisées sont disponibles pour l’hôte.

size() int#

Méthode de l’interface du backend. Invoque on_read (s’il est défini) et renvoie la taille du tampon sérialisé.

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

Méthode de l’interface du backend. Renvoie une tranche du tampon sérialisé.

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

Méthode de l’interface du backend. Décode une liste de mises à jour CBOR et applique les valeurs aux champs nommés correspondants, en invoquant on_write pour chacun.

Constantes#

Bits d’indicateur de canal (combinés au niveau du bit ; passés à protocol.register via flags ou définis automatiquement en fonction des méthodes du backend).

protocol.CHANNEL_FLAG_READ: int#

Le canal prend en charge les lectures.

protocol.CHANNEL_FLAG_WRITE: int#

Le canal prend en charge les écritures.

protocol.CHANNEL_FLAG_LOCK: int#

Le canal implémente lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

Le canal représente un transport physique (par opposition à un canal de données logique).

Identifiants de canaux intégrés.

protocol.CHANNEL_ID_TRANSPORT: int#

ID de canal réservé pour le transport actif.

protocol.CHANNEL_ID_STDIN: int#

ID de canal du canal stdin intégré.

protocol.CHANNEL_ID_STDOUT: int#

ID de canal du canal stdout intégré.

protocol.CHANNEL_ID_STREAM: int#

ID de canal du canal stream intégré.

protocol.CHANNEL_ID_PROFILE: int#

ID de canal du canal de profilage intégré (présent uniquement lorsque le micrologiciel est compilé avec le profileur activé).