protocol --- Saluran Protokol OpenMV#

Modul protocol mengekspos protokol host OpenMV ke Python. Modul ini memungkinkan tumpukan protokol sisi firmware untuk diinisialisasi dan dikonfigurasi, serta memungkinkan kode pengguna mendaftarkan saluran logis kustom yang didukung oleh objek Python yang mengimplementasikan antarmuka saluran (read, write, size, poll, dll.). Inilah yang digunakan alat pendamping desktop ketika mereka melakukan streaming data citra atau mengekspos widget interaktif ke kamera yang terhubung.

Contoh#

Melakukan streaming citra RGB565 ke alat host menggunakan backend kustom yang mengimplementasikan antarmuka saluran mentah (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

Skrip sisi host yang sesuai, menggunakan paket Python openmv (pip install openmv) untuk terhubung, mendorong skrip di kamera, dan menarik setiap bingkai:

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

Ganti /dev/ttyACM0 dengan port serial kamera (misalnya COM3 di Windows). Konstruktor openmv.camera.Camera menerima parameter protokol yang sama dengan init (crc / seq / ack / events / max_payload / max_retry / timeout) ketika tumpukan sisi kamera telah dikonfigurasi ulang agar sesuai.

Fungsi#

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#

Menginisialisasi (atau mengonfigurasi ulang) tumpukan protokol dan mendaftarkan saluran data logis default (stdin, stdout, stream dan, jika dikompilasi, profile). Memunculkan RuntimeError jika inisialisasi gagal. Firmware melakukan boot dengan tumpukan protokol USB default yang sudah berjalan, sehingga pemanggilan ini hanya diperlukan untuk mengganti transport atau menimpa parameter framing default. Memanggilnya lagi akan menginisialisasi ulang tumpukan: timer polling sebelumnya dihapus dan saluran-saluran didaftarkan kembali dari awal.

crc mengaktifkan validasi CRC pada bingkai protokol.

seq mengaktifkan pelacakan nomor urutan.

ack mengaktifkan pengakuan per bingkai.

events mengaktifkan notifikasi event saluran.

max_payload adalah ukuran payload maksimum dalam byte. Jika dihilangkan, default per kamera di bawah ini digunakan; ini diturunkan dari ukuran buffer protokol setiap board sebagai buffer - 10 (header) - 4 (CRC).

Kamera

Ukuran buffer

Payload maksimum

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 adalah jumlah upaya retransmisi. Default 3.

rtx_timeout_ms adalah timeout retransmisi dalam milidetik (digandakan setelah setiap timeout). Default 500.

lock_interval_ms adalah interval kunci minimum dalam milidetik. Default 10.

poll_ms adalah interval polling dalam milidetik. 0 (default) menonaktifkan polling timer.

protocol.is_active() → bool#

Mengembalikan True jika host saat ini terhubung dan tumpukan protokol aktif, jika tidak False.

protocol.poll() → int#

Menjalankan satu iterasi tugas protokol: mengosongkan buffer penerimaan transport, mem-parsing setiap frame yang lengkap, mendispatch perintah, dan melayani event saluran. Firmware biasanya melakukan ini untuk Anda dari interupsi USB dan dari timer poll_ms, sehingga ini hanya diperlukan untuk transport kustom yang tidak digerakkan oleh keduanya -- misalnya ketika poll_ms=0 dan transport di-polling dari loop utama skrip.

Mengembalikan 0 jika berhasil dan -1 jika tidak ada transport aktif yang terdaftar atau pemanggilan dilakukan dari konteks interupsi.

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

Mendaftarkan objek Python backend sebagai saluran logis baru dan mengembalikan handle ProtocolChannel. Metode yang tersedia pada objek backend (lihat Antarmuka Backend di bawah) menentukan kemampuan saluran; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE dan protocol.CHANNEL_FLAG_LOCK ditambahkan ke flags secara otomatis ketika metode yang sesuai diimplementasikan.

name adalah nama saluran sebagai string. Dipotong ke ukuran buffer nama saluran firmware. Wajib.

backend adalah objek Python yang mengimplementasikan antarmuka backend. Wajib. Biasanya diteruskan melalui kata kunci (backend=...).

flags adalah bit flag saluran tambahan (lihat konstanta CHANNEL_FLAG_*). Opsional; default ke 0.

Memunculkan RuntimeError jika saluran tidak dapat didaftarkan (misalnya tidak ada slot saluran yang kosong).

Kelas#

class protocol.ProtocolChannel#

Handle yang dikembalikan oleh protocol.register. Instance tidak dibuat secara langsung.

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

Mengirim notifikasi event saluran ke host.

event adalah pengenal event (integer).

wait_ack jika True memblokir hingga host mengakui event tersebut.

Memunculkan RuntimeError jika pengiriman event gagal.

Antarmuka Backend#

Objek backend yang diteruskan ke protocol.register dapat mengimplementasikan subset dari metode berikut. Hanya metode yang ada pada objek yang dihubungkan ke lapisan protokol C; metode yang hilang membuat kemampuan yang sesuai dinonaktifkan.

class protocol.backend#

Objek backend saluran yang diteruskan ke protocol.register. Metode di bawah ini menjelaskan antarmuka opsional yang dapat diimplementasikan oleh backend Python.

init() → object#

Dipanggil sekali ketika saluran diinisialisasi. Kembalikan nilai non-None apa pun jika berhasil; pengecualian atau pengembalian yang hilang diperlakukan sebagai error.

poll() → bool#

Mengembalikan True jika saluran memiliki data yang siap dibaca oleh host.

lock() → bool#

Memperoleh saluran untuk transfer. Mengembalikan True jika berhasil.

unlock() → bool#

Melepaskan saluran setelah transfer. Mengembalikan True jika berhasil.

size() → int#

Mengembalikan jumlah byte yang saat ini dapat dibaca dari saluran.

shape() → tuple#

Mengembalikan tuple hingga empat integer yang mendeskripsikan bentuk data (misalnya dimensi citra). Hingga empat elemen dikonsumsi oleh lapisan protokol.

flush() → object#

Membuang semua data yang tertunda. Mengembalikan nilai non-None apa pun jika berhasil.

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

Mengembalikan hingga size byte mulai dari offset sebagai objek seperti bytes yang mendukung protokol buffer.

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

Varian tanpa salinan dari read. Mengembalikan buffer yang memori dasarnya dibaca langsung oleh lapisan protokol; buffer harus tetap valid selama durasi transfer.

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

Menulis data pada offset. data adalah bytearray yang merujuk buffer C secara langsung. Mengembalikan jumlah byte yang ditulis, atau 0 untuk keberhasilan default.

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

Menangani ioctl. arg adalah None jika length adalah nol, jika tidak bytearray yang merujuk buffer C. Mengembalikan 0 atau None jika berhasil, atau integer negatif jika error.

is_active() → bool#

Untuk saluran transport, mengembalikan True jika transport yang mendasarinya saat ini terhubung.

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

Backend Python tingkat lebih tinggi (disediakan oleh paket protocol beku) yang menyerialisasi kolom bernama ke CBOR menggunakan kunci integer yang kompatibel dengan SenML. Mendukung widget tampilan (label, text, depth, waveform) dan kontrol interaktif (toggle, pushbutton, slider, spinbox, select, radio, lineedit) dengan callback on_read/on_write.

on_read adalah callable opsional on_read(channel) yang dipanggil sebelum saluran diserialisasi untuk host. Gunakan untuk menyegarkan nilai field.

on_write adalah callable opsional on_write(channel, name, value) yang dipanggil ketika host menulis nilai baru untuk field bernama.

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, sample_rate: float | None = None, series: int = 1, typecode: str | None = None) → None#

Menambahkan field bernama ke saluran.

name adalah nama tampilan; harus unik dalam saluran ini.

type adalah tipe widget:

  • "label" -- tampilan numerik hanya-baca dengan unit opsional.

  • "text" -- tampilan string hanya-baca.

  • "toggle" -- sakelar boolean.

  • "pushbutton" -- aksi sesaat (trigger, kalibrasi, ...). Host menulis True saat diklik dan tidak menampilkan status apa pun.

  • "slider" -- kontrol numerik dengan min/max/step.

  • "spinbox" -- entri numerik bertahap yang presisi; berbagi kolom yang sama dengan slider serta bentuk pembaruan tuple (min, max, value).

  • "select" -- drop-down dari string options.

  • "radio" -- options yang ditampilkan sebagai sekelompok tombol radio.

  • "lineedit" -- input string yang dapat ditulis.

  • "depth" -- tampilan peta kedalaman 2D berukuran width x height.

  • "waveform" -- plot deret waktu 1D dari sampel yang di-interleave.

value adalah nilai awal. Nilai default bergantung pada type: 0 untuk label, "" untuk text/lineedit/select/ radio, False untuk toggle/pushbutton, min untuk slider/spinbox.

unit adalah string satuan untuk label/slider/spinbox/ waveform (misalnya "Cel", "%RH").

min adalah nilai minimum (rentang slider/spinbox, atau rentang tampilan depth/waveform).

max adalah nilai maksimum (rentang slider/spinbox, atau rentang tampilan depth/waveform).

step adalah ukuran langkah (slider/spinbox).

options adalah daftar string opsi (select/radio), atau daftar nama per-seri (waveform).

width adalah lebar piksel (depth).

height adalah tinggi piksel (depth).

sample_rate adalah laju sampel dalam sampel per detik (waveform). Dikirim ke host sebagai periode sampel.

series adalah jumlah seri yang di-interleave dalam setiap blok sampel (waveform), misalnya 3 untuk jejak akselerometer XYZ.

typecode adalah typecode array dari sampel waveform (misalnya "H", "h", "f"). Default-nya "H" (16-bit tanpa tanda).

__getitem__(name: str) → object#

Mengembalikan nilai saat ini dari kolom bernama. Untuk kolom depth dan waveform, buffer data biner yang dikembalikan; selain itu, nilai skalarnya.

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

Mengatur nilai kolom bernama. Untuk kolom slider dan spinbox, tuple (min, max, value) memperbarui rentang dan nilai saat ini secara bersamaan. Untuk kolom depth, value adalah buffer data biner. Untuk kolom waveform, value adalah blok bytes-like berisi sampel yang di-interleave dalam typecode kolom tersebut; jumlah sampel per seri diturunkan dari panjangnya dan stempel waktu monotonik (dalam mikrodetik, diakumulasikan dari time.ticks_us()) dilampirkan agar host dapat menempatkan potongan data tersebut pada sumbu waktunya.

poll() → bool#

Metode antarmuka backend. Mengembalikan True ketika data yang diserialisasi tersedia untuk host.

size() → int#

Metode antarmuka backend. Memanggil on_read (jika diatur) dan mengembalikan ukuran buffer yang diserialisasi.

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

Metode antarmuka backend. Mengembalikan irisan dari buffer yang diserialisasi.

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

Metode antarmuka backend. Mendekode daftar pembaruan CBOR dan menerapkan nilai ke field bernama yang cocok, memanggil on_write untuk masing-masing.

Konstanta#

Bit flag saluran (digabungkan secara bitwise; diteruskan ke protocol.register melalui flags atau diatur secara otomatis berdasarkan metode backend).

protocol.CHANNEL_FLAG_READ: int#

Saluran mendukung pembacaan.

protocol.CHANNEL_FLAG_WRITE: int#

Saluran mendukung penulisan.

protocol.CHANNEL_FLAG_LOCK: int#

Saluran mengimplementasikan lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

Saluran mewakili transport fisik (sebagai lawan dari saluran data logis).

Pengenal saluran bawaan.

protocol.CHANNEL_ID_TRANSPORT: int#

ID saluran yang dicadangkan untuk transport aktif.

protocol.CHANNEL_ID_STDIN: int#

ID saluran dari saluran stdin bawaan.

protocol.CHANNEL_ID_STDOUT: int#

ID saluran dari saluran stdout bawaan.

protocol.CHANNEL_ID_STREAM: int#

ID saluran dari saluran stream bawaan.

protocol.CHANNEL_ID_PROFILE: int#

ID saluran dari saluran profiler bawaan (hanya ada ketika firmware dibangun dengan profiler diaktifkan).