<?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: ZedIoT</title>
    <description>The latest articles on DEV Community by ZedIoT (@zediot).</description>
    <link>https://dev.to/zediot</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%2F3399208%2Fd2ba7c47-d4b2-4057-969d-cee59480b9eb.png</url>
      <title>DEV Community: ZedIoT</title>
      <link>https://dev.to/zediot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zediot"/>
    <language>en</language>
    <item>
      <title>Debugging Long-Uptime ESPHome Devices on ESP32</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Tue, 08 Sep 2026 12:20:00 +0000</pubDate>
      <link>https://dev.to/zediot/debugging-long-uptime-esphome-devices-on-esp32-b3l</link>
      <guid>https://dev.to/zediot/debugging-long-uptime-esphome-devices-on-esp32-b3l</guid>
      <description>&lt;h1&gt;
  
  
  Debugging Long-Uptime ESPHome Devices on ESP32
&lt;/h1&gt;

&lt;p&gt;ESPHome devices that fail after days or weeks usually suffer from accumulated system effects: heap pressure, fragmentation, Wi-Fi instability, blocking components, sensor timing, and stale values.&lt;/p&gt;

&lt;p&gt;Many ESPHome devices look stable right after flashing. Sensors report values, Home Assistant discovers entities, and automations work. The harder failures show up later: the node reboots after several days, the API disconnects, a sensor value freezes, or the only fix seems to be power cycling the device.&lt;/p&gt;

&lt;p&gt;The core conclusion is straightforward: long-uptime ESPHome failures are rarely caused by one bad YAML line. They are usually accumulated system effects across memory behavior, blocking components, Wi-Fi conditions, logging, and sensor timing. If the device does not expose uptime, reset reason, free heap, minimum free heap, fragmentation, Wi-Fi signal, and last valid readings, it is difficult to tell a memory leak from heap fragmentation, a network issue, or a stalled peripheral.&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%2Fd5i59emlqlzqe55u0aft.webp" 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%2Fd5i59emlqlzqe55u0aft.webp" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What long-uptime debugging means:&lt;/strong&gt; diagnosing devices that work at first but fail only after days or weeks. The target is not compile errors or a single wiring mistake. The target is reboot patterns, stale values, intermittent disconnections, and runtime health signals.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why "it ran for one day" is not a stability test
&lt;/h2&gt;

&lt;p&gt;ESP32 and ESPHome prototypes can be misleading. Once the device appears in Home Assistant and updates a few entities, it is tempting to treat the firmware as finished. Long runtime exposes problems that short bench tests miss.&lt;/p&gt;

&lt;p&gt;Common long-uptime failure sources include heap leaks, memory fragmentation, Wi-Fi reconnect storms, blocking I2C/UART calls, log flooding, and stale sensor values.&lt;/p&gt;

&lt;p&gt;The key rule: if an ESPHome node exposes only business sensors and no runtime diagnostics, a failure after several weeks becomes guesswork instead of engineering analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add diagnostic entities before changing the design
&lt;/h2&gt;

&lt;p&gt;The first response should not be rewriting the YAML. The first response should be making runtime health visible. A useful minimum set is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;uptime&lt;/strong&gt; — so every restart becomes visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reset reason&lt;/strong&gt; — so software restarts, watchdogs, brownouts, and power resets are not mixed together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;free heap&lt;/strong&gt; — to track current memory availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;minimum free heap&lt;/strong&gt; — to catch low points that disappear after reboot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fragmentation or maximum block size&lt;/strong&gt; — to expose fragmented heap behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wi-Fi signal&lt;/strong&gt; — to avoid treating radio problems as firmware crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;last valid reading&lt;/strong&gt; — to distinguish stale data from fresh data.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;

&lt;span class="na"&gt;sensor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uptime&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;Node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Uptime"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
    &lt;span class="na"&gt;free&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Heap&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Free"&lt;/span&gt;
    &lt;span class="na"&gt;block&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Heap&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Max&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Block"&lt;/span&gt;
    &lt;span class="na"&gt;loop_time&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loop&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Time"&lt;/span&gt;

&lt;span class="na"&gt;text_sensor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
    &lt;span class="na"&gt;reset_reason&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reset&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Reason"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a full production template. It is the debugging boundary: business entities describe the environment, while diagnostic entities describe whether the node itself can still be trusted.&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%2Fo0nb63vscz7kgzcqkasx.webp" 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%2Fo0nb63vscz7kgzcqkasx.webp" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use one diagnostic path to narrow the failure
&lt;/h2&gt;

&lt;p&gt;The decision path starts from the observed anomaly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start from the &lt;strong&gt;device anomaly&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ask: &lt;strong&gt;did uptime reset?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;yes&lt;/strong&gt;, check the &lt;strong&gt;reset reason&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;no&lt;/strong&gt;, ask whether &lt;strong&gt;business values are stale&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;From reset reason, correlate &lt;strong&gt;heap, Wi-Fi, and power&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From stale values, check &lt;strong&gt;blocked components and bus errors&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Both paths converge on a &lt;strong&gt;minimal reproduction&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Then &lt;strong&gt;change one variable and observe for 3–7 days&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first split matters: did the device really reboot, or did one part of the data path stall? Reboots push the investigation toward reset reason, heap, power, and watchdog behavior. Stale values without a reboot push it toward sensor drivers, I2C or UART behavior, blocking calls, and external services.&lt;/p&gt;

&lt;p&gt;Do not change Wi-Fi, logging, sampling intervals, sensor configuration, and power at the same time. Long-uptime failures already take time to reproduce. Changing several variables at once makes the next result harder to interpret.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Heap debugging is about low points and fragmentation, not only current free memory
&lt;/h2&gt;

&lt;p&gt;Many ESP32 nodes have enough free heap right after boot. After days of runtime, two different problems can appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Total free heap gradually drops&lt;/strong&gt;, which can indicate a leak or unbounded cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total free heap looks acceptable, but the largest contiguous block shrinks&lt;/strong&gt;, so larger allocations fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why current free heap is not enough. A minimum-free signal can expose short low-memory events, while fragmentation or largest-block diagnostics can show that memory is available but not available in useful contiguous chunks.&lt;/p&gt;

&lt;p&gt;If an ESP32 node reboots only after reconnects, sensor faults, display refreshes, or bursts of logging, observe heap low points and largest block size before blaming the last visible component.&lt;/p&gt;

&lt;p&gt;A practical narrowing sequence is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce log verbosity so the device is not spending long periods formatting and transmitting logs.&lt;/li&gt;
&lt;li&gt;Temporarily remove nonessential components such as web server, display, Bluetooth scanning, or high-frequency template sensors.&lt;/li&gt;
&lt;li&gt;Increase sensor &lt;code&gt;update_interval&lt;/code&gt; to see whether a specific sampling cadence triggers the failure.&lt;/li&gt;
&lt;li&gt;Remove complex lambda code and string formatting to see whether the heap curve stabilizes.&lt;/li&gt;
&lt;li&gt;Run the same configuration on another board and power supply to separate firmware behavior from hardware variance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Wi-Fi and API disconnects are not always firmware crashes
&lt;/h2&gt;

&lt;p&gt;An ESPHome device showing offline in Home Assistant does not automatically mean the MCU crashed. Wi-Fi roaming, weak RSSI, router restarts, API connection behavior, mDNS resolution, and network congestion can all look like device failure from the dashboard.&lt;/p&gt;

&lt;p&gt;Ask two questions first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Did uptime reset?&lt;/strong&gt; If not, the firmware may still be running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there serial or local log output?&lt;/strong&gt; If yes, the problem may be the network or API path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For devices inside metal cabinets, distribution boxes, cold rooms, equipment rooms, or industrial spaces, radio quality is part of device stability. Do not repair a network problem as a firmware problem. Add Wi-Fi signal, connection state, and last publish time first; then decide whether to move the router, change the antenna, use Ethernet, or delegate the critical path to a more reliable gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. When ESPHome is the wrong abstraction
&lt;/h2&gt;

&lt;p&gt;ESPHome is excellent for configurable Home Assistant devices, small sensor gateways, and fast integration work. It becomes less suitable when the node turns into a production controller with complex runtime requirements.&lt;/p&gt;

&lt;p&gt;Be cautious when the project needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strict real-time control, complex state machines, or safety interlocks.&lt;/li&gt;
&lt;li&gt;local queues, protocol retries, persistent buffering, or multiple coordinated tasks.&lt;/li&gt;
&lt;li&gt;staged OTA, remote log collection, self-recovery, and fleet operations.&lt;/li&gt;
&lt;li&gt;code-level control over memory, tasks, stack behavior, and peripheral failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical boundary is this: use ESPHome for observable, configurable, low-friction edge nodes. When the device becomes a production gateway or controller, consider ESP-IDF, custom firmware, or moving the complex logic into an edge gateway or platform service.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://esphome.io/components/debug.html" rel="noopener noreferrer"&gt;ESPHome Debug Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://esphome.io/components/sensor/uptime.html" rel="noopener noreferrer"&gt;ESPHome Uptime Sensor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://esphome.io/components/sensor/wifi_signal.html" rel="noopener noreferrer"&gt;ESPHome WiFi Signal Sensor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://esphome.io/components/logger.html" rel="noopener noreferrer"&gt;ESPHome Logger Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://esphome.io/components/api.html" rel="noopener noreferrer"&gt;ESPHome API Component&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>esphome</category>
      <category>esp32</category>
      <category>homeassistant</category>
      <category>iot</category>
    </item>
    <item>
      <title>Migrating MediaPipe Gesture Recognition to RKNN: From .task to RK3566 Deployment</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Thu, 03 Sep 2026 12:20:00 +0000</pubDate>
      <link>https://dev.to/zediot/migrating-mediapipe-gesture-recognition-to-rknn-from-task-to-rk3566-deployment-45a4</link>
      <guid>https://dev.to/zediot/migrating-mediapipe-gesture-recognition-to-rknn-from-task-to-rk3566-deployment-45a4</guid>
      <description>&lt;h1&gt;
  
  
  Migrating MediaPipe Gesture Recognition to RKNN: From .task to RK3566 Deployment
&lt;/h1&gt;

&lt;p&gt;A step-by-step guide to convert MediaPipe Gesture Recognition models to RKNN and running real-time inference on the RK3566 NPU, with code and troubleshooting tips.&lt;/p&gt;

&lt;p&gt;MediaPipe gesture recognition is an important human–computer interaction technique in computer vision. Google's MediaPipe offers a complete Gesture Recognizer pipeline, which includes four stages: hand detection, hand landmark detection, embedding generation, and gesture classification. This enables real-time, end-to-end gesture recognition across many applications.&lt;/p&gt;

&lt;p&gt;However, MediaPipe is primarily optimized for PC CPU, NVIDIA GPU, and Android GPU environments. Running the same models directly on embedded SoCs such as Rockchip RK3566 or RK3588 leads to inefficient performance.&lt;/p&gt;

&lt;p&gt;To bring MediaPipe gesture recognition to embedded hardware, each model must be converted to RKNN format. Rockchip provides RKNN Toolkit 2, which converts mainstream deep-learning models (TFLite, ONNX, Caffe, etc.) into the &lt;code&gt;.rknn&lt;/code&gt; format. These models can then run on the NPU for hardware acceleration. With this toolchain, the MediaPipe gesture recognition pipeline can be migrated onto RK3566 for low-power, high-performance edge inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model Components in MediaPipe Gesture Recognizer
&lt;/h2&gt;

&lt;p&gt;The MediaPipe Gesture Recognition pipeline is built from four separate TFLite models. The &lt;code&gt;gesture_recognizer.task&lt;/code&gt; file is not a single model. It is a Task Bundle that includes several &lt;code&gt;.tflite&lt;/code&gt; models and configuration files. After unpacking it, you will see:&lt;/p&gt;

&lt;h3&gt;
  
  
  hand_landmarker.task
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hand_detector.tflite&lt;/code&gt; — Palm/hand detection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hand_landmarks_detector.tflite&lt;/code&gt; — 21 hand keypoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  hand_gesture_recognizer.task
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gesture_embedder.tflite&lt;/code&gt; — Converts keypoints into embedding vectors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;canned_gesture_classifier.tflite&lt;/code&gt; — Classifies hand gestures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they form this pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hand Detection → Landmark Detection → Embedding → Gesture Classification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why convert them one by one?
&lt;/h3&gt;

&lt;p&gt;RKNN Toolkit 2 cannot parse &lt;code&gt;.task&lt;/code&gt; files, so the four TFLite models must be extracted and each converted to &lt;code&gt;.rknn&lt;/code&gt; individually.&lt;/p&gt;

&lt;p&gt;During inference, the four RKNN models must be called sequentially, following MediaPipe's original order, to reproduce the complete gesture pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of migration steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Unpack &lt;code&gt;.task&lt;/code&gt; → extract &lt;code&gt;.tflite&lt;/code&gt; models&lt;/li&gt;
&lt;li&gt;Convert each TFLite model → &lt;code&gt;.rknn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build the pipeline on RK3566 → run full gesture recognition&lt;/li&gt;
&lt;/ul&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%2F2m8isz6rlhgu9qm83bb0.webp" 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%2F2m8isz6rlhgu9qm83bb0.webp" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Model Conversion Workflow
&lt;/h2&gt;

&lt;p&gt;During conversion, each MediaPipe Gesture Recognition model must preserve its original preprocessing rules, or the output will drift. The workflow runs in three stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage A — Unpacking (PC):&lt;/strong&gt; extract the four TFLite models (&lt;code&gt;hand_detector.tflite&lt;/code&gt;, &lt;code&gt;hand_landmarks_detector.tflite&lt;/code&gt;, &lt;code&gt;gesture_embedder.tflite&lt;/code&gt;, &lt;code&gt;canned_gesture_classifier.tflite&lt;/code&gt;) from &lt;code&gt;gesture_recognizer.task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage B — Conversion (PC):&lt;/strong&gt; set &lt;code&gt;rknn.config(target='rk3566', w8a8)&lt;/code&gt;, provide a &lt;code&gt;dataset.txt&lt;/code&gt; for the image models, then run &lt;code&gt;load_tflite → build → export_rknn&lt;/code&gt; for each model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage C — Deployment (RK3566):&lt;/strong&gt; feed the input frame through the four RKNN models sequentially and emit the gesture output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1. Install RKNN Toolkit 2
&lt;/h3&gt;

&lt;p&gt;Install on Windows/Linux x86_64 (Mac requires a VM/container):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;rknn-toolkit2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recommended Python version: 3.6–3.10. Version 2.3.2 is widely used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2. Prepare the Four TFLite Models
&lt;/h3&gt;

&lt;p&gt;Extract these from the task bundle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hand_detector.tflite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hand_landmarks_detector.tflite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gesture_embedder.tflite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;canned_gesture_classifier.tflite&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3. Write the Conversion Script
&lt;/h3&gt;

&lt;p&gt;Each model inside the MediaPipe task file must be converted from TFLite to RKNN before it can run on the RK3566 NPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; In recent RKNN Toolkit versions, you must call &lt;code&gt;rknn.config()&lt;/code&gt; before &lt;code&gt;load_tflite()&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Template conversion script
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rknn.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RKNN&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tflite_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rknn_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_image_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rknn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Configuration
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_image_model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;mean_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="n"&gt;std_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
            &lt;span class="n"&gt;target_platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rk3566&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;quantized_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w8a8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;target_platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rk3566&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;quantized_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w8a8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Load TFLite
&lt;/span&gt;    &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_tflite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tflite_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Build model
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_image_model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_quantization&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dataset.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_quantization&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Export
&lt;/span&gt;    &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rknn_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rknn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Convert all models
&lt;/span&gt;&lt;span class="nf"&gt;convert_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_detector.tflite&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;convert_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_landmarks_detector.tflite&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_landmarks_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;convert_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gesture_embedder.tflite&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gesture_embedder.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;convert_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;canned_gesture_classifier.tflite&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;canned_gesture_classifier.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common Errors and Fixes
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;E config: Invalid quantized_dtype 'asymmetric_quantized-u8'&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Cause: RKNN Toolkit 2.3.2 no longer supports this dtype.&lt;br&gt;
Fix: Use &lt;code&gt;quantized_dtype='w8a8'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;E load_tflite: Please call rknn.config first!&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Cause: Incorrect function order.&lt;br&gt;
Fix: Always call &lt;code&gt;config()&lt;/code&gt; before &lt;code&gt;load_tflite()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;E build: Dataset file dataset.txt not found!&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Cause: Quantization requires a calibration dataset.&lt;br&gt;
Fix (choose one): prepare &lt;code&gt;dataset.txt&lt;/code&gt; with RGB image paths, or disable quantization (&lt;code&gt;do_quantization=False&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RKNN file is empty&lt;/strong&gt;&lt;br&gt;
Cause: &lt;code&gt;build()&lt;/code&gt; failed.&lt;br&gt;
Fix: Check build logs, then fix the dataset or parameters.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building the Full Gesture Pipeline on RK3566
&lt;/h2&gt;

&lt;p&gt;When running MediaPipe Gesture Recognition on the RK3566 NPU, the four RKNN models must be executed in sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Flow (RKNN version)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame
  → hand_detector.rknn → box → crop/normalize ROI
  → hand_landmarks_detector.rknn → 21 keypoints → flatten/normalize
  → gesture_embedder.rknn → embedding
  → canned_gesture_classifier.rknn → gesture ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Keep the preprocessing steps the same as MediaPipe's (RGB, 0–1 normalization, input sizes; vector models take float32 directly).&lt;/p&gt;

&lt;h2&gt;
  
  
  Single-Image Inference Example (Minimal Working Path)
&lt;/h2&gt;

&lt;p&gt;This example runs MediaPipe Gesture Recognition on RK3566 using four independent RKNN models.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Different versions of the hand detector and landmark models may output different formats. Some return multiple boxes and scores, while others output center + size. This example shows a common parsing template. If your model behaves differently, print the output shapes and adjust accordingly.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rknn.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RKNN&lt;/span&gt;

&lt;span class="c1"&gt;# ========== Utility functions ==========
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_bgr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_bgr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;255.0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;crop_by_box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_bgr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box_xyxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img_bgr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box_xyxy&lt;/span&gt;
    &lt;span class="c1"&gt;# Add padding to avoid tight crops
&lt;/span&gt;    &lt;span class="n"&gt;cx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;bw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bw&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;img_bgr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;norm_landmarks_to_roi_xy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm_21x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roi_xyxy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roi_xyxy&lt;/span&gt;
    &lt;span class="n"&gt;rw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;
    &lt;span class="n"&gt;rh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;
    &lt;span class="c1"&gt;# Convert normalized ROI coordinates (0-1) back to image coordinates
&lt;/span&gt;    &lt;span class="n"&gt;pts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lm_21x2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rw&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lm_21x2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rh&lt;/span&gt;
        &lt;span class="n"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ========== Load models ==========
&lt;/span&gt;&lt;span class="n"&gt;det&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;lm&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_landmarks_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;emb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gesture_embedder.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;clf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;canned_gesture_classifier.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# ========== Run inference on one image ==========
&lt;/span&gt;&lt;span class="n"&gt;img_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;ori&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# 1) Hand detection
&lt;/span&gt;&lt;span class="n"&gt;det_in_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;det_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;det_in_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;det_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;det_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# NHWC
&lt;/span&gt;&lt;span class="n"&gt;det_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;det_in&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# ★★ Print det_out to confirm its real structure, then adjust parsing accordingly ★★
# Assume format is [N, 6]: x1, y1, x2, y2, score, class (normalized coordinates)
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;det_out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Adjust if needed
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;        &lt;span class="c1"&gt;# Score threshold
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No hand detected&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Choose the box with the highest confidence
&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;roi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roi_xyxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;crop_by_box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2) Landmark detection
&lt;/span&gt;&lt;span class="n"&gt;lm_in_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lm_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;roi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lm_in_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lm_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lm_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;lm_in&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# ★★ Print lm_out to confirm shape ★★
# Common outputs: [1,21,3] or [1,63] — x,y normalized to ROI
&lt;/span&gt;&lt;span class="n"&gt;lm_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm_out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lm_xy_roi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm_arr&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;lm_xy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;norm_landmarks_to_roi_xy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm_xy_roi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roi_xyxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3) Embedding (flatten 21x2 -&amp;gt; 42)
&lt;/span&gt;&lt;span class="n"&gt;vec_42&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm_xy_roi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vec_42&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;emb_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;vec_42&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emb_out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 4) Classification
&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gesture ID:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="c1"&gt;# Visualization
&lt;/span&gt;&lt;span class="nf"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lm_xy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;G:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FONT_HERSHEY_SIMPLEX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;result.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ori&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Release
&lt;/span&gt;&lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Saved result.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two places must be checked by printing actual output before finalizing the parsing logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The shape/meaning of &lt;code&gt;det_out&lt;/code&gt; (some models output center + size; some output absolute or normalized coordinates).&lt;/li&gt;
&lt;li&gt;The shape/meaning of &lt;code&gt;lm_out&lt;/code&gt; (some return 63 dims; some include z/visibility).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Real-Time Camera Demo (OpenCV + RKNN)
&lt;/h2&gt;

&lt;p&gt;This version handles single-hand recognition. To support multiple hands, loop through all valid detection boxes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rknn.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RKNN&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2RGB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;255.0&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]&lt;/span&gt;  &lt;span class="c1"&gt;# NHWC
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;det&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;lm&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hand_landmarks_detector.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;emb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gesture_embedder.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;clf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RKNN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_rknn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;canned_gesture_classifier.rknn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_runtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;det_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lm_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&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;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;det_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;det_size&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="c1"&gt;# ★★ Parse det_out to obtain the best hand bounding box — adjust as above ★★
&lt;/span&gt;        &lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;det_out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No hand&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RKNN Gesture&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&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;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
        &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;
        &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;roi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;lm_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;to_rgb_norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;roi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lm_size&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="c1"&gt;# ★★ Parse lm_out to get the 21x2 normalized coordinates — adjust as above ★★
&lt;/span&gt;        &lt;span class="n"&gt;lm_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lm_out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lm_xy_roi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm_arr&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# Convert back to full-image coordinates
&lt;/span&gt;        &lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lm_xy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lm_xy&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lm_xy_roi&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rw&lt;/span&gt;
        &lt;span class="n"&gt;lm_xy&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lm_xy_roi&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rh&lt;/span&gt;

        &lt;span class="c1"&gt;# embed &amp;amp; classify
&lt;/span&gt;        &lt;span class="n"&gt;vec_42&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lm_xy_roi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;vec_42&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inference&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;gid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# draw
&lt;/span&gt;        &lt;span class="nf"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lm_xy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1e-6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;G:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; p:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FPS:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RKNN Gesture&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&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;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;emb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Optimization Tips
&lt;/h2&gt;

&lt;p&gt;Optimizing preprocessing and quantization improves the accuracy of MediaPipe Gesture Recognition when deployed on RK3566.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quantization dataset:&lt;/strong&gt; Prepare 100–300 RGB images of hands with varied skin tones, lighting, and backgrounds for the detector and landmark models. This greatly improves INT8 stability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input size:&lt;/strong&gt; Use the exact input resolution defined in the TFLite models (check with Netron).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent preprocessing:&lt;/strong&gt; Use the same RGB layout and 0–1 normalization (mean=0, std=255) as in conversion. Avoid mismatches between training and runtime pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROI affine alignment:&lt;/strong&gt; If the detector outputs rotated boxes or the model expects upright palms, apply optional rotation alignment before cropping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline optimization:&lt;/strong&gt; Use frame-to-frame tracking to reduce detector frequency (run the detector once every N frames, run landmarks in between).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-hand support:&lt;/strong&gt; For each box above the threshold, run the remaining three stages independently. Limit the maximum number of hands to keep real-time performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threading / CPU pinning:&lt;/strong&gt; On RK3566, use multi-threading to separate camera capture, NPU inference, and drawing for smoother performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Troubleshooting Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&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;Wrong gesture output&lt;/td&gt;
&lt;td&gt;Incorrect model execution order&lt;/td&gt;
&lt;td&gt;Run Detection → Landmark → Embedding → Classification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accuracy drift&lt;/td&gt;
&lt;td&gt;Mismatched preprocessing&lt;/td&gt;
&lt;td&gt;Match RGB layout + 0–1 normalization to conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INT8 accuracy loss&lt;/td&gt;
&lt;td&gt;Weak quantization dataset&lt;/td&gt;
&lt;td&gt;Use 100–300 varied hand images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow frame rate&lt;/td&gt;
&lt;td&gt;Detector runs every frame&lt;/td&gt;
&lt;td&gt;Run detector every N frames, landmarks in between&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty &lt;code&gt;.rknn&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;build()&lt;/code&gt; failed&lt;/td&gt;
&lt;td&gt;Check logs, fix dataset or params&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MediaPipe's gesture embedder often applies centering, scale normalization, and mirroring to keypoints. Reproduce these steps when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;By converting the four MediaPipe gesture-recognition submodels into RKNN models and running them sequentially on RK3566, you can build a low-power, real-time gesture recognition system.&lt;/p&gt;

&lt;p&gt;The key is to maintain identical preprocessing, provide a good quantization dataset, and apply real-world engineering optimizations such as lowering detection frequency or using multithreading. This gives a complete path from MediaPipe → RKNN → RK3566 edge deployment — a strong combination of low-cost hardware and high-value CV capability for smart home, retail, education, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. What models need to be converted when deploying hand-related pipelines on RK3566?
&lt;/h3&gt;

&lt;p&gt;You must convert each model used in the pipeline — detection, landmark extraction, embedding, and classification — into &lt;code&gt;.rknn&lt;/code&gt; format so they can run efficiently on the RK3566 NPU.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What are the most common issues when converting models to RKNN?
&lt;/h3&gt;

&lt;p&gt;Typical issues include incorrect input size, mismatched normalization, unsupported quantization settings, missing calibration images, or incorrect model ordering during execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What is the correct execution order for hand-analysis pipelines on RK3566?
&lt;/h3&gt;

&lt;p&gt;The standard sequence is Detection → Landmark Extraction → Embedding → Classification. Running them in the wrong order causes incorrect outputs or reduced accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Can RK3566 achieve real-time performance for hand-analysis workloads?
&lt;/h3&gt;

&lt;p&gt;Yes. With proper preprocessing, model conversion, and optimization, RK3566 can achieve real-time performance while maintaining low power consumption, making it well-suited for embedded AI applications.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>ESP32 High-Density LED Control with RMT, DMA, and WLED</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Tue, 01 Sep 2026 12:10:00 +0000</pubDate>
      <link>https://dev.to/zediot/esp32-high-density-led-control-with-rmt-dma-and-wled-2p5b</link>
      <guid>https://dev.to/zediot/esp32-high-density-led-control-with-rmt-dma-and-wled-2p5b</guid>
      <description>&lt;h1&gt;
  
  
  High-Density LED Control with ESP32 RMT and WLED
&lt;/h1&gt;

&lt;p&gt;Driving large WS2812 or SK6812 installations with ESP32 and WLED is not just an MCU performance problem. The real constraints are LEDs per output, RMT interrupt or DMA behavior, SRAM usage, power injection, Wi-Fi load, and synchronization strategy together.&lt;/p&gt;

&lt;p&gt;When an ESP32 + WLED project grows from a short decorative strip to hundreds, thousands, or several thousand addressable LEDs, the bottleneck is rarely just "whether the ESP32 is fast enough." High-density LED control is constrained by output segmentation, serial LED timing, RMT interrupt or DMA behavior, SRAM usage, power injection, Wi-Fi load, and synchronization strategy together.&lt;/p&gt;

&lt;p&gt;If you keep adding LEDs to one data line, you will usually see lower frame rate, visible skew, voltage drop, color shift, and occasional flicker before you run out of raw MCU compute. If you only switch to a faster board without splitting outputs, redesigning power, or defining sync boundaries, the 800 kHz one-wire protocol and real installation wiring will still dominate the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What "high-density addressable LED controller" means here:&lt;/strong&gt; one or more ESP32/WLED nodes driving hundreds to thousands of WS2812, SK6812, or similar one-wire addressable LEDs. It is not just a strip-light hobby setup; it is a small edge-control system with real-time output, power, networking, and field maintenance boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key decision line:&lt;/strong&gt; If the target is above roughly 500 LEDs, design around five decisions first — LEDs per output, number of outputs, power injection, sync method, and number of controllers. If the target approaches or exceeds 2000 LEDs, multi-output or multi-controller architecture is usually safer than forcing everything through one long data line.&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%2F62y0bhyzj6e7eo5cg76s.webp" 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%2F62y0bhyzj6e7eo5cg76s.webp" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why "more pixels" is not linear scaling
&lt;/h2&gt;

&lt;p&gt;Addressable LEDs create a misleading intuition. If 100 LEDs work, it feels like 1000 LEDs should only mean buying more strip. In practice, this is not how the system scales.&lt;/p&gt;

&lt;p&gt;One-wire LED protocols are serialized. The more pixels on one output, the longer it takes to transmit a complete frame. Even if the MCU can calculate the effect, the output line still has to send timing-sensitive data one pixel after another. WLED's multi-strip documentation reflects that reality: it recommends ESP32 for more than one output and describes four outputs as a practical sweet spot. It also gives examples such as 512 LEDs per pin × 4, 800 LEDs per pin × 4, and 1000 LEDs per pin × 4, instead of encouraging one infinitely long strip.&lt;/p&gt;

&lt;p&gt;The first rule of high-density LED control is therefore not "buy a faster chip." It is &lt;strong&gt;reduce the length of each serial output chain&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why RMT, DMA, and Wi-Fi affect LED stability
&lt;/h2&gt;

&lt;p&gt;ESP32 projects commonly use the RMT peripheral to drive timing-sensitive WS2812-style signals. RMT was originally designed as a remote-control transceiver, but Espressif documents LED strip output as a practical use case. Espressif also notes a critical limitation: on non-ESP32-S3 chips, large LED output can rely heavily on interrupts and ping-pong buffering, so Wi-Fi or Bluetooth interrupt pressure can create timing exceptions.&lt;/p&gt;

&lt;p&gt;This is the source of many flicker problems. The color algorithm is not necessarily wrong. The LED output task is competing with network control, animation calculation, Web UI activity, MQTT traffic, or synchronization work.&lt;/p&gt;

&lt;p&gt;ESP32-S3 matters for this reason. Espressif's RMT FAQ recommends ESP32-S3 for RMT-heavy use because it supports RMT DMA, which moves more of the output workload away from the CPU interrupt path. The point is not that ESP32-S3 is always "faster." The point is: &lt;strong&gt;when LED output competes with Wi-Fi, Bluetooth, audio, or sync tasks, DMA and resource separation matter more than peak clock speed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The five architecture decisions in a large WLED build
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 LEDs per output set the serial refresh ceiling
&lt;/h3&gt;

&lt;p&gt;Every WS2812/SK6812 output is a serial chain. More pixels per output means lower maximum frame rate and more visible delay in fast effects. If the installation is slow ambient lighting, that may be acceptable. If it is stage lighting, a pixel matrix, or music-reactive output, per-output length must be more conservative.&lt;/p&gt;

&lt;p&gt;When LEDs per output are too high, the first thing you lose is &lt;strong&gt;frame rate and dynamic consistency&lt;/strong&gt;, not static lighting capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Output count determines how much work can be split
&lt;/h3&gt;

&lt;p&gt;WLED supports multiple outputs and lets users configure LED type, GPIO, length, and color order at runtime. For ESP32 builds, multiple outputs are not just a wiring convenience. They split one long serial queue into several shorter chains.&lt;/p&gt;

&lt;p&gt;More outputs still have a cost. They increase configuration, power, wiring, sync, and troubleshooting complexity. WLED's own guidance makes four outputs a sensible starting point for many single-controller builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 RMT or DMA decides whether output timing is fragile
&lt;/h3&gt;

&lt;p&gt;Classic ESP32 can run many WLED installations, but under high LED count, active Wi-Fi, heavy sync traffic, or audio-reactive effects, interrupt latency can become visible. ESP32-S3 RMT DMA reduces that pressure, but it does not remove the need for output segmentation, power design, and memory budgeting.&lt;/p&gt;

&lt;p&gt;If the installation needs both high-density LED output and real-time Wi-Fi control or audio reaction, choosing ESP32-S3 or splitting the load across nodes is usually safer than squeezing a classic ESP32 harder.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Power injection decides whether "it lights" also means "it is correct"
&lt;/h3&gt;

&lt;p&gt;Many LED problems are misdiagnosed as firmware problems. Large strips commonly show yellowing at the far end, voltage drop under full white, local flicker, weak common ground, and undersized power wiring. WLED includes an automatic brightness limiter, but current limiting does not replace correct power capacity, wire gauge, injection points, and grounding.&lt;/p&gt;

&lt;p&gt;When power design is weak, reducing brightness can make the system look stable, but it does not prove the control architecture is reliable.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 Multi-controller sync defines the system boundary
&lt;/h3&gt;

&lt;p&gt;As LED count rises, multiple controllers often become more realistic than forcing one controller to own the entire installation. WLED's DDP virtual LED model can attach remote WLED nodes to a controlling instance, or the system can use network-level synchronization. This is useful when the physical installation is spatially distributed, power zones are clear, and one failure should not affect the whole site.&lt;/p&gt;

&lt;p&gt;Multi-controller systems also introduce latency, sync skew, configuration drift, and recovery behavior. They work best as an intentional installation architecture, not as a patch for poor early segmentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Recommended architecture: split outputs before scaling controllers
&lt;/h2&gt;

&lt;p&gt;The decision path works backward from the pixel scale and effect target:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start from &lt;strong&gt;pixel scale and effect target&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Decide &lt;strong&gt;LEDs per output&lt;/strong&gt; and &lt;strong&gt;output count&lt;/strong&gt;, and map &lt;strong&gt;power zones&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From LEDs per output, choose the &lt;strong&gt;RMT / DMA output path&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From power zones, design &lt;strong&gt;field wiring and injection&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Both feed into &lt;strong&gt;WLED control and sync&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Only then decide between &lt;strong&gt;single or multiple controllers&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key point is to work backward from pixel scale and effect target, then decide LEDs per output, output count, and power zones. Only after those boundaries are clear should you decide whether one controller is enough. Starting with one development board and attaching all strips to it usually mixes timing, power, and maintenance risk into one hard-to-debug system.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Practical guidance by project scale
&lt;/h2&gt;

&lt;p&gt;These numbers are not hard limits. They are architecture signals. The higher the pixel count, the more the system should be broken into small, testable boundaries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under 500 LEDs:&lt;/strong&gt; get power and wiring right first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Between 500 and 2000 LEDs:&lt;/strong&gt; prioritize multi-output segmentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Above 2000 LEDs:&lt;/strong&gt; evaluate ESP32-S3, RMT DMA, multi-controller design, and network sync early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At every scale:&lt;/strong&gt; power injection and field labeling are not finishing details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A reliable installation lets each zone be powered, limited, diagnosed, and recovered on its own; whole-site sync is a coordination layer, not the only thing keeping the installation alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Pre-delivery checklist for high-density WLED systems
&lt;/h2&gt;

&lt;p&gt;Before handoff, validate at least these points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each output's LED count, GPIO, color order, and physical wiring match the configuration.&lt;/li&gt;
&lt;li&gt;Every power injection point stays within safe voltage and temperature under typical and high-brightness effects.&lt;/li&gt;
&lt;li&gt;Wi-Fi control, Web UI, MQTT, sync, or audio reaction do not cause flicker when active together.&lt;/li&gt;
&lt;li&gt;One output disconnect, one controller reboot, or a short network interruption has a clear recovery behavior.&lt;/li&gt;
&lt;li&gt;Field maintenance staff can identify every output and power zone from labels or configuration records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A high-density LED installation is reliable only when it remains explainable under network load, high brightness, partial power loss, and maintenance handoff. First-light success is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. When ESP32 + WLED should not be forced into the whole job
&lt;/h2&gt;

&lt;p&gt;ESP32 + WLED is excellent for small and medium decorative lighting, home automation, cabinets, local ambient lighting, and maintainable multi-zone installations. But some cases should not be forced through one ESP32 + WLED controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large stage or video-wall systems that require strict frame synchronization.&lt;/li&gt;
&lt;li&gt;Very high pixel counts with high refresh-rate effects.&lt;/li&gt;
&lt;li&gt;Industrial installations that require long-distance noise immunity and centralized operations.&lt;/li&gt;
&lt;li&gt;Systems that need wired networking, redundant control, or strict fault isolation.&lt;/li&gt;
&lt;li&gt;Projects where maintenance teams cannot work from GPIO, zone, and power-injection documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those systems may be better served by dedicated LED controllers, Art-Net/sACN infrastructure, Ethernet-distributed nodes, or WLED as a local zone controller rather than the whole-site master.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Conclusion: design boundaries before choosing the board
&lt;/h2&gt;

&lt;p&gt;ESP32 + WLED is valuable because it is fast to deploy, mature, configurable, and practical for real spaces. In high-density projects, however, the decisive question is not "can ESP32 light this many pixels?" The decisive question is &lt;strong&gt;whether output, power, timing, and sync have been separated into testable boundaries.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the lighting system must run for a long time and be maintained by someone else, it is not just a strip-light project. It is a small edge-control system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kno.wled.ge/advanced/multi-strip/" rel="noopener noreferrer"&gt;WLED Multi-strip Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kno.wled.ge/features/settings/" rel="noopener noreferrer"&gt;WLED Settings: LED outputs and brightness limiter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kno.wled.ge/interfaces/udp-realtime/" rel="noopener noreferrer"&gt;WLED Virtual LEDs via DDP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.espressif.com/projects/esp-faq/en/latest/software-framework/peripherals/rmt.html" rel="noopener noreferrer"&gt;Espressif ESP-FAQ: Remote Control Transceiver (RMT)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html" rel="noopener noreferrer"&gt;ESP-IDF Programming Guide: RMT peripheral&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>esp32</category>
      <category>iot</category>
      <category>esphome</category>
    </item>
    <item>
      <title>ESP32 Energy Metering with HLW8032, BL0942, and ESPHome</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Thu, 27 Aug 2026 12:40:00 +0000</pubDate>
      <link>https://dev.to/zediot/esp32-energy-metering-with-hlw8032-bl0942-and-esphome-15dm</link>
      <guid>https://dev.to/zediot/esp32-energy-metering-with-hlw8032-bl0942-and-esphome-15dm</guid>
      <description>&lt;h1&gt;
  
  
  ESP32 Energy Metering with HLW8032, BL0942, and ESPHome
&lt;/h1&gt;

&lt;p&gt;ESP32 energy metering with HLW8032, BL0942, and ESPHome is not just about reading voltage, current, power, and energy. This article explains how to design the UART boundary, reporting cadence, calibration, entity model, and diagnostics as one stable data path.&lt;/p&gt;

&lt;p&gt;Many ESP32 energy metering projects start well. You connect an HLW8032 or BL0942 module, enable the matching ESPHome component, and Home Assistant quickly shows voltage, current, power, and energy. But reading values is not the same as building an energy metering node that can run reliably over time.&lt;/p&gt;

&lt;p&gt;The core conclusion is this: the hard part of ESP32 energy metering is not whether HLW8032 or BL0942 can be read. The hard part is designing the metering chip, UART, Wi-Fi behavior, ESPHome entities, calibration, and diagnostics as one stable data path. If the project focuses only on sensor YAML, it can later fail on transient loads, serial conflicts, unstable sampling, Wi-Fi reconnects, Home Assistant database growth, and calibration drift.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this article, an ESP32 energy metering node means an edge device where ESP32 reads voltage, current, power, and energy from a metering chip such as HLW8032 or BL0942, then exposes those values through ESPHome to Home Assistant or another upper-layer platform. It is suitable for device energy monitoring, trend observation, and low-risk operational diagnostics. It should not be treated as a billing-grade meter or an electrical protection device.&lt;/p&gt;

&lt;p&gt;If the goal is to monitor the energy behavior of one appliance, one small circuit, or one commercial device inside Home Assistant, ESP32 + ESPHome + HLW8032/BL0942 is a fast and low-cost path. If the goal is billing, electrical protection, high-accuracy compliance measurement, or safety interlocking, use certified meters, protection devices, or industrial acquisition hardware instead of stretching an ESPHome node beyond its boundary.&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%2Fa660l2tks2ifhosptcyi.webp" 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%2Fa660l2tks2ifhosptcyi.webp" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why energy metering is more fragile than ordinary sensing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Metering data is not like temperature or humidity data
&lt;/h3&gt;

&lt;p&gt;Energy metering often looks like a normal sensor integration, but the data behaves differently. Temperature and humidity usually change slowly. Energy metering has to deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transient startup and shutdown behavior&lt;/li&gt;
&lt;li&gt;switching supplies, compressors, motors, and heaters&lt;/li&gt;
&lt;li&gt;relationships between voltage, current, power, power factor, and accumulated energy&lt;/li&gt;
&lt;li&gt;tradeoffs between sampling, filtering, calibration, and reporting frequency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why the first question should not be only whether ESPHome has a component for the chip. The better question is whether the data path produces stable values, whether abnormal values are diagnosable, and whether the upper platform can store and use the data over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 HLW8032 and BL0942 are metering front ends, not complete product architectures
&lt;/h3&gt;

&lt;p&gt;HLW8032 and BL0942 typically provide metering data over UART. ESPHome has official components for these chips, which makes integration much easier. But a component does not automatically solve the product architecture.&lt;/p&gt;

&lt;p&gt;A complete node still needs answers for questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is UART ownership fixed, or can it collide with logging, debugging, or other peripherals?&lt;/li&gt;
&lt;li&gt;Is the update interval aligned with the load behavior?&lt;/li&gt;
&lt;li&gt;Where do calibration values come from, and can they be checked in the field?&lt;/li&gt;
&lt;li&gt;What happens when Wi-Fi reconnects or Home Assistant is unavailable?&lt;/li&gt;
&lt;li&gt;How should accumulated energy, instant power, and abnormal state be modeled separately?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If these questions are not answered early, a working reading can hide long-term reliability risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A more reliable ESP32 energy metering stack
&lt;/h2&gt;

&lt;p&gt;It helps to think in five layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Physical wiring and electrical safety&lt;/strong&gt; — isolation, grounding, and touch protection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metering front end&lt;/strong&gt; — HLW8032/BL0942 sampling and UART transport.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESPHome integration&lt;/strong&gt; — component, update interval, and entity mapping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibration and validation&lt;/strong&gt; — known-load verification and calibration records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnostics and operations&lt;/strong&gt; — communication state, abnormal-data guards, and retention.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The point is simple: an energy metering node is not just a chip integration. It is a trustable data path from sampling to operations. When any layer takes on the wrong job, troubleshooting becomes harder later.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Five common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Leaving the UART boundary flexible for too long
&lt;/h3&gt;

&lt;p&gt;HLW8032 and BL0942 both depend on a serial communication path. ESP32 has more UART flexibility than ESP8266, but projects still fail when serial resources are treated casually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debug logging and the metering chip share a serial path&lt;/li&gt;
&lt;li&gt;RS485, a display, or another serial peripheral is added later&lt;/li&gt;
&lt;li&gt;boot logs, level shifting, and wiring order are not constrained for field use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A more reliable design fixes UART ownership from the first hardware and YAML version. Keep the metering chip's pins, baud rate, wiring, and debug strategy explicit. Energy metering nodes are not good places for loose field rewiring, because occasional serial noise can turn into permanent uncertainty.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Reporting as fast as possible
&lt;/h3&gt;

&lt;p&gt;Energy metering is not always better when it is faster. Excessive reporting creates three problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;higher Wi-Fi and ESPHome API load&lt;/li&gt;
&lt;li&gt;larger Home Assistant recorder storage&lt;/li&gt;
&lt;li&gt;more false interpretation of motor starts, relay switching, or power supply transients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better design separates use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instant power can update more often, but should avoid meaningless jitter&lt;/li&gt;
&lt;li&gt;accumulated energy can update less frequently&lt;/li&gt;
&lt;li&gt;anomaly detection should use duration, thresholds, and device state, not one spike&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If the node is used to tell whether equipment is running, whether energy behavior is abnormal, or whether a device is in standby, stable and explainable reporting is more valuable than a refresh rate that only looks real-time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.3 Treating calibration as a one-time YAML value
&lt;/h3&gt;

&lt;p&gt;Default module readings are usually only a starting point. Real calibration depends on shunts, current transformers, module batches, load type, and installation.&lt;/p&gt;

&lt;p&gt;In practice, a better workflow is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verify with a known load&lt;/li&gt;
&lt;li&gt;calibrate voltage, current, power, and energy intentionally&lt;/li&gt;
&lt;li&gt;record the date, load condition, and configuration version&lt;/li&gt;
&lt;li&gt;avoid reusing one coefficient set across different hardware batches without checking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without calibration notes, a later "8% high power reading" is hard to interpret. It could be hardware drift, configuration error, or a real load change.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Exposing too many Home Assistant entities
&lt;/h3&gt;

&lt;p&gt;It is tempting to expose every available field. That looks rich at first, but it creates long-term cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users see too many unstable or hard-to-explain entities&lt;/li&gt;
&lt;li&gt;database retention and automation logic become harder to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A cleaner model separates entities into three groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;core entities:&lt;/strong&gt; voltage, current, power, accumulated energy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;diagnostic entities:&lt;/strong&gt; communication state, last update time, error count, node RSSI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;business entities:&lt;/strong&gt; equipment running state, standby detection, energy band, anomaly flag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Raw readings should support operational judgment, not dump hardware detail into the upper layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 Not designing for offline and abnormal data
&lt;/h3&gt;

&lt;p&gt;Once an energy metering node is installed near real equipment, it will face weak Wi-Fi, power loss, load shutdown, chip communication failure, and sudden readings. Without an abnormal-data strategy, Home Assistant often shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a device that looks like it suddenly used too much power&lt;/li&gt;
&lt;li&gt;accumulated energy jumps&lt;/li&gt;
&lt;li&gt;automation triggered by one transient value&lt;/li&gt;
&lt;li&gt;unclear responsibility between equipment failure and node failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Better strategies include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate diagnostic entities for communication status and last update time&lt;/li&gt;
&lt;li&gt;guards or labels for impossible values&lt;/li&gt;
&lt;li&gt;duration thresholds for anomaly detection&lt;/li&gt;
&lt;li&gt;different states for "equipment has no load" and "metering node unavailable"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Choosing between HLW8032 and BL0942
&lt;/h2&gt;

&lt;p&gt;For most ESPHome projects, the choice should depend less on the chip name and more on module availability, wiring, documentation quality, and accuracy expectations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The HLW8032 versus BL0942 choice is rarely the largest factor in whether an ESPHome energy monitor succeeds. For most projects, reliability depends more on module quality, electrical safety, calibration workflow, UART ownership, and reporting strategy.&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%2Fun07dllrzg0ifphv4orl.webp" 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%2Fun07dllrzg0ifphv4orl.webp" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A production-minded ESPHome design direction
&lt;/h2&gt;

&lt;p&gt;The example below is not a complete drop-in configuration. It shows the design direction:&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;uart&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="s"&gt;metering_uart&lt;/span&gt;
  &lt;span class="na"&gt;tx_pin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GPIO17&lt;/span&gt;
  &lt;span class="na"&gt;rx_pin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GPIO16&lt;/span&gt;
  &lt;span class="na"&gt;baud_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4800&lt;/span&gt;

&lt;span class="na"&gt;sensor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hlw8032&lt;/span&gt;
    &lt;span class="na"&gt;uart_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metering_uart&lt;/span&gt;
    &lt;span class="na"&gt;voltage&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Meter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Voltage"&lt;/span&gt;
    &lt;span class="na"&gt;current&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Meter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Current"&lt;/span&gt;
    &lt;span class="na"&gt;power&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Meter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Power"&lt;/span&gt;
    &lt;span class="na"&gt;energy&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Meter&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Energy"&lt;/span&gt;
    &lt;span class="na"&gt;update_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a real deployment, add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calibration parameters and calibration notes&lt;/li&gt;
&lt;li&gt;filtering or value guards for readings&lt;/li&gt;
&lt;li&gt;diagnostic entities such as Wi-Fi signal, uptime, and restart reason&lt;/li&gt;
&lt;li&gt;Home Assistant recorder retention and exclusion strategy&lt;/li&gt;
&lt;li&gt;enclosure, isolation, safe wiring, and touch-protection rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configuration is only the entry point. Reliability comes from constraints across the full data path.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. When not to use ESP32 + ESPHome for energy metering
&lt;/h2&gt;

&lt;p&gt;Do not stretch this stack into these cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Billing:&lt;/strong&gt; billing needs compliance, sealing, metering class, and an audit trail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electrical protection:&lt;/strong&gt; overcurrent, leakage, and short-circuit protection belong to dedicated protection devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard real-time control:&lt;/strong&gt; protection and critical interlocks should not depend on Wi-Fi and Home Assistant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-noise industrial cabinets:&lt;/strong&gt; poor isolation, grounding, and power quality can overwhelm a lightweight ESP32 node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many circuits with high refresh rates:&lt;/strong&gt; multi-circuit acquisition is often better handled by professional meters or gateways.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;ESP32 energy metering nodes are strong for visualization, trends, auxiliary diagnostics, and low-risk automation. They are not the right boundary for billing, safety protection, or hard real-time control. Stating that boundary makes the architecture more credible, not weaker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7. Conclusion
&lt;/h2&gt;

&lt;p&gt;ESP32, HLW8032, BL0942, and ESPHome can quickly produce a node that shows energy data in Home Assistant. But the engineering value is not that the numbers appear on a dashboard. The real value is whether the data path remains stable over time, whether readings are explainable, whether abnormal states are diagnosable, and whether the upper platform is not overloaded with raw entities.&lt;/p&gt;

&lt;p&gt;If the goal is to understand whether one device is running or whether its energy trend looks abnormal, ESP32 + ESPHome is a strong option. If the goal is billing, electrical protection, or hard real-time control, the ESP32 node should stay in its proper role: a lightweight edge monitoring node, not the final authority in the electrical system.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;ESPHome HLW8032 Sensor&lt;/li&gt;
&lt;li&gt;ESPHome BL0942 Sensor&lt;/li&gt;
&lt;li&gt;ESPHome UART Bus&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>esp32</category>
      <category>iot</category>
      <category>esphome</category>
    </item>
    <item>
      <title>ESP32-C3 vs S3 vs C6: Firmware, TinyML, Matter, and Production Tradeoffs</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Tue, 25 Aug 2026 12:40:00 +0000</pubDate>
      <link>https://dev.to/zediot/esp32-c3-vs-s3-vs-c6-firmware-tinyml-matter-and-production-tradeoffs-3o0c</link>
      <guid>https://dev.to/zediot/esp32-c3-vs-s3-vs-c6-firmware-tinyml-matter-and-production-tradeoffs-3o0c</guid>
      <description>&lt;h1&gt;
  
  
  ESP32-C3 vs S3 vs C6: Firmware, TinyML, Matter, and Production Tradeoffs
&lt;/h1&gt;

&lt;p&gt;Compare ESP32-C3, S3, and C6 for CPU, memory, USB, wireless, TinyML, Matter, OTA, and production validation before choosing a custom firmware platform.&lt;/p&gt;

&lt;p&gt;For a connected sensor or compact controller, ESP32-C3 is usually the lowest-risk starting point. If the device must combine a display, camera, audio, USB, or local inference, validate ESP32-S3 first. If the roadmap explicitly requires on-chip 802.15.4, particularly Matter over Thread or Zigbee, validate ESP32-C6 first. This is not a ranking by age or headline clock speed. Each chip defines a different system boundary.&lt;/p&gt;

&lt;p&gt;The decision should be made against peak memory, concurrent peripherals, radio requirements, OTA rollback, and the maintenance path after security features are enabled. A successful prototype only proves that the happy path ran once. A production selection needs measurable margin under the worst workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Two distinctions prevent expensive mistakes. C3 and C6 include USB Serial/JTAG, but that is not the general USB OTG capability offered by S3. Also, Matter does not automatically require C6: Matter can run over Wi-Fi. C6 becomes the clear route when Thread or another on-chip 802.15.4 use case is part of the product contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freeze the workload before comparing chips
&lt;/h2&gt;

&lt;p&gt;Turn the product brief into a measurable workload sheet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connectivity:&lt;/strong&gt; simultaneous Wi-Fi/BLE, TLS sessions, MQTT reconnects, local discovery, or Thread/Zigbee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data path:&lt;/strong&gt; sensor rate, audio frames, image size, ring buffers, offline queue, and retained logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interaction:&lt;/strong&gt; display refresh, touch, USB, camera, wake word, and the maximum user-visible response time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance:&lt;/strong&gt; A/B OTA, rollback, crash capture, field diagnostics, Secure Boot, Flash Encryption, and key rotation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"MQTT works" is not a memory test. Peak pressure may occur when TLS reconnect, OTA download, log writes, and sensor acquisition overlap. A system can report adequate total free heap yet still fail a large contiguous allocation. Test the combined condition instead of estimating each subsystem in isolation.&lt;/p&gt;

&lt;p&gt;The selection flow ends up looking like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Freeze the workload.&lt;/li&gt;
&lt;li&gt;Is on-chip 802.15.4 required? → Yes: validate C6 first.&lt;/li&gt;
&lt;li&gt;Does it need USB OTG, display, camera, audio, or heavier inference? → Yes: validate S3 first.&lt;/li&gt;
&lt;li&gt;Otherwise → start validation with C3.&lt;/li&gt;
&lt;li&gt;Run worst-case production tests. If resource, RF, OTA, and security margins pass, freeze the chip and module; otherwise, re-examine the workload.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Convert specifications into firmware consequences
&lt;/h2&gt;

&lt;p&gt;Espressif documents ESP32-C3 as a single-core RISC-V device up to 160 MHz with 400 KB of on-chip SRAM, 2.4 GHz Wi-Fi 4, and Bluetooth 5 LE. ESP32-S3 has two Xtensa LX7 cores up to 240 MHz, 512 KB of on-chip SRAM, vector instructions, LCD/camera support, and USB OTG. ESP32-C6 differentiates itself with Wi-Fi 6, BLE, IEEE 802.15.4, and high-performance plus low-power RISC-V cores. Package, flash, PSRAM, and pin availability still depend on the selected SoC revision and module.&lt;/p&gt;

&lt;p&gt;Those specifications change architecture decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C3's&lt;/strong&gt; single core is sufficient for many nodes, but the network stack, application tasks, and interrupt service compete more directly. Task priorities, non-blocking drivers, and reconnect-time latency must be deliberate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3's&lt;/strong&gt; second core and vector support create room for richer edge workloads, but do not remove memory and bandwidth limits. A framebuffer, camera DMA, audio buffers, and PSRAM traffic can contend at the same time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C6&lt;/strong&gt; is primarily a protocol-roadmap decision, not a replacement for S3 multimedia. Thread/Zigbee and Wi-Fi/BLE coexistence bring RF scheduling, certification, and stack-resource work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the official ESP32-C3 datasheet, ESP32-S3 datasheet, and ESP32-C6 datasheet as the baseline. Record the chip revision, ESP-IDF version, module, and differences between the development kit and production PCB with the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a firmware budget, not a flash-size guess
&lt;/h2&gt;

&lt;p&gt;Create a resource budget before schematic freeze and make CI report the same measures on every build.&lt;/p&gt;

&lt;p&gt;PSRAM is not unlimited memory. It depends on the S3 module/variant and is commonly useful for large buffers, framebuffers, or model data. It should not blindly absorb every real-time allocation. Espressif's LCD documentation notes that framebuffers, CPU activity, and EDMA can share PSRAM bandwidth and become starved. Measure display, network, and local processing concurrently.&lt;/p&gt;

&lt;p&gt;Tie that budget to a repeatable peak-load scenario. A display device may look comfortable on a static page, then encounter DNS and TLS reconnect, OTA metadata download, screen refresh, sensor acquisition, and offline-queue writes at once. Record minimum free heap, largest free block, task-stack high-water marks, watchdog events, dropped frames, and business-response latency for a fixed workload. Replay it after ESP-IDF, TLS, model, or partition changes. If C3 retains stable margin, moving to S3 does not automatically improve the product; if the failure is a non-separable memory peak or scheduling conflict, isolated micro-optimisations may only defer the architecture decision.&lt;/p&gt;

&lt;p&gt;Freeze a chip together with its module, partition table, ESP-IDF baseline, and security configuration. Modules based on the same SoC can differ in flash, PSRAM, antenna arrangement, and usable pins, while a development board may hide power or programming constraints with external components. The design record should therefore name the module, substitution rules, strapping pins, antenna clearance, peak supply assumptions, and download/JTAG path. This makes a module substitution or SDK upgrade trigger the right validation instead of being treated as an equivalent "same ESP32" change.&lt;/p&gt;

&lt;h2&gt;
  
  
  TinyML: S3 is a natural candidate, not an automatic pass
&lt;/h2&gt;

&lt;p&gt;For wake words, vibration classification, small vision features, or compact detection models, S3's dual cores, vector instructions, and optional PSRAM often make it the practical first candidate. ESP-DL also treats quantisation as central on memory-constrained devices. But loading a model is not the acceptance criterion. Freeze and measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input shape, supported operators, INT8/INT16 method, and representative calibration data;&lt;/li&gt;
&lt;li&gt;peak tensor arena, weights, preprocessing, and business buffers at the same time;&lt;/li&gt;
&lt;li&gt;end-to-end latency including acquisition, preprocessing, inference, postprocessing, and transmission;&lt;/li&gt;
&lt;li&gt;p95/p99 latency and watchdog behaviour while Wi-Fi, display, or audio is active;&lt;/li&gt;
&lt;li&gt;accuracy, false positives, false negatives, and an explicit uncertain/manual-review path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C3 can execute sufficiently small models, so it should not be excluded by name. Conversely, an unsupported operator set, large image pipeline, or Linux-class runtime can exceed S3's sensible boundary. The right answer may be an MCU plus a dedicated accelerator or application processor.&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%2Fost8sn2blkw31lungxoy.webp" 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%2Fost8sn2blkw31lungxoy.webp" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Matter and Thread: identify the network bearer first
&lt;/h2&gt;

&lt;p&gt;"Support Matter" is not yet a complete requirement. Is it Matter over Wi-Fi or Matter over Thread? Does the product also need Zigbee? Is the device an end device, router, bridge, or part of a border-router system? How do commissioning, local control, and cloud control degrade independently?&lt;/p&gt;

&lt;p&gt;For on-chip Thread or Zigbee, C6's IEEE 802.15.4 radio is a direct advantage. For Matter over Wi-Fi, C3, S3, and C6 can all be candidates depending on memory, peripherals, and the certification plan. Protocol availability does not equal a certifiable product: antenna design, RF coexistence, credentials, device attestation, commissioning UX, and stack-version control remain production work.&lt;/p&gt;

&lt;p&gt;A gateway or bridge can also accumulate too many roles. Combining 802.15.4, Wi-Fi backhaul, model translation, OTA, and local rules on one MCU expands the fault domain. A radio coprocessor separated from the primary controller can sometimes produce a cleaner upgrade and certification boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove the selection with a failure matrix
&lt;/h2&gt;

&lt;p&gt;Before freezing the chip, run combinations that represent field failure rather than a feature demo.&lt;/p&gt;

&lt;p&gt;Do not postpone the security lifecycle. Secure Boot, Flash Encryption, and eFuse decisions can change JTAG, download, and repair access. Rehearse key injection, recovery, and RMA on a pre-production batch.&lt;/p&gt;

&lt;p&gt;The failure matrix must also separate a silicon limit from an integration defect. A control timeout during weak-signal reconnect could indicate CPU contention, but it could also come from a driver holding a lock, synchronous logging, or an incorrect backoff policy. Replacing C3 with S3 may hide the symptom without fixing the failure mode. Associate each result with reset reason, heap low-water mark, stack high-water mark, state-machine timing, and OTA rollback reason; upgrade the chip only after the evidence shows that the required workload still crosses the resource or peripheral boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  When none of these chips is the right boundary
&lt;/h2&gt;

&lt;p&gt;High-resolution multi-stream video, complex Linux applications, containers, browser-class UI, large-model inference, or substantial local storage may already be outside a sensible MCU boundary. Consider a Linux SoC, an MCU/MPU split, or a dedicated accelerator. Preserving a one-chip BOM by sacrificing observability, rollback, and performance margin usually moves cost into field maintenance.&lt;/p&gt;

&lt;p&gt;Do not upgrade an established C3 product merely because S3 or C6 exposes more features. A mature C3 design may already have a stable BSP, fixture, certification, and supply chain. Migration reopens drivers, RF, power, factory testing, and OTA risk. It is justified when a measured new workload crosses the current boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommendation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;ESP32-C3&lt;/strong&gt; when the product is a focused Wi-Fi/BLE sensing or control node and worst-case heap, latency, and OTA have passed.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;ESP32-S3&lt;/strong&gt; when USB OTG, display, camera, audio, or TinyML is the core workload and PSRAM/bandwidth/real-time margin is demonstrated.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;ESP32-C6&lt;/strong&gt; when Thread, Zigbee, or a Wi-Fi 6 roadmap is explicit and coexistence, certification, and OTA resource costs are in the plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the requirements still cannot be converted into a module, partition table, driver boundary, and validation matrix, another comparison table will not close the gap.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide uses public Espressif documentation and does not include a controlled, cross-chip benchmark on identical boards, firmware, and lab conditions. It therefore makes no universal promise about power, BOM, RF, TinyML latency, or certification. Re-test all numbers on the selected module, PCB, ESP-IDF release, and production configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ESP32-C3 Datasheet&lt;/li&gt;
&lt;li&gt;ESP32-S3 Datasheet&lt;/li&gt;
&lt;li&gt;ESP32-C6 Datasheet&lt;/li&gt;
&lt;li&gt;ESP-IDF Chip Series Comparison&lt;/li&gt;
&lt;li&gt;ESP32-C3 USB Serial/JTAG&lt;/li&gt;
&lt;li&gt;ESP32-S3 LCD and PSRAM Bandwidth Notes&lt;/li&gt;
&lt;li&gt;ESP-DL Introduction&lt;/li&gt;
&lt;li&gt;ESP-DL Quantisation Specification&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>esp32</category>
      <category>iot</category>
      <category>tinyml</category>
    </item>
    <item>
      <title>Edge AI Device OTA: Staged Rollouts, Rollbacks, and Remote Recovery</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Tue, 25 Aug 2026 10:06:15 +0000</pubDate>
      <link>https://dev.to/zediot/edge-ai-device-ota-staged-rollouts-rollbacks-and-remote-recovery-38bo</link>
      <guid>https://dev.to/zediot/edge-ai-device-ota-staged-rollouts-rollbacks-and-remote-recovery-38bo</guid>
      <description>&lt;h1&gt;
  
  
  Edge AI Device OTA: Staged Rollouts, Rollbacks, and Remote Recovery
&lt;/h1&gt;

&lt;p&gt;A practical guide to designing over-the-air updates for edge AI fleets — why firmware, model, and config must ship separately, and how to build rollback and recovery paths from day one.&lt;/p&gt;

&lt;p&gt;The hard part of Edge AI OTA is not pushing a new package. It is designing staged rollout, rollback, and remote recovery for devices whose firmware, model, and configuration all change independently.&lt;/p&gt;

&lt;p&gt;When teams talk about Edge AI deployment, they usually start with the model: can it run on-device, how fast is inference, and what is the power profile. But once devices are deployed in volume, the first serious failure often comes from the release path itself. One device gets the new firmware but not the new model. Another applies a config change before the model file finishes downloading. A third reboots into a bad state and loses the only recovery channel you had.&lt;/p&gt;

&lt;p&gt;The core conclusion is simple: Edge AI OTA should not be treated as "shipping one new package." It should be treated as a layered operations system that releases firmware, model, and configuration separately, validates health during staged rollout, and can roll back deterministically. If you keep shipping them as one bound update, fleet scale will expose recoverability problems before it exposes product problems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What Edge AI OTA means here:&lt;/strong&gt; the coordinated remote release of firmware, model artifacts, configuration, dependencies, and health rules. It is more than package delivery or firmware flashing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When it's required:&lt;/strong&gt; if an edge AI device will run continuously, receive model updates, or operate in places where onsite support is expensive, OTA must include staged rollout, automatic rollback, and remote recovery from day one. Without those three layers, every new release increases operational risk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Why Edge AI OTA breaks differently from standard IoT OTA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 In normal IoT, a failed update usually breaks a feature; in Edge AI, it can break the whole runtime chain
&lt;/h3&gt;

&lt;p&gt;For a simple telemetry or control device, a failed update often means the device stays on the old version or one function becomes unavailable. Edge AI devices are different because at least three classes of change evolve together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firmware or system runtime changes&lt;/li&gt;
&lt;li&gt;Model artifact changes&lt;/li&gt;
&lt;li&gt;Configuration changes such as thresholds, feature flags, and resource mappings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those layers are not naturally synchronized. If the platform does not model their dependencies explicitly, the fleet quickly starts to exhibit failure patterns like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new model arrives, but the old firmware cannot support its preprocessing path&lt;/li&gt;
&lt;li&gt;firmware upgrades successfully, but the configuration never switches, so inference services fail to start&lt;/li&gt;
&lt;li&gt;configuration activates first, and the device points to a model that is not fully downloaded&lt;/li&gt;
&lt;li&gt;the device enters a reboot loop, while the platform only reports that the package was delivered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the target is an ESP32 camera, an RK3566 vision box, a gateway with an NPU, or a field industrial terminal, the update process is no longer a simple binary replacement. It becomes dependency management for a live runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 From ESP32 to RK3566, release complexity does not scale linearly
&lt;/h3&gt;

&lt;p&gt;Many teams try to manage MCU-class devices and Linux edge boxes with the same mental model: OTA means replacing the software package. That may survive a PoC, but it does not survive fleet operations.&lt;/p&gt;

&lt;p&gt;The reason is that the two device classes have very different boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESP32 devices usually have tighter memory, more rigid partitions, smaller update artifacts, and weaker observability&lt;/li&gt;
&lt;li&gt;RK3566 class Linux devices can carry much larger models and dependencies, but they introduce service orchestration, disk space management, driver compatibility, and multi-process runtime issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the release platform does not adapt rollout policy to device capability and instead insists on one generic OTA path for every node, the first thing to collapse is not release success rate. It is recovery quality and troubleshooting speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What a production-safe Edge AI OTA system must separate
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Do not bind firmware, model, and configuration into one version number
&lt;/h3&gt;

&lt;p&gt;This is the first habit worth fixing in any Edge AI release pipeline. A single bundled version may look simpler, but it makes root cause analysis and rollback significantly worse.&lt;/p&gt;

&lt;p&gt;A safer structure tracks at least three version planes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firmware Version:&lt;/strong&gt; drivers, acquisition stack, inference runtime, device management agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Version:&lt;/strong&gt; model weights, quantized artifacts, label maps, pre/post-processing assets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config Version:&lt;/strong&gt; thresholds, sampling policy, upload cadence, model selection rules, feature flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this separation matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;firmware rollback and model rollback do not have the same cost or blast radius&lt;/li&gt;
&lt;li&gt;model swaps should not always require a firmware restart&lt;/li&gt;
&lt;li&gt;configuration mistakes usually deserve a fast logical revert, not a full firmware rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If an Edge AI platform cannot track firmware, model, and configuration independently, it will struggle to do low-risk staged rollout and will struggle even more to identify which layer actually failed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.2 A release system must answer one operational question first: what exactly is being released
&lt;/h3&gt;

&lt;p&gt;A production release object should make these points explicit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which device groups, customers, regions, or sites are targeted&lt;/li&gt;
&lt;li&gt;whether the change affects firmware, model, configuration, or a combination&lt;/li&gt;
&lt;li&gt;whether a minimum prerequisite version must already be present&lt;/li&gt;
&lt;li&gt;what success means for this release&lt;/li&gt;
&lt;li&gt;which layer should roll back first when health degrades&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a modeled release object, staged rollout turns into "we picked a few devices to test" and rollback turns into "we pushed the old package again and hoped for the best."&lt;/p&gt;

&lt;p&gt;A useful release model connects these five parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Release Plan
├── Target Ring        → Canary → 10% Fleet → Region/Customer → Full Rollout
├── Version Set        → Firmware Version + Model Version + Config Version
├── Health Rules       → what "success" is measured against
└── Rollback Policy    → which layer reverts first on failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The release plan links a target ring, a version set, health rules, and a rollback policy together so every release is explicit about what ships, to whom, and how it gets pulled back.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Staged rollout is not about shipping to fewer devices first; it is about testing recovery first
&lt;/h3&gt;

&lt;p&gt;Teams often reduce staged rollout to a quantity problem: first 1%, then 10%, then full deployment. That is incomplete. In Edge AI, staged rollout has to validate three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the upgraded device starts correctly&lt;/li&gt;
&lt;li&gt;whether inference quality and resource behavior remain stable&lt;/li&gt;
&lt;li&gt;whether the platform can detect failure and recover automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the staged phase only checks that the package was delivered, not whether inference, health telemetry, logs, and rollback paths all work, full rollout still carries the same operational risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How to design rollout, rollback, and remote recovery from ESP32 to RK3566
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 ESP32 needs the smallest and most deterministic rollback path
&lt;/h3&gt;

&lt;p&gt;ESP32-class devices are defined by tighter resources, broad physical distribution, and weaker observability. For them, the most valuable OTA feature is not richness. It is survivability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use explicit dual-partition or A/B firmware strategy&lt;/li&gt;
&lt;li&gt;keep model artifacts smaller or layered externally instead of tying every model change to firmware&lt;/li&gt;
&lt;li&gt;require boot health checks after update, such as management-agent connectivity, sensor initialization, or inference thread liveness&lt;/li&gt;
&lt;li&gt;roll back automatically within a bounded time window if those checks fail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Patterns to avoid:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;replacing firmware, model, and configuration in one large update&lt;/li&gt;
&lt;li&gt;treating "device came online" as enough evidence of release success&lt;/li&gt;
&lt;li&gt;depending entirely on human intervention for rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 RK3566 needs service lifecycle separation more than it needs whole-image replacement
&lt;/h3&gt;

&lt;p&gt;RK3566-class Linux devices often run multiple services at once: camera ingestion, decoding, inference, upload, and remote management. In these systems, the most common failures happen not during the file transfer but after release, when service dependencies become misaligned.&lt;/p&gt;

&lt;p&gt;A safer strategy usually looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manage system, application, and model layers separately&lt;/li&gt;
&lt;li&gt;switch models through manifests, symlinks, or service config rather than replacing the whole system every time&lt;/li&gt;
&lt;li&gt;use post-update checks for service health, disk headroom, NPU readiness, and sample inference replay&lt;/li&gt;
&lt;li&gt;prefer process-level or container-level release over whole-image replacement unless kernel or driver updates require it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Automatic rollback must be driven by health signals, not by timeout alone
&lt;/h3&gt;

&lt;p&gt;Many OTA platforms use only one rollback trigger: the device did not come back online in time. That is not enough for Edge AI. A device may be online while inference is already broken.&lt;/p&gt;

&lt;p&gt;Better rollback signals include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the model service started successfully&lt;/li&gt;
&lt;li&gt;whether inference latency exceeds a safe threshold&lt;/li&gt;
&lt;li&gt;whether memory, storage, or temperature enters an abnormal range&lt;/li&gt;
&lt;li&gt;whether critical inputs such as camera, sensor, or encoder streams disappeared&lt;/li&gt;
&lt;li&gt;whether the device still reports version and health summary consistently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is worth stating plainly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Standard IoT OTA asks whether the device came back online. Edge AI OTA asks whether the device came back online with a healthy inference path. If the platform watches only connectivity, it will misclassify many real failures as successful releases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.4 Remote recovery must be designed before the outage, not after it
&lt;/h3&gt;

&lt;p&gt;At scale, the most expensive part of a bad release is often not the failure itself. It is the requirement to send people onsite.&lt;/p&gt;

&lt;p&gt;That is why Edge AI devices should always preserve a remote recovery path, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a minimal management agent separated from the main application stack&lt;/li&gt;
&lt;li&gt;an independent safe mode or recovery partition&lt;/li&gt;
&lt;li&gt;the ability to pause auto-updates, freeze a bad version, and return to a stable model&lt;/li&gt;
&lt;li&gt;the ability to stop rollout immediately by device group, region, or customer segment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the team discovers during an incident that the management agent broke alongside the main workload, the failure is no longer just a release problem. It is an architecture problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A practical rollout cadence for Edge AI fleets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 The right sequence is not "build and push"; it is "validate health, then expand"
&lt;/h3&gt;

&lt;p&gt;A safer rollout rhythm usually looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate version dependencies on internal devices&lt;/li&gt;
&lt;li&gt;validate upgrade, inference, and rollback chains on a small canary ring&lt;/li&gt;
&lt;li&gt;expand by region, customer, or hardware family&lt;/li&gt;
&lt;li&gt;watch a stability window before full rollout&lt;/li&gt;
&lt;li&gt;keep freeze and rollback windows after rollout instead of deleting the previous version immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The release state flow follows this path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build Release
 → Internal Validation
   → Canary Rollout
     → Health Pass?
         ├─ Yes → Expand by Ring → Stable Window Passed? → Yes → Full Rollout
         │                                        └─ No → Auto Rollback
         └─ No → Auto Rollback
                                   Auto Rollback → Freeze Version / Investigate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every step either promotes to the next ring on a passed health check, or falls back to an auto rollback that freezes the version for investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 When a full staged rollout system may be overkill
&lt;/h3&gt;

&lt;p&gt;Not every Edge AI project needs a complex release orchestration system on day one. A lighter path may be enough when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the fleet is small and easy to service onsite&lt;/li&gt;
&lt;li&gt;models rarely change after deployment&lt;/li&gt;
&lt;li&gt;the device does not carry critical business risk and failed updates are cheap to fix manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even then, version tracking and basic rollback should remain in scope. The moment the project starts to scale, those become the first missing capabilities.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If an Edge AI system updates rarely, operates in small numbers, and remains easy to maintain onsite, a full staged rollout platform may not be the first investment to make. But once the fleet is expected to scale, rollback and remote recovery stop being optional.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. Conclusion: the real question is not how to push an update, but how to pull a bad release back
&lt;/h2&gt;

&lt;p&gt;For Edge AI devices, scale is determined less by the first successful deployment than by whether every later update can still be controlled safely. ESP32 and RK3566 have different runtime boundaries, but they obey the same operational rule: releases must be designed as a system that can stage, verify, roll back, and recover instead of a file transfer step.&lt;/p&gt;

&lt;p&gt;So if you are building Edge AI OTA, the highest-value investments are not the ones that make deployment slightly faster. They are the ones that make recovery predictable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;version separation:&lt;/strong&gt; track and release firmware, model, and configuration independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;staged validation:&lt;/strong&gt; promote only when health and recovery paths prove out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rollback and recovery:&lt;/strong&gt; make sure the platform can regain control after a failed release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only when those three layers exist does Edge AI OTA move from "can upgrade" to "can operate for the long run."&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Why should firmware, model, and configuration ship as separate versions in Edge AI OTA?
&lt;/h3&gt;

&lt;p&gt;Because they have different failure costs and rollback blast radius. Separating them lets you roll back a model without a firmware restart, or revert a bad config logically instead of doing a full firmware rollback, and makes root cause analysis much faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What health signals should drive automatic rollback?
&lt;/h3&gt;

&lt;p&gt;Beyond a connectivity timeout, watch whether the model service started, whether inference latency stays within a safe threshold, whether memory/storage/temperature are in a normal range, whether critical inputs (camera, sensor, encoder) still exist, and whether the device keeps reporting version and health summaries consistently.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. How should ESP32 and RK3566 devices be released differently?
&lt;/h3&gt;

&lt;p&gt;ESP32-class devices need the smallest, most deterministic rollback path — dual-partition A/B, boot health checks, and bounded-time auto rollback. RK3566-class Linux devices need service lifecycle separation: switch models via manifests or service config, with post-update checks for service health, disk headroom, and NPU readiness.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. When is a full staged rollout system overkill?
&lt;/h3&gt;

&lt;p&gt;When the fleet is small and easy to service onsite, models rarely change after deployment, and the device carries low business risk with cheap-to-fix failures. Even then, basic version tracking and rollback should stay in place for when the project starts to scale.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>iot</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>RK3566 Can Run YOLOv8 INT8 — But Only Within These Limits</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Thu, 20 Aug 2026 12:30:00 +0000</pubDate>
      <link>https://dev.to/zediot/rk3566-can-run-yolov8-int8-but-only-within-these-limits-19ca</link>
      <guid>https://dev.to/zediot/rk3566-can-run-yolov8-int8-but-only-within-these-limits-19ca</guid>
      <description>&lt;p&gt;Running &lt;strong&gt;YOLOv8 INT8 on RK3566&lt;/strong&gt; isn’t just a model conversion task—it’s a system-level alignment challenge. This article explores how quantization, operator compatibility, and detection head design define whether real-time inference is possible on RK3566, and under what strict conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why YOLOv8 INT8 Can’t Just Run on RK3566
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What’s the Real Issue with Combining RK3566 and YOLOv8
&lt;/h3&gt;

&lt;p&gt;In many edge AI projects, &lt;a href="https://www.notebookcheck.net/Rockchip-RK3566-Processor-Benchmarks-and-Specs.741611.0.html" rel="noopener noreferrer"&gt;RK3566&lt;/a&gt; is often seen as a “cost-effective platform that can also handle some AI tasks.”&lt;/p&gt;

&lt;p&gt;Its positioning isn’t aggressive: controllable power consumption, full peripheral support, and limited but non-zero compute power. This means one thing—&lt;strong&gt;it’s not designed for complex models with compute headroom.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;YOLOv8 Detection sits in a delicate spot.&lt;/p&gt;

&lt;p&gt;It’s no longer a “lightweight, run-anywhere” model but is still far from server-level detection models. In theory, it belongs to the “just about doable” category for RK3566.&lt;/p&gt;

&lt;p&gt;In practice, this assumption often fails during deployment.&lt;/p&gt;

&lt;p&gt;Many projects run smoothly at the model stage:&lt;/p&gt;

&lt;p&gt;ONNX exports fine, PC-side inference is normal, and the structure doesn’t look too complex. But once converted to RKNN and deployed on-device, performance drops—unstable FPS, high CPU usage, and rapid system resource exhaustion.&lt;/p&gt;

&lt;p&gt;This isn’t due to a single parameter being off—it’s a deeper issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RK3566 is not a platform that brute-forces inference. Usability depends on having a clean execution path.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Floating-Point Inference Has Little Value on RK3566
&lt;/h3&gt;

&lt;p&gt;Running YOLOv8 Detection in FP16 or FP32 on RK3566 usually leads to predictable results:&lt;/p&gt;

&lt;p&gt;The model runs, but runs poorly.&lt;/p&gt;

&lt;p&gt;This isn’t an “implementation issue” of RKNN or NPU, but a design logic issue of the platform.&lt;/p&gt;

&lt;p&gt;On RK3566, the NPU isn’t a fully independent compute unit.&lt;/p&gt;

&lt;p&gt;If there are unsupported operators in the model, execution falls back to the CPU. Detection models often include such unsupported operations.&lt;/p&gt;

&lt;p&gt;With floating point, the issues worsen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Low NPU coverage for FP ops&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Constant data movement between CPU and NPU&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fragmented inference, high scheduling overhead&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;p&gt;Single-digit FPS with near-maxed system load.&lt;/p&gt;

&lt;p&gt;In this state, optimizing FP16 further is meaningless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s not under-tuned; it’s the wrong execution path for this hardware.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  INT8 Isn’t a “Bonus” — It’s the Entry Point
&lt;/h3&gt;

&lt;p&gt;Switching to INT8 reveals RK3566’s true nature.&lt;/p&gt;

&lt;p&gt;INT8 isn’t just about precision—it unlocks the most stable, fully supported execution path on RK3566.&lt;/p&gt;

&lt;p&gt;In INT8 mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Operator mapping success rate rises&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ops can stay in the NPU for longer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CPU acts more like a scheduler than a compute unit&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, YOLOv8 Detection starts “really running on the NPU,” not just “partially using the NPU.”&lt;/p&gt;

&lt;p&gt;But INT8 isn’t zero-cost.&lt;/p&gt;

&lt;p&gt;Detection models are sensitive to quantization, especially in the Head. Poor quantization leads to missed detections or box jitter.&lt;/p&gt;

&lt;p&gt;So the real question isn’t “should we use INT8,” but:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How far can YOLOv8 Detection go on RK3566 with INT8—and what are the boundaries?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  YOLOv8 Detection’s Structure Determines RK3566 Compatibility
&lt;/h3&gt;

&lt;p&gt;YOLOv8 Detection isn’t structurally complex—but its complexity concentrates in subtle areas.&lt;/p&gt;

&lt;p&gt;Backbone is usually fine.&lt;/p&gt;

&lt;p&gt;As long as channel counts and input sizes aren’t extreme, RK3566’s NPU handles it stably.&lt;/p&gt;

&lt;p&gt;The real issues appear in the later stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Irregular scale changes during feature fusion&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unpredictable Concat and Upsample combinations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unnecessary tensor ops in the Detection Head&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are legal in ONNX but can prevent the RKNN compiler from statically fixing the compute graph.&lt;/p&gt;

&lt;p&gt;Most failures aren’t due to model size but &lt;strong&gt;structural elements the compiler can’t resolve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the graph can’t be fully static, NPU advantages disappear quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your model fails during RKNN compilation, the issue may lie in unsupported operators. See this &lt;a href="https://zediot.com/blog/rknn-onnx-opset-compatibility/" rel="noopener noreferrer"&gt;ONNX opset compatibility reference&lt;/a&gt; for details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Structure First — It’s a Prerequisite for INT8 Success
&lt;/h3&gt;

&lt;p&gt;On RK3566, if structure doesn’t serve the execution path, quantization only helps partially.&lt;/p&gt;

&lt;p&gt;Repeatedly validated practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fixed input size is more important than flexibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic shapes offer less benefit than cost here&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simpler Detection Heads yield more stable INT8 results&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t flashy choices—but they aim for one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the entire inference inside the NPU without interruption.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the path breaks, even aggressive quantization can’t fix the overall performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  How YOLOv8 INT8 Actually Runs on RK3566
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Happens Between Model and Device
&lt;/h3&gt;

&lt;p&gt;Placing a YOLOv8 Detection model onto RK3566 isn’t determined by export, but by the in-between steps often oversimplified.&lt;/p&gt;

&lt;p&gt;From PyTorch to on-device execution, three major changes occur:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Graph gets compressed into statically analyzable form&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data precision maps from float to fixed point&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execution path splits between NPU and CPU&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any “gray area” here nearly guarantees performance issues.&lt;/p&gt;

&lt;p&gt;INT8 adds clarity to this path.&lt;/p&gt;

&lt;p&gt;RK3566’s INT8 support goes beyond compute—it affects compilation, scheduling, and caching.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a full walkthrough of exporting, converting, and deploying YOLOv8 models to RK3566, see our &lt;a href="https://zediot.com/blog/how-to-deploy-yolov8-on-rk3566/" rel="noopener noreferrer"&gt;deployment guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  INT8 Quantization Isn’t a One-Click Step
&lt;/h3&gt;

&lt;p&gt;Many first-time users of RKNN INT8 think it’s just a switch:&lt;/p&gt;

&lt;p&gt;Enable INT8, feed a few images, done.&lt;/p&gt;

&lt;p&gt;But it’s more like a filtering process.&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%2Fwlnykhhhjrjrzw3pw1kb.webp" 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%2Fwlnykhhhjrjrzw3pw1kb.webp" alt="image" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Calibration data doesn’t “train” the model; it constrains value ranges.&lt;/p&gt;

&lt;p&gt;Detection models are highly sensitive to feature distribution shifts, especially with large object size variation.&lt;/p&gt;

&lt;p&gt;If calibration data poorly match real scenes, issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Small object confidence drops&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Box jitter across frames&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Higher false positives in complex backgrounds&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems usually occur in the Detection Head, not the Backbone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detection Head Is the Make-or-Break Point for INT8
&lt;/h3&gt;

&lt;p&gt;On RK3566, bottlenecks rarely lie in the Backbone.&lt;/p&gt;

&lt;p&gt;The real gap appears downstream.&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%2Fimages.openai.com%2Fthumbnails%2Furl%2FDZjzqHicu5mVUVJSUGylr5-al1xUWVCSmqJbkpRnoJdeXJJYkpmsl5yfq5-Zm5ieWmxfaAuUsXL0S7F0Tw6q8CgKtshKynfJKMwsc0sOMImIL0tLjM93yfYN9DQNcUxO80kJsagMcPJLTErM8zT1VSsGAJmNJtc" 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%2Fimages.openai.com%2Fthumbnails%2Furl%2FDZjzqHicu5mVUVJSUGylr5-al1xUWVCSmqJbkpRnoJdeXJJYkpmsl5yfq5-Zm5ieWmxfaAuUsXL0S7F0Tw6q8CgKtshKynfJKMwsc0sOMImIL0tLjM93yfYN9DQNcUxO80kJsagMcPJLTErM8zT1VSsGAJmNJtc" alt="image" width="300" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Detection Head has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Frequent resolution shifts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wide numerical ranges&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extreme precision demands&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it most prone to quantization distortion.&lt;/p&gt;

&lt;p&gt;Even if Backbone and Neck quantize well, aggressive Head quantization can degrade detection.&lt;/p&gt;

&lt;p&gt;That’s why many RK3566 failures stem not from “large models,” but &lt;strong&gt;Head structures misaligned with quantization.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution Continuity Beats Theoretical Compute
&lt;/h3&gt;

&lt;p&gt;Once on-device, another critical issue surfaces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can operators stay on the NPU continuously?&lt;/strong&gt;&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%2Forpyqatwme9m9mhpwqqu.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%2Forpyqatwme9m9mhpwqqu.png" alt="image" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ideally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Input goes into NPU&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiple layers execute without switching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Output returns to CPU&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if an op can’t run on NPU, control switches to CPU.&lt;/p&gt;

&lt;p&gt;On RK3566, with limited compute/bandwidth, this cost is high.&lt;/p&gt;

&lt;p&gt;INT8 greatly increases the odds of uninterrupted NPU execution.&lt;/p&gt;

&lt;p&gt;That’s why INT8 often yields &lt;strong&gt;better-than-linear performance gains&lt;/strong&gt; on the same model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantization Fails Don’t Mean Model Is Bad
&lt;/h3&gt;

&lt;p&gt;Often, models that work fine on PC fail post-quantization on RK3566.&lt;/p&gt;

&lt;p&gt;This doesn’t mean model choice was wrong. More likely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Inadequate calibration data coverage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input size mismatch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Detection Head too complex&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In such cases, simplifying structure—like reducing branches or compressing channels—is more effective than tweaking quantization parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Limits of YOLOv8 INT8 on RK3566
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Test Conditions &amp;amp; Constraints
&lt;/h3&gt;

&lt;p&gt;To avoid misleading results, all tests follow the same setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;: RK3566 (NPU enabled)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model&lt;/strong&gt;: YOLOv8 Detection (no pruning)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Input Size&lt;/strong&gt;: 640×640, fixed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inference Mode&lt;/strong&gt;: Single-frame, Batch=1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-Processing&lt;/strong&gt;: On CPU, not on NPU&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ignore extreme tuning and special trims.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Goal: Evaluate a “reusable in production” YOLOv8 Detection on RK3566.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  FPS: INT8 vs FP16 Isn’t Linearly Different
&lt;/h3&gt;

&lt;p&gt;The raw numbers tell the story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model Precision&lt;/th&gt;
&lt;th&gt;Inference FPS (640×640)&lt;/th&gt;
&lt;th&gt;Stability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FP16&lt;/td&gt;
&lt;td&gt;3 ~ 5 FPS&lt;/td&gt;
&lt;td&gt;Unstable, high CPU load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INT8&lt;/td&gt;
&lt;td&gt;12 ~ 18 FPS&lt;/td&gt;
&lt;td&gt;Stable, sustained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This isn’t just “INT8 is faster”—&lt;strong&gt;it’s a different execution mode.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FP16 triggers frequent CPU fallback.&lt;br&gt;&lt;br&gt;
INT8 allows sustained NPU-only execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Only in INT8 mode does YOLOv8 Detection become close to real-time on RK3566.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This holds true across projects; differences arise in scale, not in conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy Loss: Localized, Not Global
&lt;/h3&gt;

&lt;p&gt;FPS boost is just the start. Detection accuracy defines usability.&lt;/p&gt;

&lt;p&gt;INT8 quantization doesn’t degrade uniformly—it shows structural patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scene Type&lt;/th&gt;
&lt;th&gt;Accuracy Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Medium/Large Objects&lt;/td&gt;
&lt;td&gt;Mostly stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple Background&lt;/td&gt;
&lt;td&gt;Almost unaffected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small/Dense Objects&lt;/td&gt;
&lt;td&gt;Much more sensitive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex Textures&lt;/td&gt;
&lt;td&gt;More false positives&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Thus, INT8 doesn’t “weaken everything”—&lt;strong&gt;it amplifies pre-existing weak points.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the Detection Head and calibration data match your real use case, losses are usually acceptable.&lt;/p&gt;

&lt;h3&gt;
  
  
  No “Sweet Spot” Between FPS &amp;amp; Accuracy
&lt;/h3&gt;

&lt;p&gt;A common RK3566 myth: find a “perfect FPS with minimal accuracy drop.”&lt;/p&gt;

&lt;p&gt;Reality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Choose INT8: accept structured accuracy changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or use FP16: sacrifice real-time capability&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s not a quantization flaw—it’s a &lt;strong&gt;hardware limit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As model complexity nears RK3566’s ceiling, &lt;strong&gt;you can’t have both high FPS and full accuracy.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Turning Test Data into Engineering Judgments
&lt;/h3&gt;

&lt;p&gt;Compressing the findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usable FPS lower bound is ~10 FPS on RK3566&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Below that, load spikes and stability plummets&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;INT8 is a must—but not sufficient on its own&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model structure and Head design define quantized accuracy&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These hold across projects—this isn’t anecdotal.&lt;/p&gt;

&lt;h3&gt;
  
  
  When It’s Worth Using, When to Switch Plans
&lt;/h3&gt;

&lt;p&gt;Combined performance and accuracy limits give us clear lines:&lt;/p&gt;

&lt;h4&gt;
  
  
  Suitable Scenarios
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Single-class or few-class detection&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Medium+ object sizes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FPS target of 10–15&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prioritize response speed over peak accuracy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Not Suitable
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Many small objects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High precision needed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex post-processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expecting PC-level accuracy on RK3566&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the unsuitable cases, pushing RK3566 further yields little.&lt;br&gt;&lt;br&gt;
You must change model size, hardware, or task design.&lt;/p&gt;

&lt;h3&gt;
  
  
  YOLOv8 Detection Feasibility on RK3566 (INT8)
&lt;/h3&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;Acceptable Range&lt;/th&gt;
&lt;th&gt;Typical Issue When Exceeded&lt;/th&gt;
&lt;th&gt;Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Precision Type&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;INT8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;5 FPS in FP16/FP32&lt;/td&gt;
&lt;td&gt;INT8 is essential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input Size&lt;/td&gt;
&lt;td&gt;≤640×640&lt;/td&gt;
&lt;td&gt;Larger → nonlinear FPS drop&lt;/td&gt;
&lt;td&gt;Fixed input preferred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real FPS&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12–18 FPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;10 FPS → system overload&lt;/td&gt;
&lt;td&gt;10 FPS = lower bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NPU Utilization&lt;/td&gt;
&lt;td&gt;High (continuous)&lt;/td&gt;
&lt;td&gt;Frequent CPU fallback&lt;/td&gt;
&lt;td&gt;Path continuity &amp;gt; GFLOPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backbone&lt;/td&gt;
&lt;td&gt;Light ~ medium&lt;/td&gt;
&lt;td&gt;Rarely a problem&lt;/td&gt;
&lt;td&gt;Acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detection Head&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Simpler = better&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Box jitter / missed detects&lt;/td&gt;
&lt;td&gt;Decides success/failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small Obj. Density&lt;/td&gt;
&lt;td&gt;Low ~ medium&lt;/td&gt;
&lt;td&gt;High → misdetects increase&lt;/td&gt;
&lt;td&gt;Not ideal use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calibration Data&lt;/td&gt;
&lt;td&gt;Scene-aligned&lt;/td&gt;
&lt;td&gt;Misaligned → accuracy loss&lt;/td&gt;
&lt;td&gt;Critical for INT8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long Runtime&lt;/td&gt;
&lt;td&gt;Stable in INT8&lt;/td&gt;
&lt;td&gt;FP16 fluctuates&lt;/td&gt;
&lt;td&gt;INT8 is sustainable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Final Judgment
&lt;/h3&gt;

&lt;p&gt;If only one takeaway matters, it’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RK3566 can run YOLOv8 Detection—if you accept INT8 and understand its limits.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s not a failure platform, nor “AI-ready by default.”&lt;br&gt;&lt;br&gt;
When model, structure, and expectations align, RK3566 delivers stable, predictable results.&lt;br&gt;&lt;br&gt;
Push beyond its limits, and both performance and accuracy collapse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Decision Guide (Matrix)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your Need&lt;/th&gt;
&lt;th&gt;RK3566 + YOLOv8 INT8 Recommended?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Few-class detection&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium object sizes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Realtime (≥10 FPS)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many small objects&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-precision localization&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex post-processing&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;INT8 is the starting point for YOLOv8 on RK3566&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FPS gain comes from execution path shift, not compute boost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accuracy loss centers on Detection Head and select cases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once platform boundaries are clear, decisions become simpler&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;RK3566 can run YOLOv8 INT8—but only when you design within hard boundaries.&lt;/strong&gt; From quantization to execution path planning, success depends on matching model constraints with RKNN’s capabilities and the NPU’s limited flexibility. Push past those limits, and the system fails predictably.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking to deploy object detection in constrained edge environments, such as the RK3566?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;ZedIoT&lt;/strong&gt; builds custom AIoT pipelines designed for real-world constraints—see our &lt;a href="https://zediot.com/solutions/edge-computing-ai/" rel="noopener noreferrer"&gt;Edge AI system capabilities&lt;/a&gt; to explore what we deliver on RK3566 and beyond.&lt;/p&gt;
&lt;/blockquote&gt;







&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: What is the optimal inference mode for YOLOv8 on RK3566?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; INT8 quantization is the only viable mode. It ensures maximum NPU utilization, minimizes CPU fallback, and enables 12–18 FPS, compared to 3–5 FPS in FP16.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Why is RK3566 not suited for floating-point inference?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Floating-point ops are only partially supported by the RK3566 NPU. Unsupported ops get routed to the CPU, leading to fragmented execution and low performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Where does INT8 quantization most affect YOLOv8 accuracy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; In the Detection Head, due to frequent resolution changes and high precision needs. It’s the most fragile area post-quantization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: How can I make sure my YOLOv8 model survives INT8 quantization?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Use calibration data that mirrors deployment scenarios, fix input resolution, and simplify the Detection Head architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Is there a performance-accuracy sweet spot for YOLOv8 on RK3566?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; No. You either accept INT8’s structured accuracy loss or fall back to FP16 with unacceptably low FPS. It’s a binary choice dictated by platform limits.&lt;/p&gt;

</description>
      <category>rk3566</category>
      <category>yolov8</category>
      <category>edgeai</category>
      <category>iot</category>
    </item>
    <item>
      <title>HarmonyOS vs OpenHarmony vs HarmonyOS NEXT: What Actually Changes for a Product Team?</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Tue, 18 Aug 2026 09:51:36 +0000</pubDate>
      <link>https://dev.to/zediot/harmonyos-vs-openharmony-vs-harmonyos-next-what-actually-changes-for-a-product-team-9m4</link>
      <guid>https://dev.to/zediot/harmonyos-vs-openharmony-vs-harmonyos-next-what-actually-changes-for-a-product-team-9m4</guid>
      <description>&lt;p&gt;HarmonyOS, OpenHarmony, and HarmonyOS NEXT are not three interchangeable products that can be ranked by version number. OpenHarmony is an open-source project incubated and operated by the OpenAtom Foundation. HarmonyOS is Huawei's commercial device, application, and service ecosystem. HarmonyOS NEXT is a name still used in current developer material for the native HarmonyOS application route; it is not the next OpenHarmony release.&lt;/p&gt;

&lt;p&gt;The platforms may expose related architectural ideas, languages, or tool names, but similarity is not a compatibility contract. It does not prove that source trees, API levels, application packages, commercial kits, distribution channels, certifications, or update responsibilities are interchangeable. A team that chooses by the word "Harmony" usually discovers the missing work later, during BSP integration, third-party SDK replacement, AppGallery release, or OTA maintenance.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Put each name back inside its actual responsibility boundary
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.openharmony.cn/" rel="noopener noreferrer"&gt;official OpenHarmony documentation&lt;/a&gt; defines OpenHarmony as an open-source project incubated and operated by the OpenAtom Foundation. Its goal is to build a framework and platform for smart-terminal operating systems through an open-source model. It provides public source repositories, release branches, a Public SDK, API levels, and community governance. A device maker selecting OpenHarmony receives a system baseline it may study, tailor, port, integrate, and maintain. It does not receive a finished commercial product that automatically joins Huawei's consumer-device services and distribution ecosystem.&lt;/p&gt;

&lt;p&gt;HarmonyOS has a different boundary. Huawei's developer portal combines DevEco Studio, ArkTS, ArkUI, the HarmonyOS SDK, AppGallery Connect, and ecosystem kits into an application and distribution route. When a product must run on supported Huawei phones, tablets, wearables, or other HarmonyOS devices and use account, push, payment, marketplace, or cross-device capabilities, the integration target is the HarmonyOS commercial platform. Building an OpenHarmony-based device distribution does not substitute for that application ecosystem.&lt;/p&gt;

&lt;p&gt;HarmonyOS NEXT causes additional confusion because naming has changed across product generations. Huawei still has a "HarmonyOS NEXT Develop" entry, while recent distribution material refers to "HarmonyOS 5 or later." A product team should treat HarmonyOS NEXT as context for the current native HarmonyOS application route, then bind its implementation to specific target devices, SDK/API levels, release regions, and marketplace rules. A marketing name is not a version pin.&lt;/p&gt;

&lt;p&gt;The practical rule is direct: choose the HarmonyOS application route when the required outcome is a Huawei-device application and ecosystem service; evaluate OpenHarmony when the required outcome is an owned device OS whose source, board integration, and lifecycle remain under the manufacturer's control. If the product needs both, create two verification tracks rather than assuming that one name removes the delivery work.&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%2Fd09xn8adjg8biap5bmt8.webp" 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%2Fd09xn8adjg8biap5bmt8.webp" alt="A product team independently testing development boards, an industrial panel, and mobile devices" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. "Related" does not mean "interchangeable"
&lt;/h2&gt;

&lt;p&gt;Migration has at least five independent compatibility layers. Unless the target's official documentation or device tests prove a layer, the team should not mark it compatible.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence to collect&lt;/th&gt;
&lt;th&gt;Dangerous shortcut&lt;/th&gt;
&lt;th&gt;Likely consequence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Governance and licence&lt;/td&gt;
&lt;td&gt;Source licences, third-party notices, trademarks, product compatibility and certification&lt;/td&gt;
&lt;td&gt;Open source code is treated as permission to use every brand or ecosystem benefit&lt;/td&gt;
&lt;td&gt;Compliance or product naming fails late&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware and system&lt;/td&gt;
&lt;td&gt;SoC, kernel, BSP, drivers, SystemCapability declarations, resource budget&lt;/td&gt;
&lt;td&gt;A reference board demo is treated as a maintained production board&lt;/td&gt;
&lt;td&gt;Drivers, power, factory flashing, and OTA become hidden projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API and app model&lt;/td&gt;
&lt;td&gt;API level, ArkTS/ArkUI differences, permissions, package model, native APIs&lt;/td&gt;
&lt;td&gt;Similar language and IDE names are treated as source compatibility&lt;/td&gt;
&lt;td&gt;Build success is followed by runtime or permission failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kits and distribution&lt;/td&gt;
&lt;td&gt;Account, push, maps, payment, analytics, signing, review, and regional availability&lt;/td&gt;
&lt;td&gt;Installable is treated as commercially distributable&lt;/td&gt;
&lt;td&gt;Required services or target markets remain unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operations and upgrades&lt;/td&gt;
&lt;td&gt;Security notices, maintained branch, OTA, data migration, rollback, and support horizon&lt;/td&gt;
&lt;td&gt;First boot is treated as a completed platform decision&lt;/td&gt;
&lt;td&gt;No team owns upgrades and field recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use the matrix to find veto conditions, not to produce a single popularity score. An application that requires an AppGallery-distributed Huawei kit cannot replace that requirement with OpenHarmony source flexibility. A manufacturer that needs kernel, driver, and system-service customization for its own industrial panel cannot replace board-level ownership with access to a consumer app store.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Use two decision tracks instead of one "which is better?" question
&lt;/h2&gt;

&lt;p&gt;Many products include both an application and a device. Keep the decisions separate. The application track begins with target terminals, application form, required kits, release regions, and marketplace review. The device track begins with SoC/BSP support, system tailoring, product compatibility, OTA, and long-term maintenance.&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%2F7qnj8jtclvk2g18vzvkb.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%2F7qnj8jtclvk2g18vzvkb.png" alt="HarmonyOS vs OpenHarmony decision paths" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a Huawei-device application, write the requirement as "target devices + minimum HarmonyOS/API level + required kits + release regions." Huawei documents ArkTS as a HarmonyOS ecosystem application language and ArkUI as its UI framework. An existing Android, iOS, web, or cross-platform application is therefore not merely pointed at a different compiler. Native libraries, accounts, push, maps, payment, notifications, background work, deep links, data migration, and observability each need an owner and an acceptance test.&lt;/p&gt;

&lt;p&gt;For an owned hardware product, OpenHarmony belongs on the shortlist only when the target board and the operating responsibility are credible. Verify the release branch, Public SDK/API level, silicon support, drivers, SystemCapability set, build toolchain, production image, partitions, upgrade design, and security process. The official documentation currently opens on OpenHarmony 6.0 Release / API 20, but a larger version number is not a reason to move a product. The usable baseline is the one that the silicon vendor, BSP, security process, and OTA window can support.&lt;/p&gt;

&lt;p&gt;For a combined app-and-device product, create two acceptance tables. The app table owns devices, kits, distribution, and user journeys. The device table owns system capabilities, updates, security, and application APIs. Connect them with a separately versioned protocol, account, cloud API, or local-network contract. "Same ecosystem" is not an interface definition.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Treat migration as a set of exit-capable risk experiments
&lt;/h2&gt;

&lt;p&gt;When moving an existing mobile application to HarmonyOS, the hidden scope is usually outside the visible pages. A release-capable migration plan needs five ledgers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User-journey ledger&lt;/strong&gt;: login, payment, notifications, deep links, sharing, file selection, and cross-device continuation, each with a HarmonyOS implementation and regression test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SDK replacement ledger&lt;/strong&gt;: Android/iOS-specific SDKs for analytics, support, fraud, maps, advertising, Bluetooth, and media, each marked as directly supported, replaced, removed, or blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native and performance ledger&lt;/strong&gt;: C/C++, media, graphics, cryptography, database, and proprietary libraries, tested for build, threading, memory, file semantics, and architecture before page migration is considered complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity and data ledger&lt;/strong&gt;: account continuity, local database upgrade, key regeneration, API coexistence, and rollback when migration does not complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution and operations ledger&lt;/strong&gt;: application type, signing, privacy, test devices, release countries or regions, review timing, crash signals, and rollback or release-stop procedures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first milestone should be a thin end-to-end journey: a real account logs in on a target device, completes one primary business action, survives a notification or background restoration, and persists data. If a critical SDK has no replacement at that point, the team can reduce scope, postpone, or stop before rewriting every page. This is cheaper than discovering after full UI completion that the product cannot be distributed or operated.&lt;/p&gt;

&lt;p&gt;Moving an existing hardware platform to OpenHarmony needs a different order: board boot, critical drivers, declared system capabilities, application model, OTA, and manufacturing. A home screen on a development board does not prove production support for the target display, touch controller, audio, Bluetooth, Wi-Fi, suspend, watchdog, secure boot, or A/B recovery. Vendor forks and driver dependencies must also enter the SBOM, vulnerability-response, and support-horizon plans.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. A decision matrix that can stop a project early
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product objective&lt;/th&gt;
&lt;th&gt;Starting route&lt;/th&gt;
&lt;th&gt;Evidence required before commitment&lt;/th&gt;
&lt;th&gt;Stop condition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Release an app for supported Huawei phones, tablets, or wearables&lt;/td&gt;
&lt;td&gt;HarmonyOS current native app route&lt;/td&gt;
&lt;td&gt;Target devices, SDK/API level, kits, regions, and real-device acceptance&lt;/td&gt;
&lt;td&gt;Critical SDK has no replacement, target market is unavailable, or the core journey cannot close&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a customizable OS for an owned smart terminal&lt;/td&gt;
&lt;td&gt;OpenHarmony candidate&lt;/td&gt;
&lt;td&gt;Silicon/BSP roadmap, capabilities, compatibility plan, OTA, and security owner&lt;/td&gt;
&lt;td&gt;Production drivers or OTA have no owner; hardware has demo-only support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect an existing MCU/RTOS device to an ecosystem&lt;/td&gt;
&lt;td&gt;Evaluate protocol, gateway, or cloud API first&lt;/td&gt;
&lt;td&gt;Resource budget, certification, cloud contract, and required functions&lt;/td&gt;
&lt;td&gt;Replacing the whole OS adds cost without enabling a required capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deliver both a Huawei app and an owned device&lt;/td&gt;
&lt;td&gt;HarmonyOS app track plus OpenHarmony or existing-OS device track&lt;/td&gt;
&lt;td&gt;Two version contracts, cross-track API, compatibility tests, and separate upgrade owners&lt;/td&gt;
&lt;td&gt;Either track relies on presumed compatibility without interface evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The matrix includes an important answer: some IoT products need neither a HarmonyOS application nor an OpenHarmony system. If a resource-constrained MCU already runs a stable RTOS and the business requirement is only a cloud API, local protocol, gateway, or companion-app integration, preserving the device OS and defining the interface can be safer than replacing the platform.&lt;/p&gt;

&lt;p&gt;Teams planning custom hardware, drivers, applications, and cloud connectivity can use this matrix inside an &lt;a href="https://zediot.com/services/embedded-development/" rel="noopener noreferrer"&gt;embedded development assessment&lt;/a&gt;. The decision should end with named owners, measurable acceptance tests, and an explicit stop condition, not a preferred ecosystem label.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;HarmonyOS, OpenHarmony, and HarmonyOS NEXT are related in technical context, but their governance, product responsibility, distribution, and lifecycle contracts differ. For an application delivered to Huawei devices, use the HarmonyOS SDK, required kits, target devices, and AppGallery rules as the contract. For an operating system on an owned smart terminal, use the OpenHarmony source branch, BSP, SystemCapability set, compatibility path, OTA, and maintenance responsibility as the contract.&lt;/p&gt;

&lt;p&gt;The reliable decision is not "which platform is more open?" It is a verifiable set of conditions: what devices are targeted, which APIs and kits are required, where the product will be distributed, and which team owns drivers, security, upgrades, data migration, and rollback. Only when each condition has evidence can a relationship between platform names become deliverable compatibility.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.openharmony.cn/" rel="noopener noreferrer"&gt;OpenHarmony project and architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitee.com/openharmony/docs/tags" rel="noopener noreferrer"&gt;OpenHarmony official documentation release tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.huawei.com/consumer/cn/app/planning/" rel="noopener noreferrer"&gt;Huawei: Design and develop HarmonyOS NEXT applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.huawei.com/consumer/en/arkts/" rel="noopener noreferrer"&gt;Huawei: ArkTS language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.huawei.com/consumer/en/doc/app/agc-help-release-game-area-0000002399129161" rel="noopener noreferrer"&gt;Huawei: Release regions for HarmonyOS 5 or later&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://zediot.com/blog/harmonyos-vs-openharmony/" rel="noopener noreferrer"&gt;zediot.com&lt;/a&gt;. ZedIoT helps teams build AI + IoT products from device to cloud — from embedded development assessment to board bring-up, driver work, and application porting.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>harmonyos</category>
      <category>openharmony</category>
      <category>iot</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Remote Diagnostics for Low-Power IoT Devices: Stop Treating Them Like Servers</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Thu, 13 Aug 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/zediot/remote-diagnostics-for-low-power-iot-devices-stop-treating-them-like-servers-37j8</link>
      <guid>https://dev.to/zediot/remote-diagnostics-for-low-power-iot-devices-stop-treating-them-like-servers-37j8</guid>
      <description>&lt;p&gt;The hardest failures in low-power IoT are often not total silence. They show up as partial signals: battery voltage drifting down, RSSI getting worse, reports arriving later than expected, occasional reconnects, or one firmware version producing more resets than the rest of the fleet.&lt;/p&gt;

&lt;p&gt;If the platform copies server monitoring and asks every device to stream detailed logs, minute-level metrics, and full event traces, the diagnostic layer becomes the thing that &lt;strong&gt;drains batteries and overloads narrow links.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core principle: &lt;strong&gt;remote diagnostics for low-power devices is not about sending every log line to the cloud.&lt;/strong&gt; It's about deciding what problem is worth waking the device for, then combining minimal metrics, tiered logs, field context, and bounded diagnostic windows into enough evidence for action.&lt;/p&gt;

&lt;p&gt;When battery, cellular cost, weak coverage, and sleep intervals matter, diagnostics must be designed as part of the power and operations model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Server Monitoring Patterns Fail
&lt;/h2&gt;

&lt;p&gt;Server monitoring assumes three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The node is &lt;strong&gt;usually online&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Power is stable&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bandwidth is cheap&lt;/strong&gt; enough for frequent telemetry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Low-power IoT devices often violate &lt;strong&gt;all three&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A battery-powered sensor may wake every 15 minutes. An NB-IoT or LTE-M device may close its connection aggressively to save energy. A cold-chain, utility, or agriculture deployment may sit behind weak coverage. If the platform still demands realtime logs, high-frequency metrics, and always-on diagnostic channels, the result is not better troubleshooting - it's &lt;strong&gt;more wakeups, more retries, more airtime, and shorter device life.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Diagnostic data from constrained devices should be collected by value, not by curiosity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Minimum Useful Diagnostic Signal Set
&lt;/h2&gt;

&lt;p&gt;Low-power devices should not stream full logs continuously, but they do need to report a compact signal set. A practical baseline has five groups:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal Group&lt;/th&gt;
&lt;th&gt;Key Fields&lt;/th&gt;
&lt;th&gt;What It Explains&lt;/th&gt;
&lt;th&gt;Suggested Cadence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Power state&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;battery_voltage&lt;/code&gt;, &lt;code&gt;battery_percent&lt;/code&gt;, &lt;code&gt;power_mode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Battery decline or power instability&lt;/td&gt;
&lt;td&gt;With heartbeat or business report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Radio quality&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RSSI&lt;/code&gt;, &lt;code&gt;RSRP&lt;/code&gt;, &lt;code&gt;SNR&lt;/code&gt;, &lt;code&gt;retry_count&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Weak coverage or retry pressure&lt;/td&gt;
&lt;td&gt;On connect or failure events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;firmware_version&lt;/code&gt;, &lt;code&gt;config_version&lt;/code&gt;, &lt;code&gt;boot_id&lt;/code&gt;, &lt;code&gt;reset_reason&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Version/configuration/reboot correlation&lt;/td&gt;
&lt;td&gt;On startup and after abnormal events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data freshness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;last_sample_at&lt;/code&gt;, &lt;code&gt;last_upload_at&lt;/code&gt;, &lt;code&gt;queue_depth&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Sampling failure vs upload failure&lt;/td&gt;
&lt;td&gt;Low-frequency summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error summary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;error_code&lt;/code&gt;, &lt;code&gt;error_counter&lt;/code&gt;, &lt;code&gt;last_error_at&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Whether failures cluster by type&lt;/td&gt;
&lt;td&gt;Event-triggered or inside a window&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These fields make the fleet &lt;strong&gt;searchable by device type, batch, location, and version:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One region shows weaker RSSI + more retries? ? Start with coverage.&lt;/li&gt;
&lt;li&gt;One firmware version shows watchdog resets? ? Start with firmware tasks, memory, or timing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Define a Diagnostic Event Contract
&lt;/h2&gt;

&lt;p&gt;The five signal groups are just a data inventory. A durable implementation needs an &lt;strong&gt;event contract&lt;/strong&gt; that survives device restarts, delayed delivery, firmware coexistence, and platform upgrades.&lt;/p&gt;

&lt;p&gt;Here's a useful starting point:&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;schema_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;diag.v1&lt;/span&gt;
&lt;span class="na"&gt;device_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meter-0421&lt;/span&gt;
&lt;span class="na"&gt;boot_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;187&lt;/span&gt;
&lt;span class="na"&gt;seq&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;932&lt;/span&gt;
&lt;span class="na"&gt;observed_at&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-07-31T08:15:00Z&lt;/span&gt;
&lt;span class="na"&gt;reason_code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uplink_timeout&lt;/span&gt;
&lt;span class="na"&gt;firmware_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.8.1&lt;/span&gt;
&lt;span class="na"&gt;config_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cfg-44&lt;/span&gt;
&lt;span class="na"&gt;diagnostic_window_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dw-7f3a&lt;/span&gt;
&lt;span class="na"&gt;payload_bytes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;286&lt;/span&gt;
&lt;span class="na"&gt;correlation_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;job-20260731-18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key rules for this contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;device_id + boot_id + seq&lt;/code&gt; separates events before and after a restart&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;observed_at&lt;/code&gt; is device observation time - never overwritten by server receipt time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reason_code&lt;/code&gt; comes from a versioned enumeration, not free text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payload_bytes&lt;/code&gt; connects diagnostic activity to airtime and data cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Schema evolution policy&lt;/strong&gt; is also required: adding optional fields is OK. Renaming a field, changing its unit, or reusing an error code breaks historical comparison. Unknown schema versions should enter a quarantine stream with original payload preserved - silent drops make firmware-vs-decoder regressions indistinguishable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Turn the Diagnostic Budget Into an Acceptance Criterion
&lt;/h2&gt;

&lt;p&gt;"Send as little as possible" is not testable. Define &lt;strong&gt;four budgets per device class:&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;Budget&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Diagnostic uplink bytes/day&lt;/td&gt;
&lt;td&gt;Total diagnostic payload allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra wakeups from diagnostics&lt;/td&gt;
&lt;td&gt;How many additional wake cycles diagnostics can trigger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local queue capacity&lt;/td&gt;
&lt;td&gt;Storage on device for pending diagnostic data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max diagnostic window duration&lt;/td&gt;
&lt;td&gt;How long a detailed collection window lasts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example starting point: &lt;em&gt;"No more than 8 KB of diagnostic uplink per day and no more than two consecutive wake cycles for an exception window."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a design hypothesis - calibrate with real current traces on target hardware, weak-link retries, and compression behavior. When the budget is exhausted, the device should fall back to critical counters plus a window-termination reason. &lt;strong&gt;Continuing to retry until the battery dies is not a diagnostic strategy.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tiered Logs, Not Continuous Logs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Normal mode ? Summaries only
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- last reset reason
- counters for the most recent error categories
- last upload failure reason
- current queue depth
- latest diagnostic window ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small, aggregatable, searchable. It doesn't try to reproduce every log line - it first tells the platform &lt;strong&gt;where the problem likely sits.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exceptions ? Short diagnostic windows
&lt;/h3&gt;

&lt;p&gt;Detailed collection starts only when a condition is met:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repeated upload failures&lt;/li&gt;
&lt;li&gt;Battery voltage crossing a threshold&lt;/li&gt;
&lt;li&gt;RSSI/RSRP staying below a threshold&lt;/li&gt;
&lt;li&gt;Watchdog resets exceeding a limit&lt;/li&gt;
&lt;li&gt;Platform command that opens diagnostics until expiry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Every window needs boundaries:&lt;/strong&gt; duration, max log count, module scope, and a clear return to low-power mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verbose logs need a decision purpose
&lt;/h3&gt;

&lt;p&gt;The dangerous log is not no log. It's a large log that &lt;strong&gt;cannot change the next action.&lt;/strong&gt; Loop traces, every sampling attempt, every retry stack - consuming power and bandwidth without answering: "Replace battery? Move antenna? Rollback config? Dispatch technician?"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a field cannot support a decision, it should not be part of the normal diagnostic payload.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Priority Queues and Backpressure
&lt;/h2&gt;

&lt;p&gt;A common failure: putting business samples, heartbeats, command receipts, summaries, and verbose logs into &lt;strong&gt;one FIFO queue.&lt;/strong&gt; Opening a diagnostic window then places log volume &lt;em&gt;in front of&lt;/em&gt; data the product is supposed to deliver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate your queues:&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;Priority&lt;/th&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security receipts, command receipts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Business&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sensor data, heartbeats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Diagnostic summaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detailed verbose logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;High-priority traffic needs reserved capacity. When the detailed-log queue reaches its limit, aggregate repeated records and discard oldest detail while incrementing a &lt;code&gt;dropped_count&lt;/code&gt; summary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backpressure should flow both ways:&lt;/strong&gt; if ingestion latency rises or a device exceeds its quota, the platform returns a smaller window-byte limit. The device disables verbose collection first, then reduces summary cadence - while preserving command receipts and critical business data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotency for weak links
&lt;/h3&gt;

&lt;p&gt;On weak links, delivery may succeed while the acknowledgement is lost. End-to-end exactly-once is a poor assumption.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;at-least-once transport&lt;/strong&gt; + make ingestion idempotent with &lt;code&gt;device_id + boot_id + seq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A repeated event may increment delivery-attempt counter, but &lt;strong&gt;must not&lt;/strong&gt; fire another alert or create a second work order&lt;/li&gt;
&lt;li&gt;Retry policy: exponential backoff + jitter + max attempts + event expiry&lt;/li&gt;
&lt;li&gt;Expired verbose logs can be discarded, but retain summary of what was dropped&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Field Context Must Be Structured
&lt;/h2&gt;

&lt;p&gt;Many low-power failures are tied to physical deployment - context the device itself cannot report:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;site_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Operations console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;install_location&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installation record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enclosure_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Work order system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;power_source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;battery_batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supply chain record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;antenna_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installation record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;last_service_action&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Work order history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Without this, the platform may see 20 unstable devices in one area without noticing that &lt;strong&gt;all of them are mounted behind the same metal cabinet or use the same battery batch.&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 LR

A("Device Summary") --&amp;gt; D("Diagnostic Context")
B("Link Quality") --&amp;gt; D
C("Field Installation Data") --&amp;gt; D
E("Firmware / Config Version") --&amp;gt; D
D --&amp;gt; F("Remote Judgment")
F --&amp;gt; G("Keep Watching")
F --&amp;gt; H("Open Diagnostic Window")
F --&amp;gt; I("Rollback Config / OTA")
F --&amp;gt; J("Dispatch Field Service")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Downlink Diagnostics as Bounded Jobs
&lt;/h2&gt;

&lt;p&gt;Low-power devices should not be treated as always-available RPC targets. Diagnostic commands need four properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Expiry time&lt;/strong&gt; - command disappears if the device misses its wake window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power budget level&lt;/strong&gt; - lightweight query, short log window, restart, or rollback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency ID&lt;/strong&gt; - weak-link retries don't execute the same action twice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution receipt&lt;/strong&gt; - received, executed, failed reason, next reporting time&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Diagnostic job state machine
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;queued&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Waiting for device wake window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delivered&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device received the command envelope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accepted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device validated and will execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;running&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execution in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;succeeded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Completed, result attached&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;failed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execution failed with reason code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expired&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device did not wake before deadline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cancelled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform cancelled before execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The platform should permit only valid transitions. An expired job must not become &lt;code&gt;running&lt;/code&gt; because a delayed receipt arrived.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A duplicated operator click and a weak-link redelivery must result in &lt;strong&gt;one physical action.&lt;/strong&gt; Without idempotency guards, a device may reboot twice, export the same log bundle twice, or repeat a configuration rollback.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Fault Injection: Validate Before Rollout
&lt;/h2&gt;

&lt;p&gt;A happy-path test with one log over a stable network doesn't validate low-power diagnostics. &lt;strong&gt;Before rollout, inject five failures:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Repeatedly lose uplink acknowledgements&lt;/li&gt;
&lt;li&gt;Restart the device during a diagnostic window&lt;/li&gt;
&lt;li&gt;Fill the local queue&lt;/li&gt;
&lt;li&gt;Make ingestion reject traffic temporarily&lt;/li&gt;
&lt;li&gt;Let old firmware send an unknown schema&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Acceptance metrics&lt;/strong&gt; (beyond "diagnostic success rate"):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extra wakeups triggered&lt;/li&gt;
&lt;li&gt;Uplink and downlink bytes consumed&lt;/li&gt;
&lt;li&gt;Retry bytes&lt;/li&gt;
&lt;li&gt;Time from &lt;code&gt;queued&lt;/code&gt; to terminal job state&lt;/li&gt;
&lt;li&gt;Queue high-water mark&lt;/li&gt;
&lt;li&gt;Share of cases still requiring field visits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rollout strategy&lt;/strong&gt;: Start with a small cohort, observe budget consumption and quarantine. Widen only if command completion and business-data delivery remain healthy. Define the rollback trigger in advance (e.g., sustained increase in wakeups or command expiry).&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Operations Console Should Show
&lt;/h2&gt;

&lt;p&gt;The final consumer of diagnostics is usually an operations or support team. A practical console should show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latest valid activity&lt;/li&gt;
&lt;li&gt;Latest heartbeat summary&lt;/li&gt;
&lt;li&gt;Battery and signal trend&lt;/li&gt;
&lt;li&gt;Firmware and configuration version&lt;/li&gt;
&lt;li&gt;Recent error summary&lt;/li&gt;
&lt;li&gt;Pending diagnostic jobs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recommended next action with a reason&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Keep watching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reporting cadence normal, battery and signal stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open diagnostic window&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Repeated upload failures but device still responds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollback configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Errors cluster around one config version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dispatch field service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low battery + weak signal + repeated job timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is more useful than a red/yellow/green badge - it connects diagnostic evidence to an action.&lt;/p&gt;




&lt;h2&gt;
  
  
  When This Is Too Much
&lt;/h2&gt;

&lt;p&gt;Not every product needs a full diagnostic system. Keep it simpler when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fleet is small and field service is cheap&lt;/li&gt;
&lt;li&gt;Devices are mains-powered and connectivity is stable&lt;/li&gt;
&lt;li&gt;The business only needs recent reporting, not remote repair&lt;/li&gt;
&lt;li&gt;The device is cheap enough that replacement is the intended support model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But once the fleet grows or field visits become expensive, richer diagnostics are usually worth the design cost. Medical cold chain, agriculture, industrial sensing, outdoor metering, and distributed gateways all make mistakes expensive - a wrong diagnosis can mean a wasted truck roll, spoiled inventory, downtime, or missing data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation Checklist
&lt;/h2&gt;

&lt;p&gt;If designing diagnostics from scratch, follow this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;? Define wake cadence, reporting cadence, and diagnostic budget per device class&lt;/li&gt;
&lt;li&gt;? Collect only power, signal, version, queue, and error summaries in normal mode&lt;/li&gt;
&lt;li&gt;? Use short diagnostic windows for exception cases instead of always-on debug&lt;/li&gt;
&lt;li&gt;? Bind installation context and work-order history to the device record&lt;/li&gt;
&lt;li&gt;? Give downlink diagnostic commands expiry, power level, and idempotency&lt;/li&gt;
&lt;li&gt;? Show reasons and next actions in the operations console&lt;/li&gt;
&lt;li&gt;? Write each diagnostic action back into device history for later review&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;Remote diagnostics for low-power IoT is &lt;strong&gt;not about collecting more data.&lt;/strong&gt; It's about preserving enough evidence for a decision while minimizing wakeups, bytes, and unnecessary field work.&lt;/p&gt;

&lt;p&gt;When logs, metrics, field context, and diagnostic commands are part of one controlled model, operations can move from &lt;em&gt;guessing why a device disappeared&lt;/em&gt; to &lt;strong&gt;choosing the next action from evidence.&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>iot</category>
      <category>embedded</category>
      <category>devops</category>
      <category>edgecomputing</category>
    </item>
    <item>
      <title>OpenThread and ESP32-C6 in Practice: When Thread Mesh Beats More Wi-Fi</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Wed, 12 Aug 2026 03:38:03 +0000</pubDate>
      <link>https://dev.to/zediot/openthread-and-esp32-c6-in-practice-when-thread-mesh-beats-more-wi-fi-4b58</link>
      <guid>https://dev.to/zediot/openthread-and-esp32-c6-in-practice-when-thread-mesh-beats-more-wi-fi-4b58</guid>
      <description>&lt;p&gt;If a product only has a few connected devices, modest data volume, and stable power, Wi-Fi is usually still the simpler answer. Thread starts to make engineering sense when the product has many low-power nodes, needs mesh coverage across rooms or cabinets, and must keep working even when one router node drops out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenThread is not another smart home app protocol.&lt;/strong&gt; It is an open-source implementation of Thread. Thread solves the low-power IPv6 mesh networking layer; Matter solves the application-layer interoperability model. In practical terms, Thread decides how small devices join and route through the network, while Matter decides how devices are discovered, commissioned, and described to ecosystems such as Home Assistant, Apple Home, and Google Home.&lt;/p&gt;

&lt;p&gt;The important decision is not whether Thread is newer than Wi-Fi. The real question is whether the device fleet has enough low-power, multi-node, self-healing, and interoperability pressure to justify Thread, a Border Router, IPv6 debugging, and a more disciplined validation plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  What OpenThread Actually Solves
&lt;/h2&gt;

&lt;p&gt;Most small IoT projects begin with Wi-Fi, BLE, or Zigbee.&lt;/p&gt;

&lt;p&gt;Wi-Fi is mature, high-bandwidth, and easy to connect to existing infrastructure ??but it's not a great default for every battery-powered node. BLE is excellent for phone-based provisioning, nearby interaction, and simple peripherals, but stable multi-hop mesh productization is a different problem. Zigbee is proven for low-power mesh, yet many commercial deployments still need to deal with gateway-specific behavior, private device models, and ecosystem boundaries.&lt;/p&gt;

&lt;p&gt;Thread has a different shape. It uses IEEE 802.15.4 at the radio layer, but the network model is &lt;strong&gt;IPv6&lt;/strong&gt;. With 6LoWPAN compression, mesh routing, and device roles such as Router, End Device, and Sleepy End Device, Thread makes small low-power devices behave more like standard IP network participants.&lt;/p&gt;

&lt;p&gt;That is the core value of OpenThread: it gives engineering teams a practical Thread stack for building, testing, and productizing that network layer. It does not remove the need for application logic, device management, security, or field diagnostics. It simply gives the low-power mesh layer a more standard foundation than a fully private gateway protocol.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why ESP32-C6 and ESP32-H2 Are Natural Starting Points
&lt;/h2&gt;

&lt;p&gt;Not every ESP32 chip can become a Thread node. Standard Thread requires an IEEE 802.15.4 radio.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chip&lt;/th&gt;
&lt;th&gt;Wi-Fi&lt;/th&gt;
&lt;th&gt;BLE&lt;/th&gt;
&lt;th&gt;802.15.4 (Thread)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Classic ESP32&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESP32-S3&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ESP32-C6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;??(Wi-Fi 6)&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ESP32-H2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESP32-C5&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In ESP-IDF, OpenThread projects commonly fall into three patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SoC / Standalone Node&lt;/strong&gt;: The application and Thread stack run on the same 802.15.4-capable chip (ESP32-C6 or ESP32-H2). Good fit for sensors, switches, and actuators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RCP (Radio Co-Processor)&lt;/strong&gt;: Handles 802.15.4 radio work while the host runs more of the network stack. Common for Thread Border Router designs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NCP (Network Co-Processor)&lt;/strong&gt;: Owns more of the network stack and exposes a control interface to the host. Simplifies host-side integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a first proof of concept, two ESP32-C6 or ESP32-H2 development boards running an &lt;code&gt;ot_cli&lt;/code&gt; style example can prove network formation and basic connectivity.&lt;/p&gt;

&lt;p&gt;But for a product architecture, that's not enough. As soon as Thread devices need to reach a LAN, cloud service, Matter controller, or IoT platform, the design needs a &lt;strong&gt;Thread Border Router&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's the minimum useful validation setup:&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
 subgraph ThreadMesh["Thread Mesh Network"]
 SED("Sleepy End Device&amp;lt;br/&amp;gt;battery sensor")
 Router("Router&amp;lt;br/&amp;gt;mesh relay")
 Actuator("End Device&amp;lt;br/&amp;gt;switch / actuator")
 end

 BR("Thread Border Router&amp;lt;br/&amp;gt;RCP + Wi-Fi / Ethernet host")
 LAN("LAN / IP Network")
 Matter("Matter Controller&amp;lt;br/&amp;gt;Home Assistant / Apple Home / Google Home")
 Cloud("IoT Platform&amp;lt;br/&amp;gt;monitoring / alerts / operations")

 SED --&amp;gt; Router
 Actuator --&amp;gt; Router
 Router --&amp;gt; BR
 BR --&amp;gt; LAN
 LAN --&amp;gt; Matter
 LAN --&amp;gt; Cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prove the radio, mesh, RCP or Border Router boundary, and real application traffic before treating the design as a product path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thread, OpenThread, and Matter Are Different Layers
&lt;/h2&gt;

&lt;p&gt;Matter and Thread are often discussed together, but they solve different layers of the stack.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Matter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application-layer interoperability standard&lt;/td&gt;
&lt;td&gt;Device types, commissioning, ecosystem compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thread&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low-power IPv6 mesh networking over 802.15.4&lt;/td&gt;
&lt;td&gt;How devices join, route, and heal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenThread&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source Thread implementation&lt;/td&gt;
&lt;td&gt;The code that runs Thread on your hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When a team says "we're building a Matter over Thread device," the product actually needs at least three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A chip and radio design capable of Thread (ESP32-C6 or ESP32-H2)&lt;/li&gt;
&lt;li&gt;A Thread Border Router connecting the mesh to Wi-Fi/Ethernet&lt;/li&gt;
&lt;li&gt;An application layer implementing the relevant Matter device model ??&lt;strong&gt;not merely two Thread nodes that can ping each other&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a prototype only proves OpenThread connectivity, it proves the network layer. It does not prove Matter certification, commissioning behavior, ecosystem compatibility, or long-term product recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  When OpenThread Is the Right Choice
&lt;/h2&gt;

&lt;p&gt;OpenThread is a strong fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Many small low-power nodes&lt;/strong&gt;: Door sensors, temperature probes, leak sensors, occupancy sensors, local actuators. Battery-powered and physically distributed = Thread is usually more honest than Wi-Fi for every node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matter over Thread required&lt;/strong&gt;: For smart home / building devices targeting Home Assistant, Apple Home, or Google Home, Thread isn't decorative ??it's one of the required transports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-healing mesh matters&lt;/strong&gt;: If one router node disappears, the network routes through another. Critical in rooms, cabinets, and distributed layouts where a single gateway path is fragile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce private protocol burden&lt;/strong&gt;: IPv6-based diagnostics, service discovery, and routing are more standard than proprietary gateway protocols.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Thread does not replace the management layer. Once device data must become monitoring, alerts, permissions, or fleet operations, you still need an IoT platform.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When OpenThread Is the Wrong Tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-bandwidth devices&lt;/strong&gt;: Cameras, audio, large firmware transfers, frequent image uploads ??use Wi-Fi, Ethernet, or cellular.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Very small, simple projects&lt;/strong&gt;: One sensor + one gateway + stable power ??Wi-Fi or BLE may be cheaper and simpler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams not ready for ESP-IDF + IPv6 + Border Router topology&lt;/strong&gt;: The hard part isn't lighting up a demo. It's recovery, compatibility, upgrades, and field support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classic ESP32 / ESP32-S3 designs&lt;/strong&gt;: Without an 802.15.4 radio, you need an external RCP or a move to ESP32-C6/H2/C5.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Practical Validation Path
&lt;/h2&gt;

&lt;p&gt;A good OpenThread project should validate in stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1 ??Network formation&lt;/strong&gt;: Two 802.15.4-capable dev boards, create and join a Thread network. Proves chip, SDK, radio config, and basic network path work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2 ??Border Router boundary&lt;/strong&gt;: ESP-IDF &lt;code&gt;ot_br&lt;/code&gt; / &lt;code&gt;ot_rcp&lt;/code&gt; setup, or Linux host with an 802.15.4 RCP. Proves the boundary between Thread mesh and IP network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 3 ??Real application traffic&lt;/strong&gt;: Don't stop at ping. Send sensor data, command messages, heartbeat data over UDP, CoAP, or your project protocol. Measure latency, loss, reconnection behavior, and sleepy device recovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 4 ??Matter or platform integration&lt;/strong&gt;: Only after the network boundary is proven. Device models, credentials, permissions, state sync, and failure recovery enter the test scope.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Decision: Wi-Fi vs BLE vs Zigbee vs Thread
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Watch out for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wi-Fi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bandwidth, stable power, direct IP&lt;/td&gt;
&lt;td&gt;Battery life, congestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BLE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Phone provisioning, wearables, short-range&lt;/td&gt;
&lt;td&gt;Multi-hop mesh is hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zigbee&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mature low-power mesh, existing gateway&lt;/td&gt;
&lt;td&gt;Gateway lock-in, private device models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thread&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Many low-power nodes, Matter ecosystem, self-healing mesh&lt;/td&gt;
&lt;td&gt;Needs Border Router, IPv6 skills, disciplined validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The decision should be driven by system constraints, not by protocol novelty.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can a classic ESP32 run OpenThread directly?&lt;/strong&gt;&lt;br&gt;
No. Classic ESP32 / ESP32-S3 lacks the IEEE 802.15.4 radio. Use ESP32-C6, ESP32-H2, ESP32-C5, or an external 802.15.4 RCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is OpenThread the same as Matter?&lt;/strong&gt;&lt;br&gt;
No. OpenThread is a Thread network stack implementation. Matter is an application-layer interoperability standard. Matter can run over Thread, Wi-Fi, or Ethernet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESP32-C6 or ESP32-H2?&lt;/strong&gt;&lt;br&gt;
C6 when you benefit from Wi-Fi 6 + BLE + 802.15.4 in one SoC. H2 when the node is mainly a low-power 802.15.4 / BLE device. Final selection depends on power budget, peripherals, cost, supply, and SDK maturity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does every OpenThread project need a Border Router?&lt;/strong&gt;&lt;br&gt;
Only when Thread devices need to reach a LAN, cloud, Matter controller, or other IP network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;OpenThread with ESP32-C6 or ESP32-H2 gives low-power, multi-node, Matter-ready devices a more standard network foundation. It's not a reason to abandon Wi-Fi everywhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Few devices, high bandwidth, simple direct connection ??Wi-Fi or BLE is cleaner.&lt;/li&gt;
&lt;li&gt;Many battery-powered nodes, self-healing mesh, Matter/IPv6 IoT ecosystem ??Thread deserves early validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The path: &lt;strong&gt;prove the Thread network ??prove the Border Router boundary ??then add Matter, platform integration, and field operations.&lt;/strong&gt; A demo that can ping is not the same as a product that can be shipped and maintained.&lt;/p&gt;




</description>
      <category>esp32</category>
      <category>iot</category>
      <category>networking</category>
      <category>smarthome</category>
    </item>
    <item>
      <title>Dify vs Custom Development: When Visual AI Workflows Actually Work (and When They Don't)</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/zediot/dify-vs-custom-development-when-visual-ai-workflows-actually-work-and-when-they-dont-62h</link>
      <guid>https://dev.to/zediot/dify-vs-custom-development-when-visual-ai-workflows-actually-work-and-when-they-dont-62h</guid>
      <description>&lt;p&gt;Dify can accelerate Workflow, RAG, Agent, and API-based AI applications, but it should not replace a complex business backend. This decision guide draws a clear boundary between what Dify should own and what belongs in custom code — so your prototype doesn't become a production liability.&lt;/p&gt;




&lt;p&gt;If the main job is to connect user input, knowledge retrieval, model calls, branches, tools, and output into an observable AI process, Dify is often faster than starting from an empty backend. It puts workflows, knowledge bases, model configuration, plugins, and application APIs in one workspace, which gives product, domain, and engineering teams a shared operating surface.&lt;/p&gt;

&lt;p&gt;Dify is not a replacement for every business backend. When a system requires strong transactional consistency, complex domain state, long-running scheduling, strict latency targets, fine-grained multi-tenant authorization, or core rules that must be protected by code review — keep custom services in control. Use Dify &lt;strong&gt;above&lt;/strong&gt; them for AI orchestration, retrieval, prompts, and bounded tool calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Decision Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project condition&lt;/th&gt;
&lt;th&gt;Default choice&lt;/th&gt;
&lt;th&gt;Cost to accept&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FAQ, document Q&amp;amp;A, internal knowledge assistant&lt;/td&gt;
&lt;td&gt;Dify RAG / Chatflow first&lt;/td&gt;
&lt;td&gt;Govern document access, chunking, retrieval, and citations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content generation, ticket summaries, lead classification&lt;/td&gt;
&lt;td&gt;Dify Workflow first&lt;/td&gt;
&lt;td&gt;Add versioning, tests, and failure handling as flows grow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent with a small, low-risk tool set&lt;/td&gt;
&lt;td&gt;Dify Agent or Workflow + Tool&lt;/td&gt;
&lt;td&gt;Enforce allowlists, budgets, timeouts, and confirmation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orders, billing, inventory, device state, permissions&lt;/td&gt;
&lt;td&gt;Custom backend owns the ledger&lt;/td&gt;
&lt;td&gt;Slower delivery, but state and transaction boundaries are testable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High concurrency, low latency, queues, or long jobs&lt;/td&gt;
&lt;td&gt;Custom execution plane + Dify orchestration&lt;/td&gt;
&lt;td&gt;Define idempotency, callbacks, and observability across layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast discovery with a likely productization path&lt;/td&gt;
&lt;td&gt;Validate in Dify, then extract by boundary&lt;/td&gt;
&lt;td&gt;Plan data and API exit paths early&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The decision is not "low code versus code." It is &lt;strong&gt;who owns the final state.&lt;/strong&gt; If one failed run can affect money, inventory, devices, access, or compliance evidence, the final state should not exist only in a visual workflow run record.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Which Part of Delivery Does Dify Accelerate?
&lt;/h2&gt;

&lt;p&gt;Dify is strongest in the AI application orchestration layer. Its quick start demonstrates how input, extraction, branching, document processing, model nodes, templates, and output form a testable workflow. The knowledge layer connects document ingestion and retrieval to model context. The plugin system extends models, tools, data sources, triggers, and endpoints.&lt;/p&gt;

&lt;p&gt;Together, these capabilities shorten the path from a business hypothesis to a runnable AI process. Teams can validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether user requests can be classified reliably&lt;/li&gt;
&lt;li&gt;whether retrieval provides enough evidence&lt;/li&gt;
&lt;li&gt;which steps should be deterministic and which require a model&lt;/li&gt;
&lt;li&gt;where a tool call needs human approval&lt;/li&gt;
&lt;li&gt;how model quality, latency, and cost compare&lt;/li&gt;
&lt;li&gt;whether domain owners can understand and maintain the process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When those questions contain most of the project uncertainty, a visual workflow and accessible run history are valuable. The team can prove the AI path first and decide later which capabilities deserve long-lived custom code.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What Should Workflow, RAG, and Agent Each Own?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Workflow: The Deterministic Skeleton
&lt;/h3&gt;

&lt;p&gt;Workflow is a good fit for validation, variable transformation, branching, model calls, tool calls, and output formatting. If a process can be described with explicit nodes and exit conditions, prefer a Workflow over asking an Agent to discover every step.&lt;/p&gt;

&lt;p&gt;A support-ticket flow, for example, can identify the product and fault category, retrieve relevant documents, draft a recommendation, check risk terms, return low-risk answers, and route high-risk cases to a person. The sequence is visible and regression tests are easier to reproduce.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 RAG: Controlled Knowledge Context
&lt;/h3&gt;

&lt;p&gt;RAG is useful when answers depend on enterprise documents, manuals, policies, or project records. It addresses what evidence the model should see, but it does not automatically solve document authorization, version conflicts, retrieval quality, or citation integrity.&lt;/p&gt;

&lt;p&gt;A production RAG system should record the source, document version, chunking policy, retrieved passages, and final citations. &lt;strong&gt;Tenant or sensitive-document filtering must happen through a trusted identity and authorization layer before retrieval.&lt;/strong&gt; A prompt that tells the model not to leak data is not an access-control system.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Agent: Bounded Autonomy
&lt;/h3&gt;

&lt;p&gt;An Agent is useful when the goal is stable but the exact sequence cannot be fully predefined — such as querying several approved systems and comparing results. It should not have unlimited tools or execute high-risk actions without confirmation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use a tool allowlist, call budget, timeout, step limit, structured output, and human approval for payments, deletion, permission changes, or device control.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. What Must Stay in Custom Services?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Core business state.&lt;/strong&gt; Orders, invoices, inventory, device shadows, account permissions, and approvals require durable models, transactions, concurrency control, and audit. Dify may read them or request a change, but it should not be the only ledger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex domain logic.&lt;/strong&gt; Rules with many interacting constraints, exact calculations, regulatory traceability, or cross-request state machines are usually safer as reviewed code with focused tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance and scheduling.&lt;/strong&gt; High request volume, long-running jobs, retry queues, priorities, batch processing, GPU scheduling, and strict latency targets need dedicated infrastructure. An AI workflow may launch work and summarize results, but it should not carry every scheduling responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity and authorization.&lt;/strong&gt; Enterprise SSO, tenant boundaries, object-level permissions, secrets, and audit retention belong in mature IAM and backend services. Workflow nodes should receive only the minimum authorized context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stable product contracts.&lt;/strong&gt; When mobile apps, customer systems, or partners depend on an API, versioning, idempotency, rate limits, error semantics, and backward compatibility need an explicit service contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. A Practical Hybrid Architecture
&lt;/h2&gt;

&lt;p&gt;This split lets Dify iterate quickly on prompts, retrieval, models, and AI flow without owning identity or final business state. Tool calls enter custom APIs, where domain services enforce idempotency, transactions, queues, audit, and rollback.&lt;/p&gt;

&lt;p&gt;Every cross-layer request should carry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;request_id&lt;/code&gt; or idempotency key&lt;/li&gt;
&lt;li&gt;user, tenant, and authorized scope&lt;/li&gt;
&lt;li&gt;Workflow or App version&lt;/li&gt;
&lt;li&gt;input and output schema versions&lt;/li&gt;
&lt;li&gt;timeout and maximum retry policy&lt;/li&gt;
&lt;li&gt;human-approval result when required&lt;/li&gt;
&lt;li&gt;final business status and audit reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Dify replays a node, the domain service must recognize the duplicate rather than charging twice, opening a second ticket, or sending a second device command.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. When Should a Dify Node Become a Service?
&lt;/h2&gt;

&lt;p&gt;Extract a node into a custom service when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Code node accumulates domain logic that is hard to unit-test&lt;/li&gt;
&lt;li&gt;Several workflows copy the same rule and drift during updates&lt;/li&gt;
&lt;li&gt;The node needs long-lived state, a queue, or a distributed lock&lt;/li&gt;
&lt;li&gt;The business requires a defined P95/P99 latency or isolated capacity&lt;/li&gt;
&lt;li&gt;The capability needs independent deployment, canary release, and rollback&lt;/li&gt;
&lt;li&gt;Audit requires a code version, approval, and change record&lt;/li&gt;
&lt;li&gt;Third parties depend on a stable API contract&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Extraction does not mean removing Dify.&lt;/strong&gt; Keep it as the experience and AI orchestration layer, and replace the complex node with a controlled Tool or HTTP API. The business process remains visible while the critical capability enters a normal software lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. 10-Point Production Checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Identify whether Dify stores any core business state&lt;/li&gt;
&lt;li&gt;Version Workflow, knowledge, and model configuration&lt;/li&gt;
&lt;li&gt;Maintain fixed inputs, expected outputs, and a regression dataset&lt;/li&gt;
&lt;li&gt;Record source, authorization, version, and citation for retrieval&lt;/li&gt;
&lt;li&gt;Add idempotency and server-side authorization to every write&lt;/li&gt;
&lt;li&gt;Limit Agent tools, budget, timeout, and maximum steps&lt;/li&gt;
&lt;li&gt;Add human confirmation and secondary validation to sensitive actions&lt;/li&gt;
&lt;li&gt;Correlate Dify runs with backend traces, business audit, and alerts&lt;/li&gt;
&lt;li&gt;Test degradation for model, vector store, plugin, and external API failures&lt;/li&gt;
&lt;li&gt;Document the data and interface exit path for extracting a node or leaving Dify&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Without items 1–5, the project is still close to a demo. Without 6–10, a pilot may run, but production operations and exit cost remain unresolved.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can Dify serve as an enterprise AI backend?
&lt;/h3&gt;

&lt;p&gt;It can own AI orchestration, retrieval, model calls, and application-facing AI APIs. It should &lt;strong&gt;not&lt;/strong&gt; be the sole ledger for orders, billing, inventory, permissions, or device state. Use authenticated, idempotent, auditable domain services for those responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  How should a team choose between Dify and LangGraph?
&lt;/h3&gt;

&lt;p&gt;Dify is usually faster when the team values visual orchestration, domain collaboration, integrated knowledge, and quick publishing. LangGraph or a custom framework is stronger when the system needs code-first state machines, deep testing, a customized runtime, and fine-grained execution control. They can also be layered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does self-hosting Dify solve data security?
&lt;/h3&gt;

&lt;p&gt;No. Self-hosting changes the deployment location, but identity, network controls, secrets, plugin supply chain, document authorization, log redaction, backups, and vulnerability management still need explicit design.&lt;/p&gt;

&lt;h3&gt;
  
  
  When is Dify unnecessary?
&lt;/h3&gt;

&lt;p&gt;If the application makes one simple model call and an existing backend already handles it reliably, another platform may only add operations cost. If nearly every node requires complex custom code or bypasses platform constraints, the core system probably belongs in custom services.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Dify is strongest as an orchestration layer for AI applications that change quickly, require cross-functional collaboration, and center on models and knowledge. It can compress the validation cycle for Workflow, RAG, Agent, and API-based applications while making prompts, knowledge, and runs visible.&lt;/p&gt;

&lt;p&gt;A sound architecture does not treat Dify and custom development as mutually exclusive. &lt;strong&gt;Let Dify manage AI flow. Let custom systems manage identity, state, transactions, scheduling, audit, and stable contracts.&lt;/strong&gt; Clear ownership is what allows a prototype to become a production system without a full rewrite.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your experience with Dify in production? Have you hit a wall where a visual workflow should have become a service? I'd love to hear about it in the comments.&lt;/em&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://zediot.com/blog/dify-vs-custom-ai-app-development/" rel="noopener noreferrer"&gt;zediot.com&lt;/a&gt;. ZedIoT helps teams build AI + IoT products from device to cloud — from Dify workflow design to custom AI agent development and private platform deployment.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>backend</category>
      <category>rag</category>
    </item>
    <item>
      <title>Matter 1.4 in Production: What Actually Changed for IoT Device Makers</title>
      <dc:creator>ZedIoT</dc:creator>
      <pubDate>Wed, 29 Jul 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/zediot/matter-14-in-production-what-actually-changed-for-iot-device-makers-1g2l</link>
      <guid>https://dev.to/zediot/matter-14-in-production-what-actually-changed-for-iot-device-makers-1g2l</guid>
      <description>&lt;p&gt;If you're building IoT devices with Matter support — or planning to — you've probably heard about Matter 1.4. The spec documents are thorough, but between the pages of formal protocol definitions and the reality of a production firmware release, there's a gap.&lt;/p&gt;

&lt;p&gt;After working through Matter 1.4 integration on Tuya-enabled devices and supporting factory firmware pipelines for 300+ enterprise customers, here's what actually changed on the ground.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Changes That Matter Most
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Enhanced Multi-Admin: Now It Actually Works
&lt;/h3&gt;

&lt;p&gt;Matter 1.3 introduced Multi-Admin. In theory, a light bulb could join both Apple Home and Google Home simultaneously. In practice? Commissioning flows were fragile. If one fabric removed the device, the other controller often lost state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What 1.4 fixes:&lt;/strong&gt; The commissioning flow now includes explicit fabric synchronization handshakes. When a device is removed from one fabric, remaining controllers receive a notification and can choose to keep or drop the node. This sounds small, but it eliminates the #1 support ticket we saw in multi-ecosystem deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production impact:&lt;/strong&gt; Your device firmware needs to handle the new &lt;code&gt;FabricSync&lt;/code&gt; cluster. If you're using Tuya's Matter SDK, this is abstracted — but you still need to test the removal-then-rejoin path from each ecosystem controller.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Energy Management Cluster: The Silent Game-Changer
&lt;/h3&gt;

&lt;p&gt;Matter 1.4 adds a proper Energy Management cluster. Not just basic power reporting — it includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Power/energy measurement&lt;/strong&gt; with configurable reporting intervals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EVSE (Electric Vehicle Supply Equipment)&lt;/strong&gt; support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forecast-based scheduling&lt;/strong&gt; — the device can report predicted energy usage, allowing controllers to optimize charging/discharging windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For smart plug and appliance makers, this cluster replaces proprietary vendor extensions that were barely interoperable. If you're building any device that plugs into a wall outlet, this should be on your Q4 roadmap.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Water Management Devices: A New Device Type
&lt;/h3&gt;

&lt;p&gt;Leak detectors, water valves, and rain sensors now have standardized device types. Previously, these were custom clusters that only worked within a single ecosystem.&lt;/p&gt;

&lt;p&gt;The practical win: a Tuya-based water leak sensor can now trigger automations across any Matter controller without custom drivers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Did NOT Change (But Should Have)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OTA remains ecosystem-dependent.&lt;/strong&gt; Matter 1.4 doesn't mandate a common OTA mechanism — each controller platform runs its own update pipeline. If you ship a device that joins both Apple Home and Google Home, you need to handle two separate OTA paths, or pick a primary controller for updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thread Border Router interoperability is still uneven.&lt;/strong&gt; The spec says "any certified Thread Border Router should work," but in our testing with 50+ router models, about 15% had quirks with large networks (30+ devices). The Thread Group is working on it, but don't assume plug-and-play for dense deployments yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Should You Migrate Now?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;New product in development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Target Matter 1.4 from day one. The energy cluster and improved multi-admin are worth the SDK upgrade.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Existing product, single-ecosystem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wait. The breaking changes are minimal, but there's no urgency unless you need multi-admin or energy features.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Existing product, multi-ecosystem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Plan migration within 2 quarters. The multi-admin fixes alone will reduce support overhead significantly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Smart plug / appliance / EV charger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prioritize — the Energy Management cluster replaces proprietary extensions you're probably already maintaining.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  One Real-World Migration Story
&lt;/h2&gt;

&lt;p&gt;We recently helped a smart plug manufacturer migrate from Matter 1.2 to 1.4. The firmware side took about 3 engineer-weeks — most of the time wasn't on Matter itself, but on updating the factory test suite to validate the new energy reporting endpoints.&lt;/p&gt;

&lt;p&gt;The surprise: after deploying 1.4 firmware to 5,000 test units, the multi-admin success rate jumped from ~72% to ~94% across Alexa + Google Home pairings. That alone justified the migration.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Matter 1.4 is an incremental but meaningful release. It doesn't revolutionize the protocol, but it fixes real pain points — especially multi-admin and energy management. If you're shipping IoT devices at scale, the migration is worth planning now, even if you don't deploy until Q1.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What's your experience with Matter in production? Are you planning a 1.4 migration, or waiting for 1.6?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>iot</category>
      <category>smarthome</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
