<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ZhengZhiCong</title>
    <description>The latest articles on DEV Community by ZhengZhiCong (@mickey_zzc).</description>
    <link>https://dev.to/mickey_zzc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3972064%2Fd8a4b5b3-f282-4858-b6f6-9f246118a9b0.png</url>
      <title>DEV Community: ZhengZhiCong</title>
      <link>https://dev.to/mickey_zzc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mickey_zzc"/>
    <language>en</language>
    <item>
      <title>MiBee Eye Notebook: turn a spare laptop into a network camera, microphone included</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Wed, 16 Sep 2026 13:32:25 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/mibee-eye-notebook-turn-a-spare-laptop-into-a-network-camera-microphone-included-48l4</link>
      <guid>https://dev.to/mickey_zzc/mibee-eye-notebook-turn-a-spare-laptop-into-a-network-camera-microphone-included-48l4</guid>
      <description>&lt;p&gt;MiBee Eye has had two implementations so far, both aimed at Raspberry Pi-class boards. This is the new one: laptop, desktop and mini-PC support, capturing the camera and microphone attached to the machine and presenting them as a device your existing NVR can pick up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The laptop in the drawer
&lt;/h2&gt;

&lt;p&gt;Most of us have one: a laptop that's too old to be a daily driver and too good to throw away. It has a webcam, a microphone, a NIC and a disk. What it doesn't have is a reason to be switched on.&lt;/p&gt;

&lt;p&gt;At the same time, buying a camera means accepting the usual deal — someone else's cloud gets the video, a subscription unlocks the useful parts, and the firmware stops when the product line does.&lt;/p&gt;

&lt;p&gt;MiBee Eye Notebook is the middle layer for the spare machine. It runs on a desktop, laptop or mini-PC, captures the camera and microphone physically attached to that machine, and exposes them as a standard device an NVR can discover. Day to day you drive it from a browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is, and what it deliberately isn't
&lt;/h2&gt;

&lt;p&gt;The scope is narrow on purpose, and the boundaries are written down as definitional rather than as engineering decisions someone might revisit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is&lt;/strong&gt; a local capture agent. It runs as a system service. It captures USB webcams, built-in laptop cameras, and USB or built-in microphones from the machine it's installed on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is not&lt;/strong&gt; a network camera scanner. It never discovers, scans or pulls RTSP/ONVIF streams from remote cameras — if the camera isn't plugged into this machine, it's out of scope. It isn't an NVR: it doesn't manage or record third-party cameras. It isn't a cloud service, and it's designed to run fully air-gapped. It isn't multi-tenant: one admin account, no per-user ACLs.&lt;/p&gt;

&lt;p&gt;Inside the machine:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    CAM["Camera&amp;lt;br/&amp;gt;USB or built-in"] --&amp;gt; CAP["Capture&amp;lt;br/&amp;gt;V4L2 read"]
    CAP --&amp;gt; ENC["H.264 encode&amp;lt;br/&amp;gt;openh264"]
    MIC["Microphone&amp;lt;br/&amp;gt;USB or built-in"] --&amp;gt; AUD["Audio encode&amp;lt;br/&amp;gt;G.711 or AAC"]
    ENC --&amp;gt; HUB["Encoded frame hub"]
    HUB --&amp;gt; LOCAL["Local output"]
    LOCAL --&amp;gt; PRE["Browser preview"]
    LOCAL --&amp;gt; REC["MP4 recordings"]
    HUB --&amp;gt; PULL["Pull from outside"]
    PULL --&amp;gt; RTSP["RTSP server"]
    PULL --&amp;gt; OV["ONVIF device"]
    HUB --&amp;gt; PUSH["Push to outside"]
    PUSH --&amp;gt; RTMP["RTMP push"]
    PUSH --&amp;gt; GB["GB28181 device"]
    AUD --&amp;gt; REC&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Default ports: web UI &lt;code&gt;8443&lt;/code&gt; (TLS), RTSP &lt;code&gt;8554&lt;/code&gt;, RTMP &lt;code&gt;1935&lt;/code&gt;, SIP &lt;code&gt;5060&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's part of the MiBee Eye family alongside mibee-eye-go and mibee-eye-rs, which target Raspberry Pi-class boards. Same protocols, same SPEC v1 web API, same NVRs. Pick by the machine you have: a board with a CSI camera or a USB camera goes to one of those; a PC or laptop goes here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your microphone is a first-class input
&lt;/h2&gt;

&lt;p&gt;This is the part the board-based implementations don't do, and it's what makes repurposing a laptop interesting.&lt;/p&gt;

&lt;p&gt;The microphone path forks into two encoders for two purposes:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    MIC["Microphone"] --&amp;gt; PCM["PCM capture"]
    PCM --&amp;gt; G711["G.711 encode"]
    PCM --&amp;gt; AAC["AAC encode&amp;lt;br/&amp;gt;optional feature"]
    G711 --&amp;gt; GB["GB28181 stream"]
    AAC --&amp;gt; REC["MP4 recordings"]
    AAC --&amp;gt; RTMP["RTMP push"]
    PLAT["Platform talkback"] --&amp;gt; DEC["G.711 decode"]
    DEC --&amp;gt; SPK["Local speaker"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;G.711 μ-law is the default: pure Rust, used for the GB28181 live stream and for decoding platform talkback. AAC covers MP4 recordings and RTMP push, but it sits behind a compile-time feature that's off by default, because it pulls in an external encoder library.&lt;/p&gt;

&lt;p&gt;Talkback runs the other direction too. When a platform opens an audio-only INVITE, the device decodes G.711 and plays it out of the local speaker. That's on by default. If it's disabled in config, or the machine has no usable output device, those INVITEs get a &lt;strong&gt;488&lt;/strong&gt; — not a 200 OK with the audio quietly dropped. That preference for an honest refusal over a fake success shows up in a few places in this codebase.&lt;/p&gt;

&lt;p&gt;Recordings are rolling MP4 segments: 900 seconds by default, a 10,240 MB capacity cap, oldest-first pruning, and only files the application wrote are ever deleted. Segment rotation happens on keyframe boundaries, so you don't get half-written files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything outbound is off until you turn it on
&lt;/h2&gt;

&lt;p&gt;The default posture is worth calling out because it's unusual. RTSP server, RTMP push, ONVIF device, GB28181 device — all four ship disabled. You enable what you need from the web UI, per protocol, and the state lives in SQLite so it survives a restart.&lt;/p&gt;

&lt;p&gt;The rest of the security posture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TLS is mandatory&lt;/strong&gt; on the web UI. rustls only, no HTTP fallback, not even on localhost. A self-signed dev certificate is generated on first run.&lt;/li&gt;
&lt;li&gt;Admin credentials are bcrypt-hashed. Session cookies last 24 hours with &lt;code&gt;HttpOnly; Secure; SameSite=Strict&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Per-IP rate limiting on auth endpoints, 20 requests per 60 seconds by default, reset on successful login, plus a per-user lockout with exponential backoff after five failures.&lt;/li&gt;
&lt;li&gt;Double-submit CSRF on state-changing requests, and a strict CSP header.&lt;/li&gt;
&lt;li&gt;The only anonymous endpoints are &lt;code&gt;/health&lt;/code&gt; and &lt;code&gt;/metrics&lt;/code&gt;. Everything else needs a session.&lt;/li&gt;
&lt;li&gt;Two-factor auth and mutual TLS are explicitly out of scope for v1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the real limitation: if you expose this to the internet, a strong password over TLS is the whole authentication story. It's built as a LAN device, and it should be deployed like one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only interface is the browser
&lt;/h2&gt;

&lt;p&gt;The web UI isn't a convenience wrapper around a config file — it's the primary surface, and the CLI is the escape hatch.&lt;/p&gt;

&lt;p&gt;Live preview picks its transport from what the device and browser support: MSE first (fragmented MP4 over chunked HTTP), falling back to MJPEG in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, then snapshot polling.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    OPEN["Open live view"] --&amp;gt; P1["MSE over HTTP"]
    P1 --&amp;gt; P2["MJPEG fallback"]
    P2 --&amp;gt; P3["Snapshot polling"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The MSE engine has a stall timer, exponential backoff on reconnect, active SourceBuffer pruning to stay near the live edge, and a playhead watchdog that reconnects if &lt;code&gt;currentTime&lt;/code&gt; stops advancing for about eight seconds — because a stuck decoder keeps bytes flowing while the picture is frozen, and a byte-level stall timer never notices.&lt;/p&gt;

&lt;p&gt;Frames for the preview and the snapshot endpoint come from a JPEG tap maintained inside the capture loop, not from a transcoder. For MJPG cameras the raw JPEG bytes are forwarded at no cost; for YUYV-only cameras a JPEG is re-encoded every Nth frame. Snapshot and preview both read that tap.&lt;/p&gt;

&lt;h2&gt;
  
  
  No ffmpeg, no subprocesses
&lt;/h2&gt;

&lt;p&gt;Earlier versions shelled out to ffmpeg for five different jobs. All five are now in-process Rust:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Former ffmpeg job&lt;/th&gt;
&lt;th&gt;Replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;H.264 encode&lt;/td&gt;
&lt;td&gt;openh264 (Cisco, BSD-2, built from source)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MJPEG decode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jpeg-decoder&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YUYV to JPEG preview&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jpeg-encoder&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audio encode&lt;/td&gt;
&lt;td&gt;G.711 μ-law, or optional FDK-AAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MP4 segment muxing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;muxide&lt;/code&gt;, a pure-Rust fMP4 muxer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two consequences worth stating plainly: ffmpeg is no longer a runtime dependency (the container image, the systemd unit and the bare-metal install steps all dropped it), and the encoding pipeline is crash-recoverable without process supervision. The project's own changelog puts the saving at roughly 64 MB of subprocess overhead per camera. That's the project's figure, not a third-party benchmark, and I haven't reproduced it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small things that matter on a machine you also use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol hot-toggle.&lt;/strong&gt; Turning ONVIF, GB28181 or RTMP on and off from the web UI doesn't restart the service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot-plug.&lt;/strong&gt; A udev listener watches for camera add/remove events, so plugging in a webcam makes it appear, and unplugging it marks the camera offline and shuts its recording output down cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE event stream.&lt;/strong&gt; Camera online/offline events reach the browser without a refresh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability.&lt;/strong&gt; 14+ Prometheus metrics, OpenTelemetry tracing wired through 132 annotated spans, structured JSON logs, and an optional Loki-compatible log shipper that fails open — a missing backend is a warning, not a crash.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Platform reality
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linux x86_64 and aarch64 are the only targets that compile today.&lt;/strong&gt; Windows and macOS are planned and don't build yet. The stated blockers are concrete: &lt;code&gt;libc::getifaddrs&lt;/code&gt; is POSIX-only, parts of the capture code have &lt;code&gt;#[cfg(unix)]&lt;/code&gt; without a Windows branch, and device paths are hardcoded as &lt;code&gt;/dev/videoN&lt;/code&gt;. macOS would need AVFoundation-aware device enumeration on top of that.&lt;/p&gt;

&lt;p&gt;That matters because the obvious user for this product is someone with a spare Windows laptop, and today they can't run it.&lt;/p&gt;

&lt;p&gt;Other things to know before you build a plan on it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No releases yet.&lt;/strong&gt; No tags, no GitHub releases, the changelog is entirely under Unreleased at version 0.1.0. If you want it, you compile it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The README lags the code in two places.&lt;/strong&gt; Its per-crate line-count table is stale, and the browser-preview row still describes ffmpeg transcoding, which was removed — MSE is the current primary path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The resource figures are targets, not measurements.&lt;/strong&gt; Idle CPU under 5%, memory under 200 MB, first frame under 500 ms, at most 16 concurrent streams. There's no published measurement to check them against, which is different from the board-based siblings, whose READMEs do carry measured numbers with their conditions attached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One authentication factor.&lt;/strong&gt; A strong password. No 2FA, no mTLS, no per-user ACLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cloud, no mobile app, no push notifications.&lt;/strong&gt; By design, but it's a gap if that's what you wanted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's brand new.&lt;/strong&gt; First pushed on 2026-09-16, one star. I won't make claims about maintenance cadence or response times.&lt;/li&gt;
&lt;li&gt;The toolchain requirement is Rust 1.88 with edition 2024, which is on the new side.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The protocol layer is the same two Rust libraries
&lt;/h2&gt;

&lt;p&gt;The ONVIF device service is &lt;code&gt;onvif-device-rs&lt;/code&gt;; the GB28181 device stack, including GB 35114 A-level, is &lt;code&gt;gb28181-rs&lt;/code&gt;. Both are MIT, both are maintained as standalone libraries, and both can be used without this application if you're building an NVR, a platform, or a device simulator. The signal-plane code is hand-written in this repo; the protocol semantics live in those two crates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# System dependencies (Linux)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libv4l-dev libasound2-dev libclang-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; video &lt;span class="nv"&gt;$USER&lt;/span&gt;     &lt;span class="c"&gt;# reclaim webcam access; requires a fresh login&lt;/span&gt;

git clone https://github.com/xiqing85/mibee-eye-notebook
&lt;span class="nb"&gt;cd &lt;/span&gt;mibee-eye-notebook
cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;

&lt;span class="nb"&gt;cp &lt;/span&gt;config.toml config.local.toml   &lt;span class="c"&gt;# gitignored; keep local overrides here&lt;/span&gt;

cargo run &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--config&lt;/span&gt; config.local.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;https://&amp;lt;host&amp;gt;:8443&lt;/code&gt;. First run walks you through creating the admin account and generates the self-signed certificate, so the browser warning is expected.&lt;/p&gt;

&lt;p&gt;Three config gotchas: &lt;code&gt;[web] advertised_host&lt;/code&gt; left empty auto-detects the LAN IP with a UDP probe, which picks wrong on multi-NIC or NAT'd hosts and produces stream URLs clients can't reach. &lt;code&gt;[capture] video_device&lt;/code&gt; defaults to &lt;code&gt;/dev/video0&lt;/code&gt;, so check the device number on a multi-camera machine. And if you lose the password, &lt;code&gt;--reset-password&lt;/code&gt; is the way back in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project links
&lt;/h2&gt;

&lt;p&gt;mibee-eye-notebook: &lt;a href="https://github.com/xiqing85/mibee-eye-notebook" rel="noopener noreferrer"&gt;https://github.com/xiqing85/mibee-eye-notebook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Raspberry Pi-class implementations in the same family:&lt;/p&gt;

&lt;p&gt;mibee-eye-go: &lt;a href="https://github.com/xiqing85/mibee-eye-go" rel="noopener noreferrer"&gt;https://github.com/xiqing85/mibee-eye-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;mibee-eye-rs: &lt;a href="https://github.com/xiqing85/mibee-eye-rs" rel="noopener noreferrer"&gt;https://github.com/xiqing85/mibee-eye-rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The protocol libraries underneath:&lt;/p&gt;

&lt;p&gt;onvif-rs: &lt;a href="https://github.com/mickeyzzc/onvif-rs" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/onvif-rs&lt;/a&gt; (published on crates.io as &lt;code&gt;onvif-device-rs&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;gb28181-rs: &lt;a href="https://github.com/mickeyzzc/gb28181-rs" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/gb28181-rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Licence: Apache-2.0 for the capture agent. The two protocol libraries are MIT.&lt;/p&gt;

</description>
      <category>camera</category>
      <category>rust</category>
      <category>laptop</category>
    </item>
    <item>
      <title>MiBee Eye: turn a spare Linux board into an ONVIF camera your NVR already trusts</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Tue, 15 Sep 2026 19:12:30 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/mibee-eye-turn-a-spare-linux-board-into-an-onvif-camera-your-nvr-already-trusts-455b</link>
      <guid>https://dev.to/mickey_zzc/mibee-eye-turn-a-spare-linux-board-into-an-onvif-camera-your-nvr-already-trusts-455b</guid>
      <description>&lt;h2&gt;
  
  
  The camera you already own, and the bill attached to it
&lt;/h2&gt;

&lt;p&gt;Most consumer cameras sold today work like this: the video goes to a company's servers, the useful features sit behind a monthly plan, and the device stops getting firmware the moment the product line is retired. You bought the hardware, but the footage and the feature set aren't really yours.&lt;/p&gt;

&lt;p&gt;The alternative used to mean buying into an ecosystem anyway — a vendor NVR, a vendor app, a vendor account. What's actually missing is the boring middle layer: something that takes a camera sensor on a board you already own and presents it to the software you already run, using the protocols that software already speaks.&lt;/p&gt;

&lt;p&gt;That's what MiBee Eye is. It turns any Linux board into an ONVIF Profile S device with RTSP streaming, and for installations that need it, a GB/T 28181 device. There are two independent implementations, Go and Rust. No account, no cloud round-trip, no subscription. Two terminal commands and a systemd unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it presents to the rest of your network
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;What's there&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8080&lt;/td&gt;
&lt;td&gt;ONVIF device service (Device / Media / Imaging) + WS-Discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8554&lt;/td&gt;
&lt;td&gt;RTSP, H.264 over TCP interleaved or UDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8088&lt;/td&gt;
&lt;td&gt;Embedded admin UI, English and Chinese&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ONVIF Profile S device.&lt;/strong&gt; Device, Media and Imaging SOAP services plus WS-Discovery multicast announcements, so an NVR finds it without a manual address entry. Against an ONVIF NVR it looks like an ordinary camera.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GB/T 28181 device (optional path).&lt;/strong&gt; SIP registration over UDP or TCP with digest auth, catalog and device-info queries, live/playback/download PS streams, SIP INFO playback control, platform snapshot commands. Relevant if you're integrating with a Chinese standards-based video platform rather than an ONVIF VMS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GB 35114 A-level (compile-time opt-in).&lt;/strong&gt; SM2 certificate registration and keyed-SM3 integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous local recording.&lt;/strong&gt; H.264 segments on disk with an &lt;code&gt;index.jsonl&lt;/code&gt; index, retention days and a storage cap. This doubles as the source for GB28181 playback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTMP push&lt;/strong&gt; to a cloud ingest, and JPEG snapshots over &lt;code&gt;GET /snapshot&lt;/code&gt;. The Go build also carries a pure-Go MPEG-TS segmenter, so browsers get HLS without ffmpeg.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imaging controls&lt;/strong&gt; for brightness, contrast, saturation, sharpness, white balance and exposure mode. The Rust build adds an OSD watermark — custom text plus a live clock — burned in pre-encode, so it appears on RTSP, RTMP, recordings and the web preview alike.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood everything hangs off one encoded frame hub:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    CAM["Camera&amp;lt;br/&amp;gt;CSI or USB"] --&amp;gt; CAP["Capture&amp;lt;br/&amp;gt;board-native path"]
    CAP --&amp;gt; ENC["H.264 encode&amp;lt;br/&amp;gt;HW or SW"]
    ENC --&amp;gt; HUB["Encoded frame hub"]
    HUB --&amp;gt; RTSP["RTSP :8554"]
    RTSP --&amp;gt; NVR["NVR or VMS"]
    HUB --&amp;gt; REC["Local recording"]
    REC --&amp;gt; GB["GB28181 device"]
    GB --&amp;gt; PLAT["Platform"]
    HUB --&amp;gt; WEB["Web UI :8088"]
    WEB --&amp;gt; BROWSER["Browser"]
    ENC -. tap .-&amp;gt; AI["Detection&amp;lt;br/&amp;gt;opt-in"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Detection is a passive subscriber on that hub. If it's slow, or the model won't load, streaming is unaffected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detection that never leaves the board
&lt;/h2&gt;

&lt;p&gt;Object detection is compile-time opt-in, and it runs locally or not at all. Nothing is uploaded, and there's no cloud inference bill because there's no cloud inference.&lt;/p&gt;

&lt;p&gt;The Rust build taps the shared pre-encode YUV buffer, so inference never touches the capture or encoding path. The Go build subscribes to the encoded-frame hub and decodes keyframes for inference.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    YUV["Shared YUV&amp;lt;br/&amp;gt;pre-encode"] --&amp;gt; PRE["Preprocess&amp;lt;br/&amp;gt;YUV to 320 square"]
    PRE --&amp;gt; RUN["ONNX Runtime&amp;lt;br/&amp;gt;NanoDet-Plus"]
    RUN --&amp;gt; POST["Postprocess&amp;lt;br/&amp;gt;argmax, GFL, NMS"]
    POST --&amp;gt; API["REST and SSE&amp;lt;br/&amp;gt;boxes in native px"]
    GUARD["Guardrails&amp;lt;br/&amp;gt;memory and core"] -. controls .-&amp;gt; RUN&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The model is NanoDet-Plus-m 320 in ONNX form, Apache-2.0 licensed. There's a small model registry with runtime hot-swap between a 320 and a 416 variant, plus a YOLOX-nano decoder if you want a different family. Uploading new models at runtime is supported but off by default behind an explicit config gate, since a model file is untrusted input to an inference engine.&lt;/p&gt;

&lt;p&gt;Detection fails open. If the runtime library or the model file is missing, the capability reports &lt;code&gt;ai:false&lt;/code&gt; and the API says so — it never returns fabricated boxes. The trade-off is honest: &lt;code&gt;libonnxruntime.so&lt;/code&gt; and the model are not bundled in the release tarball, so detection is a setup step, not a checkbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which build
&lt;/h2&gt;

&lt;p&gt;Two codebases, same protocols, same web API, same SPEC v1 JSON envelope. NVRs can't tell them apart. The choice is about the board and about how you like to deploy.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    Q["Pick a build"] --&amp;gt; P1["Want the easiest&amp;lt;br/&amp;gt;cross-compile"]
    Q --&amp;gt; P2["Tightest memory&amp;lt;br/&amp;gt;and flash"]
    Q --&amp;gt; P3["Want OSD or&amp;lt;br/&amp;gt;zero subprocesses"]
    P1 --&amp;gt; G["Go build"]
    P2 --&amp;gt; R["Rust build"]
    P3 --&amp;gt; R
    G --&amp;gt; N1["Ships HLS and&amp;lt;br/&amp;gt;a bilingual UI"]
    R --&amp;gt; N2["About 2 MB binary&amp;lt;br/&amp;gt;no subprocesses"]&lt;/code&gt;&lt;/pre&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your situation&lt;/th&gt;
&lt;th&gt;Pick&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fastest path, no cross-compile friction&lt;/td&gt;
&lt;td&gt;Go — zero CGO, stock cross-compile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smallest RAM and flash footprint&lt;/td&gt;
&lt;td&gt;Rust — ~2 MB binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermark burned into every output&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HLS playback or bilingual UI out of the box&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32-bit board&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What it costs to run
&lt;/h2&gt;

&lt;p&gt;Both builds are static binaries with a systemd unit in the repo. No Docker, no runtime, no language toolchain on the device.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Go build&lt;/th&gt;
&lt;th&gt;Rust build&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Binary size&lt;/td&gt;
&lt;td&gt;~15 MB&lt;/td&gt;
&lt;td&gt;~2 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;15–25 MB (+~15 MB with the AI build)&lt;/td&gt;
&lt;td&gt;~6–12 MB bare pipeline; 93.8 MB measured full-feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subprocesses&lt;/td&gt;
&lt;td&gt;mtxrpicam or ffmpeg&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU, 720p@15fps&lt;/td&gt;
&lt;td&gt;~15%&lt;/td&gt;
&lt;td&gt;124–146% measured full-feature (~139% of four cores)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read those conditions before drawing conclusions, because they're not the same measurement.&lt;/p&gt;

&lt;p&gt;The Rust figures come from a run on 2026-09-14 on a Raspberry Pi 3B: 720p at 15fps, hardware M2M encoder, GB28181 registered, local recording on, OSD watermark on, per-frame INFO logging enabled, one RTSP-over-TCP client, 60 seconds, 13 samples. RSS held flat at 93.8 MB. A bare pipeline — no GB28181, no recording, no watermark — sits far lower, which is where the ~6 MB figure in the repo description comes from. Those two numbers are different measurement scopes, and I'd rather show you both than quote the flattering one.&lt;/p&gt;

&lt;p&gt;The Go CPU figure comes from the v0.1.0 release notes, also measured on a Pi 3B. Different scope again, so I'm not going to declare a winner. Both repos ship &lt;code&gt;bench/rpi-bench.sh&lt;/code&gt; if you want your own numbers on your own board.&lt;/p&gt;

&lt;p&gt;For context, there's a Pi 3B — a 2016 board with 1 GB of RAM — holding up a 720p stream with recording and a standards-based registration on top. That's the repurposing argument in one line.&lt;/p&gt;

&lt;p&gt;The repo README also carries a comparison against MediaMTX on memory and CPU for the camera-side role. MediaMTX is a media server and a good one, and it isn't trying to be a camera; the comparison only covers turning a Pi plus a sensor into a device an NVR can discover, and the numbers are indicative measurements from my own deployments rather than a controlled benchmark.&lt;/p&gt;

&lt;p&gt;Onboards without a V4L2 M2M encoder node — x86 boxes, NAS units, most ARM SBCs — the Rust build falls back to an in-process openh264 software encoder, and the Go build to a resident ffmpeg subprocess. That's why "any Linux board" is more than a tagline, and it's also why the subprocess column is worth reading before you pick. Note the CPU budget on weak boards: 720p@15 is the sane ceiling on A53-class silicon.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts that were hard
&lt;/h2&gt;

&lt;p&gt;The most useful file in the Rust repo isn't code. It's a 386-line interop notebook documenting ten real bugs found while getting a gortsplib-based NVR to actually stay connected. Four of them explain why cheap RTSP implementations fail where ffmpeg succeeds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A duplicated &lt;code&gt;Content-Length&lt;/code&gt; header.&lt;/strong&gt; The RTSP DESCRIBE response had the header twice — added once by the response constructor and once by the handler. ffmpeg tolerates it; the NVR's parser rejected the whole response and refused to connect. Strict parsers catch what lenient ones hide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partial frames are worse than missing frames.&lt;/strong&gt; One encoded frame usually becomes several RTP packets. Under TCP interleaved mode, sending them individually meant a timeout mid-frame delivered half an FU-A fragment, which a decoder can't reconstruct. Fix: batch every packet of an access unit into a single &lt;code&gt;write_all&lt;/code&gt;, so the frame arrives whole or not at all. Then rewind the RTP sequence numbers for the packets you didn't send, because a gap in the sequence makes a strict NVR declare packet loss. Then drop subsequent P-frames until the next keyframe, because those reference the frame you just threw away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timestamps must be strictly monotonic.&lt;/strong&gt; RTP timestamps computed at send time rather than capture time produced duplicates whenever frames came out of the encoder in a burst. ffmpeg reported non-monotonically increasing DTS and the NVR dropped frames. The current implementation takes the capture timestamp and enforces a minimum increment of one frame tick between consecutive packets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pi undervoltage corrupts H.264.&lt;/strong&gt; An ageing Pi 3B with marginal power delivery drops voltage under encoder load, the firmware caps the clock, and the transition produces corrupted NAL units. &lt;code&gt;vcgencmd get_throttled&lt;/code&gt; returned &lt;code&gt;0x50005&lt;/code&gt; — undervoltage happening now plus a frequency cap. The encoder thread now watches firmware throttle state and skips every other frame while throttled, which halves the load, lets the clock recover, and self-corrects. After that change, a 60-second decode test reported zero errors.&lt;/p&gt;

&lt;p&gt;One frontend fix in the same notebook: Chrome 120+ no longer renders &lt;code&gt;multipart/x-mixed-replace&lt;/code&gt; inside an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, so the MJPEG preview went black. Replacing it with H.264 over WebSocket plus Media Source Extensions fixed it, and reworking the buffering brought preview latency from 3–9 seconds down to under a second.&lt;/p&gt;

&lt;p&gt;Code and test volume, measured 2026-09-15: the Go repo is 93 &lt;code&gt;.go&lt;/code&gt; files and 22,022 lines with 287 test functions across 34 test files; the Rust repo is 63 first-party &lt;code&gt;.rs&lt;/code&gt; files and 25,387 lines with 469 test functions. Documentation is 7 markdown files in Go and 13 in Rust, four of them design documents for unbuilt features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The protocol layer is four libraries you can use on their own
&lt;/h2&gt;

&lt;p&gt;None of those fixes live inside the camera service. The ONVIF and GB28181 logic in both builds is extracted into four standalone libraries — one protocol, two languages — and all four are MIT licensed, which is a shorter licence than the Apache-2.0 the camera service itself carries. Versions and counts in this section were pulled on 2026-09-15.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    P["Your product"] --&amp;gt; O["ONVIF side"]
    P --&amp;gt; G["GB28181 side"]
    O --&amp;gt; OG["onvif-go&amp;lt;br/&amp;gt;client + server"]
    O --&amp;gt; OR["onvif-rs&amp;lt;br/&amp;gt;device server"]
    G --&amp;gt; GG["gb28181-go&amp;lt;br/&amp;gt;UAC, UAS, cascade"]
    G --&amp;gt; GR["gb28181-rs&amp;lt;br/&amp;gt;UAC"]&lt;/code&gt;&lt;/pre&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Latest tag&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;onvif-go&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;v2.0.0-rc6&lt;/td&gt;
&lt;td&gt;ONVIF client + virtual camera server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onvif-rs&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;v0.6.0&lt;/td&gt;
&lt;td&gt;ONVIF device-side server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gb28181-go&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;v0.10.0&lt;/td&gt;
&lt;td&gt;GB28181 device + platform + cascade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gb28181-rs&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;v0.11.0&lt;/td&gt;
&lt;td&gt;GB28181 device side&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;onvif-go&lt;/strong&gt; works from both ends: a client you can point at someone else's camera, and a virtual camera server for testing against. Its &lt;code&gt;go.mod&lt;/code&gt; is a module line and a Go version — zero third-party dependencies, which you can verify by opening one file. It ships prebuilt CLI tools for discovery, diagnostics, a quick-start helper and a server simulator, which is what you want when there's no hardware on the bench. Device interop is exercised against Hikvision, Dahua, Axis, Bosch and ESP32 hardware. v2 was a breaking refactor from v1, and the repo carries a MIGRATION.md.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;onvif-rs&lt;/strong&gt; is the device-side server, published on crates.io as &lt;code&gt;onvif-device-rs&lt;/code&gt; because the &lt;code&gt;onvif-rs&lt;/code&gt; name is held by an abandoned 2018 placeholder. Its response XML is byte-stable, pinned by golden-string tests, which is the property strict NVR parsers actually need. Production code contains no &lt;code&gt;unwrap&lt;/code&gt; or &lt;code&gt;expect&lt;/code&gt; and CI enforces that; auth has a UsernameToken replay guard and a lockout, and handler panics are isolated so one bad request can't take the server down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gb28181-go&lt;/strong&gt; covers the most ground: device (UAC), platform (UAS) and multi-level cascade. The cascade path has loop prevention — each channel carries an &lt;code&gt;OriginDeviceID&lt;/code&gt;, so a middle platform never hands an upper platform its own channels back, and their INVITEs get a 404. The GB/T 28181-2022 alignment lives here too: five new information queries pinned to the standard's annexes, opt-in &lt;code&gt;X-GB-Ver&lt;/code&gt; version negotiation, and a wire-format bug found by reading the spec text rather than a peer implementation (SVAC audio &lt;code&gt;stream_type&lt;/code&gt; corrected from 0x81 to 0x9B).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gb28181-rs&lt;/strong&gt; is the device side of the same thing, sharing golden wire-format contracts with its Go twin. It's what gives the Rust camera build its GB28181 capability.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Tests&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;onvif-go&lt;/td&gt;
&lt;td&gt;723 (693 tests + 25 benchmarks + 5 fuzz targets)&lt;/td&gt;
&lt;td&gt;177 &lt;code&gt;.go&lt;/code&gt; files, 55,284 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gb28181-go&lt;/td&gt;
&lt;td&gt;574 (563 + 7 + 4)&lt;/td&gt;
&lt;td&gt;161 &lt;code&gt;.go&lt;/code&gt; files, 39,695 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onvif-rs&lt;/td&gt;
&lt;td&gt;174&lt;/td&gt;
&lt;td&gt;21 &lt;code&gt;.rs&lt;/code&gt; files, 8,922 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gb28181-rs&lt;/td&gt;
&lt;td&gt;218&lt;/td&gt;
&lt;td&gt;40 &lt;code&gt;.rs&lt;/code&gt; files, 16,876 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's 1,689 tests across the four. The Go line counts include their test files: 88 files and 27,156 lines in onvif-go, 89 files and 21,114 lines in gb28181-go. The Rust crates keep test modules inline as well as under &lt;code&gt;tests/&lt;/code&gt;. Both declare MSRV 1.80; the GB 35114 feature needs 1.85 or newer.&lt;/p&gt;

&lt;p&gt;If you're building an NVR, a platform, or a device simulator, you can take the library without taking the camera service.&lt;/p&gt;

&lt;p&gt;What you should know before depending on them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;onvif-go is still a release candidate.&lt;/strong&gt; v2.0.0-rc6, and v1 to v2 was a breaking refactor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;onvif-rs covers an NVR-pairing action set.&lt;/strong&gt; Its own release notes say no GetSnapshotUri and no Events, and that compatibility with third-party clients beyond NVR-style consumers is untested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gb28181-rs is device-side only&lt;/strong&gt;, and the Go platform side doesn't implement the 2022 HTTP media plane for recorded-video search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neither GB28181 library implements subscriptions, alarm notifications, mobile-position reporting or PTZ command intake yet.&lt;/strong&gt; That's the v0.3.0 scope, and it lands in the libraries first while the camera service only wires to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All four are new.&lt;/strong&gt; onvif-go was created in July 2026, the other three in August 2026, with between 0 and 4 stars. I'm not going to make promises about maintenance cadence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it doesn't do yet
&lt;/h2&gt;

&lt;p&gt;Worth reading before you plan around it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Profile S only.&lt;/strong&gt; No Media2, no Profile T, no Recording service, no Analytics. If your NVR needs server-side recording control or two-way audio, this isn't the device for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No H.265, no WebRTC, no multi-camera.&lt;/strong&gt; All three have design documents in the Rust repo and empty feature flags in the code. You can enable them and get a log line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motion detection is half-landed.&lt;/strong&gt; The Rust build implements a frame-delta detector in &lt;code&gt;src/motion/&lt;/code&gt; but it isn't wired into the web API yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI needs firmware-side setup.&lt;/strong&gt; The ONNX runtime library and the model aren't in the release archive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No 32-bit artifact from the Rust repo at v0.2.0.&lt;/strong&gt; An upstream width bug blocked it; it's fixed upstream and lands with v0.3.0. The Go repo already ships armv7.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's new.&lt;/strong&gt; Both repos were created on 2026-09-12 and have one contributor. I'm not going to make claims about response times or maintenance cadence, and you shouldn't build a procurement decision on them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GB 35114 only goes to A-level&lt;/strong&gt;, and I have no third-party test report to point at.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frozen v0.3.0 scope is a complete GB/T 28181-2022 device-side capability package, published as a synchronized release across both implementations. Three things are explicitly out of scope and will stay that way: SVAC video and GB 35114 B/C levels, which would need SVAC hardware codecs; SIP over WebSocket, which isn't a device-side requirement in practice; and a platform-side (UAS) role, because the product is a device. The 2022 HTTP media plane for recorded-video search is deferred to v0.4.x rather than squeezed in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Go build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xiqing85/mibee-eye-go
&lt;span class="nb"&gt;cd &lt;/span&gt;mibee-eye-go
make build

&lt;span class="nb"&gt;cp &lt;/span&gt;configs/config.example.yaml config.yaml
&lt;span class="c"&gt;# set camera parameters and onvif.username / onvif.password&lt;/span&gt;

&lt;span class="nb"&gt;sudo cp &lt;/span&gt;deploy/mibee-eye.service /etc/systemd/system/
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; mibee-eye
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust build, cross-compiling from an x86 workstation to a Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xiqing85/mibee-eye-rs
&lt;span class="nb"&gt;cd &lt;/span&gt;mibee-eye-rs

&lt;span class="c"&gt;# fully static, no external toolchain needed&lt;/span&gt;
rustup target add aarch64-unknown-linux-musl
make cross-build

./deploy/install.sh pi@192.168.1.100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point your NVR at it. WS-Discovery should surface it on the local network; the manual address is &lt;code&gt;http://&amp;lt;board-ip&amp;gt;:8080/onvif/device_service&lt;/code&gt; and the stream is &lt;code&gt;rtsp://&amp;lt;board-ip&amp;gt;:8554/stream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two config defaults worth changing before you expose anything: the ONVIF password is empty by default, and &lt;code&gt;gb28181.enabled&lt;/code&gt; is off unless you turn it on. Environment variables with the &lt;code&gt;MIBEE_EYE_&lt;/code&gt; prefix override any config key, which is handy for injecting secrets from systemd rather than writing them into a file.&lt;/p&gt;

&lt;p&gt;One caveat on integrations: the ONVIF surface is the standard one, and the only NVR I have deep interop notes for is a gortsplib-based one. Synology Surveillance Station, Blue Iris and Scrypted consume standard ONVIF Profile S devices, and there's nothing custom here, but I'm not going to claim tested-and-verified for hardware I haven't run. If you try one of them and it misbehaves, that's exactly the kind of bug report the interop notebook exists to collect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project links
&lt;/h2&gt;

&lt;p&gt;mibee-eye-go: &lt;a href="https://github.com/xiqing85/mibee-eye-go" rel="noopener noreferrer"&gt;https://github.com/xiqing85/mibee-eye-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;mibee-eye-rs: &lt;a href="https://github.com/xiqing85/mibee-eye-rs" rel="noopener noreferrer"&gt;https://github.com/xiqing85/mibee-eye-rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The protocol libraries underneath:&lt;/p&gt;

&lt;p&gt;onvif-go: &lt;a href="https://github.com/mickeyzzc/onvif-go" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/onvif-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;onvif-rs: &lt;a href="https://github.com/mickeyzzc/onvif-rs" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/onvif-rs&lt;/a&gt; (published on crates.io as &lt;code&gt;onvif-device-rs&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;gb28181-go: &lt;a href="https://github.com/mickeyzzc/gb28181-go" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/gb28181-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;gb28181-rs: &lt;a href="https://github.com/mickeyzzc/gb28181-rs" rel="noopener noreferrer"&gt;https://github.com/mickeyzzc/gb28181-rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Licence: Apache-2.0 for both camera builds, MIT for the four libraries. Note that v0.1.0 shipped under CC BY-NC 4.0, which forbids commercial use, so take v0.2.0 or later; third-party terms are listed in NOTICE.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>linux</category>
      <category>opensource</category>
    </item>
    <item>
      <title>What's new in MiBee NVR v0.11.0: WHIP ingest, mDNS discovery, and browser-side AI on a Pi</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Tue, 18 Aug 2026 13:05:32 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/whats-new-in-mibee-nvr-v0110-whip-ingest-mdns-discovery-and-browser-side-ai-on-a-pi-31dj</link>
      <guid>https://dev.to/mickey_zzc/whats-new-in-mibee-nvr-v0110-whip-ingest-mdns-discovery-and-browser-side-ai-on-a-pi-31dj</guid>
      <description>&lt;p&gt;Home security cameras have a dirty secret: the software is the weak link. You buy a decent ONVIF camera and then you're locked into the vendor's app, or you sign up for a cloud service that bills per camera, or you stand up an NVR that wants a dedicated machine with a Coral TPU before it does anything interesting.&lt;/p&gt;

&lt;p&gt;I wanted the opposite. One binary I could drop on a Raspberry Pi, a web UI that doesn't feel like 2005, cameras that get discovered instead of configured, and no monthly fee. That project became MiBee NVR. It runs on a 512MB memory budget and the README's claim, which I stand by, is that a Pi 3B is the minimum baseline. The repo is at &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr" rel="noopener noreferrer"&gt;github.com/Mi-Bee-Studio/MiBeeNvr&lt;/a&gt;, current version 0.11.0.&lt;/p&gt;

&lt;p&gt;The pitch in one line: single Go binary, zero dependencies (&lt;code&gt;CGO_ENABLED=0&lt;/code&gt;), an embedded Svelte 5 SPA, no cloud, no subscriptions. Install is one &lt;code&gt;curl&lt;/code&gt; piped to &lt;code&gt;bash&lt;/code&gt;, or a prebuilt binary for amd64, arm64, or armv7, or Docker from &lt;code&gt;ghcr.io/mi-bee-studio/mibeenvr&lt;/code&gt;. The UI runs on port 9090 and comes with dark/light themes and English/Chinese out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  ONVIF that behaves
&lt;/h2&gt;

&lt;p&gt;The thing I'm proudest of is the ONVIF handling, because it fixes the pain I kept hitting with real cameras. Cameras on WiFi roam between access points and come back with a new IP. Your NVR still has the old one stored, the feed dies, and you go digging through the router's DHCP table to fix it. MiBee NVR instead enrolls ONVIF cameras automatically in the background when they join the LAN, the way a Hikvision NVR does. No manual scan.&lt;/p&gt;

&lt;p&gt;Two details I like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unauthenticated cameras start recording immediately. If it's open, it's in.&lt;/li&gt;
&lt;li&gt;Authenticated cameras are added as pending until you supply credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the IP self-healing: when a camera changes IP, the NVR relocates it by serial number and reconnects. The unicast probing works across routed subnets, so it isn't limited to one broadcast domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser-side AI, no Coral required
&lt;/h2&gt;

&lt;p&gt;Most NVR setups push detection to a separate box, often a USB Coral or an Nvidia card. MiBee NVR runs inference in the browser tab instead. ONNX Runtime Web with WebGPU acceleration, a 5.4MB &lt;code&gt;yolo11n&lt;/code&gt; model by default (squeezenet is there as a fallback). The backend does zero inference, so the Pi never breaks a sweat, and the video never leaves the device to get "analyzed" somewhere else. If your browser supports WebGPU you get object detection; if it doesn't, you still get a fully working NVR.&lt;/p&gt;

&lt;p&gt;One real constraint worth knowing: without &lt;code&gt;crossOriginIsolated&lt;/code&gt; there's no &lt;code&gt;SharedArrayBuffer&lt;/code&gt;, so the runtime is single-threaded. Fine for one camera, something to remember if you plan to run several detections at once.&lt;/p&gt;

&lt;p&gt;The war story: model files kept coming back corrupted and ORT threw &lt;code&gt;INVALID_PROTOBUF&lt;/code&gt;. Root cause was our own gzip middleware. Calling &lt;code&gt;Close()&lt;/code&gt; on a &lt;code&gt;gzip.Writer&lt;/code&gt; that never wrote anything appends an empty gzip member, about 20 bytes, to the end of binary responses. The ONNX file was fine except for those 20 bytes. The fix tracks whether the writer ever wrote and only calls &lt;code&gt;Close()&lt;/code&gt;/&lt;code&gt;Flush()&lt;/code&gt; after the first &lt;code&gt;Write()&lt;/code&gt;. That's issue #109 if you want the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every protocol, in and out
&lt;/h2&gt;

&lt;p&gt;NVR is a messy word because it covers two directions: getting streams in and getting streams out.&lt;/p&gt;

&lt;p&gt;Inbound, MiBee NVR speaks RTSP (H.264, H.265, MJPEG), HTTP JPEG snapshots, RTMP ingest, SRT ingest, and WHIP, which is WebRTC push. The WHIP support is the headline feature of v0.11.0 and it's fun: you can publish from a browser tab or OBS with a stream key mapped to a camera, no extra software.&lt;/p&gt;

&lt;p&gt;Outbound to your browser: WebRTC via WHEP for sub-second latency, HLS with LL-HLS for on-demand, HTTP-FLV, and raw WebSocket frames. If you need to push somewhere else, there's a native Go relay that forwards any camera to a remote RTMP or RTSP target. Point it at a streaming platform and you have a cheap live production pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording, the part people forget
&lt;/h2&gt;

&lt;p&gt;An NVR that can't scrub is a paperweight. Recordings are MP4 segments with per-camera retention policies, audio capture (AAC, G.711, Opus), and a timelapse mode. Playback chains segments with double buffering so there's no gap when you're watching back, and the VOD timeline covers a full day, so you can drag across recordings and the gaps between them. There's also an AVI frame-browse mode when you need to step through frame by frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for the homelab, not around it
&lt;/h2&gt;

&lt;p&gt;This was a design goal, so the integration list is deliberately boring: MQTT for trigger-based recording (Home Assistant people know what to do), WebDAV and FTP for pulling files off the box, a Prometheus metrics endpoint with full docs, a REST API with BasicAuth and bcrypt, and a stable &lt;code&gt;device_id&lt;/code&gt; in &lt;code&gt;/api/health&lt;/code&gt; so your automation has something to hold onto. Discovery is mDNS (&lt;code&gt;_mibee-nvr._tcp&lt;/code&gt;) with a UDP responder as fallback for networks that block multicast.&lt;/p&gt;

&lt;p&gt;Remote access is your choice: Tailscale, Cloudflare Tunnel, or WebRTC ICE/STUN/TURN configuration. The docs cover all three.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment story
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One-liner: &lt;code&gt;curl -fsSL https://raw.githubusercontent.com/Mi-Bee-Studio/MiBeeNvr/main/install.sh | sudo bash&lt;/code&gt;, which creates a systemd service, a system user, and a config.&lt;/li&gt;
&lt;li&gt;Binaries for amd64, arm64, and armv7 from GitHub Releases.&lt;/li&gt;
&lt;li&gt;Docker images on GHCR with exact, minor, major, and &lt;code&gt;latest&lt;/code&gt; tags.&lt;/li&gt;
&lt;li&gt;NAS guides for unRAID, Synology, and QNAP, plus fnOS, iStoreOS, and 极空间 if you're in the Chinese NAS ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The China-market side, in one paragraph
&lt;/h2&gt;

&lt;p&gt;MiBee NVR also speaks GB/T 28181, the Chinese national standard for video surveillance, as an experimental SIP platform, and it can adopt Xiaomi cloud cameras (CS2 and legacy TUTK models). If you're not in China, ignore both of those; they exist because the home market runs on them. Everything else in this post is the same code path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering hygiene and licensing
&lt;/h2&gt;

&lt;p&gt;The repo has 3991 Go tests, 389 frontend tests, and 13 Playwright E2E tests, with golangci-lint v2 (gofumpt, perfsprint) enforced in CI. One honest note on licensing: versions up to v0.10.1 are MIT. From v0.11.0 the project moved to AGPL-3.0-only, with a linking exception for &lt;code&gt;pkg/&lt;/code&gt; so you can build against the extension interfaces without triggering AGPL obligations. Running it, recording cameras, watching streams: no obligations, commercial use included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Mi-Bee-Studio/MiBeeNvr/main/install.sh | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or grab a binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/Mi-Bee-Studio/MiBeeNvr/releases/latest/download/mibee-nvr-arm64
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x mibee-nvr-arm64
./mibee-nvr-arm64 init &lt;span class="nt"&gt;--password&lt;/span&gt; yourpassword
./mibee-nvr-arm64 &lt;span class="nt"&gt;-config&lt;/span&gt; mibee-nvr.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://localhost:9090&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There's documentation for everything mentioned here: getting started, a full configuration reference, the REST API, an ONVIF guide, transcoding, metrics, and per-NAS deployment. The architecture doc is a good place to start if you want to read the code with a map.&lt;/p&gt;

&lt;p&gt;If you run it and something breaks, the issues tracker is open. I read everything, and the WHIP ingest that just landed came straight out of user requests.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>opensource</category>
      <category>raspberrypi</category>
      <category>software</category>
    </item>
    <item>
      <title>Your Cameras Can Now Go Live — MiBeeNVR v0.8.1 Brings Native Live Streaming</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Fri, 03 Jul 2026 06:05:11 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/your-cameras-can-now-go-live-mibeenvr-v081-brings-native-live-streaming-7k1</link>
      <guid>https://dev.to/mickey_zzc/your-cameras-can-now-go-live-mibeenvr-v081-brings-native-live-streaming-7k1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;An open-source NVR running on a Raspberry Pi, competing with OBS for live streaming work? This post covers how we took the "surveillance camera → live stream" pipeline from functional to polished — and how many protocol-layer traps we hit along the way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhw0ox3ga8j2qnzsmnkm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhw0ox3ga8j2qnzsmnkm0.png" alt=" " width="315" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Above: Live stream viewed on the mobile app. Relay handled natively by MiBeeNVR's Go implementation — no FFmpeg dependency.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MiBeeNVR v0.8.1 supports pushing any connected camera feed directly to live streaming platforms via RTMP.&lt;/strong&gt; Pure Go implementation — no FFmpeg installation, no external processes. Configure your stream URL and go live.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The author has only verified streaming on the Douyu platform so far. Other platforms are theoretically compatible (all implement the FMS standard), but remain untested. If you run into issues, please report them on &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Runs on Raspberry Pi 3B and above. Supports both H.264 and H.265 cameras.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is MiBeeNVR?
&lt;/h2&gt;

&lt;p&gt;MiBeeNVR is an open-source Network Video Recorder (NVR) written in Go, optimized for embedded devices like the Raspberry Pi and Banana Pi. Prior to v0.8.1, it already shipped with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-camera management and recording (RTSP / ONVIF / Xiaomi dual-camera)&lt;/li&gt;
&lt;li&gt;H.264/H.265 hardware transcoding&lt;/li&gt;
&lt;li&gt;Time-lapse recording and synthesis&lt;/li&gt;
&lt;li&gt;LL-HLS / WebRTC low-latency playback&lt;/li&gt;
&lt;li&gt;One-command Docker deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One thing was missing, though: live streaming.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The community kept asking: "Can we push camera feeds directly to a live stream?" Pet owners wanted pet live streams. Security folks wanted public surveillance channels. Hardware tinkerers just wanted to play. v0.8.1 fills that gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Live Streaming: Looks Simple, Hurts Plenty
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Make It Work First
&lt;/h3&gt;

&lt;p&gt;The most straightforward approach is to call FFmpeg — it's the Swiss Army knife of the streaming industry, and OBS uses it under the hood. Configure an RTMP target in MiBeeNVR's "relay output," spin up an FFmpeg subprocess on the backend, and the feed does reach the live stream.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkimk8wzsphnvrbfdd5aq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkimk8wzsphnvrbfdd5aq.png" alt=" " width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But here's the problem: &lt;strong&gt;FFmpeg is an external process.&lt;/strong&gt; It needs separate installation, separate lifecycle management, and extra memory. For a project aiming to "run lightweight on a Raspberry Pi," dragging along a multi-megabyte external process for streaming isn't elegant.&lt;/p&gt;

&lt;p&gt;What we wanted: &lt;strong&gt;a pure Go RTMP streaming implementation, embedded in the NVR process, with zero external dependencies.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Six Layers of Hell in Pure Go Streaming
&lt;/h3&gt;

&lt;p&gt;The RTMP protocol looks simple — handshake, connect, push data. But when you actually interface with live streaming platforms (whose backends are all Adobe FMS-compatible implementations), you'll find that every single layer can block you.&lt;/p&gt;

&lt;p&gt;We didn't encounter one problem — we encountered &lt;strong&gt;six stacked problems&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["L1 Handshake digest&amp;lt;br/&amp;gt;HMAC-SHA256 signing"] --&amp;gt; B["L2 Chunk size&amp;lt;br/&amp;gt;mismatched block size"]
    B --&amp;gt; C["L3 Chunk header&amp;lt;br/&amp;gt;Type 0/1/2/3 format"]
    C --&amp;gt; D["L4 Background read&amp;lt;br/&amp;gt;buffer overflow"]
    D --&amp;gt; E["L5 Metadata fields&amp;lt;br/&amp;gt;missing width/height/fps"]
    E --&amp;gt; F["L6 StreamID byte order&amp;lt;br/&amp;gt;big-endian vs little-endian ⭐"]
    F --&amp;gt; G["✅ Stream success"]

    style A fill:#FF9800,color:#fff
    style B fill:#FF9800,color:#fff
    style C fill:#FF9800,color:#fff
    style D fill:#FF9800,color:#fff
    style E fill:#FF9800,color:#fff
    style F fill:#f44336,color:#fff
    style G fill:#4CAF50,color:#fff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After fixing the first four layers — handshake passing, connection established, publish command accepted — &lt;strong&gt;the first video frame would be sent, and 74 milliseconds later, the connection would be reset by the server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That 74ms is critical: it rules out a network timeout (those take ~5 seconds). The server was rejecting the data at the frame level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Boss: A Tale of Byte Order
&lt;/h3&gt;

&lt;p&gt;The root cause was buried so deep that it took us a full day to find.&lt;/p&gt;

&lt;p&gt;The RTMP protocol has a field called &lt;code&gt;MessageStreamID&lt;/code&gt; that identifies which stream a message belongs to. Per the RTMP spec, this field should be &lt;strong&gt;little-endian&lt;/strong&gt;. So we dutifully wrote it as little-endian in our code, exactly as the spec says.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But every major implementation — FFmpeg, OBS, gortmplib — uses big-endian.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And so do the live streaming platforms.&lt;/p&gt;

&lt;p&gt;Our code used the third-party library's standard path (big-endian) for most messages, but our own custom write path used little-endian for others. &lt;strong&gt;On the same connection — sometimes big-endian, sometimes little-endian. The server received this schizophrenic packet stream and immediately sent an RST.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We found the bug through an in-process byte-level dump — logging every byte sent, parsing each chunk header one by one — until we spotted the endianness inconsistency in the streamID field. The fix was 4 lines of code. Finding it took an entire day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A["Go standard Writer&amp;lt;br/&amp;gt;streamID = big-endian"] --&amp;gt; C["Live platform&amp;lt;br/&amp;gt;expects big-endian ✅"]
    B["Our Writer&amp;lt;br/&amp;gt;streamID = little-endian"] --&amp;gt; D["Live platform&amp;lt;br/&amp;gt;receives little-endian ❌ RST"]

    style A fill:#4CAF50,color:#fff
    style B fill:#f44336,color:#fff
    style C fill:#4CAF50,color:#fff
    style D fill:#f44336,color:#fff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;TL;DR: &lt;strong&gt;The spec says go left. The entire world goes right. You follow the spec — and the entire world rejects you.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;With all six layers fixed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Pure Go relay streaming works — no FFmpeg dependency&lt;/li&gt;
&lt;li&gt;✅ TCP connection stays stable, continuous streaming without interruption&lt;/li&gt;
&lt;li&gt;✅ Raspberry Pi camera feeds push to live streams in real-time, ~2-3 second latency&lt;/li&gt;
&lt;li&gt;✅ H.264 cameras supported; H.265 streaming coming soon&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Do not enable the &lt;code&gt;use_ffmpeg&lt;/code&gt; option.&lt;/strong&gt; FFmpeg-based streaming has known issues: playback may stutter mid-stream due to frame rate mismatch, and it won't auto-recover. The pure Go relay path is thoroughly validated and is the recommended streaming method.&lt;/p&gt;

&lt;p&gt;💡 The pure Go relay is the focus of this release. If you encounter Go streaming issues on other platforms, &lt;strong&gt;please prioritize filing an Issue about the Go path&lt;/strong&gt; to help the project keep improving — rather than falling back to FFmpeg.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: MiBeeNVR UI Configuration
&lt;/h3&gt;

&lt;p&gt;In the camera settings, find "Relay Output" and enter your live streaming platform's RTMP URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rtmp://&amp;lt;server&amp;gt;/live/&amp;lt;stream-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the stream key from your live streaming platform's broadcast settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Docker Deployment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mibee-nvr &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mibee-nvr-data:/var/lib/mibee-nvr &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/mi-bee-studio/mibee-nvr:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://&amp;lt;device-ip&amp;gt;:8080&lt;/code&gt; to access the admin interface, add your cameras, then configure relay output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Status &amp;amp; Caveats
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tested platform&lt;/strong&gt;: Douyu Live (companion app mode, LAN streaming) ✅&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theoretically compatible&lt;/strong&gt;: All FMS-standard live streaming backends. However, implementation details vary across platforms — untested ones may present issues. &lt;strong&gt;If you hit problems, please report them on &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;&lt;/strong&gt; and the author will follow up.&lt;/p&gt;

&lt;h4&gt;
  
  
  ⚠️ Important Notice
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Continuous long-term streaming to domestic live platforms is not recommended.&lt;/strong&gt; Each platform has its own streaming policies and restrictions. Prolonged unattended camera streaming may trigger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Throttling (reduced bitrate/resolution)&lt;/li&gt;
&lt;li&gt;Stream key expiration leading to disconnection&lt;/li&gt;
&lt;li&gt;Account warnings or even bans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure to understand your platform's policies and follow their rules. MiBeeNVR provides a technical capability — &lt;strong&gt;how you use it, on which platform, and what you stream is your own responsibility.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This live streaming project left us with three key takeaways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Comparative Experimentation Is the Gold Standard for Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the Go code failed to stream, we couldn't tell if it was a code issue or an environment issue. Only after manually streaming via FFmpeg CLI on the same device and succeeding did we confirm the problem was in the Go protocol layer. Without that control experiment, all the guessing in the world is meaningless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Binary Protocol Bugs Require Looking at Bytes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Byte-order issues are invisible when reading code — because the code "looks correct" (it follows the spec). Only by dumping the actual bytes sent over the wire and parsing them one by one can you spot the endianness mismatch. &lt;strong&gt;When debugging binary protocols: add a dump first, analyze second.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Spec ≠ Reality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The RTMP spec says &lt;code&gt;MessageStreamID&lt;/code&gt; is little-endian, but the entire world uses big-endian. This kind of "spec vs. practice divergence" is extremely common in legacy protocols. When your spec-compliant code hits walls everywhere, go look at how others actually implemented it — &lt;strong&gt;de facto standards are more persuasive than paper specifications.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  About MiBeeNVR
&lt;/h2&gt;

&lt;p&gt;MiBeeNVR is an open-source project by &lt;a href="https://github.com/Mi-Bee-Studio" rel="noopener noreferrer"&gt;Mi-Bee-Studio&lt;/a&gt;, written in Go and optimized for embedded devices like the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/MiBeeNvr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-camera management (RTSP / ONVIF / Xiaomi dual-camera)&lt;/li&gt;
&lt;li&gt;H.264/H.265 hardware transcoding (V4L2 / VAAPI / NVENC auto-detection)&lt;/li&gt;
&lt;li&gt;Time-lapse recording and synthesis (pure Go muxer, zero encoding overhead)&lt;/li&gt;
&lt;li&gt;LL-HLS / WebRTC low-latency playback&lt;/li&gt;
&lt;li&gt;RTMP live streaming (new in v0.8.1, pure Go)&lt;/li&gt;
&lt;li&gt;One-command Docker deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Supported Platforms&lt;/strong&gt;: Raspberry Pi 3B+ and above / x86_64 / ARM64 / Docker&lt;/p&gt;




&lt;p&gt;&lt;em&gt;MiBeeNVR v0.8.1 has been released. See the full changelog at &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr/releases" rel="noopener noreferrer"&gt;GitHub Release Notes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're building interesting projects with Raspberry Pi, come chat with us on GitHub.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Embedded Camera Module Selection Handbook — From Basics to Purchasing Reference</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Mon, 29 Jun 2026 23:07:58 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/embedded-camera-module-selection-handbook-from-basics-to-purchasing-reference-3g1i</link>
      <guid>https://dev.to/mickey_zzc/embedded-camera-module-selection-handbook-from-basics-to-purchasing-reference-3g1i</guid>
      <description>&lt;h2&gt;
  
  
  About This Handbook
&lt;/h2&gt;

&lt;p&gt;This handbook is a revised edition of my own "Embedded Camera Module Technical Manual" that I previously compiled and shared online. The original covered eight mainstream sensors: OV2640, OV5640, OV7725, GC0309, OV7670, OV3660, GC2053, and GC4653. This revision expands coverage with 12 additional sensors (OV9281, OV5647, IMX219, IMX273, IMX296, IMX307, IMX335, IMX415, IMX477, IMX678, IMX708, IMX766), for a total of 20. The content was comprehensive, but when I sat down to use it as a purchasing reference, I felt uneasy — a single wrong voltage parameter can fry a board, and I didn't want to ship mistakes I couldn't vouch for.&lt;/p&gt;

&lt;p&gt;So I did one thing: &lt;strong&gt;verified every key parameter against official datasheets&lt;/strong&gt;. Sources include OmniVision's official product pages, GalaxyCore datasheets, SparkFun / DigiKey / Mouser datasheet mirrors, and Espressif's official esp32-camera driver source code.&lt;/p&gt;

&lt;p&gt;The result: &lt;strong&gt;most parameters were correct, but there were several critical errors&lt;/strong&gt;, especially around voltage, frame rates, and some marketing terminology. This handbook is the corrected version — every error-prone parameter is annotated with reliability status and evidence. Use it as a purchasing checklist with confidence.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reliability annotation legend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Verified: Parameter matches official datasheet exactly&lt;/li&gt;
&lt;li&gt;⚠️ Note: Original had errors or was misleading; corrected with evidence&lt;/li&gt;
&lt;li&gt;🔍 Unconfirmed: Official datasheet unclear, or varies by module manufacturer&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick Selection Guide
&lt;/h2&gt;

&lt;p&gt;Start with this table to find your use case, then read the details below.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Top Pick&lt;/th&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;th&gt;Key Advantage&lt;/th&gt;
&lt;th&gt;Price Tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Education (Arduino/STM32)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OV7670&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;Richest ecosystem, most tutorials&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wi-Fi streaming (ESP32-CAM)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OV2640&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;Built-in JPEG compression&lt;/td&gt;
&lt;td&gt;Low-mid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-res photo + autofocus&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OV5640&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5MP&lt;/td&gt;
&lt;td&gt;DVP/MIPI&lt;/td&gt;
&lt;td&gt;Only entry-level with AF&lt;/td&gt;
&lt;td&gt;Mid-high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Night vision / machine vision&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OV7725&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;6.0µm pixels, low-light king&lt;/td&gt;
&lt;td&gt;Mid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra low cost (access/POS)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;GC0309&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;Lowest price&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3MP upgrade (ESP32)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OV3660&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3MP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;BSI tech, drop-in for OV2640&lt;/td&gt;
&lt;td&gt;Low-mid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security / surveillance&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;GC2053&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;DVP/MIPI&lt;/td&gt;
&lt;td&gt;1/2.9" sensor + 2.8µm pixels&lt;/td&gt;
&lt;td&gt;Mid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-end security / AI vision&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;GC4653&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4MP&lt;/td&gt;
&lt;td&gt;MIPI only&lt;/td&gt;
&lt;td&gt;4MP + BSI high sensitivity&lt;/td&gt;
&lt;td&gt;Mid-high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raspberry Pi / Jetson entry&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IMX219&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8MP&lt;/td&gt;
&lt;td&gt;MIPI 2L&lt;/td&gt;
&lt;td&gt;Pi V2 standard, most mature driver&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4K security / NVR&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IMX415&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8MP&lt;/td&gt;
&lt;td&gt;MIPI 2/4L&lt;/td&gt;
&lt;td&gt;4K STARVIS, rich module ecosystem&lt;/td&gt;
&lt;td&gt;Mid-high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-end 4K security (2024)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IMX678&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8MP&lt;/td&gt;
&lt;td&gt;MIPI 2/4/8L&lt;/td&gt;
&lt;td&gt;STARVIS 2 + Clear HDR&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1080P night security&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IMX307&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;MIPI 2/4L&lt;/td&gt;
&lt;td&gt;2.9µm + STARVIS, strong night vision&lt;/td&gt;
&lt;td&gt;Mid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Industrial FA / global shutter&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;IMX273&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.58MP&lt;/td&gt;
&lt;td&gt;MIPI 1L&lt;/td&gt;
&lt;td&gt;Pregius global shutter, 226fps&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;ESP32 users note:&lt;/strong&gt; ESP32 series only supports DVP-interface sensors. &lt;strong&gt;GC4653 is MIPI-only and cannot be used with ESP32&lt;/strong&gt;. OV3660 also only supports DVP (claims of MIPI support in the original document are wrong — see below).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  DVP Interface Basics
&lt;/h2&gt;

&lt;p&gt;DVP (Digital Video Port) transmits uncompressed raw image data via parallel data lines with a sync clock. Understanding DVP signals and timing is prerequisite for wiring and debugging all DVP modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal Definitions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Typical Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;XCLK&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;External master clock, typically 6–24 MHz ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCLK&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Pixel clock for synchronous data sampling ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VSYNC&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Frame sync signal ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HREF / HSYNC&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Line sync signal ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D[7:0]&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;8-bit parallel pixel data bus ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIOC / SCL&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;SCCB / I²C clock ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIOD / SDA&lt;/td&gt;
&lt;td&gt;Bidirectional&lt;/td&gt;
&lt;td&gt;SCCB / I²C data ✅&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PWDN&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;Power-down control&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RESET&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;Hardware reset (active low)&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Timing
&lt;/h3&gt;

&lt;p&gt;A complete frame is bounded by VSYNC. Within a frame, multiple HREF pulses occur, each corresponding to one line of valid pixel data. When both VSYNC and HREF are active, D[7:0] data latched on PCLK rising edge is valid pixel data.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
The red scan line sweeps left to right. When VSYNC + HREF are both HIGH, each PCLK rising edge latches one pixel byte (D0, D1, D2…).





&lt;h3&gt;
  
  
  SCCB Control Interface
&lt;/h3&gt;

&lt;p&gt;SCCB (Serial Camera Control Bus) is OmniVision's I²C-compatible 2-wire serial protocol. SCL clock frequency supports up to 400 kHz ✅ (OV2640 datasheet Table 9 confirms). If your MCU lacks internal pull-ups, add 4.7 kΩ pull-up resistors on SCL and SDA to 3.3V ✅.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;I²C addresses are sensor-specific, NOT uniform 0x30/0x60!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many online sources casually claim "OV series address is 0x30/0x60" — this is wrong. Verified 7-bit slave addresses per sensor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sensor&lt;/th&gt;
&lt;th&gt;7-bit Address&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OV2640&lt;/td&gt;
&lt;td&gt;0x30&lt;/td&gt;
&lt;td&gt;Datasheet: 8-bit write 0x60 / read 0x61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OV5640&lt;/td&gt;
&lt;td&gt;0x3C&lt;/td&gt;
&lt;td&gt;Datasheet: 8-bit write 0x78 / read 0x79&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OV7725&lt;/td&gt;
&lt;td&gt;0x21&lt;/td&gt;
&lt;td&gt;Datasheet: 8-bit write 0x42 / read 0x43&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OV7670&lt;/td&gt;
&lt;td&gt;0x21&lt;/td&gt;
&lt;td&gt;Linux kernel driver ov7670.c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC series&lt;/td&gt;
&lt;td&gt;Vendor-defined&lt;/td&gt;
&lt;td&gt;Check specific datasheet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If SCCB configuration fails, &lt;strong&gt;verify I²C address first&lt;/strong&gt;, not just pull-ups.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  MIPI CSI-2 Interface Basics
&lt;/h2&gt;

&lt;p&gt;When resolution and frame rate go up (1080P@30fps+), DVP's 8-bit parallel bus can't keep up — too many wires, limited speed, poor noise immunity. All Sony IMX sensors below use MIPI CSI-2. Here's the foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  DVP vs MIPI CSI-2
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;DVP&lt;/th&gt;
&lt;th&gt;MIPI CSI-2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data lines&lt;/td&gt;
&lt;td&gt;8–10 parallel + sync signals&lt;/td&gt;
&lt;td&gt;1–4 differential pairs (lanes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clock&lt;/td&gt;
&lt;td&gt;Single-ended XCLK / PCLK&lt;/td&gt;
&lt;td&gt;Differential clock lane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth&lt;/td&gt;
&lt;td&gt;≤ ~200 MB/s&lt;/td&gt;
&lt;td&gt;Up to ~2.5 GB/s (4-lane)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Noise immunity&lt;/td&gt;
&lt;td&gt;Poor, long cables cause artifacts&lt;/td&gt;
&lt;td&gt;Good, differential rejects common-mode noise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controller complexity&lt;/td&gt;
&lt;td&gt;Low (straight to FIFO)&lt;/td&gt;
&lt;td&gt;High (needs D-PHY + CSI-2 controller)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical controllers&lt;/td&gt;
&lt;td&gt;ESP32, STM32, RP2040&lt;/td&gt;
&lt;td&gt;Raspberry Pi, Jetson, Hi3516, RV1106&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical sensors&lt;/td&gt;
&lt;td&gt;OV2640, OV5640, OV7670&lt;/td&gt;
&lt;td&gt;IMX219, IMX415, IMX678&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lane Count and Bandwidth
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lanes&lt;/th&gt;
&lt;th&gt;Total Bandwidth&lt;/th&gt;
&lt;th&gt;Suitable For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-lane&lt;/td&gt;
&lt;td&gt;~500 MB/s&lt;/td&gt;
&lt;td&gt;1080P@30fps RAW8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2-lane&lt;/td&gt;
&lt;td&gt;~1 GB/s&lt;/td&gt;
&lt;td&gt;1080P@60fps or 4K@15fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4-lane&lt;/td&gt;
&lt;td&gt;~2 GB/s&lt;/td&gt;
&lt;td&gt;4K@30fps+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Purchasing note:&lt;/strong&gt; More lanes isn't always better — your controller's MIPI receiver must support the lane count. For example, RV1106 supports 2-lane/4-lane MIPI input; connecting a 1-lane IMX273 may not work. Check controller specs before buying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESP32 users note:&lt;/strong&gt; ESP32 (including ESP32-S3) has &lt;strong&gt;no MIPI CSI-2 receiver&lt;/strong&gt;. None of the Sony IMX sensors below work with ESP32. You need a MIPI-capable controller like Raspberry Pi, Jetson, RV1106, Hi3516, etc.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Sensor Technology Principles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Shutter Types: Rolling vs Global
&lt;/h3&gt;

&lt;p&gt;Most sensors in this handbook use &lt;strong&gt;rolling shutter&lt;/strong&gt; — exposing rows sequentially with a time gap between rows. Fast-moving objects appear skewed/warped (&lt;strong&gt;jello effect&lt;/strong&gt;). Only IMX273, OV9281, and IMX296 use &lt;strong&gt;global shutter&lt;/strong&gt; — the entire frame is exposed simultaneously, zero motion distortion.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
A blue ball moves left to right. Rolling shutter exposes row-by-row, capturing the ball at different positions per row → diagonal skew. Global shutter exposes all rows simultaneously → clean bar.





&lt;h3&gt;
  
  
  Backside Illumination (BSI) vs Frontside Illumination (FSI)
&lt;/h3&gt;

&lt;p&gt;Traditional CMOS sensors (FSI) have metal wiring above the photodiode, blocking some light. &lt;strong&gt;BSI (Backside Illumination)&lt;/strong&gt; flips the sensor so light enters from the back, reaching the photodiode directly — &lt;strong&gt;30%–60% more light&lt;/strong&gt;. STARVIS, Exmor R, and OmniBSI all use BSI technology.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
Yellow dots are photons. FSI's wiring blocks ~50% of light. BSI flips the structure so all photons reach the sensor.





&lt;h3&gt;
  
  
  HDR Multi-Exposure Fusion
&lt;/h3&gt;

&lt;p&gt;In backlit scenes, short exposure captures highlights (sky) but shadows are black; long exposure captures shadows but highlights are blown. HDR merges multiple exposures into one image, &lt;strong&gt;preserving both highlight and shadow detail&lt;/strong&gt;. IMX307/IMX335/IMX678 all support hardware-level HDR.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
Short exposure preserves highlights, long exposure preserves shadows — merged HDR significantly expands dynamic range. Frames appear sequentially to show the fusion process.





&lt;h3&gt;
  
  
  Quad Bayer Pixel Binning
&lt;/h3&gt;

&lt;p&gt;High-resolution sensors (e.g., IMX766 50MP) have tiny pixels (1.0µm) with insufficient light per pixel. &lt;strong&gt;Quad Bayer&lt;/strong&gt; groups 4 adjacent same-color pixels, reading them as 1 larger pixel — 4× area, effective pixel size doubles (1.0µm → 2.0µm), dramatically improving low-light performance. Full resolution by day, auto-binned by night.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
4 pixels of 1.0µm merge into 1 pixel of 2.0µm. 4× area = 4× light = significantly better SNR.








&lt;h2&gt;
  
  
  OV2640 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision 1/4-inch, 2MP CMOS sensor with built-in JPEG compression engine. The standard choice for ESP32-CAM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV2640&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;2MP (1600 × 1200)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.2 µm × 2.2 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP 8-bit parallel + SCCB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;YUV422, YUV420, RGB565, RGB555, RAW RGB, JPEG&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;UXGA@15fps, SVGA@30fps, CIF@60fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Master clock XCLK&lt;/td&gt;
&lt;td&gt;6 – 24 MHz (typical 24 MHz)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;Analog 2.5–3.0V, I/O 1.7–3.3V, Core 1.2/1.3V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;~125 mW (15fps UXGA YUV), ~140 mW (JPEG mode)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Official OmniVision OV2640 datasheet (uctronics.com/download/OV2640_DS.pdf)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Resolution &amp;amp; Frame Rate
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Typical fps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UXGA&lt;/td&gt;
&lt;td&gt;1600 × 1200&lt;/td&gt;
&lt;td&gt;15 fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SXGA&lt;/td&gt;
&lt;td&gt;1280 × 1024&lt;/td&gt;
&lt;td&gt;15 fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVGA&lt;/td&gt;
&lt;td&gt;800 × 600&lt;/td&gt;
&lt;td&gt;30 fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VGA&lt;/td&gt;
&lt;td&gt;640 × 480&lt;/td&gt;
&lt;td&gt;30 fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QVGA&lt;/td&gt;
&lt;td&gt;320 × 240&lt;/td&gt;
&lt;td&gt;60 fps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Typical Pinout (24-PIN FPC)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Verified: OV2640 modules do use 24-pin 0.5mm pitch FPC (confirmed by OV2640FSL datasheet, Arducam module spec). However, &lt;strong&gt;pin order may vary by module manufacturer&lt;/strong&gt; — always check your specific board's datasheet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pin&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Pin&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;D3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;VCC (3.3V)&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;D4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;D5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PWDN&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;D6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;RESET&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;D7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;XCLK&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;NC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;VSYNC&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;SIOC (SCL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;HREF&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;SIOD (SDA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;PCLK&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;NC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;D0&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;NC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;D1&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;NC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;D2&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;NC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; OV2640's built-in JPEG compression outputs compressed streams directly, significantly reducing MCU RAM and bus bandwidth requirements. Best price/performance for ESP32-CAM.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV5640 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision 1/4-inch, 5MP CMOS sensor with integrated autofocus (AF) voice coil motor driver. The image quality ceiling for entry-level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV5640&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;5MP (2592 × 1944)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.4 µm × 1.4 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP 8/10-bit OR MIPI CSI-2 (2-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;RAW RGB, RGB565/555/444, YCbCr422, JPEG&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;QSXGA@15fps, 1080P@30fps, 720P@60fps, VGA@90fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Master clock XCLK&lt;/td&gt;
&lt;td&gt;6 – 27 MHz (typical 24 MHz)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autofocus&lt;/td&gt;
&lt;td&gt;Integrated VCM driver, contrast-detect AF&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features&lt;/td&gt;
&lt;td&gt;OmniBSI, HDR, AE/AG/AWB, 2D NR, lens correction, anti-shake&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current&lt;/td&gt;
&lt;td&gt;~140 mA (active), 20 µA (standby)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OV5640 datasheet v2.03 (cdn.sparkfun.com), OV5640 Product Brief (media.digikey.com)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface note:&lt;/strong&gt; OV5640 supports both DVP and MIPI CSI-2. Cheap modules typically use DVP 8-bit parallel. MIPI versions are usually for phone/tablet designs. &lt;strong&gt;For AF versions, verify AF_VCC supply&lt;/strong&gt; — the VCM motor needs separate power.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV7725 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision high-performance 1/4-inch VGA sensor using OmniPixel² technology, focused on high sensitivity, low noise, and low-light performance. At 6.0 µm pixel size, it's the strongest low-light performer in the 0.3MP class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV7725&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;0.3MP (640 × 480)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.0 µm × 6.0 µm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP 8/10-bit parallel + SCCB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;RAW RGB, RGB565/555/444, YCbCr422&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;VGA@60fps, QVGA@120fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating temp&lt;/td&gt;
&lt;td&gt;-20°C to +70°C&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensitivity&lt;/td&gt;
&lt;td&gt;3800 mV/Lux-sec (extremely high)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic range&lt;/td&gt;
&lt;td&gt;60 dB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features&lt;/td&gt;
&lt;td&gt;OmniPixel², high sensitivity low-light, AE/AG/AWB, edge enhancement, noise suppression&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OV7725 official product page (ovt.com/products/ov7725), OV7725 datasheet&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;EOL notice:&lt;/strong&gt; OV7725 is marked End-of-Life on OmniVision's website. For new production designs, evaluate alternatives. But surplus/secondhand stock remains plentiful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is 6.0µm so big?&lt;/strong&gt; Both OV7670 and OV7725 are 0.3MP, but OV7670 pixels are only 3.6µm while OV7725 reaches 6.0µm — nearly 3x the pixel area, meaning 3x the light gathering. This is why it crushes same-resolution competitors in low light. For night vision, license plate recognition, or industrial inspection, this is the one.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  GC0309-8225N V2 Module
&lt;/h2&gt;

&lt;p&gt;Based on GalaxyCore GC0309, a 0.3MP fixed-focus module with DVP interface, focused on ultra-high cost-effectiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;GalaxyCore GC0309&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/9 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;0.3MP (640 × 480, actual 648×488)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.5 µm × 2.5 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel structure&lt;/td&gt;
&lt;td&gt;4-transistor (4T), low noise&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP 8-bit parallel&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;YCbCr422, RGB565, RAW Bayer&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;VGA@30fps (24MHz clock)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.8V typical (2.7–3.3V range)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lens&lt;/td&gt;
&lt;td&gt;Fixed focus, typical 62° FOV&lt;/td&gt;
&lt;td&gt;🔍 module-vendor defined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features&lt;/td&gt;
&lt;td&gt;On-chip 10-bit ADC, embedded ISP, AE, AWB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: GC0309 official datasheet V1.0 (2009-12-28, GalaxyCore Inc.)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Voltage correction:&lt;/strong&gt; The original document's "3.3V single supply" is inaccurate. The official datasheet explicitly states "Single power supply requirement (2.8V)", typical 2.8V, range 2.7–3.3V. &lt;strong&gt;Recommended supply is 2.8V LDO&lt;/strong&gt; — 3.3V is within range but not the optimal operating point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; GC0309-8225N V2 is a complete module model customized by module vendors based on the GC0309 sensor. Pin definitions, lens parameters, and FPC interfaces may vary between vendors. Always refer to your specific module's datasheet.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV7670 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision's classic 1/6-inch VGA sensor — the most historically significant and ecosystem-mature sensor in embedded vision. The de facto standard for Arduino, STM32, and FPGA education.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV7670&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/6 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;0.3MP (640 × 480)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;3.6 µm × 3.6 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP 8-bit parallel + SCCB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;YUV422, RGB565, RGB555, RAW RGB, Processed Bayer&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;VGA@30fps, QVGA@60fps, CIF@60fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Master clock XCLK&lt;/td&gt;
&lt;td&gt;10 – 24 MHz (typical 24 MHz)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;~60 mW (15fps VGA YUV), standby &amp;lt;20 µA&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OV7670 official datasheet (w2.electrodragon.com/Chip-dat/OmniVision-dat/OV7670-dat/OV7670-ds.pdf)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Voltage correction (important):&lt;/strong&gt; The original's "2.5–3.0V single voltage" is simplified and misleading. OV7670 actually requires &lt;strong&gt;three separate supply domains&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Supply Domain&lt;/th&gt;
&lt;th&gt;Voltage Range&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DVDD (digital core)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.8V&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires dedicated LDO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AVDD (analog)&lt;/td&gt;
&lt;td&gt;2.45–3.0V&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOVDD (I/O)&lt;/td&gt;
&lt;td&gt;1.7–3.0V&lt;/td&gt;
&lt;td&gt;Can connect to 3.3V&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Purchasing note: &lt;strong&gt;Cheap non-FIFO modules typically only expose DOVDD&lt;/strong&gt; — DVDD and AVDD are stepped down via on-board LDOs. But bare minimum boards may require external regulation. Non-FIFO versions are extremely demanding on MCU real-time performance; with STM32, pair with AL422B FIFO buffer chip.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV3660 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision 3MP (2048×1536) sensor using OmniBSI backside-illumination technology. Natively supported by ESP32's esp32-camera library.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Multiple corrections in this section — read carefully.&lt;/strong&gt; My original version had errors in interface, frame rate, voltage, and current — all corrected against the official datasheet below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV3660&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/5 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;3MP (2048 × 1536)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.4 µm × 1.4 µm (OmniBSI)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DVP 8-bit parallel ONLY&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;RAW RGB, RGB565/555/444, YCbCr422, JPEG compression&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;QXGA@15fps, &lt;strong&gt;1080P@20fps&lt;/strong&gt;, &lt;strong&gt;720P@45fps&lt;/strong&gt;, VGA@60fps&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Core 1.5V, I/O 1.8V/2.8V, Analog 2.6–3.0V&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~98 mA (active), 20 µA (standby)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensitivity&lt;/td&gt;
&lt;td&gt;670 mV/Lux-sec&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic range&lt;/td&gt;
&lt;td&gt;70 dB @ 8x gain&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating temp&lt;/td&gt;
&lt;td&gt;-20°C to 70°C&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XCLK&lt;/td&gt;
&lt;td&gt;6 – 27 MHz&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OV3660 official datasheet (download.kamami.pl/p1196945-OV3660%20Datasheet.pdf), ESP32 Camera Driver (github.com/espressif/esp32-camera)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Correction 1 — No MIPI:&lt;/strong&gt; The original claimed OV3660 supports "DVP 8-bit OR MIPI CSI-2 (1-lane)" — this is &lt;strong&gt;wrong&lt;/strong&gt;. The official datasheet only mentions Digital Video Parallel Port with no MIPI interface description. ESP32 can use it precisely because it's DVP.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Correction 2 — Frame rate:&lt;/strong&gt; Original claimed 1080P@30fps, 720P@60fps; actual is &lt;strong&gt;1080P@20fps, 720P@45fps&lt;/strong&gt;. Original overstated by ~33%.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Correction 3 — Voltage:&lt;/strong&gt; Original "I/O 1.8–3.3V, core 1.2V, analog 2.8V" is wrong. Actual: core 1.5V, I/O 1.8V or 2.8V, analog 2.6–3.0V.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Correction 4 — Current:&lt;/strong&gt; Original "~130mA" is too high; datasheet specifies 98mA active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; OV3660 offers 50% higher resolution than OV2640 (3MP vs 2MP) with better low-light performance via BSI. If your project already uses OV2640 and you want to upgrade image quality while staying on ESP32, OV3660 is the best drop-in replacement (both DVP, directly supported by esp32-camera).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  GC2053 Module
&lt;/h2&gt;

&lt;p&gt;GalaxyCore 2MP (1920×1080) sensor with 1/2.9-inch large sensor and 2.8µm large pixels, supporting both MIPI and DVP interfaces. Standout low-light performance in its price tier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;GalaxyCore GC2053&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.9 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;2MP (1920 × 1080)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.8 µm × 2.8 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2-lane) or DVP&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;RAW Bayer 10-bit / 8-bit&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;1080P@30fps, 720P@60fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.8V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current&lt;/td&gt;
&lt;td&gt;~57 mA total (analog 17.3 + digital 37.4 + I/O 2.5)&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADC&lt;/td&gt;
&lt;td&gt;10-bit&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: GC2053 datasheet V1.1 (2018-11-29, GalaxyCore / CK Vision)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Current correction:&lt;/strong&gt; Original claimed "~95 mA"; datasheet measures approximately 57mA total (analog 17.3mA + digital 37.4mA + I/O 2.5mA). Actual power consumption is lower than original — thermal and power design can be more relaxed. MIPI 2-lane bandwidth of 1.5Gbps/lane 🔍 pending final datasheet confirmation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why choose GC2053:&lt;/strong&gt; The 1/2.9-inch sensor is larger than both OV2640 (1/4") and OV5640 (1/4"), and 2.8µm pixels are also bigger. Bigger sensor + bigger pixels = more light per pixel = better low-light performance. It has a clear advantage over same-price OV solutions for security surveillance.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  GC4653 Module
&lt;/h2&gt;

&lt;p&gt;GalaxyCore's high-performance 4MP sensor using BSI backside-illumination technology for enhanced low-light sensitivity. A high-end choice for security cameras and intelligent vision applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;"STARVIS" terminology correction.&lt;/strong&gt; The original claimed GC4653 uses "STARVIS architecture technology" — this is incorrect. STARVIS is Sony's registered trademark and exclusive technology; GalaxyCore does not use STARVIS. GC4653 actually uses in-house BSI (backside-illuminated) pixel technology for high sensitivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;GalaxyCore GC4653&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/3 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;4MP (2560 × 1440)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.0 µm × 2.0 µm (BSI)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter&lt;/td&gt;
&lt;td&gt;Electronic rolling&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MIPI CSI-2 ONLY (2-lane, RAW12/RAW10)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;RAW Bayer 12-bit / 10-bit&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;4MP@30fps, 1080P@60fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.8V, DVDD 1.2V, I/O 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current&lt;/td&gt;
&lt;td&gt;~120 mA&lt;/td&gt;
&lt;td&gt;🔍 pending datasheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;BSI high-sensitivity low-light&lt;/strong&gt;, HDR, PDAF phase AF, 2D/3D noise reduction&lt;/td&gt;
&lt;td&gt;⚠️ see above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: GC4653 datasheet (Camemaker), Sipeed Wiki, Sony Semiconductor official site (STARVIS trademark confirmation)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;ESP32 incompatible:&lt;/strong&gt; GC4653 is MIPI-only, incompatible with traditional DVP parallel interface. ESP32 series has no MIPI CSI-2 receiver capability and &lt;strong&gt;cannot use this sensor&lt;/strong&gt;. Requires a controller with MIPI CSI-2, such as HiSilicon Hi3516, Ingenic T31, Rockchip RV1106, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; Confirm your controller has MIPI CSI-2 before choosing GC4653. If you're doing an ESP32 project, don't buy this sensor — you won't be able to light it up.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV9281 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision 1/4-inch 1MP &lt;strong&gt;global shutter&lt;/strong&gt; sensor using OmniPixel®3-GS technology. Like the IMX273, it's a global shutter sensor, but much cheaper with DVP+MIPI dual interface — the most affordable global shutter option for makers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV9281&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;1MP (1280 × 800)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;3.0 µm × 3.0 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter type&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Global Shutter&lt;/strong&gt; (OmniPixel®3-GS)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2-lane) + DVP&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW8 / RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;120 fps&lt;/strong&gt; (1280×800), 180fps (VGA)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.8V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;156 mW (active), 150 µA (standby)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S/N ratio&lt;/td&gt;
&lt;td&gt;38 dB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic range&lt;/td&gt;
&lt;td&gt;68 dB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;In production&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Machine vision, drones, barcode scanning, AR/VR, 3D depth&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OmniVision OV9281 Product Brief v1.4 (ovt.com/wp-content/uploads/2024/05/OV9281-PB-v1.4-WEB.pdf)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;ESP32 compatibility:&lt;/strong&gt; OV9281 is &lt;strong&gt;NOT&lt;/strong&gt; in the esp32-camera library (classic ESP32/S2/S3). ESP32-P4 has experimental support but unstable (esp-idf #17795). For global shutter on ESP32, there's currently no good option; use Raspberry Pi + Arducam OV9281 module instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why OV9281 over IMX273:&lt;/strong&gt; Both are global shutter, but OV9281 is much cheaper ($15-25 vs $50+) and supports both DVP and MIPI. Downsides: lower resolution (1MP vs 1.58MP) and monochrome only (no color version).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OV5647 Module
&lt;/h2&gt;

&lt;p&gt;OmniVision 1/4-inch 5MP CMOS sensor with OmniBSI technology. The original Raspberry Pi Camera Module V1 (2013) sensor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;OmniVision OV5647&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;5MP (2592 × 1944)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.4 µm × 1.4 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shutter type&lt;/td&gt;
&lt;td&gt;Rolling shutter&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;OmniBSI backside illumination&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2-lane) + DVP&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW8 / RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;5MP@15fps, 1080P@30fps, 720P@60fps, VGA@90fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;Analog 2.6–3.0V (typical 2.8V), Core 1.5V, I/O 1.7–3.0V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;End-of-Life (EOL)&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Raspberry Pi Camera V1, phones, notebooks&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OmniVision OV5647 datasheet (v-visiontech.com/web/userfiles/download/OV5647DS.pdf), Raspberry Pi official documentation&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Discontinued (EOL):&lt;/strong&gt; OV5647 is on OmniVision's discontinued product list. Surplus stock still available ($8-15), but &lt;strong&gt;not recommended for new designs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selection comparison:&lt;/strong&gt; OV5647 vs OV5640 — both 5MP. OV5647 is the Pi V1 sensor; OV5640 is the ESP32 ecosystem 5MP option (with AF). Pins and drivers are incompatible. For Pi projects on a budget, OV5647 surplus still works; for ESP32, use OV5640.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;NOT in esp32-camera support list.&lt;/strong&gt; ESP32 users should use OV2640 or OV5640.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  IMX219 Module
&lt;/h2&gt;

&lt;p&gt;Sony 1/4-inch 8MP CMOS sensor with Exmor R backside-illumination technology. The standard sensor for Raspberry Pi Camera Module V2, and the most ubiquitous MIPI camera in the Raspberry Pi ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX219&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;8MP (3280 × 2464)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.12 µm × 1.12 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Exmor R&lt;/strong&gt; BSI (NOT STARVIS)&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;1080P@30fps, 720P@60fps, VGA@90fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.8V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;~38 mA @2.8V + ~160 mA @1.2V (≈ 0.26 W)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;In production&lt;/strong&gt; (at least until Jan 2028)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX219 datasheet (dlscorp.com), Raspberry Pi official documentation&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Exmor R is NOT STARVIS:&lt;/strong&gt; Some online articles classify IMX219 as STARVIS — this is wrong. IMX219 uses Exmor R (BSI). STARVIS is Sony's later security-focused technology line with higher sensitivity and dynamic range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; The IMX219 module (Raspberry Pi Camera V2) is the lowest-cost entry to MIPI. Pi 4B/5 CSI connector works directly, driver is in mainline kernel, plug-and-play.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX273 Module
&lt;/h2&gt;

&lt;p&gt;Sony Pregius series global shutter sensor, 1/2.9-inch, 1.58MP. Designed for industrial FA cameras and ITS (Intelligent Traffic Systems), 226fps at full resolution — a high-performance choice for high-speed machine vision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX273&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.9 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;1.58MP (1456 × 1088)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;3.45 µm × 3.45 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Pregius Global Shutter&lt;/strong&gt; (NOT STARVIS)&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (1-lane) OR Sub LVDS (8-ch)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10 / RAW12&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;226 fps&lt;/strong&gt; (full resolution, 10-bit)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;3.3V / 1.8V / 1.2V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Industrial FA cameras, ITS, barcode scanning, defect inspection&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX273 official Flyer (sony-semicon.com/files/62/flyer_industry/IMX273_287_296_297_Flyer.pdf)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Global vs Rolling Shutter:&lt;/strong&gt; Every other sensor in this handbook uses rolling shutter, which causes jello effect on fast-moving objects. IMX273 has a global shutter — the entire image is exposed simultaneously, &lt;strong&gt;zero jello effect on high-speed motion&lt;/strong&gt;. This is the core reason to choose it for industrial vision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; IMX273 comes in two variants — MIPI 1-lane and Sub LVDS 8-ch. Confirm which interface you need before buying.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX307 Module
&lt;/h2&gt;

&lt;p&gt;Sony STARVIS 2MP security sensor, 1/2.8-inch, 2.9µm large pixels. HDR support, a mainstream choice for 1080P security cameras, commonly paired with HiSilicon Hi3516 series SoCs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX307&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.8 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;2.07MP (1920 × 1080)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.9 µm × 2.9 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;STARVIS (original)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2/4-lane) + LVDS&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10 / RAW12&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;60 fps (1080P)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;Multi-exposure HDR + Digital overlap HDR&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.9V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Security surveillance, FA cameras&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX307 official Flyer (sony-semicon.com/files/62/flyer_security/IMX307LQD_LQR_Flyer.pdf)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highlight:&lt;/strong&gt; 2.9µm large pixels + STARVIS = excellent night vision. Paired with Hi3516C V300 is the classic security combo.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX335 Module
&lt;/h2&gt;

&lt;p&gt;Sony STARVIS 5MP security sensor, 1/2.8-inch. Steps up from IMX307's 2MP to 5MP while maintaining good day/night image quality. Commonly paired with Hi3516 / RV1106.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX335&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.8 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;5.04MP (2592 × 1944)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.0 µm × 2.0 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;STARVIS (original)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2/4-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10 / RAW12&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;60 fps (full resolution, 10-bit)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;Multi-exposure HDR + Digital overlap HDR&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.9V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common SoC pairing&lt;/td&gt;
&lt;td&gt;Hi3516 series, RV1106&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX335 official Flyer (sony-semicon.com/files/62/flyer_security/IMX335LQN_Flyer.pdf)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selection comparison:&lt;/strong&gt; IMX335 vs IMX307 — same 1/2.8" sensor. IMX307 is 2MP/2.9µm (better low light), IMX335 is 5MP/2.0µm (more detail). Choose IMX335 if you need to see far details, IMX307 for night surveillance.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX415 Module
&lt;/h2&gt;

&lt;p&gt;Sony STARVIS 4K (8MP) security sensor, 1/2.8-inch. The workhorse sensor for 4K security cameras, with the most mature module ecosystem — Jetson / Raspberry Pi / RV1106 all have ready drivers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX415&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.8 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;8.29MP (3840 × 2160, 4K UHD)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.45 µm × 1.45 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;STARVIS (original)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2/4-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW8 / RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;4K up to 90fps (typical 30–60fps)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;Multi-exposure HDR&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.9V, DVDD 1.1V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announced&lt;/td&gt;
&lt;td&gt;June 2019&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;4K security, NVR, industrial cameras&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX415 official Flyer (sony-semicon.com/files/62/pdf/p-12_IMX415-AAQR_AAMR_Flyer.pdf)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; IMX415 has the best DIY/embedded module ecosystem of all Sony IMX sensors. LuckFox, Radxa, e-con Systems all offer ready-made modules with drivers for Jetson, Raspberry Pi, RV1106. The top pick for 4K MIPI projects.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX678 Module
&lt;/h2&gt;

&lt;p&gt;Sony STARVIS 2 4K (8MP) security sensor, 1/1.8-inch large sensor. A 2024 new release that comprehensively upgrades the IMX415 — bigger sensor, newer STARVIS 2 technology, faster frame rate, stronger HDR.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX678&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1/1.8 inch&lt;/strong&gt; (much larger than IMX415's 1/2.8")&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;8.29MP (3840 × 2160, 4K UHD)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.0 µm × 2.0 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;STARVIS 2&lt;/strong&gt; (next-gen)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2/4/8-lane, 4-lane×2ch)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10 / RAW12&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;72 fps&lt;/strong&gt; (4K, 10-bit) / 60fps (12-bit)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;Digital overlap HDR + &lt;strong&gt;Clear HDR&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AVDD 3.3V&lt;/strong&gt;, DVDD 1.1V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;⚠️ see below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announced&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common SoC pairing&lt;/td&gt;
&lt;td&gt;Hi3519DV500&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX678 official Flyer (sony-semicon.com/files/62/flyer_security/IMX678-AAQR_AAQR1_Flyer.pdf, © 2024)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Power note:&lt;/strong&gt; IMX678's AVDD is &lt;strong&gt;3.3V&lt;/strong&gt;, different from IMX335/IMX307/IMX415's 2.9V. Change the LDO output when migrating from other IMX designs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMX678 vs IMX415:&lt;/strong&gt; Both are 4K 8MP, but IMX678 has a bigger sensor (1/1.8" vs 1/2.8"), bigger pixels (2.0µm vs 1.45µm), newer tech (STARVIS 2), and stronger HDR (Clear HDR). The trade-off is more complex power/lane requirements and higher module cost as a 2024 new release.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX766 Module
&lt;/h2&gt;

&lt;p&gt;Sony 50MP smartphone sensor, 1/1.56-inch large sensor, Quad Bayer array. Used in OPPO Find X3 Pro, Reno7 Pro and other flagship phones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;NOT recommended for DIY / embedded projects.&lt;/strong&gt; The following is for reference only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX766&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/1.56 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;50MP (8192 × 6144)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.0 µm (4-in-1 binning → 2.0 µm)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel array&lt;/td&gt;
&lt;td&gt;Quad Bayer&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (4-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10 / RAW8&lt;/td&gt;
&lt;td&gt;🔍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;Full res ~24fps; binned 30fps; 1080P 60–120fps&lt;/td&gt;
&lt;td&gt;🔍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 2.8V, DVDD 1.1V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;🔍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone implementations&lt;/td&gt;
&lt;td&gt;OPPO Find X3 Pro, OnePlus, Realme&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: OPPO official specs, IADIY module page, DxOMark. ⚠️ &lt;strong&gt;Sony has not published an official IMX766 datasheet&lt;/strong&gt; — some parameters are from phone manufacturers and module vendors.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Why not recommended for DIY:&lt;/strong&gt; No official datasheet (register config and power sequencing require reverse engineering), needs custom driver (no mainline Linux driver), non-standard FPC, 50MP Quad Bayer needs powerful ISP, many modules include OIS motors needing extra drivers. For high-res MIPI, choose &lt;strong&gt;IMX415&lt;/strong&gt; or &lt;strong&gt;IMX477&lt;/strong&gt; (Raspberry Pi HQ Camera) instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sony IMX Series Key Parameters Comparison
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;All 15 sensors in one table would exceed mobile screen width. OV/GC comparison table above; Sony IMX series below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;IMX219&lt;/th&gt;
&lt;th&gt;IMX273&lt;/th&gt;
&lt;th&gt;IMX307&lt;/th&gt;
&lt;th&gt;IMX335&lt;/th&gt;
&lt;th&gt;IMX415&lt;/th&gt;
&lt;th&gt;IMX678&lt;/th&gt;
&lt;th&gt;IMX766&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;Exmor R&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pregius GS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;STARVIS&lt;/td&gt;
&lt;td&gt;STARVIS&lt;/td&gt;
&lt;td&gt;STARVIS&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;STARVIS 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quad Bayer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolution&lt;/td&gt;
&lt;td&gt;8MP&lt;/td&gt;
&lt;td&gt;1.58MP&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;5MP&lt;/td&gt;
&lt;td&gt;8MP (4K)&lt;/td&gt;
&lt;td&gt;8MP (4K)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50MP&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4"&lt;/td&gt;
&lt;td&gt;1/2.9"&lt;/td&gt;
&lt;td&gt;1/2.8"&lt;/td&gt;
&lt;td&gt;1/2.8"&lt;/td&gt;
&lt;td&gt;1/2.8"&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1/1.8"&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1/1.56"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.12 µm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.45 µm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.9 µm&lt;/td&gt;
&lt;td&gt;2.0 µm&lt;/td&gt;
&lt;td&gt;1.45 µm&lt;/td&gt;
&lt;td&gt;2.0 µm&lt;/td&gt;
&lt;td&gt;1.0 µm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI 2L&lt;/td&gt;
&lt;td&gt;MIPI 1L/LVDS&lt;/td&gt;
&lt;td&gt;MIPI 2/4L&lt;/td&gt;
&lt;td&gt;MIPI 2/4L&lt;/td&gt;
&lt;td&gt;MIPI 2/4L&lt;/td&gt;
&lt;td&gt;MIPI 2/4/8L&lt;/td&gt;
&lt;td&gt;MIPI 4L&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max fps&lt;/td&gt;
&lt;td&gt;1080P@30fps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;226fps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1080P@60fps&lt;/td&gt;
&lt;td&gt;5MP@60fps&lt;/td&gt;
&lt;td&gt;4K@90fps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4K@72fps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50MP@24fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Clear&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global shutter&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AVDD&lt;/td&gt;
&lt;td&gt;2.8V&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;td&gt;2.9V&lt;/td&gt;
&lt;td&gt;2.9V&lt;/td&gt;
&lt;td&gt;2.9V&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.3V&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.8V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best use case&lt;/td&gt;
&lt;td&gt;Raspberry Pi V2&lt;/td&gt;
&lt;td&gt;Industrial FA&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;4K Security&lt;/td&gt;
&lt;td&gt;High-end 4K&lt;/td&gt;
&lt;td&gt;⚠️ Phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DIY recommended&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ Industrial&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Top pick&lt;/td&gt;
&lt;td&gt;⚠️ Pricey&lt;/td&gt;
&lt;td&gt;❌ Not recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  IMX477 Module
&lt;/h2&gt;

&lt;p&gt;Sony 12.3MP sensor, 1/2.3-inch, with interchangeable C/CS-mount lens. The standard sensor for Raspberry Pi HQ Camera — ideal for projects needing high-quality imaging and interchangeable lenses: microscopy, astronomy, industrial inspection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX477&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.3 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;12.3MP (4056 × 3040)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.55 µm × 1.55 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;BSI stacked CMOS&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (2/4-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW12 / RAW10 / RAW8&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;Full res@60fps (10-bit), 4K@60fps, 1080P@240fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;DOL-HDR (Digital Overlap HDR)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lens mount&lt;/td&gt;
&lt;td&gt;C-mount / CS-mount (interchangeable)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;In production&lt;/strong&gt; (at least until Jan 2030)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announced&lt;/td&gt;
&lt;td&gt;April 2020&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Raspberry Pi HQ Camera, microscopy/astronomy, industrial&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX477 Flyer, Raspberry Pi HQ Camera product page&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purchasing tip:&lt;/strong&gt; IMX477's biggest advantage is &lt;strong&gt;interchangeable lenses&lt;/strong&gt; — C-mount and CS-mount cover wide-angle to telephoto to macro. Best image quality in the Pi ecosystem. $50-70.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX708 Module
&lt;/h2&gt;

&lt;p&gt;Sony 12MP Quad Bayer sensor, 1/2.3-inch, with PDAF phase-detection autofocus and HDR. The standard sensor for Raspberry Pi Camera Module 3 — the first Pi camera with hardware autofocus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX708&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.3 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;12MP (4608 × 2592)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;1.4 µm × 1.4 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;BSI stacked CMOS, Quad Bayer&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;1080P@50fps, 720P@100fps, 480P@120fps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autofocus&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;PDAF phase-detection autofocus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDR&lt;/td&gt;
&lt;td&gt;Yes (up to 3MP output)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;In production&lt;/strong&gt; (at least until Jan 2030)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announced&lt;/td&gt;
&lt;td&gt;January 2023&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Raspberry Pi Camera Module 3 (standard/wide/telephoto)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Raspberry Pi Camera Module 3 product page and product brief&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highlight:&lt;/strong&gt; IMX708 brings hardware autofocus (PDAF) to Raspberry Pi for the first time. Available in standard, wide, and telephoto versions. $25-30 — best price/performance for Pi MIPI cameras.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IMX296 Module
&lt;/h2&gt;

&lt;p&gt;Sony Pregius global shutter sensor, 1/2.9-inch, 1.58MP. The standard sensor for Raspberry Pi Global Shutter Camera. Like the IMX273, it's a Pregius global shutter sensor, but with official Pi ecosystem module and driver support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Spec&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor&lt;/td&gt;
&lt;td&gt;Sony IMX296&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/2.9 inch&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective pixels&lt;/td&gt;
&lt;td&gt;1.58MP (1456 × 1088)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;3.45 µm × 3.45 µm&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pregius Global Shutter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;MIPI CSI-2 (1-lane)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;RAW10&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max frame rate&lt;/td&gt;
&lt;td&gt;60.3 fps (full resolution, 10-bit)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating voltage&lt;/td&gt;
&lt;td&gt;AVDD 3.3V, DVDD 1.2V, IOVDD 1.8V&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lens mount&lt;/td&gt;
&lt;td&gt;C-mount / CS-mount (interchangeable)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production status&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;In production&lt;/strong&gt; (at least until Jan 2032)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announced&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical applications&lt;/td&gt;
&lt;td&gt;Raspberry Pi Global Shutter Camera, high-speed capture, industrial&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Source: Sony IMX296 Flyer, Raspberry Pi Global Shutter Camera product page&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;IMX296 vs IMX273:&lt;/strong&gt; Both are Pregius global shutter, 3.45µm pixels. IMX296 is the official Pi Global Shutter Camera sensor with full driver/docs; IMX273 is more industrial FA focused. For Pi projects choose IMX296, for industrial FA choose IMX273.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Module Comparison Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Parameters Comparison
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Table corrected per official datasheets. Bolded items had errors in the original or need special attention.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;OV2640&lt;/th&gt;
&lt;th&gt;OV5640&lt;/th&gt;
&lt;th&gt;OV7725&lt;/th&gt;
&lt;th&gt;GC0309&lt;/th&gt;
&lt;th&gt;OV7670&lt;/th&gt;
&lt;th&gt;OV3660&lt;/th&gt;
&lt;th&gt;GC2053&lt;/th&gt;
&lt;th&gt;GC4653&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vendor&lt;/td&gt;
&lt;td&gt;OmniVision&lt;/td&gt;
&lt;td&gt;OmniVision&lt;/td&gt;
&lt;td&gt;OmniVision&lt;/td&gt;
&lt;td&gt;GalaxyCore&lt;/td&gt;
&lt;td&gt;OmniVision&lt;/td&gt;
&lt;td&gt;OmniVision&lt;/td&gt;
&lt;td&gt;GalaxyCore&lt;/td&gt;
&lt;td&gt;GalaxyCore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolution&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;5MP&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;0.3MP&lt;/td&gt;
&lt;td&gt;3MP&lt;/td&gt;
&lt;td&gt;2MP&lt;/td&gt;
&lt;td&gt;4MP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max size&lt;/td&gt;
&lt;td&gt;1600×1200&lt;/td&gt;
&lt;td&gt;2592×1944&lt;/td&gt;
&lt;td&gt;640×480&lt;/td&gt;
&lt;td&gt;640×480&lt;/td&gt;
&lt;td&gt;640×480&lt;/td&gt;
&lt;td&gt;2048×1536&lt;/td&gt;
&lt;td&gt;1920×1080&lt;/td&gt;
&lt;td&gt;2560×1440&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optical format&lt;/td&gt;
&lt;td&gt;1/4"&lt;/td&gt;
&lt;td&gt;1/4"&lt;/td&gt;
&lt;td&gt;1/4"&lt;/td&gt;
&lt;td&gt;1/9"&lt;/td&gt;
&lt;td&gt;1/6"&lt;/td&gt;
&lt;td&gt;1/5"&lt;/td&gt;
&lt;td&gt;1/2.9"&lt;/td&gt;
&lt;td&gt;1/3"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel size&lt;/td&gt;
&lt;td&gt;2.2 µm&lt;/td&gt;
&lt;td&gt;1.4 µm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.0 µm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.5 µm&lt;/td&gt;
&lt;td&gt;3.6 µm&lt;/td&gt;
&lt;td&gt;1.4 µm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.8 µm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.0 µm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;DVP/MIPI&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;DVP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DVP only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DVP/MIPI&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MIPI only&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max fps&lt;/td&gt;
&lt;td&gt;UXGA@15fps&lt;/td&gt;
&lt;td&gt;QSXGA@15fps&lt;/td&gt;
&lt;td&gt;VGA@60fps&lt;/td&gt;
&lt;td&gt;VGA@30fps&lt;/td&gt;
&lt;td&gt;VGA@30fps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1080P@20fps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1080P@30fps&lt;/td&gt;
&lt;td&gt;4MP@30fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output formats&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW/JPEG&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW/JPEG&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW&lt;/td&gt;
&lt;td&gt;YUV/RGB/RAW/JPEG&lt;/td&gt;
&lt;td&gt;RAW Bayer&lt;/td&gt;
&lt;td&gt;RAW Bayer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autofocus&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (VCM)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;PDAF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low-light&lt;/td&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voltage&lt;/td&gt;
&lt;td&gt;1.7–3.3V (I/O)&lt;/td&gt;
&lt;td&gt;1.7–3.3V (I/O)&lt;/td&gt;
&lt;td&gt;3.3V&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.8V typical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.8V/2.45-3V/1.7-3V&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.5V core multi-rail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.8V (I/O)&lt;/td&gt;
&lt;td&gt;1.8V (I/O)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical power&lt;/td&gt;
&lt;td&gt;~125 mW&lt;/td&gt;
&lt;td&gt;~140 mA&lt;/td&gt;
&lt;td&gt;~80 mA&lt;/td&gt;
&lt;td&gt;~60 mA&lt;/td&gt;
&lt;td&gt;~60 mW&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~98 mA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~57 mA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~120 mA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I²C address&lt;/td&gt;
&lt;td&gt;0x30&lt;/td&gt;
&lt;td&gt;0x3C&lt;/td&gt;
&lt;td&gt;0x21&lt;/td&gt;
&lt;td&gt;Vendor&lt;/td&gt;
&lt;td&gt;0x21&lt;/td&gt;
&lt;td&gt;See DS&lt;/td&gt;
&lt;td&gt;Vendor&lt;/td&gt;
&lt;td&gt;Vendor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price tier&lt;/td&gt;
&lt;td&gt;Low-mid&lt;/td&gt;
&lt;td&gt;Mid-high&lt;/td&gt;
&lt;td&gt;Mid&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low-mid&lt;/td&gt;
&lt;td&gt;Mid&lt;/td&gt;
&lt;td&gt;Mid-high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESP32 compatible&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ needs FIFO&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ DVP mode only&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Selection Recommendations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Education (Arduino/STM32):&lt;/strong&gt; OV7670 — Richest ecosystem, most tutorials. Note three-rail power.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wi-Fi streaming (ESP32-CAM):&lt;/strong&gt; OV2640 — Built-in JPEG, balanced power/quality, de facto standard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-res photo (ESP32/STM32):&lt;/strong&gt; OV5640 — 5MP + autofocus, best entry-level quality. Power VCM on AF versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Night vision / machine vision (low-light):&lt;/strong&gt; OV7725 — 6.0µm pixels, low-light king. ⚠️ EOL, surplus stock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ultra low cost (access/POS):&lt;/strong&gt; GC0309-8225N — Lowest price, meets basic needs. Power at 2.8V.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3MP upgrade (ESP32):&lt;/strong&gt; OV3660 — BSI, drop-in for OV2640. Note lower frame rate than OV2640.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security surveillance (indoor/outdoor):&lt;/strong&gt; GC2053 — Large sensor/pixels, MIPI high-speed, low power.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-end security / AI vision:&lt;/strong&gt; GC4653 — 4MP + BSI high sensitivity. ⚠️ MIPI only, not for ESP32.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raspberry Pi / Jetson entry (MIPI):&lt;/strong&gt; IMX219 — Pi Camera V2 standard, most mature driver, plug-and-play.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Industrial vision / high-speed capture:&lt;/strong&gt; IMX273 — Global shutter, 226fps, zero jello effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security (1080P nighttime):&lt;/strong&gt; IMX307 — 2.9µm + STARVIS, strong night vision. Pair with Hi3516.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security (high resolution):&lt;/strong&gt; IMX335 — 5MP STARVIS, more detail. Pair with Hi3516/RV1106.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4K security / NVR:&lt;/strong&gt; IMX415 — 4K STARVIS, best module ecosystem, top pick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-end 4K security (2024):&lt;/strong&gt; IMX678 — STARVIS 2 + Clear HDR, best image quality. ⚠️ AVDD 3.3V.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚠️ Don't buy for DIY:&lt;/strong&gt; IMX766 — 50MP phone sensor, no official datasheet, needs custom driver.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Application &amp;amp; Wiring Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ESP32 Typical Wiring
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important disclaimer:&lt;/strong&gt; The pin mapping below is based on common AI-Thinker ESP32-CAM module configurations. &lt;strong&gt;There is no official unified pin standard for ESP32-CAM&lt;/strong&gt; — the esp32-camera library examples explicitly state "pin configurations vary by board". Different vendors (AI-Thinker, Freenove, SunFounder, Espressif official EVB) may all differ. &lt;strong&gt;Always verify which board you have before wiring.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI-Thinker ESP32-CAM common mapping (applies to OV2640 / OV5640 / OV7725):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module Signal&lt;/th&gt;
&lt;th&gt;ESP32 GPIO&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;XCLK&lt;/td&gt;
&lt;td&gt;GPIO 27&lt;/td&gt;
&lt;td&gt;Master clock via LEDC PWM at 20 MHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCLK&lt;/td&gt;
&lt;td&gt;GPIO 22&lt;/td&gt;
&lt;td&gt;Pixel clock input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VSYNC&lt;/td&gt;
&lt;td&gt;GPIO 25&lt;/td&gt;
&lt;td&gt;Frame sync input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HREF&lt;/td&gt;
&lt;td&gt;GPIO 23&lt;/td&gt;
&lt;td&gt;Line sync input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D0&lt;/td&gt;
&lt;td&gt;GPIO 32&lt;/td&gt;
&lt;td&gt;Data bit 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D1&lt;/td&gt;
&lt;td&gt;GPIO 35&lt;/td&gt;
&lt;td&gt;Data bit 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D2&lt;/td&gt;
&lt;td&gt;GPIO 34&lt;/td&gt;
&lt;td&gt;Data bit 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D3&lt;/td&gt;
&lt;td&gt;GPIO 5&lt;/td&gt;
&lt;td&gt;Data bit 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D4&lt;/td&gt;
&lt;td&gt;GPIO 39&lt;/td&gt;
&lt;td&gt;Data bit 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D5&lt;/td&gt;
&lt;td&gt;GPIO 18&lt;/td&gt;
&lt;td&gt;Data bit 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D6&lt;/td&gt;
&lt;td&gt;GPIO 36&lt;/td&gt;
&lt;td&gt;Data bit 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D7&lt;/td&gt;
&lt;td&gt;GPIO 19&lt;/td&gt;
&lt;td&gt;Data bit 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIOC&lt;/td&gt;
&lt;td&gt;GPIO 26&lt;/td&gt;
&lt;td&gt;SCCB clock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIOD&lt;/td&gt;
&lt;td&gt;GPIO 21&lt;/td&gt;
&lt;td&gt;SCCB data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PWDN&lt;/td&gt;
&lt;td&gt;GPIO 0&lt;/td&gt;
&lt;td&gt;Power-down control (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RESET&lt;/td&gt;
&lt;td&gt;GPIO 15&lt;/td&gt;
&lt;td&gt;Reset control (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Verification: esp32-camera repo examples/camera_example/main/camera_pinout.h (PCLK/VSYNC/HREF confirmed line-by-line against source).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  STM32 Wiring
&lt;/h3&gt;

&lt;p&gt;STM32F4/F7/H7 series feature DCMI (Digital Camera Memory Interface) peripheral for direct hardware reception of DVP data streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect D0–D7 to DCMI_D0–DCMI_D7 corresponding GPIOs&lt;/li&gt;
&lt;li&gt;PCLK → DCMI_PIXCK&lt;/li&gt;
&lt;li&gt;HSYNC → DCMI_HSYNC&lt;/li&gt;
&lt;li&gt;VSYNC → DCMI_VSYNC&lt;/li&gt;
&lt;li&gt;I²C_SCL/SDA → any I²C peripheral&lt;/li&gt;
&lt;li&gt;Enable DCMI + DMA transfer, target buffer in SRAM or SDRAM&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Power Design
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated regulation:&lt;/strong&gt; Camera modules can draw 100–150 mA 🔍 during capture (varies significantly by sensor — OV5640/GC4653 higher, GC2053/OV7670 lower). Use a dedicated LDO (e.g., AMS1117-3.3) to avoid voltage sag from sharing MCU regulator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling caps:&lt;/strong&gt; Place 10 µF + 100 nF parallel decoupling caps near the module VCC pin, as close to the FPC connector as possible. Standard practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal integrity:&lt;/strong&gt; Keep DVP trace length within 10 cm; for longer runs, add 22–33 Ω series termination resistors on PCLK and data lines. ⚠️ Low-speed DVP (PCLK &amp;lt; 30MHz) may not need termination — depends on actual PCB layout.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No image, no PCLK waveform&lt;/td&gt;
&lt;td&gt;XCLK missing or wrong frequency&lt;/td&gt;
&lt;td&gt;Measure XCLK with scope, confirm 6–24 MHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong colors / color cast&lt;/td&gt;
&lt;td&gt;Output format mismatch with MCU decoder&lt;/td&gt;
&lt;td&gt;Check register config, confirm RGB565 or YUV422&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image banding / jitter&lt;/td&gt;
&lt;td&gt;Power ripple or PCLK interference&lt;/td&gt;
&lt;td&gt;Improve power filtering, shorten data lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCCB config failure&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Wrong I²C address&lt;/strong&gt; or no pull-ups&lt;/td&gt;
&lt;td&gt;Check address table in this doc, add 4.7kΩ pull-ups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low frame rate&lt;/td&gt;
&lt;td&gt;Low XCLK or resolution too high&lt;/td&gt;
&lt;td&gt;Raise XCLK to 24 MHz, reduce resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blurry image (OV5640 AF)&lt;/td&gt;
&lt;td&gt;VCM unpowered or AF registers unconfigured&lt;/td&gt;
&lt;td&gt;Verify AF_VCC supply, initialize AF firmware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OV7670 garbled&lt;/td&gt;
&lt;td&gt;DVDD not separately powered&lt;/td&gt;
&lt;td&gt;Confirm on-board 1.8V LDO output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Practical Purchasing Advice
&lt;/h2&gt;

&lt;p&gt;Lessons learned from my own mistakes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose controller first, camera second.&lt;/strong&gt; ESP32 only does DVP — choose OV2640/OV5640/OV7725/OV3660. Only MIPI-capable controllers (RV1106, Hi3516) can use 4MP sensors like GC4653. Your controller's interface defines your options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check if the module has FIFO.&lt;/strong&gt; OV7670 comes in versions with and without AL422B FIFO. Running non-FIFO on bare STM32 is nearly impossible for normal imaging. The cheap tier is usually non-FIFO.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AF versions need AF_VCC pin.&lt;/strong&gt; OV5640 AF modules need separate VCM motor power. Some cheap modules merge AF power with main supply, some don't expose it. Check pinout before buying.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Voltage mismatch is the most common board-frying cause.&lt;/strong&gt; Don't just connect 3.3V blindly. GC0309's optimal point is 2.8V, OV7670 core needs 1.8V. When unsure, check the voltage table in this doc or read the datasheet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EOL parts (OV7725) carry inventory risk.&lt;/strong&gt; Officially discontinued; still available on Taobao/LCSC now, but not recommended for new mass production. Fine for prototyping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GalaxyCore datasheets are harder to find than OmniVision.&lt;/strong&gt; If Chinese channels don't have GC-series datasheets, try galaxycore.com.cn official site, or module vendor pages like MuchVision, Camemaker. Sipeed Wiki is also a good supplemental source.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Sony IMX Series Purchasing Supplement
&lt;/h2&gt;

&lt;p&gt;The purchasing advice above focuses on OV/GC series (DVP ecosystem). Here are tips specific to Sony IMX series (MIPI ecosystem):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Where to buy IMX modules:&lt;/strong&gt; IMX219 is widely available on Taobao/LCSC (Pi V2 compatibles). IMX415 modules from LuckFox, Radxa, Camemaker are recommended. IMX335/IMX307 are mostly found through security solution providers. IMX678 is a 2024 new release, modules are still scarce.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't buy phone salvage parts for embedded use.&lt;/strong&gt; Phone sensors like IMX766 are cheap on Taobao (50-300 RMB), but have no datasheet, no driver, and non-standard FPC. You'll most likely waste time trying to light them up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Watch the STARVIS generation.&lt;/strong&gt; Sony officially defines three generations: STARVIS (original, 2014), STARVIS 2 (2021, wider dynamic range), STARVIS 3 (latest, LOFIC structure). Confirm which generation you're getting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global shutter is only in the Pregius line.&lt;/strong&gt; If your project involves high-speed motion capture (conveyor belts, sports analysis, 3D scanning), only Pregius global shutter sensors like IMX273 can avoid jello effect. All other IMX and OV/GC sensors are rolling shutter.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;All parameters in this article verified against the following official datasheets and authoritative sources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OmniVision official product pages — OV2640 / OV5640 / OV7725 / OV7670 / OV3660 (ovt.com)&lt;/li&gt;
&lt;li&gt;OV2640 datasheet — uctronics.com/download/OV2640_DS.pdf&lt;/li&gt;
&lt;li&gt;OV5640 datasheet v2.03 — cdn.sparkfun.com/datasheets/Sensors/LightImaging/OV5640_datasheet.pdf&lt;/li&gt;
&lt;li&gt;OV5640 Product Brief — media.digikey.com/pdf/Data%20Sheets/OmniVision%20PDFs/OV5640_PB_3-4-11.pdf&lt;/li&gt;
&lt;li&gt;OV7725 datasheet — pdf.datasheet.live/10e069da/ovt.com/OV07725-V28A.pdf&lt;/li&gt;
&lt;li&gt;OV7670 datasheet — w2.electrodragon.com/Chip-dat/OmniVision-dat/OV7670-dat/OV7670-ds.pdf&lt;/li&gt;
&lt;li&gt;OV3660 datasheet — download.kamami.pl/p1196945-OV3660%20Datasheet.pdf&lt;/li&gt;
&lt;li&gt;GC0309 datasheet V1.0 (2009-12-28) — GalaxyCore Inc.&lt;/li&gt;
&lt;li&gt;GC2053 datasheet V1.1 (2018-11-29) — GalaxyCore / CK Vision&lt;/li&gt;
&lt;li&gt;GC4653 datasheet — Camemaker; Sipeed Wiki&lt;/li&gt;
&lt;li&gt;Espressif esp32-camera driver source — github.com/espressif/esp32-camera&lt;/li&gt;
&lt;li&gt;Sony Semiconductor Solutions — STARVIS trademark confirmation (STARVIS is Sony-exclusive tech)&lt;/li&gt;
&lt;li&gt;Linux Kernel ov7670.c — I²C address definition (android.googlesource.com)&lt;/li&gt;
&lt;li&gt;SCCB Functional Specification v2.2 — protocol spec&lt;/li&gt;
&lt;li&gt;Sony IMX219 datasheet — dlscorp.com/wp-content/uploads/2018/04/Sony-IMX219-Datasheet.pdf&lt;/li&gt;
&lt;li&gt;Sony IMX273 official Flyer — sony-semicon.com/files/62/flyer_industry/IMX273_287_296_297_Flyer.pdf&lt;/li&gt;
&lt;li&gt;Sony IMX307 official Flyer — sony-semicon.com/files/62/flyer_security/IMX307LQD_LQR_Flyer.pdf&lt;/li&gt;
&lt;li&gt;Sony IMX335 official Flyer — sony-semicon.com/files/62/flyer_security/IMX335LQN_Flyer.pdf&lt;/li&gt;
&lt;li&gt;Sony IMX415 official Flyer — sony-semicon.com/files/62/pdf/p-12_IMX415-AAQR_AAMR_Flyer.pdf&lt;/li&gt;
&lt;li&gt;Sony IMX678 official Flyer — sony-semicon.com/files/62/flyer_security/IMX678-AAQR_AAQR1_Flyer.pdf (© 2024)&lt;/li&gt;
&lt;li&gt;Sony IMX766 — OPPO official specs / IADIY module page / DxOMark&lt;/li&gt;
&lt;li&gt;Sony STARVIS technology — sony-semicon.com/en/technology/security/&lt;/li&gt;
&lt;li&gt;Raspberry Pi Camera documentation — raspberrypi.com/documentation/accessories/camera.html&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>hardware</category>
      <category>iot</category>
      <category>learning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Privacy-First Open Source ESP32-S3 Camera with RTSP for MiBee NVR</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Sat, 13 Jun 2026 12:12:57 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/building-a-privacy-first-open-source-esp32-s3-camera-with-rtsp-for-frigate-nvr-4ie4</link>
      <guid>https://dev.to/mickey_zzc/building-a-privacy-first-open-source-esp32-s3-camera-with-rtsp-for-frigate-nvr-4ie4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Like many self-hosted enthusiasts, I've been frustrated with the state of affordable IP cameras for years. Most cheap cameras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Phone home to unknown servers&lt;/li&gt;
&lt;li&gt;Require cloud subscriptions for basic features&lt;/li&gt;
&lt;li&gt;Don't support standard RTSP streams&lt;/li&gt;
&lt;li&gt;Run closed-source firmware you can't audit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;seeed-esp32s3-cam&lt;/strong&gt; - a 100% open source, privacy-first camera firmware that works natively with &lt;strong&gt;MiBeeNvr&lt;/strong&gt;, Blue Iris, and every self-hosted NVR system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardware
&lt;/h2&gt;

&lt;p&gt;This firmware is optimized for the &lt;strong&gt;Seeed Studio ESP32-S3 Sense&lt;/strong&gt; board - an incredibly capable camera module for around &lt;strong&gt;$15 USD&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESP32-S3 dual-core processor&lt;/li&gt;
&lt;li&gt;OV2640 2MP camera sensor&lt;/li&gt;
&lt;li&gt;Built-in WiFi&lt;/li&gt;
&lt;li&gt;Microphone support&lt;/li&gt;
&lt;li&gt;SD card slot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect for monitoring garages, sheds, entrances, or anywhere you want an inexpensive camera without sacrificing privacy.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Key Features
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📹 &lt;strong&gt;Native RTSP Server&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Works with MiBeeNvr / Blue Iris / VLC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌐 &lt;strong&gt;HTTP MJPEG Streaming&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Low latency browser preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔍 &lt;strong&gt;On-device Motion Detection&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Configurable sensitivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌙 &lt;strong&gt;Night Mode&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Auto exposure adjustment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚙️ &lt;strong&gt;Web Configuration UI&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Full settings via browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ &lt;strong&gt;Pre-built Binaries&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Flash in 2 minutes, no compiling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔒 &lt;strong&gt;100% Local Only&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ No cloud, no phone home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📄 &lt;strong&gt;MIT Licensed&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅ Open source forever&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🚀 Getting Started in 5 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Flash the Firmware
&lt;/h3&gt;

&lt;p&gt;No compiling needed! Use the pre-built binaries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the latest release from &lt;a href="https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Flash using &lt;code&gt;esptool.py&lt;/code&gt; or your favorite flasher:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 write_flash 0x0 firmware.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Configure WiFi
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Connect to the WiFi hotspot &lt;code&gt;ESP32-CAM-XXXX&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;192.168.4.1&lt;/code&gt; in your browser&lt;/li&gt;
&lt;li&gt;Enter your WiFi credentials&lt;/li&gt;
&lt;li&gt;The camera will reboot and connect to your network&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Access the Web UI
&lt;/h3&gt;

&lt;p&gt;Find your camera's IP address in your router, then open it in a browser. You'll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live video preview&lt;/li&gt;
&lt;li&gt;Stream quality settings&lt;/li&gt;
&lt;li&gt;Motion detection configuration&lt;/li&gt;
&lt;li&gt;Network settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎯 MiBeeNvr Integration (The Good Stuff)
&lt;/h2&gt;

&lt;p&gt;This is why I built this - &lt;strong&gt;native MiBeeNvr compatibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Add this to your MiBeeNvr configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cameras&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;esp32_cam&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rtsp://your-camera-ip:8554/stream&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Garage Camera&lt;/span&gt;
    &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;detect_motion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! No hacks, no custom scripts, just standard RTSP. Your camera will show up instantly in the MiBeeNvr web UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MiBeeNvr GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/MiBeeNvr&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Technical Details
&lt;/h2&gt;

&lt;p&gt;Built with &lt;strong&gt;ESP-IDF v5.x&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardware-accelerated JPEG encoding&lt;/li&gt;
&lt;li&gt;Optimized memory management for 24/7 operation&lt;/li&gt;
&lt;li&gt;Lightweight RTSP server implementation&lt;/li&gt;
&lt;li&gt;Non-blocking WiFi handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The firmware is designed to run stable indefinitely - I've had cameras running for months without a single reboot.&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Full Documentation
&lt;/h2&gt;

&lt;p&gt;For a complete step-by-step guide including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced MiBeeNvr configuration&lt;/li&gt;
&lt;li&gt;Performance tuning&lt;/li&gt;
&lt;li&gt;Troubleshooting common issues&lt;/li&gt;
&lt;li&gt;Motion detection optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;strong&gt;full tutorial&lt;/strong&gt;:&lt;br&gt;
👉 &lt;a href="https://blog.mickeyzzc.tech/en/posts/iot/esp32s3-cam-monitor/" rel="noopener noreferrer"&gt;https://blog.mickeyzzc.tech/en/posts/iot/esp32s3-cam-monitor/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 Both Projects Are Open Source
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Camera Firmware:&lt;/strong&gt; &lt;a href="https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MiBeeNvr NVR Server:&lt;/strong&gt; &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/MiBeeNvr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PRs, feature requests, and bug reports are all welcome! This is a community project - let's build the best open source camera + NVR ecosystem together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In an era where every IoT device seems to want your data, having fully open, local-only options is more important than ever. This camera + NVR stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Never sends data anywhere&lt;/li&gt;
&lt;li&gt;✅ You can audit every line of code&lt;/li&gt;
&lt;li&gt;✅ You control all configuration&lt;/li&gt;
&lt;li&gt;✅ Works with your existing self-hosted tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No subscriptions, no accounts, no tracking. Just a complete surveillance system that works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-discovery between cameras and MiBeeNvr&lt;/li&gt;
&lt;li&gt;MQTT support for Home Assistant&lt;/li&gt;
&lt;li&gt;SD card recording on cameras&lt;/li&gt;
&lt;li&gt;Two-way audio&lt;/li&gt;
&lt;li&gt;ONVIF support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know what features you'd like to see!&lt;/p&gt;

</description>
      <category>esp32</category>
      <category>iot</category>
      <category>opensource</category>
    </item>
    <item>
      <title>VictoriaMetrics Stream Aggregation: A Three-Year Retrospective (2026)</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Sun, 07 Jun 2026 22:56:28 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/victoriametrics-stream-aggregation-a-three-year-retrospective-2026-2ojg</link>
      <guid>https://dev.to/mickey_zzc/victoriametrics-stream-aggregation-a-three-year-retrospective-2026-2ojg</guid>
      <description>&lt;p&gt;It's been exactly three years since the &lt;a href="https://blog.mickeyzzc.tech/en/posts/telemetry/stream-metrics-one/" rel="noopener noreferrer"&gt;first article&lt;/a&gt; in this series was published in March 2023. The VictoriaMetrics ecosystem has changed dramatically since then. Let's revisit the problems we laid out, see what the official project has resolved, and assess where our custom &lt;code&gt;stream-metrics-route&lt;/code&gt; gateway stands today.&lt;/p&gt;




&lt;h2&gt;
  
  
  I. The Problems We Identified in 2023
&lt;/h2&gt;

&lt;p&gt;Here's a quick recap of the core issues from the original post:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;2023 Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Collection gap inflation&lt;/td&gt;
&lt;td&gt;Network jitter or performance issues cause time gaps that inflate stream aggregation deltas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Single-node compute limits&lt;/td&gt;
&lt;td&gt;Stream aggregation has no historical state, fast but single-instance bottlenecked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P3&lt;/td&gt;
&lt;td&gt;Distributed task allocation&lt;/td&gt;
&lt;td&gt;Which compute node should each sample be assigned to?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P4&lt;/td&gt;
&lt;td&gt;Out-of-order discarding for same-dimension metrics&lt;/td&gt;
&lt;td&gt;Multiple nodes computing the same dimension with different time windows causes later values to be discarded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P5&lt;/td&gt;
&lt;td&gt;Resource balancing&lt;/td&gt;
&lt;td&gt;Uneven load across distributed compute nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P6&lt;/td&gt;
&lt;td&gt;Task ID dimension explosion&lt;/td&gt;
&lt;td&gt;Stream aggregation inserts node IDs into aggregated time series — the label cardinality grows with every node you add&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To address these, we built &lt;a href="https://github.com/Mi-Bee-Studio/stream-metrics-route" rel="noopener noreferrer"&gt;&lt;code&gt;stream-metrics-route&lt;/code&gt;&lt;/a&gt;, a Go-based distributed stream aggregation gateway.&lt;/p&gt;




&lt;h2&gt;
  
  
  II. Three Years Later — What the Official Project Has Done
&lt;/h2&gt;

&lt;p&gt;I reviewed VictoriaMetrics changelogs from v1.86 through v1.138.0 and the official documentation. Here's the scorecard.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Perfectly Resolved
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Issues P3, P5: Distributed Task Allocation &amp;amp; Resource Balancing
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Official solution:&lt;/strong&gt; vmagent now natively supports &lt;code&gt;-remoteWrite.shardByURL&lt;/code&gt; with &lt;strong&gt;consistent hashing&lt;/strong&gt; sharding.&lt;/p&gt;

&lt;p&gt;Starting from v1.86, basic &lt;code&gt;shardByURL&lt;/code&gt; was introduced. &lt;strong&gt;v1.138.0 (March 2026)&lt;/strong&gt; was the real milestone — it upgraded the data distribution algorithm from round-robin to &lt;strong&gt;consistent hashing&lt;/strong&gt;, which significantly reduces data redistribution ratios during node changes.&lt;/p&gt;

&lt;p&gt;The architecture evolution looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐     ┌─────────────────┐
│ Prometheus      │     │ Prometheus      │
│ Agent 1         │     │ Agent 2         │
└────────┬────────┘     └────────┬────────┘
         │ remote write          │ remote write
         ▼                       ▼
┌─────────────────────────────────────────┐
│           vmagent Cluster               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐│
│  │vmagent-0 │  │vmagent-1 │  │vmagent-2 ││
│  └─────┬────┘  └─────┬────┘  └─────┬────┘│
│        │             │             │     │
│        └─────────────┼─────────────┘     │
│                      ▼                   │
│           ┌──────────────────┐           │
│           │ Consistent Hash │           │
│           └────────┬─────────┘           │
└────────────────────┼─────────────────────┘
                     │ shard by hash
         ┌───────────┼───────────┐
         ▼           ▼           ▼
   ┌──────────┐ ┌──────────┐ ┌──────────┐
   │vmstorage │ │vmstorage │ │vmstorage │
   │    -0    │ │    -1    │ │    -2    │
   └──────────┘ └──────────┘ └──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The VictoriaMetrics blog provides specific algorithm recommendations. Combined with VictoriaMetrics Operator, you can manage shards via &lt;code&gt;shardCount&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Issue P2: Single-Node Compute Scaling
&lt;/h4&gt;

&lt;p&gt;vmagent now supports horizontal scaling natively with &lt;code&gt;replicas&lt;/code&gt; + &lt;code&gt;shardCount&lt;/code&gt;, including HA support — see &lt;a href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5573" rel="noopener noreferrer"&gt;Issue #5573&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Out-of-Order / Delayed Data Accuracy (P1 — Partial Mitigation)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;v1.112.0 (February 2025)&lt;/strong&gt; was a key release, adding &lt;strong&gt;Aggregation Windows&lt;/strong&gt; — dual-window buffering for histogram and rate calculations. Instead of flushing immediately, the output is delayed by a &lt;code&gt;samples_lag&lt;/code&gt; window, which significantly improves accuracy for late-arriving data. The tradeoff: roughly doubled memory usage (maintaining two aggregation windows simultaneously).&lt;/p&gt;

&lt;p&gt;How it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Collector          vmagent           VictoriaMetrics
   │                  │                    │
   │  sample1 @T0    │                    │
   ├─────────────────►│  Write to          │
   │                  │  Window A (current)│
   │                  │                    │
   │  sample2 @T1    │                    │
   │  (delayed)      │                    │
   ├─────────────────►│  Write to          │
   │                  │  Window B (previous)│
   │                  │                    │
   │                  │  Aggr result A @T2 │
   │                  ├────────────────────►│
   │                  │  Aggr result B @T3 │
   │                  ├────────────────────►│
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the official docs on &lt;a href="https://docs.victoriametrics.com/stream-aggregation/#aggregation-windows" rel="noopener noreferrer"&gt;streaming aggregation windows&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Still Unresolved
&lt;/h3&gt;

&lt;h4&gt;
  
  
  True Distributed Stream Aggregation Coordination
&lt;/h4&gt;

&lt;p&gt;vmagent's stream aggregation is &lt;strong&gt;single-instance&lt;/strong&gt;. There is no coordination mechanism between instances — if two vmagent instances aggregate the same metric, you get duplicate or conflicting output. The official recommendation is to use &lt;code&gt;without&lt;/code&gt;/&lt;code&gt;by&lt;/code&gt; label clauses to divide responsibility between instances, rather than providing a cross-instance coordination protocol.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task ID Dimension Explosion (P6)
&lt;/h4&gt;

&lt;p&gt;Official vmagent still inserts internal labels (such as &lt;code&gt;_aggr&lt;/code&gt;-related labels) into aggregated time series, but lacks a &lt;code&gt;stream_task_id&lt;/code&gt; pre-marking plus dimension control design.&lt;/p&gt;




&lt;h2&gt;
  
  
  III. stream-metrics-route: Current Status and Value
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Code Architecture
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;router.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Routing core — filters metrics based on relabel rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remotecluster.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dual hashmod scheduling core&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remotewrite.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote write HTTP client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kafka.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kafka producer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Core Algorithm (from &lt;code&gt;remotecluster.go&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Dual hashmod scheduling&lt;/span&gt;
&lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sortLabelsHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dime&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;hashMod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// First hashmod → task partition ID&lt;/span&gt;

&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"stream_task_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c"&gt;// Insert stream_task_id label&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;hashnode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sortLabelsHashKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filterLabels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Second hashmod → node selection&lt;/span&gt;
&lt;span class="n"&gt;tmpch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;hashMod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uplen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashnode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;// Which backend writer to send to&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is It Still Needed in 2026?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Yes — but with an adjusted role.&lt;/strong&gt; The positioning should shift from "full stream aggregation gateway" to &lt;strong&gt;"metric distribution routing gateway + Kafka integration layer."&lt;/strong&gt; The core differentiated value:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dual hashmod scheduling + &lt;code&gt;stream_task_id&lt;/code&gt; pre-injection&lt;/strong&gt; — tags metrics at the gateway layer, so all downstream nodes route consistently by this ID. This solves dimension control earlier in the pipeline than the official approach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-backend async distribution&lt;/strong&gt; — supports async distribution to both Kafka and remote write, solving the "synchronous forwarding blocks the time window" problem from the original post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Native Prometheus relabeling integration&lt;/strong&gt; — works with standard Prometheus relabel configs, no custom syntax to learn.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  IV. Recommended 2026 Hybrid Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐   ┌──────────────────┐
│  Prometheus     │   │  Business System │
│  Agent Cluster  │   │  Metrics (Kafka) │
└────────┬────────┘   └────────┬─────────┘
         │                     │
         ▼                     ▼
    ┌──────────────────────────────┐
    │   stream-metrics-route       │
    │   (Routing Layer)            │
    │   - Dual hashmod scheduling  │
    │   - stream_task_id injection │
    │   - Relabeling               │
    └──────┬──────┬──────┬─────────┘
           │      │      │
    task=0 │ task=1│task=2│
           ▼      ▼      ▼
    ┌─────────────────────────┐
    │   vmagent Cluster       │
    │  (v1.112.0+ with        │
    │   aggregation windows)  │
    └──────────┬──────────────┘
               │
               ▼
    ┌──────────────────┐      ┌────────────┐
    │  VictoriaMetrics │      │   Kafka    │
    │  (Storage)       │      │   (Topic)  │
    └──────────┬───────┘      └────────────┘
               │
               ▼
    ┌──────────────────┐
    │  vmalert         │
    │  Grafana         │
    └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;vmagent version requirement:&lt;/strong&gt; &amp;gt;= v1.112.0, with aggregation windows enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# stream aggregation config&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http_request_duration_seconds_bucket'&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
  &lt;span class="na"&gt;without&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;instance&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;enable_windows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# Critical! Enables dual-window buffering&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;rate_sum&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  V. Evolution Recommendations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Short-term
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upgrade vmagent to &amp;gt;= v1.112.0&lt;/td&gt;
&lt;td&gt;Enable &lt;code&gt;enable_windows: true&lt;/code&gt; to improve histogram aggregation accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluate stream-metrics-route necessity&lt;/td&gt;
&lt;td&gt;If you have no Kafka requirement or high-cardinality &lt;code&gt;stream_task_id&lt;/code&gt; control need, consider migrating away&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Medium-term
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retain stream-metrics-route as front-end routing only&lt;/td&gt;
&lt;td&gt;Keep hashmod task allocation + Kafka distribution; remove aggregation responsibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable raw metric persistence&lt;/td&gt;
&lt;td&gt;Write only stream-aggregated results to storage to reduce volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add metadata management module&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;ruler-handle-process&lt;/code&gt; from the original post (dynamic Record Rule by dimension) is worth building or contributing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Long-term
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contribute &lt;code&gt;stream_task_id&lt;/code&gt; dimension control upstream&lt;/td&gt;
&lt;td&gt;If the dual hashmod design proves out in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Improve monitoring metrics&lt;/td&gt;
&lt;td&gt;Add business-level metrics — queue depth per routing rule, distribution latency, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Assessment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Problem resolution rate&lt;/td&gt;
&lt;td&gt;~50% — 2 of 4 core problems resolved via official upgrades; 2 still need custom solutions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is stream-metrics-route still needed?&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes&lt;/strong&gt; — repositioned as "metric distribution routing gateway + Kafka integration layer"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended architecture&lt;/td&gt;
&lt;td&gt;Prometheus → stream-metrics-route → vmagent v1.112.0+ → VictoriaMetrics Storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three years is a long time in the observability space. The VictoriaMetrics ecosystem has matured significantly — consistent hashing, aggregation windows, and native sharding all address problems that required custom tooling in 2023. But the hard problems around true &lt;em&gt;distributed&lt;/em&gt; stream aggregation coordination and dimension control at the gateway layer remain open.&lt;/p&gt;

&lt;p&gt;If you're running a similar stack, the hybrid approach — letting the official project handle what it's good at (single-node aggregation, storage) while keeping custom routing for what it isn't (distributed coordination, dimension pre-injection, Kafka bridging) — has proven to be the right call for us.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://blog.mickeyzzc.tech/en/posts/telemetry/stream-metrics-two/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>performance</category>
    </item>
    <item>
      <title>Scaling VictoriaMetrics Stream Aggregation: A Deep Dive into Distributed Design（2023）</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Sun, 07 Jun 2026 22:50:31 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/scaling-victoriametrics-stream-aggregation-a-deep-dive-into-distributed-design-629</link>
      <guid>https://dev.to/mickey_zzc/scaling-victoriametrics-stream-aggregation-a-deep-dive-into-distributed-design-629</guid>
      <description>&lt;p&gt;VictoriaMetrics' stream aggregation is a powerful feature for reducing metric cardinality in real time. But when you push it to millions of time series across a distributed fleet, the native implementation starts to show cracks.&lt;/p&gt;

&lt;p&gt;This post walks through what I found analyzing the stream aggregation source code, the real-world problems that emerged at scale, and the distributed gateway we built to solve them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Community VM Stream Aggregation — Capability Analysis
&lt;/h2&gt;

&lt;p&gt;Stream aggregation was integrated into &lt;code&gt;vmagent&lt;/code&gt; starting from version 1.86 (&lt;a href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3460" rel="noopener noreferrer"&gt;GitHub issue #3460&lt;/a&gt;). Let's look at what it actually does under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Computation: The &lt;code&gt;pushSample&lt;/code&gt; Function
&lt;/h3&gt;

&lt;p&gt;The heart of stream aggregation lives in the &lt;code&gt;pushSample&lt;/code&gt; function. Here's the simplified logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;totalAggrState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pushSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outputKey&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fasttime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnixTimestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;deleteDeadline&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intervalSecs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intervalSecs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;again&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;totalStateValue&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lastValues&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;lastValueState&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;vNew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadOrStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vNew&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;totalStateValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deleted&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lastValueState&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ignoreInputDeadline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deleteDeadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deleteDeadline&lt;/span&gt;
        &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deleteDeadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deleteDeadline&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;again&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core idea: each incoming sample's value is compared against the last seen value for that time series. The &lt;em&gt;delta&lt;/em&gt; is accumulated into a running total. This works well for counters that monotonically increase, but there's a critical detail — the time window logic is simple periodic checking with no sophisticated handling for delayed or out-of-order arrivals.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Stream Aggregation Looks Like in Practice
&lt;/h3&gt;

&lt;p&gt;In theory, stream aggregation should cleanly reduce high-cardinality metrics down to manageable summaries. In practice, the picture is messier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ideal model&lt;/strong&gt;: every sample arrives on time, windows align perfectly, aggregation is lossless&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reality&lt;/strong&gt;: samples arrive late, retries flood old data, gaps appear from network or service issues&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Issues with Native Stream Aggregation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Collection Gap Problem
&lt;/h3&gt;

&lt;p&gt;Collection gaps are inevitable. Network blips, service restarts, GC pauses — any of these can cause a gap in metric collection. For high-precision stream aggregation, gaps create a specific failure mode:&lt;/p&gt;

&lt;p&gt;When a counter's last tracked value is &lt;code&gt;1000&lt;/code&gt;, and a gap causes the next received value to be &lt;code&gt;5000&lt;/code&gt;, the delta is &lt;code&gt;4000&lt;/code&gt; — which may span an unknown number of actual increments. If the gap occurred &lt;em&gt;within&lt;/em&gt; a single aggregation window, the inflated value corrupts that window's result. If the gap crosses window boundaries, you get compounding errors in downstream calculations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed Computing Challenges
&lt;/h3&gt;

&lt;p&gt;Stream aggregation doesn't persist historical data, so it's fast — but even the fastest service has single-node limits. When you need to scale horizontally, a cascade of new problems appears:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is vmagent's built-in collection viable at scale?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In testing, enabling vmagent shard + replica collection with real-time stream aggregation caused significant resource spikes. At very large scales, collection gaps became more frequent, which &lt;em&gt;amplified&lt;/em&gt; the calculation errors from gaps rather than mitigating them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which compute node should each sample be assigned to?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without a consistent routing strategy, the same metric dimension gets split across nodes, producing partial results that can't be safely combined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you handle the same dimension set being computed by multiple nodes with different values?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When multiple nodes produce results for the same dimension within the same time window, VictoriaMetrics triggers its out-of-order handling logic — which discards later values. You lose data silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you balance resources in distributed computation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Uneven distribution means some nodes are overloaded while others sit idle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about the new dimensions introduced by routing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you insert a task ID to differentiate compute nodes, you've just added a new dimension that grows with every node you add — defeating part of the purpose of aggregation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design and Implementation: A Distributed Stream Aggregation Gateway
&lt;/h2&gt;

&lt;p&gt;After analyzing these problems, it became clear that a frontend module was needed to address them at the entry point. Since &lt;code&gt;vmgateway&lt;/code&gt; is an enterprise component, we built our own: &lt;strong&gt;&lt;code&gt;vm-receive-route&lt;/code&gt;&lt;/strong&gt;, a distributed stream aggregation gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Insight from Source Code
&lt;/h3&gt;

&lt;p&gt;Two aspects of the native implementation are particularly relevant:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time window range:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fasttime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnixTimestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;deleteDeadline&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intervalSecs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intervalSecs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The window has a 50% grace period (&lt;code&gt;intervalSecs &amp;gt;&amp;gt; 1&lt;/code&gt;) beyond the configured interval. This is the only protection against late-arriving data — and it's quite generous, which means stale data can still influence results within that extended window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calculation logic:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lastValueState&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ignoreInputDeadline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The aggregation only checks if a previous value exists and is smaller — it doesn't handle the case where a late-arriving sample with a lower value (after a restart, for example) creates a negative delta that's simply ignored. There's no deduplication, no gap detection, no intelligent merging.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Gateway Solves
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Asynchronous Processing
&lt;/h4&gt;

&lt;p&gt;Most &lt;code&gt;remote write&lt;/code&gt; adapters (like &lt;code&gt;prometheus-kafka-adapter&lt;/code&gt;) do synchronous forwarding — they wait for the downstream (Kafka, etc.) to acknowledge before accepting the next batch. Stream aggregation has &lt;em&gt;window constraints&lt;/em&gt;: if the write pipeline blocks and samples arrive late, they miss their window and calculations drift.&lt;/p&gt;

&lt;p&gt;The gateway decouples ingestion from forwarding using an internal buffer. The &lt;code&gt;remote write&lt;/code&gt; endpoint returns immediately, and samples are forwarded asynchronously to the stream aggregation backend. This prevents back-pressure from creating cascading delays.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Time Window Filtering
&lt;/h4&gt;

&lt;p&gt;Since stream aggregation already computes deltas between successive values, there's no need for complex out-of-order handling at this layer. The gateway simply cooperates with the aggregation window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Samples arriving within the window → forward normally&lt;/li&gt;
&lt;li&gt;Samples arriving outside the window → discard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This solves two problems at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus retries that send large backlogs of old samples no longer corrupt real-time results&lt;/li&gt;
&lt;li&gt;The resource overhead of processing those retries is eliminated at the gateway level — the aggregation backend never sees them&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Dimension Control
&lt;/h4&gt;

&lt;p&gt;The stream aggregation component inserts a node ID into each aggregated time series to distinguish labels across compute nodes. But as nodes scale horizontally, the cardinality of &lt;em&gt;that label&lt;/em&gt; scales too. You need a way to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Control dimension growth from node identities&lt;/li&gt;
&lt;li&gt;Route time series by dimension to the correct node&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We designed a &lt;strong&gt;dual hashmod scheduling algorithm&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The gateway assigns a hash-based task ID to each time series based on its stable dimensions (not the node identity)&lt;/li&gt;
&lt;li&gt;The same series always routes to the same compute node, regardless of which gateway instance processed it&lt;/li&gt;
&lt;li&gt;The task ID dimension is bounded by the number of unique dimension combinations, not the number of gateway nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By moving the task ID labeling to the gateway layer, we eliminated the unbounded dimension growth that horizontal scaling would otherwise cause.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Backend Service Migration for Failures
&lt;/h4&gt;

&lt;p&gt;When a compute node fails, its in-memory aggregation state is lost. The gateway detects failures via health checks and reroutes traffic to healthy nodes. Since the dual hashmod ensures consistent routing, the remaining nodes can immediately pick up the work, though there will be a brief period of incomplete aggregation until the state rebuilds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Record Rule Dimension Task Generator
&lt;/h2&gt;

&lt;p&gt;Stream aggregation is great for reducing cardinality of &lt;em&gt;single metrics&lt;/em&gt;. But real-world monitoring scenarios require combining multiple metrics with functions — which is where Prometheus &lt;code&gt;Record Rule&lt;/code&gt; comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Record Rules at Scale
&lt;/h3&gt;

&lt;p&gt;Consider an HTTP request metric with a &lt;code&gt;req_path&lt;/code&gt; dimension:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before stream aggregation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight prometheus"&gt;&lt;code&gt;&lt;span class="n"&gt;a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"30021"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"202"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;req_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/api/foo?abc=xyz"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"30023"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"202"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;req_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/api/bar?abc=def"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10021"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"202"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;req_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/api/baz?abc=ghi"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After stream aggregation (dropping &lt;code&gt;req_path&lt;/code&gt; and &lt;code&gt;src_port&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight prometheus"&gt;&lt;code&gt;&lt;span class="n"&gt;agg_a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"202"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;agg_a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"500"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;agg_a_http_req_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bj"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;src_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dis_svr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"192.168.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"400"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what you &lt;em&gt;actually&lt;/em&gt; want to display is the success rate per target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sum by (dis_svr) (
    rate(a_http_req_total{code=~"2.*"}[5m])
)
/
sum by (dis_svr) (
    rate(a_http_req_total{}[5m])
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;Record Rule&lt;/code&gt; to precompute this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;a_http_req_total:sum:rate:5m&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (src_svr, dis_svr, code) (rate(a_http_req_total{}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;a_http_req_total:sum:rate:5m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem? &lt;code&gt;Record Rule&lt;/code&gt; loads &lt;em&gt;all&lt;/em&gt; dimensions of the metric into memory. When dimension counts reach critical thresholds, it triggers OOM. Even below that threshold, higher cardinality means slower computation.&lt;/p&gt;

&lt;p&gt;In production, &lt;code&gt;istio_requests_total&lt;/code&gt; QPS could be delayed by &lt;strong&gt;20 minutes&lt;/strong&gt; at high dimension counts. After applying stream aggregation to reduce from tens of millions of time series down to tens of thousands, the delay dropped to 1-2 minutes — better, but still far from real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Dimension-Split Record Rules
&lt;/h3&gt;

&lt;p&gt;The issue is that &lt;code&gt;Record Rule&lt;/code&gt; evaluates one query per group, loading everything into memory. But if you split the query by specific dimension values, you can process them concurrently.&lt;/p&gt;

&lt;p&gt;The static approach after stream aggregation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m-2xx&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (src_svr, dis_svr, code) (rate(agg_a_http_req_total{code=~"2.*"}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m-4xx&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (src_svr, dis_svr, code) (rate(agg_a_http_req_total{code=~"4.*"}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m-5xx&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (src_svr, dis_svr, code) (rate(agg_a_http_req_total{code=~"5.*"}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agg_a_http_req_total:sum:rate:5m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the problem is that in production, dimension labels are dynamic. You can't hardcode splits for every dimension value. You need a &lt;strong&gt;label metadata management system&lt;/strong&gt; that watches dimension combinations and dynamically generates split queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;ruler-handle-process&lt;/code&gt; Component
&lt;/h3&gt;

&lt;p&gt;We built a small metadata watch and rule builder that automates this. Its configuration looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;recode_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
    &lt;span class="na"&gt;recode_to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total:sum:rate:5m&lt;/span&gt;
    &lt;span class="na"&gt;metric_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total&lt;/span&gt;
    &lt;span class="na"&gt;aggr_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum&lt;/span&gt;
    &lt;span class="na"&gt;vector_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate&lt;/span&gt;
    &lt;span class="na"&gt;vector_range&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
    &lt;span class="na"&gt;group_by_and_filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;source_workload&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;destination_workload&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cluster&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;namespace&lt;/span&gt;
    &lt;span class="na"&gt;group_by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;response_code&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;namespace&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;source_workload_namespace&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;destination_workload_namespace&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;destination_service_name&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cluster&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;reporter&lt;/span&gt;
    &lt;span class="na"&gt;filter_by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k8s-hw-bj-xxxxxx"&lt;/span&gt;
    &lt;span class="na"&gt;with_out&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;source_workload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ingressgateway-workflows"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component watches the actual dimension combinations under the metric name &lt;code&gt;istio_requests_total&lt;/code&gt; and generates a set of &lt;code&gt;Record Rule&lt;/code&gt; configurations — one per unique dimension combination. Combined with Prometheus's Rule component for concurrent evaluation, this reduced computation latency from minutes to &lt;strong&gt;seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The generated rules look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total:sum:rate:5m-7218756fe8a0bc327e818812cefb02f7&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (...) (rate(istio_requests_total{cluster="k8s-hw-bj-1-prod", destination_workload="skyaxe-778-flink", ...}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total:sum:rate:5m&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total:sum:rate:5m-8e30244048f8d5519a6332f309578ed4&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sum by (...) (rate(istio_requests_total{cluster="k8s-hw-bj-1-prod", destination_workload="t-bean-portal", ...}[5m]))&lt;/span&gt;
        &lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio_requests_total:sum:rate:5m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each unique dimension combination gets its own rule group, enabling true concurrent computation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Combinations for Different Scales
&lt;/h2&gt;

&lt;p&gt;Using community open-source components alongside our custom gateway and rule builder, we assembled a tiered architecture that handles everything from small deployments to massive fleets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small Scale: Tens of Thousands of Single Metrics
&lt;/h3&gt;

&lt;p&gt;Minimal setup — vmagent with built-in stream aggregation. No gateway needed. The single-node limits aren't reached yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Medium Scale: Tens of Thousands of Multi-Metrics
&lt;/h3&gt;

&lt;p&gt;Add Record Rules for computed metrics. Use the dimension-split approach to keep computation fast. Stream aggregation reduces cardinality before Record Rule evaluation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large Scale: Millions+ of Single Metrics
&lt;/h3&gt;

&lt;p&gt;This is where the distributed gateway comes in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple vmagent instances behind the gateway for horizontal ingestion&lt;/li&gt;
&lt;li&gt;Dual hashmod scheduling ensures consistent routing&lt;/li&gt;
&lt;li&gt;Time window filtering at the gateway prevents retries from polluting results&lt;/li&gt;
&lt;li&gt;Asynchronous forwarding prevents back-pressure from creating cascading delays&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Large Scale: Scenario-Based Computational Aggregation for Millions of Metrics
&lt;/h3&gt;

&lt;p&gt;The full stack:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stream aggregation&lt;/strong&gt; — first pass cardinality reduction at ingestion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed gateway&lt;/strong&gt; — routing, filtering, and load distribution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic rule builder&lt;/strong&gt; — watches dimension metadata and generates concurrent Record Rule groups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule engine&lt;/strong&gt; — evaluates split queries in parallel&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;VictoriaMetrics' stream aggregation is a solid foundation, but it was designed for single-node operation. When you need to scale horizontally, you encounter a series of interconnected problems — collection gaps, dimension explosion, record rule performance bottlenecks — that the native implementation doesn't address.&lt;/p&gt;

&lt;p&gt;The solutions aren't particularly complex individually (async processing, time window filtering, hash-based routing, dimension-aware rule generation), but they need to work together as a coherent system. The gateway pattern — intercepting the &lt;code&gt;remote write&lt;/code&gt; path before it reaches the aggregation layer — proved to be the right abstraction point for injecting these capabilities without modifying upstream code.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ruler-handle-process&lt;/code&gt; component was the second key insight: rather than fighting Prometheus Record Rule's single-query-per-group limitation, we embraced it by dynamically splitting queries by dimension and running them concurrently. This turned a 20-minute computation into a seconds-level operation.&lt;/p&gt;

&lt;p&gt;If you're running VictoriaMetrics at scale and hitting these issues, the patterns described here should be applicable regardless of your specific stack. The gateway approach is generic enough to work with any Prometheus-compatible &lt;code&gt;remote write&lt;/code&gt; backend.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://blog.mickeyzzc.tech/en/posts/telemetry/stream-metrics-one/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>monitoring</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>MiBeeNvr: A Lightweight Home NVR System I Built</title>
      <dc:creator>ZhengZhiCong</dc:creator>
      <pubDate>Sun, 07 Jun 2026 09:27:47 +0000</pubDate>
      <link>https://dev.to/mickey_zzc/mibeenvr-a-lightweight-home-nvr-system-i-built-41b</link>
      <guid>https://dev.to/mickey_zzc/mibeenvr-a-lightweight-home-nvr-system-i-built-41b</guid>
      <description>&lt;p&gt;I have several cameras at home — a few Xiaomi cameras, some DIY ESP32 cameras, and multiple Raspberry Pi CSI cameras. I'd been using cloud storage solutions, but I was never comfortable with them: vendor lock-in, network dependency, and the costs add up. So I decided to build my own NVR system, called MiBeeNvr.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Build MiBeeNvr
&lt;/h2&gt;

&lt;p&gt;To be honest, I was never satisfied with existing cloud storage solutions. Take Xiaomi cameras, for example. By default, you can only view them through the Mi Home app. Recordings are either stored on an SD card (limited capacity, frequent plugging/unplugging) or in the cloud. Cloud storage costs tens of dollars per month, and there's the privacy concern — you never know when the manufacturer might use your video data for AI training or sell it to third parties. Not to mention vendor lock-in — switching platforms is nearly impossible.&lt;/p&gt;

&lt;p&gt;ESP32 cameras have a similar problem. I built several ESP32 cameras, storing recordings on SD cards, but viewing and playback were inconvenient. I needed a unified management platform.&lt;/p&gt;

&lt;p&gt;I also tried other open-source solutions: ZoneMinder requires a LAMP stack — installing and deploying it is more complex than my entire project; Shinobi's configuration is a nightmare; and some smaller projects are basically unmaintained. Frigate is nice but primarily focused on AI detection and depends on Docker — too heavy.&lt;/p&gt;

&lt;p&gt;In short, I wanted something that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single binary file — download and run&lt;/li&gt;
&lt;li&gt;Lightweight enough to run on a Raspberry Pi&lt;/li&gt;
&lt;li&gt;Supports multiple camera types, especially Xiaomi's proprietary protocol&lt;/li&gt;
&lt;li&gt;Clean Web interface without frontend complexity&lt;/li&gt;
&lt;li&gt;Auto-cleanup of old recordings, won't fill up the disk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After searching around, none of the existing solutions fit. So I wrote my own.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is MiBeeNvr
&lt;/h2&gt;

&lt;p&gt;MiBeeNvr is a lightweight NVR system written in Go, designed to solve local storage for home cameras.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overall Architecture
&lt;/h3&gt;

&lt;p&gt;The system has three layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Camera End&lt;/strong&gt; — multiple camera types connect through different protocols:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Xiaomi cameras use the proprietary "miss" protocol (encrypted, multi-layer)&lt;/li&gt;
&lt;li&gt;ESP32 cameras send HTTP JPEG/MJPEG streams directly&lt;/li&gt;
&lt;li&gt;Raspberry Pi CSI cameras output standard RTSP H.264 via MediaMTX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Protocol Bridge Layer&lt;/strong&gt; — proprietary protocols get converted to standard RTSP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;go2rtc&lt;/code&gt; handles Xiaomi's miss protocol decryption and transcodes it to RTSP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MediaMTX&lt;/code&gt; converts Raspberry Pi CSI interface video to RTSP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MiBeeNvr Core&lt;/strong&gt; — handles the recording logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST API receives all video streams&lt;/li&gt;
&lt;li&gt;Recording engine segments incoming video into MP4 files&lt;/li&gt;
&lt;li&gt;SQLite stores metadata (pure Go, no CGO, no separate DB installation)&lt;/li&gt;
&lt;li&gt;Auto-cleanup daemon removes old recordings per retention policy&lt;/li&gt;
&lt;li&gt;HLS live streaming for real-time viewing (up to 4 concurrent)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Access Methods&lt;/strong&gt; — users interact through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in Web UI (Svelte 5 SPA, embedded in the single binary)&lt;/li&gt;
&lt;li&gt;WebDAV (read-write) and FTP for file-level access&lt;/li&gt;
&lt;li&gt;Prometheus metrics for system monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: cameras → protocol bridge → MiBeeNvr core → user access, all in a single binary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording Pipeline
&lt;/h3&gt;

&lt;p&gt;Video processing follows a straightforward pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt; — RTSP streams are handled via &lt;code&gt;gortsplib&lt;/code&gt;, HTTP JPEG streams are periodically grabbed as frames&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decode &amp;amp; Mux&lt;/strong&gt; — RTP packets are depacketized via &lt;code&gt;pion/rtp&lt;/code&gt;, then muxed into MP4 via &lt;code&gt;go-mp4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt; — video is segmented into files (configurable: 30s or 10m intervals), SQLite tracks metadata, files go to disk&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The frontend uses Svelte 5 — the entire SPA is compiled to static assets and embedded into the Go binary. Deployment is a single file, no separate Web server needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend tech stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.26 + modernc.org/sqlite (pure Go, no CGO dependency)&lt;/li&gt;
&lt;li&gt;chi routing library, clean and efficient&lt;/li&gt;
&lt;li&gt;gortsplib for RTSP/RTP protocol&lt;/li&gt;
&lt;li&gt;pion/rtp for real-time streaming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SQLite was chosen because it's single-file, pure Go, performs well enough for home use, supports concurrent access, and most importantly, doesn't require a separate database installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Philosophy
&lt;/h3&gt;

&lt;p&gt;The entire project's design philosophy is &lt;strong&gt;"simple and straightforward:"&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single binary file, no external dependencies&lt;/li&gt;
&lt;li&gt;Supports cross-compilation, runs on AMD64/ARM64&lt;/li&gt;
&lt;li&gt;YAML configuration, intuitive&lt;/li&gt;
&lt;li&gt;Built-in Web interface, open browser to use&lt;/li&gt;
&lt;li&gt;Minimal resource usage, runs smoothly on Raspberry Pi 4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports multiple camera protocols: RTSP (H.264/H.265), HTTP JPEG&lt;/li&gt;
&lt;li&gt;Built-in Web interface with dark/light theme switching&lt;/li&gt;
&lt;li&gt;Chinese/English bilingual support&lt;/li&gt;
&lt;li&gt;WebDAV (read-write), FTP, REST API&lt;/li&gt;
&lt;li&gt;MQTT-triggered recording, ideal for smart home integration&lt;/li&gt;
&lt;li&gt;Prometheus monitoring metrics&lt;/li&gt;
&lt;li&gt;Per-camera independent retention policies&lt;/li&gt;
&lt;li&gt;MP4 segmented recording, auto-cleanup of old files&lt;/li&gt;
&lt;li&gt;Supports HLS live streaming (up to 4 concurrent)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  My Actual Deployment
&lt;/h2&gt;

&lt;p&gt;I run it on an ARM64 mini host with 512MB RAM and 2GB storage. The system runs very stably — basically set it and forget it.&lt;/p&gt;

&lt;p&gt;Connected 4 cameras, each with its own characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Raspberry Pi CSI Camera&lt;/strong&gt; — RTSP bridge via MediaMTX, converting CSI interface video to standard RTSP. Configured as &lt;code&gt;rtsp_h264&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESP32-S3 Camera&lt;/strong&gt; — DIY, running MJPEG stream via HTTP protocol. Configured as &lt;code&gt;http_jpeg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xiaomi Camera (Balcony)&lt;/strong&gt; — Protocol conversion via go2rtc (Xiaomi proprietary → RTSP), 2K resolution, configured as &lt;code&gt;rtsp_h265&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xiaomi Camera (Living Room)&lt;/strong&gt; — Same as above, 1080P.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Configuration is 30-second segment recording with 1-day retention. This interval is a trade-off: too short creates too many files, too long makes it inconvenient to look up incidents. WebDAV (read-write) and FTP are enabled for convenient phone viewing and backup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ezz6dqetie2wev5hqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ezz6dqetie2wev5hqs.png" alt="Camera management page" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Web interface is clean — camera management and recording lists are straightforward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqppfsih7x2m5bczottro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqppfsih7x2m5bczottro.png" alt="Recording list (dark)" width="800" height="957"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Settings page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4795f8lnr5rtl9hsaris.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4795f8lnr5rtl9hsaris.png" alt="Settings page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration File
&lt;/h3&gt;

&lt;p&gt;The complete configuration file looks like this — YAML format, clear at a glance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:9090"&lt;/span&gt;

&lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;root_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/mnt/data/nvr"&lt;/span&gt;
  &lt;span class="na"&gt;segment_duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;30s"&lt;/span&gt;

&lt;span class="na"&gt;cameras&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rpi-csi-cam"&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RPi&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CSI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Camera"&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp_h264"&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp://10.0.1.100:8554/stream"&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;esp32-cam"&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ESP32-S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Camera"&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http_jpeg"&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://10.0.1.101/capture"&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xiaomi-balcony"&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Xiaomi&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Camera"&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp_h265"&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp://10.0.1.102:8554/xiaomi_stream"&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;retention_days&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="na"&gt;disk_threshold_percent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95&lt;/span&gt;

&lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin"&lt;/span&gt;
  &lt;span class="na"&gt;password_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mibee-nvr&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hash-password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;generate"&lt;/span&gt;

&lt;span class="na"&gt;webdav&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;path_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dav"&lt;/span&gt;
  &lt;span class="na"&gt;read_write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;ftp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2121&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Xiaomi Camera Integration
&lt;/h2&gt;

&lt;p&gt;Xiaomi camera protocol is a major headache. It uses its proprietary "miss" (Mi Secure Streaming) protocol with multi-layer encryption, without a standard RTSP interface. Even if you know the camera's IP, you can't pull a stream with VLC.&lt;/p&gt;

&lt;p&gt;Fortunately, there's &lt;a href="https://github.com/AlexxIT/go2rtc" rel="noopener noreferrer"&gt;go2rtc&lt;/a&gt;, a lifesaver. The integration works in four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Account auth &amp;amp; key exchange&lt;/strong&gt; — go2rtc logs into your Xiaomi account, retrieves device lists and encryption keys from Xiaomi Cloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Establish P2P connection&lt;/strong&gt; — go2rtc initiates a miss protocol handshake with the camera, establishing a peer-to-peer link&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video stream relay&lt;/strong&gt; — the camera sends its encrypted miss stream to go2rtc continuously; go2rtc decrypts and transcodes it into a standard RTSP stream (H.265); MiBeeNvr receives this as a normal RTSP camera and records segmented MP4 files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independent access&lt;/strong&gt; — once set up, you no longer need the Mi Home app or Xiaomi cloud subscription; all recordings are accessible via MiBeeNvr's Web UI, WebDAV, or FTP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The entire process requires no firmware flashing, no camera disassembly, and no Xiaomi cloud storage subscription. go2rtc handles all the protocol conversion.&lt;/p&gt;

&lt;h3&gt;
  
  
  go2rtc Deployment
&lt;/h3&gt;

&lt;p&gt;The easiest way to deploy go2rtc is with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create configuration file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go2rtc.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
streams:
  xiaomi_balcony:
    - xiaomi://your_account:cn@10.0.1.100?did=your_camera_did&amp;amp;model=isa.camera.hlc7
  xiaomi_living_room:
    - xiaomi://your_account:cn@10.0.1.101?did=your_camera_did&amp;amp;model=isa.camera.mj200

rtsp:
  listen: ":8554"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Run container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; go2rtc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8554:8554 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 1984:1984 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/go2rtc.yaml:/config.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  alexxit/go2rtc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;xiaomi://&lt;/code&gt; protocol requires Xiaomi account and password authentication&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;did&lt;/code&gt; is the device's unique identifier, &lt;code&gt;model&lt;/code&gt; is the device model (can be found in the Mi Home app)&lt;/li&gt;
&lt;li&gt;go2rtc automatically handles P2P connection and miss protocol decryption&lt;/li&gt;
&lt;li&gt;The final standard RTSP stream is exposed on port 8554, and MiBeeNvr connects to it like any normal camera&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then point to go2rtc in MiBeeNvr's config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cameras&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xiaomi-balcony"&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Xiaomi&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Camera"&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp_h265"&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rtsp://localhost:8554/xiaomi_balcony"&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pitfalls Encountered
&lt;/h3&gt;

&lt;p&gt;Xiaomi camera integration has several pitfalls:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First-time network connection&lt;/strong&gt;: Xiaomi cameras must be able to reach the internet for key exchange with Xiaomi servers. After connection is established, subsequent transmission is over LAN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device ID acquisition&lt;/strong&gt;: Each camera's &lt;code&gt;did&lt;/code&gt; is unique. Use go2rtc's WebUI (port 1984) for auto-discovery, or dig through the Mi Home app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not all models are supported&lt;/strong&gt;: go2rtc maintains a compatibility list — check before buying a camera.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;H.265 vs H.264&lt;/strong&gt;: Newer Xiaomi cameras mostly use H.265. MiBeeNvr supports both codecs, but H.265 saves storage space.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ESP32 Camera Projects
&lt;/h2&gt;

&lt;p&gt;While working on MiBeeNvr, I also built several ESP32 camera firmware projects. ESP32 cameras had their share of pitfalls, but were also quite interesting.&lt;/p&gt;

&lt;p&gt;I built three firmware projects with different positioning, all designed as upstream capture endpoints for MiBeeNvr — cameras handle video capture, MiBeeNvr handles unified storage and management.&lt;/p&gt;

&lt;h3&gt;
  
  
  MiBeeCam — ESP32-S3-A10 Solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Mi-Bee-Studio/luatos-esp32s3-a10-camera" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; · MIT License&lt;/p&gt;

&lt;p&gt;This is the most successful solution. ESP32-S3-A10 dev board + OV2640 camera (8225N module), 16MB Flash, ESP-IDF v5.4.3 development. Features include MJPEG stream, frame-differencing motion detection, Web config interface, Prometheus metrics. Having an LCD screen makes debugging much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Thinker ESP32-CAM — Classic Solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Mi-Bee-Studio/ai-thinker-esp32-cam" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; · MIT License&lt;/p&gt;

&lt;p&gt;Entry-level choice, AI Thinker ESP32-CAM dev boards are widely available for around $10-15. 4MB Flash + 4MB PSRAM, runs MJPEG stream without issues. Highlights include SD card storage and NAS upload (WebDAV/HTTP), plus adaptive dark scene detection — automatically switches to infrared mode at night. Downside: no screen, 4MB Flash is limited.&lt;/p&gt;

&lt;h3&gt;
  
  
  MiBeeHomeCam — XIAO ESP32-S3 Sense
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; · GPL v3.0&lt;/p&gt;

&lt;p&gt;The most advanced solution. XIAO ESP32-S3 Sense board is compact and refined, with dual camera support (OV2640/OV3660), 8MB Octal PSRAM. Highlights include AVI segmented recording (real video recording, not just snapshots), FTP/WebDAV dual-protocol upload, watchdog anti-freeze, chip temperature monitoring, batch file management. Suitable for long-term stable operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Selection Guide
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beginners&lt;/strong&gt;: Choose AI Thinker ESP32-CAM — cheap with plenty of resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily use&lt;/strong&gt;: Choose MiBeeCam — LCD screen makes debugging convenient&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum features&lt;/strong&gt;: Choose XIAO ESP32-S3 Sense — most powerful&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  System Service Configuration
&lt;/h2&gt;

&lt;p&gt;For stable operation, I use systemd to manage MiBeeNvr:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;MiBee NVR&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nvr&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/data/nvr/bin/mibee-nvr -config /mnt/data/nvr/mibee-nvr.yaml&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/data/nvr&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="c"&gt;# Security hardening
&lt;/span&gt;&lt;span class="py"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectSystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;strict&lt;/span&gt;
&lt;span class="py"&gt;ReadWritePaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/data/nvr&lt;/span&gt;
&lt;span class="py"&gt;PrivateTmp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save to &lt;code&gt;/etc/systemd/system/mibee-nvr.service&lt;/code&gt;, then &lt;code&gt;systemctl enable --now mibee-nvr&lt;/code&gt;. Auto-start on boot, auto-restart on failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;MiBeeNvr is open source, stars and contributions welcome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MiBeeNvr&lt;/strong&gt;: &lt;a href="https://github.com/Mi-Bee-Studio/MiBeeNvr" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/MiBeeNvr&lt;/a&gt; (MIT License)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiBeeCam&lt;/strong&gt;: &lt;a href="https://github.com/Mi-Bee-Studio/luatos-esp32s3-a10-camera" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/luatos-esp32s3-a10-camera&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Thinker ESP32-CAM&lt;/strong&gt;: &lt;a href="https://github.com/Mi-Bee-Studio/ai-thinker-esp32-cam" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/ai-thinker-esp32-cam&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiBeeHomeCam&lt;/strong&gt;: &lt;a href="https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam" rel="noopener noreferrer"&gt;https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam&lt;/a&gt; (GPL v3.0)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Documentation is comprehensive, with detailed deployment and configuration instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing Thoughts
&lt;/h3&gt;

&lt;p&gt;To be honest, I built this project mainly because I was dissatisfied with all the existing solutions. Cloud storage is too expensive, open-source solutions are too heavy, and commercial products are too closed. Building my own was just right: lightweight, free, and fully under my control.&lt;/p&gt;

&lt;p&gt;Oh, about the name MiBeeNvr — "Mi" stands for me (Mickey), "Bee" stands for... let's keep that a secret, and "Nvr" is naturally Network Video Recorder. Simple, memorable, and a bit meaningful.&lt;/p&gt;

&lt;p&gt;If you also have home camera needs or ideas about NVR systems, feel free to reach out. Issues are welcome on GitHub.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://blog.mickeyzzc.tech/en/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>github</category>
      <category>nvr</category>
      <category>camera</category>
    </item>
  </channel>
</rss>
