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