From 53f986b3f9fcc7c548b2a1eabf6e079104a438ce Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 27 Aug 2025 10:35:25 +0200 Subject: [PATCH 01/15] common: Add circular buffer API for data streaming Add a simple ring buffer implementation with claiming/committing, consumption tracking, and automatic compaction when needed. A better solution would be to implement Bip Buffer: https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist Signed-off-by: iabdalkader --- common/omv_buffer.h | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 common/omv_buffer.h diff --git a/common/omv_buffer.h b/common/omv_buffer.h new file mode 100644 index 000000000..96e000f56 --- /dev/null +++ b/common/omv_buffer.h @@ -0,0 +1,94 @@ +#ifndef __OMV_BUFFER_H__ +#define __OMV_BUFFER_H__ + +#include +#include +#include +#include + +typedef struct { + size_t size; // Total buffer size + uint8_t *read_ptr; // Where to read/process from + uint8_t *write_ptr; // Where to write new data + size_t used; // Bytes currently in buffer (unprocessed) + uint8_t *data; // The actual buffer memory +} omv_buffer_t; + +// Initialize buffer with pre-allocated memory +static inline void omv_buffer_init(omv_buffer_t *buf, uint8_t *buffer, size_t size) { + buf->data = buffer; + buf->size = size; + buf->read_ptr = buffer; + buf->write_ptr = buffer; + buf->used = 0; +} + +// Reset buffer to empty state +static inline void omv_buffer_clear(omv_buffer_t *buf) { + buf->read_ptr = buf->data; + buf->write_ptr = buf->data; + buf->used = 0; +} + +// Get number of bytes available for reading +static inline size_t omv_buffer_avail(omv_buffer_t *buf) { + return buf->used; +} + +// Get number of bytes available for writing +static inline size_t omv_buffer_free(omv_buffer_t *buf) { + return buf->size - buf->used; +} + +// Get pointer to readable data +// Returns data pointer or NULL if buffer is empty +static inline void *omv_buffer_data(omv_buffer_t *buf) { + return (buf->used > 0) ? buf->read_ptr : NULL; +} + +// Peek at 16-bit value at start of buffer +// Caller must ensure omv_buffer_avail(buf) >= 2 +static inline uint16_t omv_buffer_peek16(omv_buffer_t *buf) { + return *((uint16_t *) buf->read_ptr); +} + +// Mark data as consumed and advance read pointer +// Resets buffer to beginning when empty +static inline void omv_buffer_consume(omv_buffer_t *buf, size_t consumed) { + buf->read_ptr += consumed; + buf->used -= consumed; + + // Reset to beginning if buffer is empty + if (buf->used == 0) { + buf->read_ptr = buf->data; + buf->write_ptr = buf->data; + } +} + +// Claim contiguous space for writing, compacting buffer if needed +static inline void *omv_buffer_claim(omv_buffer_t *buf, size_t requested_size) { + // Check if we have enough total space + if (buf->used + requested_size > buf->size) { + return NULL; // Buffer full + } + + size_t space_to_end = buf->size - (buf->write_ptr - buf->data); + + // If not enough contiguous space at the end, move unprocessed data to beginning + if (space_to_end < requested_size && buf->used > 0) { + // Move unprocessed data to the beginning + memmove(buf->data, buf->read_ptr, buf->used); + buf->read_ptr = buf->data; + buf->write_ptr = buf->data + buf->used; + } + + return buf->write_ptr; +} + +// Commit written data to the buffer +// Call this after successfully writing to the claimed space +static inline void omv_buffer_commit(omv_buffer_t *buf, size_t written) { + buf->write_ptr += written; + buf->used += written; +} +#endif // __OMV_BUFFER_H__ From 78cd3eea439709bd3c1cd6cbee93d24b128a1c8a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 12 Sep 2025 17:53:23 +0200 Subject: [PATCH 02/15] tools/pyopenmv: Remove obsolete tools. Signed-off-by: iabdalkader --- tools/pyopenmv.py | 305 ---------------------- tools/pyopenmv_fb.py | 564 ---------------------------------------- tools/pyopenmv_multi.py | 281 -------------------- tools/pyopenmv_test.py | 77 ------ 4 files changed, 1227 deletions(-) delete mode 100755 tools/pyopenmv.py delete mode 100755 tools/pyopenmv_fb.py delete mode 100644 tools/pyopenmv_multi.py delete mode 100755 tools/pyopenmv_test.py diff --git a/tools/pyopenmv.py b/tools/pyopenmv.py deleted file mode 100755 index 328aacc3d..000000000 --- a/tools/pyopenmv.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/env python -# This file is part of the OpenMV project. -# -# Copyright (c) 2013-2021 Ibrahim Abdelkader -# Copyright (c) 2013-2021 Kwabena W. Agyeman -# -# This work is licensed under the MIT license, see the file LICENSE for details. -# -# Openmv module. - -import struct -import sys,time -import serial -import platform -import numpy as np -from PIL import Image - -__serial = None -__FB_HDR_SIZE = 12 - -# USB Debug commands -__USBDBG_CMD = 48 -__USBDBG_FW_VERSION = 0x80 -__USBDBG_FRAME_SIZE = 0x81 -__USBDBG_FRAME_DUMP = 0x82 -__USBDBG_ARCH_STR = 0x83 -__USBDBG_SCRIPT_EXEC = 0x05 -__USBDBG_SCRIPT_STOP = 0x06 -__USBDBG_SCRIPT_SAVE = 0x07 -__USBDBG_SCRIPT_RUNNING = 0x87 -__USBDBG_TEMPLATE_SAVE = 0x08 -__USBDBG_DESCRIPTOR_SAVE= 0x09 -__USBDBG_ATTR_READ = 0x8A -__USBDBG_ATTR_WRITE = 0x0B -__USBDBG_SYS_RESET = 0x0C -__USBDBG_SYS_RESET_TO_BL= 0x0E -__USBDBG_FB_ENABLE = 0x0D -__USBDBG_TX_BUF_LEN = 0x8E -__USBDBG_TX_BUF = 0x8F -__USBDBG_GET_STATE = 0x93 -__USBDBG_PROFILE_SIZE = 0x94 -__USBDBG_PROFILE_DUMP = 0x95 -__USBDBG_PROFILE_MODE = 0x16 -__USBDBG_PROFILE_EVENT = 0x17 -__USBDBG_PROFILE_RESET = 0x18 - -__USBDBG_FLAG_SCRIPT_RUNNING = (1 << 0) -__USBDBG_FLAG_TEXTBUF_NOTEMPTY = (1 << 1) -__USBDBG_FLAG_FRAMEBUF_LOCKED = (1 << 2) -__USBDBG_FLAG_PROFILE_ENABLED = (1 << 3) -__USBDBG_FLAG_PROFILE_HAS_PMU = (1 << 4) - -ATTR_CONTRAST = 0 -ATTR_BRIGHTNESS = 1 -ATTR_SATURATION = 2 -ATTR_GAINCEILING = 3 - -__BOOTLDR_START = 0xABCD0001 -__BOOTLDR_RESET = 0xABCD0002 -__BOOTLDR_ERASE = 0xABCD0004 -__BOOTLDR_WRITE = 0xABCD0008 - -def init(port, baudrate=921600, timeout=0.3): - global __serial - # open CDC port - __serial = serial.Serial(port, baudrate=baudrate, timeout=timeout) - -def disconnect(): - global __serial - try: - if (__serial): - __serial.close() - __serial = None - except: - pass - -def write_pack(fmt, *values): - __serial.write(struct.pack(fmt, *values)) - -def read_unpack(fmt): - return struct.unpack(fmt, __serial.read(struct.calcsize(fmt))) - -def set_timeout(timeout): - __serial.timeout = timeout - -def fb_size(): - # read fb header - write_pack(" 2 else (w * h * size) - - # read fb data - write_pack(">11)*255.0/31.0).astype(np.uint8) - g = (((arr & 0x07E0) >>5) *255.0/63.0).astype(np.uint8) - b = (((arr & 0x001F) >>0) *255.0/31.0).astype(np.uint8) - buff = np.column_stack((r,g,b)) - else: # JPEG - fmt = "JPEG" - try: - buff = np.asarray(Image.frombuffer("RGB", (w, h), buff, "jpeg", "RGB", "")) - except Exception as e: - raise ValueError(f"JPEG decode error (%e)") - - if (buff.size != (w*h*3)): - raise ValueError(f"Unexpected frame size. Expected: {w*h*3} received: {buff.size}") - - return w, h, buff.reshape((h, w, 3)), num_bytes, text, fmt, profile - - -def fb_dump(): - size = fb_size() - - if (not size[0]): - # frame not ready - return None - - if (size[2] > 2): #JPEG - num_bytes = size[2] - else: - num_bytes = size[0]*size[1]*size[2] - - # read fb data - write_pack(">11)*255.0/31.0).astype(np.uint8) - g = (((arr & 0x07E0) >>5) *255.0/63.0).astype(np.uint8) - b = (((arr & 0x001F) >>0) *255.0/31.0).astype(np.uint8) - buff = np.column_stack((r,g,b)) - else: # JPEG - try: - buff = np.asarray(Image.frombuffer("RGB", size[0:2], buff, "jpeg", "RGB", "")) - except Exception as e: - print ("JPEG decode error (%s)"%(e)) - return None - - if (buff.size != (size[0]*size[1]*3)): - return None - - return (size[0], size[1], buff.reshape((size[1], size[0], 3))) - -def read_profile(): - records = [] - - # Read and unpack profiling data size - write_pack(" 0 else [] - }) - - offset += record_size - - return records - -def set_profile_mode(mode): - write_pack("