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,streamet, s’il est compilé,profile). LèveRuntimeErrorsi 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.crcactive la validation CRC sur les trames du protocole.seqactive le suivi des numéros de séquence.ackactive les accusés de réception par trame.eventsactive les notifications d’événements de canal.max_payloadest 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 selonbuffer - 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_retriesest le nombre de tentatives de retransmission. Par défaut3.rtx_timeout_msest le délai d’expiration de retransmission en millisecondes (doublé après chaque expiration). Par défaut500.lock_interval_msest l’intervalle de verrouillage minimal en millisecondes. Par défaut10.poll_msest l’intervalle d’interrogation en millisecondes.0(la valeur par défaut) désactive l’interrogation par minuteur.
- protocol.is_active() bool¶
Renvoie
Truesi un hôte est actuellement connecté et que la pile de protocole est active, sinonFalse.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel¶
Enregistre un objet Python
backendcomme nouveau canal logique et renvoie un descripteurProtocolChannel. Les méthodes disponibles de l’objetbackend(voir Interface du backend ci-dessous) déterminent les capacités du canal ;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEetprotocol.CHANNEL_FLAG_LOCKsont ajoutés automatiquement àflagslorsque les méthodes correspondantes sont implémentées.nameest le nom du canal sous forme de chaîne. Tronqué à la taille du tampon de nom de canal du micrologiciel. Obligatoire.backendest l’objet Python qui implémente l’interface du backend. Obligatoire. Généralement passé par mot-clé (backend=...).flagscorrespond à des bits d’indicateur de canal supplémentaires (voir les constantesCHANNEL_FLAG_*). Facultatif ; vaut0par défaut.Lève
RuntimeErrorsi 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.
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
Noneen cas de succès ; une exception ou l’absence de valeur de retour est considérée comme une erreur.
- 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
Noneen cas de succès.
- read(offset: int, size: int) bytes¶
Renvoie jusqu’à
sizeoctets à partir deoffsetsous forme d’objet de typebytesprenant 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.dataest unbytearrayréférençant directement le tampon C. Renvoie le nombre d’octets écrits, ou0en cas de succès par défaut.
- 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 rappelon_read/on_write.on_readest un appelable optionnelon_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_writeest un appelable optionnelon_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.
nameest le nom d’affichage ; il doit être unique au sein de ce canal.typeest le type de widget :"label","toggle","slider","select"ou"depth".valueest la valeur initiale. La valeur par défaut dépend detype.unitest la chaîne d’unité pourlabel/slider(par ex."Cel","%RH").minest la valeur minimale (plage du slider ou plage de depth).maxest la valeur maximale (plage du slider ou plage de depth).stepest le pas (slider).optionsest la liste des chaînes d’options (select).widthest la largeur en pixels (depth).heightest 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 champsdepth,valueest le tampon de données binaires.
- poll() bool¶
Méthode de l’interface du backend. Renvoie
Truelorsque 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é.
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_PHYSICAL: int¶
Le canal représente un transport physique (par opposition à un canal de données logique).
Identifiants de canaux intégrés.