.. _changelog_v5_0_0: v5.0.0 ====== v5.0.0 is a major release. Headlining it: the rebuilt **OpenMV Protocol V2** host link, a class-based :mod:`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. .. contents:: On this page :local: :depth: 1 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 :mod:`protocol` module lets scripts create their own transports and data channels. See :ref:`the protocol module changes `. - **Class-based** :mod:`csi` **camera API.** ``import sensor`` becomes ``import csi`` / :class:`csi.CSI`, with native multi-camera support. See :ref:`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 :class:`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 :ref:`the build / tooling changes `). New features ------------ - **The** :mod:`protocol` **module** — create custom transports and data channels from Python: :func:`protocol.init`, :func:`protocol.register`, :func:`protocol.is_active`, and a :class:`protocol.ProtocolChannel` class with :meth:`~protocol.ProtocolChannel.send_event`, plus ``CHANNEL_FLAG_*`` and ``CHANNEL_ID_*`` constants. The final :func:`protocol.init` signature is documented in :ref:`the protocol module changes `. - :class:`protocol.CBORChannel` — a frozen :mod:`protocol` extension package that serializes named fields to CBOR with display widgets (label, depth) and interactive controls (toggle, slider, select). - **Host memory and stream introspection** — a new ``SYS_MEMORY`` protocol command exposes per-pool runtime memory statistics to the IDE, and a new ``STREAM_SOURCE`` stream ioctl lets the host pick which camera feeds the preview on multi-camera boards (protocol version 1.0.1). - **Multi-camera streaming** — :class:`csi.CSI` takes a ``stream=`` argument that selects which sensor feeds the IDE preview; the stream frame header now carries an EMA-smoothed FPS so the IDE shows the frame rate without ``clock.fps()`` boilerplate. See :ref:`the csi migration ` and :ref:`the csi follow-ups `. - **GenX320 event sensor** — a new Spatio-Temporal Contrast filter (:data:`csi.IOCTL_GENX320_SET_STC` with the :data:`csi.GENX320_STC_DISABLE`, :data:`csi.GENX320_STC_ONLY`, :data:`csi.GENX320_STC_TRAIL_ONLY`, and :data:`csi.GENX320_STC_TRAIL` modes) and raw event reading (:data:`csi.IOCTL_GENX320_READ_EVENTS_RAW`), with new example scripts. - :class:`MoveNet ` — a new MediaPipe single-pose post-processor (``threshold``, ``nms_threshold``, ``nms_sigma`` kwargs) returning ``((x, y, w, h), score, keypoints)`` with a 17-joint COCO keypoint array; a ``movenet_singlepose_192.tflite`` model and example are bundled on the AE3 and N6. - :func:`ml.utils.draw_predictions` — a new optional ``scores=`` argument appends per-label confidence, font and box stroke now auto-scale to image width, and a new ``format="point"`` mode draws a center marker for centerpoint/peak detectors. - **The new** ``display.TVDisplay`` **class** (with a generic ``ioctl()``) replaces the standalone ``tv`` module. See :ref:`the display module changes `. - **A new** :meth:`~image.Image.find_line_segments` **detector** (ED-Lines) — now available on all builds, with a new ``threshold=`` argument. See :ref:`the image module changes `. 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 :ref:`the ML library changes `). - **ST Edge AI tooling updated to 4.0** — affects on-device ST model compilation and deployment. - :class:`ml.Model` — the ``load_to_fb`` keyword argument was removed; model memory is handled automatically by the unified allocator. - :meth:`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: - :meth:`~image.Image.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 :data:`csi.IOCTL_GENX320_READ_EVENTS_RAW` reads no longer scramble the IDE preview. - FLIR Lepton :data:`csi.IOCTL_LEPTON_SET_MODE` via :meth:`csi.CSI.ioctl` now works when called with a single argument. Image processing: - Fixed :meth:`~image.Image.draw_image` / :meth:`~image.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 :class:`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: - :func:`tof.read_depth` no longer raises on transient ranging errors and auto-recovers from bus faults; the default :func:`tof.read_depth` / :func:`tof.snapshot` timeout is now 100 ms (see :ref:`the tof changes `). - Fixed VL53L5CX / VL53L8CX multi-zone depth-data corruption. 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 :class:`machine.RTC` wakeup. - **High-resolution AprilTags** — full-sensor-resolution :meth:`~image.Image.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 :ref:`migration checklist ` at the end for a condensed to-do list. Each commit hash links to its diff on GitHub. .. _v5_0_0_s1: ``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 :class:`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 :mod:`csi`. *Commits:* `945c5853c `__, `61f835b7e `__ **Module to class** Before (``sensor``): .. code-block:: python import sensor sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time=2000) img = sensor.snapshot() After (``csi``): .. code-block:: python 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 (``windowing`` → ``window``). The new-API column links to the reference docs. .. list-table:: :header-rows: 1 :widths: 50 50 * - ``sensor`` (old) - :class:`csi.CSI` (new) * - ``set_pixformat(fmt)`` / ``get_pixformat()`` - :meth:`pixformat([fmt]) ` * - ``set_framesize(sz)`` / ``get_framesize()`` - :meth:`framesize([sz]) ` * - ``set_framerate(fps)`` / ``get_framerate()`` - :meth:`framerate([fps]) ` * - ``set_windowing(roi)`` / ``get_windowing()`` - :meth:`window([roi]) ` * - ``set_framebuffers(n)`` / ``get_framebuffers()`` - :meth:`framebuffers([n]) ` * - ``set_gainceiling(g)`` - :meth:`gainceiling([g]) ` * - ``set_brightness(v)`` - :meth:`brightness([v]) ` * - ``set_contrast(v)`` - :meth:`contrast([v]) ` * - ``set_saturation(v)`` - :meth:`saturation([v]) ` * - ``set_quality(v)`` - :meth:`quality([v]) ` * - ``set_colorbar(b)`` - :meth:`colorbar([b]) ` * - ``set_special_effect(e)`` - :meth:`special_effect([e]) ` * - ``set_lens_correction(...)`` - :meth:`lens_correction(...) ` * - ``set_hmirror(b)`` / ``get_hmirror()`` - :meth:`hmirror([b]) ` * - ``set_vflip(b)`` / ``get_vflip()`` - :meth:`vflip([b]) ` * - ``set_transpose(b)`` / ``get_transpose()`` - :meth:`transpose([b]) ` * - ``set_auto_rotation(b)`` / ``get_auto_rotation()`` - :meth:`auto_rotation([b]) ` * - ``set_auto_gain(b, [db, ceiling])`` / ``get_gain_db()`` - :meth:`auto_gain(...) ` / :meth:`gain_db() ` * - ``set_auto_exposure(b, [us])`` / ``get_exposure_us()`` - :meth:`auto_exposure(...) ` / :meth:`exposure_us() ` * - ``set_auto_whitebal(b, [rgb])`` / ``get_rgb_gain_db()`` - :meth:`auto_whitebal(...) ` / :meth:`rgb_gain_db() ` * - ``set_auto_blc(b, [regs])`` / ``get_blc_regs()`` - :meth:`auto_blc(...) ` / :meth:`blc_regs() ` * - ``set_color_palette(p)`` / ``get_color_palette()`` - :meth:`color_palette([p]) ` * - ``set_frame_callback(cb)`` - :meth:`frame_callback(cb) ` * - ``set_vsync_callback(cb)`` - :meth:`vsync_callback(cb) ` * - ``get_id()`` - :meth:`cid() ` **Functions with no direct equivalent** .. list-table:: :header-rows: 1 :widths: 40 60 * - ``sensor`` (removed) - What to use instead * - ``sensor.alloc_extra_fb(w, h, pixfmt)`` / ``sensor.dealloc_extra_fb()`` - :class:`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=...)`` - :meth:`csi.CSI.snapshot` — skip-frames folded into ``snapshot`` via its ``time=`` / ``frames=`` arguments. * - ``sensor.disable_delays(...)`` / ``sensor.disable_full_flush(...)`` - Moved into the :class:`csi.CSI` constructor: ``csi.CSI(delays=False)`` / ``csi.CSI(fflush=False)``. * - ``sensor.get_frame_available()`` - :meth:`csi.CSI.readable` * - ``sensor.get_fb()`` - Removed. The image returned by :meth:`csi.CSI.snapshot` is the canonical handle. * - ``sensor.set_framebuffers(n, expand=True)`` - :meth:`csi.CSI.framebuffers` — the ``expand`` argument was removed (see :ref:`the csi follow-ups `). **New on** :class:`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 :ref:`the csi follow-ups `). - ``csi.CSI(cid=N)`` / :func:`csi.devices` — multi-CSI support for boards with more than one image sensor. .. _v5_0_0_s2: ``image`` module — signature overhaul *(major)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :mod:`image` module saw the widest API change after :mod:`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 `__) .. list-table:: :header-rows: 1 :widths: 50 50 * - 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 :class:`image.Image`. **Result objects converted to attrtuple** These types are now MicroPython ``attrtuple`` objects: :class:`~image.similarity`, :class:`~image.statistics`, :class:`~image.percentile`, :class:`~image.threshold`, :class:`~image.line`, :class:`~image.circle`, :class:`~image.rect`, :class:`~image.qrcode`, :class:`~image.apriltag`, :class:`~image.datamatrix`, :class:`~image.barcode`, :class:`~image.displacement`, :class:`~image.kptmatch`. Attribute access without parentheses is now canonical. *Commit:* `3399d302e `__ Before (method-style): .. code-block:: python img.draw_cross(match.cx(), match.cy()) img.draw_rectangle(blob.rect()) After (attribute-style): .. code-block:: python img.draw_cross((match.cx, match.cy)) img.draw_rectangle(blob.rect) :class:`~image.blob` and :class:`~image.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** :meth:`image.Image.find_features` — ``scale_factor=`` was renamed to ``scale=``. *Commit:* `be4c5cd73 `__ **get_regression — now always robust, target_size added** :meth:`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** :meth:`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** :meth:`image.Image.find_apriltags` — the AprilTag detector was replaced with a new implementation. The family set changed: .. list-table:: :header-rows: 1 :widths: 50 50 * - Removed - Added * - ``image.TAG25H7``, ``image.ARTOOLKIT`` - :data:`image.TAGCIRCLE21H7`, :data:`image.TAGCIRCLE49H12`, :data:`image.TAGCUSTOM48H12`, :data:`image.TAGSTANDARD41H12`, :data:`image.TAGSTANDARD52H13` *Commit:* `e813bada7 `__ .. _v5_0_0_s3: ``csi`` module follow-up *(minor)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Smaller :mod:`csi` follow-ups on top of :ref:`the csi migration `. **snapshot(update=...) removed** The ``update`` kwarg on :meth:`csi.CSI.snapshot` is gone. To keep a CSI device from feeding the preview framebuffer, opt out at construction time: .. code-block:: python 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** :meth:`csi.CSI.framebuffers` — the third positional argument (``expand``) is gone; the signature is now ``framebuffers([count])``. *Commit:* `86cb3a5de `__ .. _v5_0_0_s4: ``protocol`` module *(minor)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Only affects scripts that drove the host link directly. See :mod:`protocol`. **timer_ms renamed to poll_ms** :func:`protocol.init` — the ``timer_ms`` argument was renamed to ``poll_ms``. .. code-block:: python 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** :func:`protocol.init` — the ``soft_reboot`` argument is gone. All current transports tolerate soft-reboots, so the behavior is now unconditional. *Commit:* `0bf766aa2 `__ .. _v5_0_0_s10: display modules *(minor)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TV output now goes through a ``display.TVDisplay`` object instead of the standalone ``tv`` module. :mod:`display` also gained a generic ``ioctl()``. *Commits:* `f0accb389 `__, `1a5a87121 `__, `920c097a0 `__, `9eac55098 `__ .. _v5_0_0_s5: ``tof`` module *(behavior)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Same API as before; the defaults and error handling changed. See :mod:`tof`. **Default timeout changed** :func:`tof.read_depth` and :func:`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 :func:`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 `__ .. _v5_0_0_s6: ML library *(behavior)* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Same API, different numbers — re-check any tuned ML pipeline. **Preprocessing now stretches instead of letterboxes** :class:`~ml.preprocessing.Normalization` now uses :data:`image.SCALE_ASPECT_IGNORE` (stretch) instead of :data:`image.SCALE_ASPECT_EXPAND` (letterbox). :class:`~ml.utils.NMS` post-processing also switched to independent x/y scaling. .. note:: **Impact.** YOLO-style detectors and keypoint regressors generally improve. The :class:`~ml.postprocessing.mediapipe.BlazeFace`, :class:`~ml.postprocessing.mediapipe.BlazePalm`, :class:`~ml.postprocessing.mediapipe.FaceLandmarks`, and :class:`~ml.postprocessing.mediapipe.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 `__ .. _v5_0_0_s11: 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. .. _v5_0_0_s12: Migration checklist ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For a clean port to v5.0.0 the typical work is: #. Replace ``import sensor`` with ``import csi; csi0 = csi.CSI()`` and translate every ``set_*``/``get_*`` call to its :class:`csi.CSI` accessor (:ref:`the csi migration `). #. Wrap coordinate arguments to ``img.draw_*``, :meth:`~image.Image.get_pixel`, and :meth:`~image.Image.set_pixel` in tuples (:ref:`the image module changes `). #. Drop ``()`` from attrtuple result accessors if you want the new idiomatic form, or leave the old style alone since attrtuples still support callable accessors (:ref:`the image module changes `). #. Audit :meth:`~image.Image.find_line_segments`, :meth:`~image.Image.get_regression`, and any :meth:`~image.Image.find_apriltags` family choice (:ref:`the image module changes `). #. Rename ``timer_ms`` → ``poll_ms`` in :func:`protocol.init` calls; remove ``protocol.poll()`` and ``soft_reboot=`` (:ref:`the protocol module changes `). #. For ML workflows: revisit any model that needed letterboxed input (:ref:`the ML library changes `).