v5.0.0

v5.0.0 is a major release. Headlining it: the rebuilt OpenMV Protocol V2 host link, a class-based csi camera API that scales to multi-camera boards, a runnable simulator target, MoveNet single-pose estimation, MicroPython 1.28, and a large batch of camera, ML, and ToF fixes. It also lands a number of breaking API changes — every user-visible change since v4.8.1 is listed below, along with exactly how to migrate.

Highlights

  • OpenMV Protocol V2. The host/IDE link was rebuilt from the ground up: framed, sequenced, CRC-checked, with multiplexed channels for stdio, the stream preview, and user data. A new protocol module lets scripts create their own transports and data channels. See the protocol module changes.

  • Class-based csi camera API. import sensor becomes import csi / csi.CSI, with native multi-camera support. See the csi migration.

  • Simulator target. Firmware now builds and runs under the Arm FVP / QEMU simulator (MPS2/MPS3), including NPU, ROMFS, and PSRAM emulation — vision and ML scripts can run with no hardware attached.

  • MoveNet pose estimation. A new MoveNet post-processor plus a bundled movenet_singlepose_192.tflite model on the OpenMV AE3 and N6.

  • MicroPython 1.28 and ulab 6.12.0, ST Edge AI 4.0 tooling, and the externalized OpenMV SDK (see the build / tooling changes).

New features

Other changes and improvements

  • MicroPython updated to 1.28.0 from the v4.8.1 base. Adds high-speed SD card mode on H5/H7/N6, AHB5 clocking in low-power mode, and controllable JTAG pins as GPIOs on the OPENMV_AE3.

  • ulab updated to 6.12.0 — native % operator on ndarrays (the ml.utils.mod() helper is removed; see the ML library changes).

  • ST Edge AI tooling updated to 4.0 — affects on-device ST model compilation and deployment.

  • ml.Model — the load_to_fb keyword argument was removed; model memory is handled automatically by the unified allocator.

  • image.Image.scale() in place — scaling an image up in place (for example img.scale(x_scale=2.0, y_scale=2.0)) now grows the frame buffer to fit instead of failing.

  • Larger stdio buffer — the default text buffer to the IDE grew from 512 to 1024 bytes on OpenMV 2/3/4, Nicla Vision, AE3, and N6, so larger print() bursts are not truncated.

  • Smoother host event flow — stdout NOTIFY events to the host are throttled to at most one per host read instead of one per print() crossing the ring buffer watermark.

  • Interruptible long operations — long image-drawing, GPU (Nema/Dave2D), and NPU wait loops now service events on a deterministic interval, so scripts stay responsive to the IDE Stop button during heavy work.

Bug fixes

Camera and sensors:

  • find_apriltags() no longer corrupts results on D-cache/GPU boards (N6, AE3), and now works on the AE3.

  • Fixed Bayer image output from the STM32 N6 ISP after switching pixel formats.

  • Fixed green auto-white-balance blowout on bright scenes and an uninitialized first-frame AWB-stats case; raised the STM32 ISP gamma clamp (32 to 63) for a wider gamma/contrast/brightness range.

  • PS5520 auto-exposure no longer oscillates in bright light; PAG7936 AEC/AGC behavior was reworked (combined control, corrected gain ceiling).

  • OV5640 autofocus firmware upload restored on Portenta/Nicla (MIMXRT I2C SUSPEND fix).

  • Fixed a camera capture deadlock when a frame-rate limit is combined with JPEG capture (STM32).

  • GenX320 csi.IOCTL_GENX320_READ_EVENTS_RAW reads no longer scramble the IDE preview.

  • FLIR Lepton csi.IOCTL_LEPTON_SET_MODE via csi.CSI.ioctl() now works when called with a single argument.

Image processing:

  • Fixed draw_image() / blend() alpha blending when a mask is supplied.

  • Fixed 1-bit (BINARY) PNG encode/decode bit ordering and grayscale-from-1-bit decoding.

  • Fixed mjpeg.Mjpeg recording duration/FPS metadata.

  • Fixed a software-JPEG-decode stack overflow on small-stack boards (OpenMV M7).

  • Fixed JPEG/PNG file-format auto-detection on non-ARM hosts (simulator).

Time-of-Flight:

ML and system:

  • The NPU is cleaned up correctly when inference is interrupted on the N6.

  • Deep-sleep / standby wakeup restored on the N6; the AE3 jump-to-bootloader hang is fixed.

  • Fixed memory leaks on soft-reset (STM32 Nema GPU) and a prematurely collected auxiliary frame buffer.

  • Custom Python protocol channels now survive a soft-reboot, the USB transport recovers from bus reset / stalled endpoints, and USB SOF interrupt flooding is fixed.

Hardware and board support

  • OpenMV N6 — Ethernet enabled (wired networking); NPU AXI SRAM (1.75 MB) merged into a shared transient pool for more RAM between inferences; deep-sleep/standby wakeup; bundled TFLite models and Haar cascades in ROMFS.

  • OpenMV AE3 — bundled models and cascades in ROMFS; cbor2 frozen into the firmware.

  • Alif (AE3, N6) — low-power machine.RTC wakeup.

  • High-resolution AprilTags — full-sensor-resolution find_apriltags() on the AE3, Arduino Giga, and Arduino Portenta H7.

  • Simulator targets — MPS2_AN500 / MPS3_AN547 (Arm FVP / QEMU), with NPU, ROMFS, and PSRAM emulation.

Breaking API changes

User-visible API breaks between v4.8.1 and v5.0.0. Scope: Python C-modules in modules/ and Python libraries in scripts/libraries/.

Each change is tagged with its impact:

  • major — most scripts need edits.

  • minor — narrow API; only affects scripts that used it.

  • behavior — same API, different results; re-check tuned scripts.

  • tooling — only affects building from source / downstream forks.

Changes are grouped by impact in that order — major first, then minor, behavior, and tooling. If you just want to port your code, jump to the migration checklist at the end for a condensed to-do list. Each commit hash links to its diff on GitHub.

sensor replaced by csi (major)

Every official example was rewritten to drop import sensor in favor of import csi. The legacy module-level functional API (sensor.reset(), sensor.set_pixformat(), …) is superseded by the class-based csi.CSI API, which scales naturally to boards with multiple cameras (csi0, csi1, …) and is required for all new features (the stream= kwarg, multi-sensor streaming, …).

The sensor qstr is still wired up in modules/py_csi.c for backwards-compatible firmware builds, but it will not gain new features, and all examples, docs, and library code now assume csi.

Commits: 945c5853c, 61f835b7e

Module to class

Before (sensor):

import sensor
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time=2000)
img = sensor.snapshot()

After (csi):

import csi
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
csi0.snapshot(time=2000)
img = csi0.snapshot()

Setter/getter pairs collapsed to combined accessors

In the new API a method called without arguments returns the current value; called with a value it sets it. The set_*/get_* prefixes are gone. Method names also lost the ing suffix where it was redundant (windowingwindow). The new-API column links to the reference docs.

sensor (old)

csi.CSI (new)

set_pixformat(fmt) / get_pixformat()

pixformat([fmt])

set_framesize(sz) / get_framesize()

framesize([sz])

set_framerate(fps) / get_framerate()

framerate([fps])

set_windowing(roi) / get_windowing()

window([roi])

set_framebuffers(n) / get_framebuffers()

framebuffers([n])

set_gainceiling(g)

gainceiling([g])

set_brightness(v)

brightness([v])

set_contrast(v)

contrast([v])

set_saturation(v)

saturation([v])

set_quality(v)

quality([v])

set_colorbar(b)

colorbar([b])

set_special_effect(e)

special_effect([e])

set_lens_correction(...)

lens_correction(...)

set_hmirror(b) / get_hmirror()

hmirror([b])

set_vflip(b) / get_vflip()

vflip([b])

set_transpose(b) / get_transpose()

transpose([b])

set_auto_rotation(b) / get_auto_rotation()

auto_rotation([b])

set_auto_gain(b, [db, ceiling]) / get_gain_db()

auto_gain(...) / gain_db()

set_auto_exposure(b, [us]) / get_exposure_us()

auto_exposure(...) / exposure_us()

set_auto_whitebal(b, [rgb]) / get_rgb_gain_db()

auto_whitebal(...) / rgb_gain_db()

set_auto_blc(b, [regs]) / get_blc_regs()

auto_blc(...) / blc_regs()

set_color_palette(p) / get_color_palette()

color_palette([p])

set_frame_callback(cb)

frame_callback(cb)

set_vsync_callback(cb)

vsync_callback(cb)

get_id()

cid()

Functions with no direct equivalent

sensor (removed)

What to use instead

sensor.alloc_extra_fb(w, h, pixfmt) / sensor.dealloc_extra_fb()

image.Image (w, h, pixfmt) — a regular heap-allocated image. The framebuffer is no longer carved up for user buffers.

sensor.skip_frames(time=..., frames=...)

csi.CSI.snapshot() — skip-frames folded into snapshot via its time= / frames= arguments.

sensor.disable_delays(...) / sensor.disable_full_flush(...)

Moved into the csi.CSI constructor: csi.CSI(delays=False) / csi.CSI(fflush=False).

sensor.get_frame_available()

csi.CSI.readable()

sensor.get_fb()

Removed. The image returned by csi.CSI.snapshot() is the canonical handle.

sensor.set_framebuffers(n, expand=True)

csi.CSI.framebuffers() — the expand argument was removed (see the csi follow-ups).

New on csi.CSI

  • csi.CSI(stream=True|False) — a construct-time selector that chooses which CSI feeds the preview framebuffer (replaces the per-snapshot update= kwarg, see the csi follow-ups).

  • csi.CSI(cid=N) / csi.devices() — multi-CSI support for boards with more than one image sensor.

image module — signature overhaul (major)

The image module saw the widest API change after csi — drawing signatures, result objects, and several detectors all changed.

Coordinate arguments must be tuples

modules/py_image.c was rewritten on top of mp_arg_parse_all. All drawing / pixel methods that previously took separate x, y, ... positional arguments now require those coordinates packed into a single tuple.

Commits: d18bbc472, 0c60c94b9 (PR #3061)

Before

After

img.draw_arrow(x0, y0, x1, y1, color=...)

img.draw_arrow((x0, y0, x1, y1), color=...)

img.draw_line(x0, y0, x1, y1, ...)

img.draw_line((x0, y0, x1, y1), ...)

img.draw_cross(x, y, ...)

img.draw_cross((x, y), ...)

img.draw_circle(x, y, r, ...)

img.draw_circle((x, y, r), ...)

img.draw_rectangle(x, y, w, h, ...)

img.draw_rectangle((x, y, w, h), ...)

img.draw_string(x, y, "txt", ...)

img.draw_string((x, y), "txt", ...)

img.draw_ellipse(x, y, rx, ry, rot, ...)

img.draw_ellipse((x, y, rx, ry, rot), ...)

img.flood_fill(x, y, ...)

img.flood_fill((x, y), ...)

img.get_pixel(x, y, rgbtuple=...)

img.get_pixel((x, y), rgbtuple=...)

img.set_pixel(x, y, color)

img.set_pixel((x, y), color)

All are methods of image.Image.

Result objects converted to attrtuple

These types are now MicroPython attrtuple objects: similarity, statistics, percentile, threshold, line, circle, rect, qrcode, apriltag, datamatrix, barcode, displacement, kptmatch. Attribute access without parentheses is now canonical.

Commit: 3399d302e

Before (method-style):

img.draw_cross(match.cx(), match.cy())
img.draw_rectangle(blob.rect())

After (attribute-style):

img.draw_cross((match.cx, match.cy))
img.draw_rectangle(blob.rect)

blob and histogram are unchanged — they keep their existing types and () accessors (an attrtuple can’t express a blob’s lazily-computed values or a histogram’s argument-taking accessors).

find_features haar parameter rename

image.Image.find_features()scale_factor= was renamed to scale=.

Commit: be4c5cd73

get_regression — now always robust, target_size added

image.Image.get_regression() now always uses the robust (Theil-Sen) regression. The old fast least-squares path was removed, so the robust= keyword is gone — what used to require robust=True is now the only behavior. A new target_size=(w, h) kwarg (default (80, 60)) area-scales the ROI down before the O(N^2) Theil-Sen fit so it always runs on a sane image size; the fitted line endpoints are mapped back to source coordinates. The linear_regression_robust.py example was deleted and linear_regression_fast.py was renamed to linear_regression.py.

Commits: c7c2e69a0, 0ff2afa72

find_line_segments — new algorithm

image.Image.find_line_segments() — the old LSD detector was replaced by ED-Lines, and it gained a new threshold=50 kwarg. Output of previously-tuned scripts will differ.

Commits: 87da2a7b7, 2c47b5735

AprilTag library replaced

image.Image.find_apriltags() — the AprilTag detector was replaced with a new implementation. The family set changed:

Commit: e813bada7

csi module follow-up (minor)

Smaller csi follow-ups on top of the csi migration.

snapshot(update=…) removed

The update kwarg on csi.CSI.snapshot() is gone. To keep a CSI device from feeding the preview framebuffer, opt out at construction time:

csi0 = csi.CSI(stream=False)                  # was: csi0.snapshot(update=False)
csi1.snapshot(blocking=False, image=fir_img)  # was: ...(update=False, ...)

Commits: 9a8077827, 26b79a2c5

framebuffers() expand arg removed

csi.CSI.framebuffers() — the third positional argument (expand) is gone; the signature is now framebuffers([count]).

Commit: 86cb3a5de

protocol module (minor)

Only affects scripts that drove the host link directly. See protocol.

timer_ms renamed to poll_ms

protocol.init() — the timer_ms argument was renamed to poll_ms.

protocol.init(..., poll_ms=10)   # was: timer_ms=10

Commits: 8a0635e8c, 95a290607

protocol.poll() removed

The protocol task is scheduled internally now. Calls to protocol.poll() will raise AttributeError.

Commit: 8a0635e8c

soft_reboot config arg removed

protocol.init() — the soft_reboot argument is gone. All current transports tolerate soft-reboots, so the behavior is now unconditional.

Commit: 0bf766aa2

display modules (minor)

TV output now goes through a display.TVDisplay object instead of the standalone tv module. display also gained a generic ioctl().

Commits: f0accb389, 1a5a87121, 920c097a0, 9eac55098

tof module (behavior)

Same API as before; the defaults and error handling changed. See tof.

Default timeout changed

tof.read_depth() and tof.snapshot() (called with timeout=-1) now default to 100 ms instead of waiting indefinitely. Pass an explicit larger value if you need the old behavior.

Commit: b6772b80d

Automatic recovery

The driver now hard-resets the I2C bus and the sensor on ranging/timeout errors. Examples no longer call tof.reset() in their exception handlers — user code that did manual recovery should remove it (it will fight the new auto-recovery).

Commits: b6772b80d, 80ffaa5c3

ML library (behavior)

Same API, different numbers — re-check any tuned ML pipeline.

Preprocessing now stretches instead of letterboxes

Normalization now uses image.SCALE_ASPECT_IGNORE (stretch) instead of image.SCALE_ASPECT_EXPAND (letterbox). NMS post-processing also switched to independent x/y scaling.

Note

Impact. YOLO-style detectors and keypoint regressors generally improve. The BlazeFace, BlazePalm, FaceLandmarks, and HandLandmarks examples now require a manual square crop on the input ROI — the example scripts were updated; custom user code must do the same.

Commit: 68dc29a33

ml.utils.mod() helper removed

ulab 6.12.0 supports % on ndarrays natively. Code that imported mod from ml.utils must use a % b.

Commits: 35ece5728, 82fbd858c

Build / tooling (tooling)

None of this affects MicroPython scripts. Building firmware from source now requires the external OpenMV SDK (1.6.0, previously in-tree). Several in-tree build tools were removed and the N6 moved to the TinyUSB stack; downstream forks should review the firmware repository history — notably the file_open() signature dropping its buffered argument.

Migration checklist

For a clean port to v5.0.0 the typical work is:

  1. Replace import sensor with import csi; csi0 = csi.CSI() and translate every set_*/get_* call to its csi.CSI accessor (the csi migration).

  2. Wrap coordinate arguments to img.draw_*, get_pixel(), and set_pixel() in tuples (the image module changes).

  3. Drop () from attrtuple result accessors if you want the new idiomatic form, or leave the old style alone since attrtuples still support callable accessors (the image module changes).

  4. Audit find_line_segments(), get_regression(), and any find_apriltags() family choice (the image module changes).

  5. Rename timer_mspoll_ms in protocol.init() calls; remove protocol.poll() and soft_reboot= (the protocol module changes).

  6. For ML workflows: revisit any model that needed letterboxed input (the ML library changes).