protocol — OpenMV Protocol Channels¶
The protocol module exposes the OpenMV host protocol to Python. It allows
the firmware-side protocol stack to be initialized and configured, and lets
user code register custom logical channels backed by a Python object that
implements the channel interface (read, write, size, poll,
etc.). This is what desktop companion tools talk to when they stream image
data or expose interactive widgets to a connected camera.
Examples¶
Stream an RGB565 image to a host tool using a custom backend that
implements the raw channel interface (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
The matching host-side script, using the openmv Python package
(pip install openmv) to connect, push the on-camera script, and pull
each frame:
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()
Replace /dev/ttyACM0 with the camera’s serial port (e.g. COM3 on
Windows). The openmv.camera.Camera constructor accepts the same protocol
parameters as init (crc / seq / ack / events /
max_payload / max_retry / timeout) when the camera-side stack
has been reconfigured to match.
Functions¶
- 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¶
Initialize (or reconfigure) the protocol stack and register the default logical data channels (
stdin,stdout,streamand, if compiled in,profile). RaisesRuntimeErrorif initialization fails. The firmware boots with a default USB protocol stack already running, so calling this is needed only to change the transport or override the default framing parameters.crcenables CRC validation on protocol frames.seqenables sequence number tracking.ackenables per-frame acknowledgements.eventsenables channel event notifications.max_payloadis the maximum payload size in bytes. If omitted the per-camera default below is used; it is derived from each board’s protocol buffer size asbuffer - 10 (header) - 4 (CRC).Camera
Buffer size
Max payload
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_retriesis the number of retransmission attempts. Default3.rtx_timeout_msis the retransmission timeout in milliseconds (doubled after each timeout). Default500.lock_interval_msis the minimum lock interval in milliseconds. Default10.poll_msis the polling interval in milliseconds.0(the default) disables timer polling.
- protocol.is_active() bool¶
Return
Trueif a host is currently connected and the protocol stack is active, otherwiseFalse.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel¶
Register a Python
backendobject as a new logical channel and return aProtocolChannelhandle. Thebackendobject’s available methods (see Backend Interface below) determine the channel’s capabilities;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEandprotocol.CHANNEL_FLAG_LOCKare added toflagsautomatically when the corresponding methods are implemented.nameis the channel name as a string. Truncated to the firmware’s channel-name buffer size. Required.backendis the Python object implementing the backend interface. Required. Typically passed by keyword (backend=...).flagsis additional channel flag bits (see theCHANNEL_FLAG_*constants). Optional; defaults to0.Raises
RuntimeErrorif the channel cannot be registered (e.g. no free channel slots).
Classes¶
- class protocol.ProtocolChannel¶
Handle returned by
protocol.register. Instances are not constructed directly.
Backend Interface¶
A backend object passed to protocol.register may implement any subset of
the following methods. Only the methods present on the object are wired to
the C protocol layer; missing methods leave the corresponding capability
disabled.
- class protocol.backend¶
Channel backend object passed to
protocol.register. The methods below describe the optional interface a Python backend may implement.- init() object¶
Called once when the channel is initialized. Return any non-
Nonevalue on success; an exception or missing return is treated as an error.
- shape() tuple¶
Return a tuple of up to four integers describing the data shape (e.g. image dimensions). Up to four elements are consumed by the protocol layer.
- read(offset: int, size: int) bytes¶
Return up to
sizebytes starting atoffsetas abytes-like object that supports the buffer protocol.
- readp(offset: int, size: int) bytes¶
Zero-copy variant of
read. Returns a buffer whose underlying memory is read directly by the protocol layer; the buffer must remain valid for the duration of the transfer.
- write(offset: int, data: bytearray) int¶
Write
dataatoffset.datais abytearrayreferencing the C buffer directly. Return the number of bytes written, or0on default success.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)¶
A higher-level Python backend (provided by the frozen
protocolpackage) that serializes named fields to CBOR using SenML-compatible integer keys. Supports display widgets (label,depth) and interactive controls (toggle,slider,select) withon_read/on_writecallbacks.on_readis an optional callableon_read(channel)invoked before the channel is serialized for the host. Use it to refresh field values.on_writeis an optional callableon_write(channel, name, value)invoked when the host writes a new value for a named field.- 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¶
Add a named field to the channel.
nameis the display name; must be unique within this channel.typeis the widget type:"label","toggle","slider","select", or"depth".valueis the initial value. The default depends ontype.unitis the unit string forlabel/slider(e.g."Cel","%RH").minis the minimum value (slider range or depth range).maxis the maximum value (slider range or depth range).stepis the step size (slider).optionsis the list of option strings (select).widthis the pixel width (depth).heightis the pixel height (depth).
- __getitem__(name: str) object¶
Return the current value of the named field. For
depthfields the binary data buffer is returned, otherwise the scalar value.
- __setitem__(name: str, value: Any) None¶
Set the value of the named field. For
sliderfields, a(min, max, value)tuple updates the range and current value simultaneously. Fordepthfields,valueis the binary data buffer.
- size() int¶
Backend interface method. Invokes
on_read(if set) and returns the size of the serialized buffer.
Constants¶
Channel flag bits (combined bitwise; passed to protocol.register via
flags or set automatically based on the backend’s methods).
- protocol.CHANNEL_FLAG_PHYSICAL: int¶
The channel represents a physical transport (as opposed to a logical data channel).
Built-in channel identifiers.