GENX320 - streaming raw event data over USB

Hey everyone,

I’m using the OpenMV H7 Plus with the GENX320 event camera. I’m using scripts slated for the next firmware release in Github to stream the raw event data (genx320_event_mode_high_speed.py), but I’m having a hard time actually streaming the raw data over USB. I need to use the raw event data, as I’m trying to feed it through an event-based object detection model (RVT from the folks over at University of Zurich), and thus can’t process the events onboard. I tried making my own python script for interpreting the event data, but I often get broken streams, where only parts of events are streamed back over USB. The Micropython script I’m using to stream back to my laptop is linked below:

import csi
import time
# https://micropython-ulab.readthedocs.io/en/latest/index.html
from ulab import numpy as np

# Stores camera events
# Shape: (EVT_res, 6) where EVT_res is the event resolution
# EVT_res: must be a power of two between 1024 and 65536.
# Columns:
#   [0]  Event type
#   [1]  Seconds timestamp
#   [2]  Milliseconds timestamp
#   [3]  Microseconds timestamp
#   [4]  X coordinate 0 to 319 for GENX320
#   [5]  Y coordinate 0 to 319 for GENX320
events = np.zeros((2048, 6), dtype=np.uint16)

# Initialize the sensor.
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])

clock = time.clock()
t = time.ticks_us()

i = 0
while True:
    clock.tick()
    t1 = time.ticks_us()
    diff = time.ticks_diff(t1, t)
    t = t1
    i += 1

    # Reads up to 2048 events from the camera.
    # Returns the number of valid events (0-2048) or a negative error code.
    # Note that old events in the buffer are not cleared to save CPU time.
    event_count = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    new_events = events[:event_count]

    # Sub-sample the event rate output to not impact performance of event
    # data processing. The overhead of sending stats outputs to the IDE can
    # become significant at high event rates.
    if not i % 10:
        #print(event_count)
        for event in new_events:
            print(list(event))

My question is: are there any plans for extending pyopenmv.py to handle raw event data? If not, are there any scripts you have for reliably transferring raw event data over USB?

We are aware of this issue and have a new prototcol in the works which will resolve the issue: common: OpenMV Protocol V2 Implementation. by iabdalkader · Pull Request #2824 · openmv/openmv · GitHub

We are working on getting a release done now, once that is finished we will be updating the protocol over the next month in the firmware and IDE. The new protocol supports customs streams which can send the data without dropping it. This will allow data transfer to the PC over USB easily at high speeds.

In the mean-time, the H7 Plus is not suitable at all for sending the event data off camera. it only has 12 Mb/s USB. This is far below the event rate of 48MB/s. The RT1062 is much closer to being able to send the event data by printing in. However, for the best RAW transfer speed using the current firmware you have two options:

  1. Use the RT1062 with the PoE Shield and ethernet. You can then transmit the NDarray as a byte structure via UDP sockets at close to 100Mb/s. This is by far the easiest and most flexible way to stream the data to a PC.
  2. The current USB debug logic is very good at sending the frame buffer as a large transfer quickly at 480 Mb/s. However, it’s designed to sample images from the camera, not capture all data with perfect fidelity. However, I can make a hacked version of the firmware that transfers the captured image into the jpeg buffer for you per frame capture. This will let you transfer very large event captures with some reliability without data loss.

I’d recommend the Ethernet approach, though, as it doesn’t require anything custom.

How is the new protocol for this coming about?

@shulkinj I am curious how you ended up solving this issue, or what you did to stream the raw event data in the end? Did you manage over USB or did you end up using ethernet? I have a very high events/s rate that I need either logged into a sd card, or quickly transferred, to capture as much events as possible.Thanks!

@tijnven We are working on integrating it into the IDE right now. It’s somewhat of the highest priority item at the moment.

Anyway, please see my reply to your post and fixing your issue.

@kwagyeman is there any branch I can already look at to try the feature? And if so a small example script for using USB transferring? I do not mind building the firmware. Thanks in advance! I have acces to both a H7 plus and a RT1062 for testing.

Hi, it’s not really ready yet. We need to get an updated IDE out with the new protocol support and then we can really start showing off this feature for you to create custom channels to move data at high speeds to the PC.

And IDE release with the new protocol is coming imminently.

1 Like

@tijnven We have some updates on the new USB protocol. We are getting closer to releasing an IDE with support for it and with it you’ll be able to transport data to the PC via command line tools.

I was just testing out some demo scripts that we’ll provide soon which allow the RT1062 to transfer raw event buffers to the PC at about 12-16MB/s on average. This allows you to move round 1M+ events a second to the PC using the RT1062. The N6 can do about 7MB/s right now, there are some USB stability issues preventing it from hitting the same speed as the RT1062, but, we should be able to mostly resolve these in the future.

Here’s how to run the code:

  1. Checkout this branch: GitHub - openmv/openmv at omv_protocol_v2
    1. Build the firmware for the RT1062. Flash it with OpenMV IDE. Note that the IDE will lockup trying to talk to the camera after this as it will be using the new protocol. We’ll have a release with OpenMV IDE and the new protocol in less than a week.
  2. Install this: GitHub - openmv/openmv-python
    1. Run these scripts:
      1. genx320_event_mode_streaming_on_cam.py (3.0 KB)

        genx320_event_mode_streaming_on_pc.py (8.2 KB)

        1. You just need to execute the one for the pc like: python -u genx320_event_mode_streaming_on_pc.py --port COM5 --script genx320_event_mode_streaming_on_cam.py
          1. Note that you may need to turn on the CRC --crc True if there are some stability issues. Also, see the buffer size in the on_cam script for how big to make the event buffer. 8192 might be too big. You might need to make it 2048. I was able to push it to 32768 though in some cases and get up to 16MB/s transfer performance. Anyway, we’re hitting above 100Mb/s, so, better than Ethernet performance while still using Python.
1 Like

Might be able to further increase the performance: GENX320 Event Streaming Frame Rate Improvement · Issue #2945 · openmv/openmv · GitHub.

Update to this thread. You just need OpenMV IDE 4.8.1 installed. Then do, Tools→Install Latest Dev release. After that, you can run the above scripts on your camera and PC. Avoid using Windows with the CLI scripts.

1 Like

Thank you very much for the IDE update! I tried out the updated version on my H7 Plus board, and I was able to pull around 800,000 - 1M events per second, usually at 10-15 Hz or so. I just have a couple more questions:

  1. Is there any particular reason to avoid using Windows with the CLI scripts? Is there some incompatibility baked into the scripts, or is it more of a performance issue? I am currently developing on a windows platforms, so I would appreciate some more insight into this.

  2. Now that raw event streaming is implemented, are there any plans to extend the GENX320 firmware to allow control of the camera’s event-rate controller / ERC?

Yeah, so, on Windows, the IDE has to handle the kernel literally throwing away USB packets that are sent by the IDE and also received by the camera. The issue may not affect pyserial which is what the CLI scripts are built with… but, with QSerialPort, it happens often that the camera sends USB data, which I can see was sent and accepted on the USB bus using a serial debugger, and then the packet is never received by the application. I get the next packet after the one that went missing too, which means it was thrown away inside the kernel or QSerialPort (though I looked through QSerialPort code for this bug and could not find it being the culprit). On the outbound side… the same issue happens to where windows will toss a packet I send the camera. Similarly verified by sending the packet in the app and seeing it never hit the USB bus physically. This is much more rare, though. The receive side issue happens extremely frequently, like every couple of seconds.

This issue does not happen on Mac/Linux. For image streaming, I just drop corrupt images and keep going, everything works, but, with events, I assume you do not want data loss.

Now that raw event streaming is implemented, are there any plans to extend the GENX320 firmware to allow control of the camera’s event-rate controller / ERC?

If you can build the firmware, please send a PR for what features you want. Alternatively, you can use the __read_reg() and __write_reg() methods for low-level control using the sensor registers.

1 Like

Added the scripts to a project: openmv-projects/genx320-event-streaming at master · openmv/openmv-projects · GitHub

Thanks for all of the work here! If you don’t mind, I could use some help on a related problem. I’m streaming events over the H7 Plus, and while the event stream itself is consistent, I’m having trouble displaying it properly. When I try to display each packet and reconstruct the stream, I get jumpy behavior, where the stream will seem to skip frames. My overall goal is to get the same smoothness that would be seen from the OpenMV IDE but using the raw events over USB. Here are the scripts I’m using as well as the command I’m running to use them:
stream_test.py

#!/usr/bin/env python3
#
# This work is licensed under the MIT license.
# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# This example shows off using the genx320 event camera from Prophesee
# using event streaming mode and sending the data back to the PC.
#
# This script is meant to be run using https://github.com/openmv/openmv-python
# from a PC using desktop tools. Statistics on the events being transferred
# are printed to the console.

import sys
import os
import argparse
import time
import logging
import signal
import atexit
import numpy as np
from openmv.camera import Camera
import threading
from pathlib import Path
from einops import rearrange, reduce
import cv2

def events_to_image(events, width=320, height=320):
    """
    Converts raw GENX320 events (N, 6) into a displayable BGR image.
    """
    # Create a neutral gray background
    img = np.full((height, width, 3), 127, dtype=np.uint8)

    # Filter for valid coordinates just in case
    x = events[:, 4].astype(int)
    y = events[:, 5].astype(int)
    
    # Simple polarity: Type is usually in column [0] 
    # (Check your GENX320 script, but typically 1 is ON, 0 is OFF)
    p = events[:, 0]

    # Mask for ON events (White) and OFF events (Black)
    img[y[p == 1], x[p == 1]] = [255, 255, 255]
    img[y[p == 0], x[p == 0]] = [0, 0, 0]

    return img

COLOR_CAMERA = "\033[32m"  # green
COLOR_RESET  = "\033[0m"

class GENX320(threading.Thread):
    def __init__(self, args, zmq_pub_addr="tcp://127.0.0.1:5556", save_path: Path = None):
        super().__init__()

        # Extract GENX320 specific configuration
        self.port = args.port
        self.script = args.script
        self.poll = args.poll
        self.timeout = args.timeout
        self.debug = args.debug
        self.baudrate = args.baudrate
        self.crc = args.crc
        self.seq = args.seq
        self.ack = args.ack
        self.events = args.events
        self.max_retry = args.max_retry
        self.max_payload = args.max_payload
        self.drop_rate = args.drop_rate
        self.quiet = args.quiet
        self.alpha = args.alpha

        # Extract temporal accumulation configuration
        self.window_duration_us = args.window_duration_us

        # Variable to track whether we send events or not
        self.active = True

        # Register signal handlers for clean exit
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        atexit.register(self.cleanup_and_exit)

        # Configure logging
        if self.debug:
            log_level = logging.DEBUG
        elif not self.quiet:
            log_level = logging.INFO
        else:
            log_level = logging.ERROR

        logging.basicConfig(
            format="%(relativeCreated)010.3f - %(message)s",
            level=log_level,
        )

        # Enable file saving if a save_path is provided
        self.save_path = save_path


    def cleanup_and_exit(self):
        os._exit(0)

    def signal_handler(self, signum, frame):
        self.cleanup_and_exit()

    def run(self):
        # Load script
        with open(self.script, 'r') as f:
            script = f.read()
        logging.info(f"Loaded script from {self.script}")

        log_file = None
        if self.save_path:
            log_file = self.save_path.open("wb")
            logging.info(f"Logging raw events to {self.save_path}")

        try:
            with Camera(self.port, baudrate=self.baudrate, crc=self.crc, seq=self.seq,
                        ack=self.ack, events=self.events,
                        timeout=self.timeout, max_retry=self.max_retry,
                        max_payload=self.max_payload, drop_rate=self.drop_rate) as camera:
                logging.info(f"Connected to OpenMV camera on {self.port}")

                # Stop any running script
                camera.stop()
                time.sleep(0.500)

                # Execute script
                camera.exec(script)
                logging.info("Script executed...")

                # Stat variables
                start_time = time.perf_counter()
                last_time = start_time
                total_events = 0
                total_bytes = 0
                event_rate_ema = 0.0
                mbps_ema = 0.0

                last_window_time = time.perf_counter()
                window_fps = 0.0

                # Temporal accumulation variables
                accum_events = []
                accum_start_time_us = None

                while self.active:
                    t0 = time.time()
                    # Read camera status
                    status = camera.read_status()

                    # Read text output
                    if not self.quiet and status and status.get('stdout'):
                        if text := camera.read_stdout():
                            print(f"{COLOR_CAMERA}{text}{COLOR_RESET}", end='')

                    if not camera.has_channel('events'):
                        #time.sleep(0.01)
                        continue

                    if not status.get('events'):
                        #time.sleep(0.01)
                        continue

                    size = camera.channel_size('events')
                    if size <= 0:
                        #time.sleep(0.01)
                        continue

                    now = time.perf_counter()
                    dt = now - last_time
                    last_time = now
                    if dt <= 0.0:
                        continue

                    data = camera.channel_read('events', size)

                    # Each event row: 6 x uint16 (little-endian)
                    if (len(data) % (6 * 2)) != 0:
                        print(f"Warning: misaligned packet: {len(data)} bytes (not multiple of 12)")
                        continue
                    
                    events = np.frombuffer(data, dtype='<u2').reshape(-1, 6)
                    # Shape: (event_count, 6)
                    # Columns:
                    #   [0]  Event type
                    #   [1]  Seconds timestamp
                    #   [2]  Milliseconds timestamp
                    #   [3]  Microseconds timestamp
                    #   [4]  X coordinate 0 to 319 for GENX320
                    #   [5]  Y coordinate 0 to 319 for GENX320
                    event_count = events.shape[0]
                    
                    # put events into file if provided
                    if log_file:
                        log_file.write(data)

                    ts_chunk_us = (events[:, 1].astype(np.uint64) * 1000000 + 
                                   events[:, 2].astype(np.uint64) * 1000 + 
                                   events[:, 3].astype(np.uint64))
                    if accum_start_time_us is None:
                        accum_start_time_us = ts_chunk_us[0]

                    accum_events.append(events)
                    current_duration = ts_chunk_us[-1] - accum_start_time_us

                    # publish events
                    if current_duration >= self.window_duration_us:
                        # Concatenate and publish
                        full_packet = np.vstack(accum_events)
                        now_window = time.perf_counter()
                        window_dt = now_window - last_window_time
                        window_fps = 1.0 / window_dt if window_dt > 0 else 0
                        last_window_time = now_window
                        
                        # Reset for next window
                        accum_events = []
                        accum_start_time_us = None
                        
                        # Log stats only when we actually send a window
                        logging.info(f"Published Window: {full_packet.shape[0]} events over {current_duration/1000:.2f}ms")

                        img = events_to_image(full_packet)
                        cv2.putText(img, f"Disp FPS: {window_fps:.1f}", (10, 30), 
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

                        cv2.imshow("GENX320 Event Histogram", img)
                        cv2.waitKey(1)

                    # Instantaneous rates
                    events_per_sec = event_count / dt
                    mb_per_sec = len(data) / 1048576.0 / dt

                    # EMA smoothing
                    if event_rate_ema == 0.0:
                        event_rate_ema = events_per_sec
                    else:
                        event_rate_ema = event_rate_ema * (1.0 - self.alpha) + events_per_sec * self.alpha

                    if mbps_ema == 0.0:
                        mbps_ema = mb_per_sec
                    else:
                        mbps_ema = mbps_ema * (1.0 - self.alpha) + mb_per_sec * self.alpha

                    total_events += event_count
                    total_bytes += len(data)

                    t1 = time.time()
                    fps = 1 / (t1 - t0)
                    elapsed = now - start_time

                    logging.info(
                        f"events={event_count:6d}    "
                        f"rate={event_rate_ema:9.0f} ev/s    "
                        f"bw={mbps_ema:6.2f} MB/s    "
                        f"total_events={total_events:10d}    "
                        f"uptime={elapsed:7.1f}s    "
                        f"FPS={fps:4.2f}"
                    )

        except KeyboardInterrupt:
            logging.info("Interrupted by user")
        except Exception as e:
            logging.error(f"Error: {e}")
            if self.debug:
                import traceback
                logging.error(f"{traceback.format_exc()}")
            sys.exit(1)

    def stop(self):
        self.active = False

def str2bool(v):
    """Convert string to boolean for argparse"""
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('--port',
                        action='store', default='/dev/ttyACM0',
                        help='Serial port (default: /dev/ttyACM0)')

    parser.add_argument("--script",
                        action="store", required=True,
                        help="Script file")

    parser.add_argument('--poll', action='store',
                        default=4, type=int,
                        help='Poll rate in ms (default: 4)')

    parser.add_argument('--timeout',
                        action='store', type=float, default=1.0,
                        help='Protocol timeout in seconds')

    parser.add_argument('--debug',
                        action='store_true',
                        help='Enable debug logging')

    parser.add_argument('--baudrate',
                        type=int, default=921600,
                        help='Serial baudrate (default: 921600)')

    parser.add_argument('--crc',
                        type=str2bool, nargs='?', const=True, default=False,
                        help='Enable CRC validation (default: false)')

    parser.add_argument('--seq',
                        type=str2bool, nargs='?', const=True, default=True,
                        help='Enable sequence number validation (default: true)')

    parser.add_argument('--ack',
                        type=str2bool, nargs='?', const=True, default=False,
                        help='Enable packet acknowledgment (default: false)')

    parser.add_argument('--events',
                        type=str2bool, nargs='?', const=True, default=True,
                        help='Enable event notifications (default: true)')

    parser.add_argument('--max-retry',
                        type=int, default=3,
                        help='Maximum number of retries (default: 3)')

    parser.add_argument('--max-payload',
                        type=int, default=4096,
                        help='Maximum payload size in bytes (default: 4096)')

    parser.add_argument('--drop-rate',
                        type=float, default=0.0,
                        help='Packet drop simulation rate (0.0-1.0, default: 0.0)')

    parser.add_argument('--quiet',
                        action='store_true',
                        help='Suppress script output text')

    parser.add_argument("--alpha",
                        type=float, default=0.2,
                        help="EMA smoothing factor (0<alpha<=1). Default: 0.2")

    parser.add_argument("--window_duration_us",
                        type=float, default=50000,
                        help="Duration of each event time slice in us. Default: 50000")
    
    args = parser.parse_args()

    driver = GENX320(args=args)
    driver.start()

if __name__ == '__main__':
    main()

genx320_stream.py:

# This work is licensed under the MIT license.
# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# This example shows off using the genx320 event camera from Prophesee
# using event streaming mode and sending the data back to the PC.
#
# This script is meant to be run using https://github.com/openmv/openmv-python
# from a PC using desktop tools. No visualization or text output is generated
# by this script for OpenMV IDE.

import csi
import protocol
# https://micropython-ulab.readthedocs.io/en/latest/index.html
from ulab import numpy as np

# Event buffers arrive from the camera and are stored in
# a fifo buffer before being processed by python.
CSI_FIFO_DEPTH = 8

# Raw events post-processed by python, put into another
# fifo buffer, and then sent to the PC.
EVENT_FIFO_DEPTH = 8

# Stores camera events (8 buffers)
# Shape: (EVT_res, 6) where EVT_res is the event resolution
# EVT_res: must be a power of two between 1024 and 65536.
# Columns:
#   [0]  Event type
#   [1]  Seconds timestamp
#   [2]  Milliseconds timestamp
#   [3]  Microseconds timestamp
#   [4]  X coordinate 0 to 319 for GENX320
#   [5]  Y coordinate 0 to 319 for GENX320
events = [np.zeros((4096, 6), dtype=np.uint16) for i in range(EVENT_FIFO_DEPTH)]
event_counts = [0 for i in range(EVENT_FIFO_DEPTH)]

# ULAB .tobytes() creates shallow copy bytearrays. Wrap these in memoryviews
# for fast slicing without copies.
mv_events = [memoryview(events[i].tobytes()) for i in range(EVENT_FIFO_DEPTH)]

# Event buffer fifo pointers.
wr_index = 0
rd_index = 0


def read_available():
    a = wr_index - rd_index
    if a < 0:
        a += EVENT_FIFO_DEPTH
    return a


def write_available():
    return EVENT_FIFO_DEPTH - 1 - read_available()

# Initialize the sensor.
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events[0].shape[0])
csi0.framebuffers(CSI_FIFO_DEPTH)

# set sensor biases
BIASES = {"DIFF_OFF": 30, "DIFF_ON": 30, "HPF": 40, "FO": 34, "REFR": 10}

csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_DIFF_OFF, BIASES["DIFF_OFF"])
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_DIFF_ON,  BIASES["DIFF_ON"])
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF,      BIASES["HPF"])
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_FO,       BIASES["FO"])
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_REFR,     BIASES["REFR"])

# # set AFK filter, band-stop from 50 to 500 FPS
enable = 1
bsl = 50
bsh = 500
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, enable, bsl, bsh)
#csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0)

class EventChannel:
    def size(self):
        if read_available():
            return event_counts[rd_index] * 12
        return 0

    def readp(self, offset, size):
        global rd_index

        if read_available():
            end = offset + size
            mv = mv_events[rd_index][offset:end]
            # Free the buffer after all data has been read.
            if end == event_counts[rd_index] * 12:
                rd_index = (rd_index + 1) % EVENT_FIFO_DEPTH
            return mv
        return bytes(size)

    def poll(self):
        return read_available()


protocol.register(name='events', backend=EventChannel())

while True:
    if (write_available()):
        # Reads up to 32768 events from the camera.
        # Returns the number of valid events (0-32768) or a negative error code.
        # Note that old events in the buffer are not cleared to save CPU time.
        event_counts[wr_index] = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events[wr_index])
        wr_index = (wr_index + 1) % EVENT_FIFO_DEPTH

To run these, i used the command:

python3 driver/stream_test.py --poll 1 --script driver/openmv_scripts/genx320_event_mode_streaming_on_cam.py --port /dev/ttyACM0 --crc True

Hi, if you want to do this without dropping stuff, you have to create a second thread that does the GUI logic and leave the other one for reading the serial data. Then, the GUI thread can only sub-sample whatever the serial thread is doing; it should not attempt to grab more than some limited amount of the data buffer.

But, I wouldn’t do all of that just yet; you should be using the RT1062. The H7’s serial bandwidth is insufficient to handle the GENX320’s data rate. It’s 480 Mb/s versus 12 Mb/s. The actual camera output burst rate is 384Mb/s. There’s no way the H7 doesn’t drop things.

The reason it works on device… is because the H7 is similar in performance to the RT1062 for doing in-memory work and creating an image which the IDE can pull at a slower pace.

Thank you very much for the insight here. I don’t have an RT1062 on hand, but I do have an N6 board available. Just out of curiosity, do you know if it is possible to stream out the values over the N6 ethernet? If so, is it able to handle the 384 Mb/s spike?

The N6 has HS USB, so, just use that, like the same exact code will run on it as the H7. And yes, with the latest dev firmware N6 ethernet is operational, you could use UDP packets via it too.

I’d also recommend using USB for now, at least until you fix all other bugs, but yes, it is possible. You can replace the default USB transport with anything basically, for example this uses UART as the protocol transport:

Note if your script replaces the transport, you may need to save it asmain.py,Not run it from the IDE, as it will replace the transport that the IDE is using when it runs. I never actually tried running something like this before from the IDE, but it might work.

@kwagyeman first of all thank you for all the work and adding this feature!

I have tried usb transfer on the rt1062 of raw events exactly using the method you suggest, max I can do seems to be around 1M event/s(13 Mb/s), which is a little bit more than what I could do on the H7 plus with modded firmware to a SD-card.

The main bottleneck for me seems to still be the amount of buffer memory I can allocate, the buffer still overflows and therefore my data has blackouts. I have included a video of (playback of the raw events in rerun) for reference(its a 6 second video of my laptop while moving the sensor). This is while the sensor is on default bias settings. And I am running the _on_cam.py script at: (At EVT_res=32768, and a FIFO_DEPTH=16)

I am correct to conclude from this, that my problem is not the usb transfer speed. But rather the max CSI_FIFO I can create? Since I am getting such high burst of events, the csi buffer simply overflows and events from the sensor get lost.

Yes, the default logic of our capture system is to toss all the buffers when things overflow. You can turn this off and not toss the buffers on overflow, but, then the overflowed events are lost still and you end up seeing old events.

However, it’s not clear this problem is happening because the USB is too slow or that you are pulling the data too slow. As mentioned, when you draw the image on the PC, that takes time, which means you weren’t pulling data off the camera then.

So, for your app, you need to have a second thread that’s pulling the data as fast as possible and writing it to a very large fifo buffer in PC RAM, like hundreds of megabytes in size. Then a second thread should read from that and generate the image.

The blacks outs you are getting look like the csi logic resetting on overflow, but, it appears to work for a bit. So, I’d explore the dual thread thing as it’s likely the case that data is just not being pulled fast enough.