Starting on-camera script fails every other time

Hi

I’m trying to use the openmv / protocol module to transmit blob data from the camera to a host PC. Once it’s running, it works flawlessly, and I’m getting the data I expect, without errors.
However, every other time, after stopping the host script and then running it again, the step of starting the script on the camera blocks forever, and I have to kill the with Ctrl-C. The next run then executes fine again.
(scripts at the end)

When the script runs fine, I’m getting the output I expect:

connected: True
stopped
script running
b'"\x1f\x00\x00\x88J\x00\x00' 8
connected: False

However, every other time it hangs at cam.stop():

connected: True
stopped

I then have to kill it with Ctrl-C. The next run then executes fine again.

This is on Win10. Same behaviour under Linux (Mint 22.3 on a VM running under the same Win10 host). On Linux I sometimes get a warning: WARNING:root:⚠️ Unknown Event: channel=2, event=0xFFFF, irrespective if the script blocks or runs through fine.

Any ideas what I’m doing wrong here?

Thanks
Felix


Minimal host-side script:

from openmv.camera import Camera

script = open("blobs_streamer_on_cam.py", encoding="utf-8").read()

port = "COM3"
#port = "/dev/ttyACM0"
cam = Camera(port, baudrate=921600)

cam.connect()
print("connected:", cam.is_connected())

cam.stop()
print("stopped")

cam.exec(script) # <-- every other time this blocks indefinitely
print("script running")

while True:
    # Wait for some channel data to arrive
    status = cam.read_status()

    if not cam.has_channel("blobs") or not status.get("blobs"):
        pass
    else:
        break

data = cam.channel_read("blobs")    # dummy read
print(data, len(data))

cam.disconnect()
print("connected:", cam.is_connected())

Corresponding camera side script:

import csi
import protocol
import struct
import time

sensor = csi.CSI()
sensor.reset()
sensor.pixformat(csi.GRAYSCALE)
sensor.framesize(csi.VGA)
sensor.auto_exposure(False, exposure_us=8000)
sensor.auto_gain(False, gain_db=0)

class BlobsChannel:

    def size(self):
        return len(data)

    def poll(self):
        return frame_ready

    def read(self, offset, size):
        global frame_ready
        end = offset + size
        chunk = data[offset:end]
        if end >= len(data):
            frame_ready = False
        return chunk

protocol.register(name="blobs", backend=BlobsChannel())
t_old = time.ticks_us()
frame_ready = False

while True:

    if not frame_ready:

        img = sensor.snapshot()
        t = time.ticks_us()
        blobs = img.find_blobs([(255, 255)], x_stride=2, y_stride=2, merge=False)
        exposure_us = sensor.exposure_us()
        data = struct.pack("<Ii", exposure_us, t-t_old)

        for blob in blobs:
            data += struct.pack("<HHffHHI", blob.x, blob.y, blob.cxf, blob.cyf,
                                            blob.w, blob.h, blob.pixels)
            img.draw_rectangle(blob.rect, color=0)

        # print(len(data))
        t_old = t
        frame_ready = True

Reproduced on an N6 with your two scripts, exactly as you describe. Not your fault.

What happens: stop() on a running script makes the camera soft-reboot. The commands exec() sends right after land in the middle of that reboot and get rejected with a sequence error (or eaten). The openmv Python package then drops that rejection and hangs forever because the unread stdout (the KeyboardInterrupt traceback plus “MPY: soft reboot”) makes the camera send a notification every 50 ms, which keeps resetting the package’s timeout. That’s your channel=2, event=0xFFFF warning. It alternates because the bad run leaves the camera idle, so the next run’s stop() is a no-op.

Fix is going into the openmv package, with a matching firmware change so the reboot window can’t swallow the script. Until then, change the start of your host script to:

cam.connect()
if cam.read_status()["stdin"]:        # a script is running: stopping it soft-reboots the camera
    cam.stop()
    time.sleep(1.0)                   # let the reboot finish
    cam.disconnect()
    cam.connect()                     # fresh session with the rebooted camera
cam.exec(script)

Tested 8/8 with the current package and firmware. Also call cam.read_stdout() now and then in your loop; unread output is what turns a lost command into a hang on the current package.