protocol — Canales del protocolo OpenMV#

El módulo protocol expone el protocolo de host de OpenMV a Python. Permite inicializar y configurar la pila de protocolo del lado del firmware, y deja que el código de usuario registre canales lógicos personalizados respaldados por un objeto Python que implementa la interfaz del canal (read, write, size, poll, etc.). Esto es con lo que se comunican las herramientas complementarias de escritorio cuando transmiten datos de imagen o exponen widgets interactivos a una cámara conectada.

Ejemplos#

Transmite una imagen RGB565 a una herramienta de host usando un backend personalizado que implementa la interfaz del canal en bruto (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

El script del lado del host correspondiente, que usa el paquete Python openmv (pip install openmv) para conectarse, enviar el script de la cámara y recuperar cada fotograma:

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

Reemplace /dev/ttyACM0 con el puerto serie de la cámara (por ejemplo COM3 en Windows). El constructor openmv.camera.Camera acepta los mismos parámetros de protocolo que init (crc / seq / ack / events / max_payload / max_retry / timeout) cuando la pila del lado de la cámara se ha reconfigurado para que coincida.

Funciones#

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#

Inicializa (o reconfigura) la pila de protocolo y registra los canales de datos lógicos predeterminados (stdin, stdout, stream y, si está compilado, profile). Lanza RuntimeError si la inicialización falla. El firmware arranca con una pila de protocolo USB predeterminada ya en ejecución, por lo que llamar a esto solo es necesario para cambiar el transporte o anular los parámetros de tramado predeterminados.

crc habilita la validación CRC en las tramas del protocolo.

seq habilita el seguimiento de números de secuencia.

ack habilita los acuses de recibo por trama.

events habilita las notificaciones de eventos de canal.

max_payload es el tamaño máximo de la carga útil en bytes. Si se omite, se usa el valor predeterminado por cámara que se indica a continuación; se deriva del tamaño del búfer de protocolo de cada placa como buffer - 10 (header) - 4 (CRC).

Cámara

Tamaño del búfer

Carga útil máxima

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 es el número de intentos de retransmisión. Predeterminado 3.

rtx_timeout_ms es el tiempo de espera de retransmisión en milisegundos (se duplica tras cada tiempo de espera agotado). Predeterminado 500.

lock_interval_ms es el intervalo mínimo de bloqueo en milisegundos. Predeterminado 10.

poll_ms es el intervalo de sondeo en milisegundos. 0 (el valor predeterminado) deshabilita el sondeo por temporizador.

protocol.is_active() bool#

Devuelve True si actualmente hay un host conectado y la pila de protocolo está activa; de lo contrario, False.

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

Registra un objeto Python backend como un nuevo canal lógico y devuelve un manejador ProtocolChannel. Los métodos disponibles del objeto backend (consulte Interfaz del backend más abajo) determinan las capacidades del canal; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE y protocol.CHANNEL_FLAG_LOCK se añaden a flags automáticamente cuando se implementan los métodos correspondientes.

name es el nombre del canal como cadena. Se trunca al tamaño del búfer de nombres de canal del firmware. Obligatorio.

backend es el objeto Python que implementa la interfaz del backend. Obligatorio. Normalmente se pasa por palabra clave (backend=...).

flags son bits de indicador de canal adicionales (consulte las constantes CHANNEL_FLAG_*). Opcional; el valor predeterminado es 0.

Lanza RuntimeError si el canal no se puede registrar (por ejemplo, no hay ranuras de canal libres).

Clases#

class protocol.ProtocolChannel#

Manejador devuelto por protocol.register. Las instancias no se construyen directamente.

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

Envía una notificación de evento de canal al host.

event es el identificador del evento (entero).

wait_ack si es True se bloquea hasta que el host acuse recibo del evento.

Lanza RuntimeError si el envío del evento falla.

Interfaz del backend#

Un objeto backend pasado a protocol.register puede implementar cualquier subconjunto de los siguientes métodos. Solo los métodos presentes en el objeto se conectan a la capa de protocolo en C; los métodos ausentes dejan deshabilitada la capacidad correspondiente.

class protocol.backend#

Objeto backend de canal pasado a protocol.register. Los métodos siguientes describen la interfaz opcional que puede implementar un backend Python.

init() object#

Se llama una vez cuando se inicializa el canal. Devuelve cualquier valor distinto de None en caso de éxito; una excepción o la ausencia de valor de retorno se tratan como un error.

poll() bool#

Devuelve True si el canal tiene datos listos para que el host los lea.

lock() bool#

Adquiere el canal para una transferencia. Devuelve True en caso de éxito.

unlock() bool#

Libera el canal después de una transferencia. Devuelve True en caso de éxito.

size() int#

Devuelve el número de bytes actualmente legibles del canal.

shape() tuple#

Devuelve una tupla de hasta cuatro enteros que describen la forma de los datos (por ejemplo, las dimensiones de la imagen). La capa de protocolo consume hasta cuatro elementos.

flush() object#

Vacía cualquier dato pendiente. Devuelve cualquier valor distinto de None en caso de éxito.

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

Devuelve hasta size bytes a partir de offset como un objeto similar a bytes que admite el protocolo de búfer.

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

Variante de read sin copia (zero-copy). Devuelve un búfer cuya memoria subyacente lee directamente la capa de protocolo; el búfer debe permanecer válido durante toda la transferencia.

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

Escribe data en offset. data es un bytearray que referencia directamente el búfer de C. Devuelve el número de bytes escritos, o 0 en caso de éxito predeterminado.

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

Gestiona un ioctl. arg es None si length es cero; de lo contrario, un bytearray que referencia el búfer de C. Devuelve 0 o None en caso de éxito, o un entero negativo en caso de error.

is_active() bool#

Para canales de transporte, devuelve True si el transporte subyacente está actualmente conectado.

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

Un backend Python de nivel superior (proporcionado por el paquete congelado protocol) que serializa campos con nombre a CBOR usando claves enteras compatibles con SenML. Admite widgets de visualización (label, depth) y controles interactivos (toggle, slider, select) con funciones de retorno (callbacks) on_read/on_write.

on_read es un invocable opcional on_read(channel) que se invoca antes de serializar el canal para el host. Úselo para actualizar los valores de los campos.

on_write es un invocable opcional on_write(channel, name, value) que se invoca cuando el host escribe un nuevo valor para un campo con nombre.

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#

Añade un campo con nombre al canal.

name es el nombre para mostrar; debe ser único dentro de este canal.

type es el tipo de widget: "label", "toggle", "slider", "select" o "depth".

value es el valor inicial. El valor predeterminado depende de type.

unit es la cadena de unidad para label/slider (por ejemplo, "Cel", "%RH").

min es el valor mínimo (rango del slider o rango de profundidad).

max es el valor máximo (rango del slider o rango de profundidad).

step es el tamaño del paso (slider).

options es la lista de cadenas de opciones (select).

width es el ancho en píxeles (depth).

height es la altura en píxeles (depth).

__getitem__(name: str) object#

Devuelve el valor actual del campo con nombre. Para los campos depth se devuelve el búfer de datos binarios; de lo contrario, el valor escalar.

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

Establece el valor del campo con nombre. Para los campos slider, una tupla (min, max, value) actualiza el rango y el valor actual simultáneamente. Para los campos depth, value es el búfer de datos binarios.

poll() bool#

Método de la interfaz del backend. Devuelve True cuando hay datos serializados disponibles para el host.

size() int#

Método de la interfaz del backend. Invoca on_read (si está definido) y devuelve el tamaño del búfer serializado.

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

Método de la interfaz del backend. Devuelve una porción del búfer serializado.

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

Método de la interfaz del backend. Decodifica una lista de actualización CBOR y aplica los valores a los campos con nombre coincidentes, invocando on_write para cada uno.

Constantes#

Bits de indicador de canal (combinados a nivel de bits; pasados a protocol.register mediante flags o establecidos automáticamente en función de los métodos del backend).

protocol.CHANNEL_FLAG_READ: int#

El canal admite lecturas.

protocol.CHANNEL_FLAG_WRITE: int#

El canal admite escrituras.

protocol.CHANNEL_FLAG_LOCK: int#

El canal implementa lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

El canal representa un transporte físico (en contraposición a un canal de datos lógico).

Identificadores de canal integrados.

protocol.CHANNEL_ID_TRANSPORT: int#

ID de canal reservado para el transporte activo.

protocol.CHANNEL_ID_STDIN: int#

ID de canal del canal integrado stdin.

protocol.CHANNEL_ID_STDOUT: int#

ID de canal del canal integrado stdout.

protocol.CHANNEL_ID_STREAM: int#

ID de canal del canal integrado stream.

protocol.CHANNEL_ID_PROFILE: int#

ID de canal del canal del perfilador integrado (solo presente cuando el firmware se compila con el perfilador habilitado).