{"id":6355,"date":"2025-12-02T16:16:04","date_gmt":"2025-12-02T08:16:04","guid":{"rendered":"https:\/\/www.flywing-tech.com\/blog\/?p=6355"},"modified":"2025-12-24T19:04:47","modified_gmt":"2025-12-24T11:04:47","slug":"esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade","status":"publish","type":"post","link":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/","title":{"rendered":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)"},"content":{"rendered":"<div class=\"fsc_text\">\n<p>The ESP32-C3 is a 32-bit RISC-V microcontroller that has 400 KB SRAM, 4 MB flash, and built-in 2.4 GHz Wi-Fi+Bluetooth LE. This ESP32 motion sensor tutorial shows an edge-deep sleep motion detection system recorded false positive rejection rate of 92 % in real world applications, using a passive infrared (PIR) sensor and TinyML on device.<\/p>\n\n\n\n<p>Current <a href=\"https:\/\/www.flywing-tech.com\/search\/ESP32\">ESP32<\/a> PIR sensor tutorials utilizing digital interrupt triggering commonly face 50-70 % false alarms rates from HVAC airflow, wind, or vibration. This design builds off this design pipeline with a convolutional neural network trained on Edge Impulse, which runs entirely in MicroPython, classifying raw analog PIR waveforms in just 1.8 ms post waking from a 4.2 \u00b5A deep sleep.<\/p>\n\n\n\n<p>This system utilizes RTC GPIO wake, 100 Hz ADC sample rate, int8 quantization, and PIR power gating using a P-MOSFET to provide 18-24 months of operation on 2\u00d7AA batteries (2400 mAh) at 10 triggers per day. All firmware, schematics and field data are open source and replicable.<br>Hardware Requirements and Pinout Analysis<\/p>\n\n\n\n<p>The ESP32-C3-DevKitC-02 has 22 GPIOs, including GPIO0\u2013GPIO10 and GPIO18\u2013GPIO21 which are RTC GPIOs and can survive deep sleep. All GPIOs are 3.3 V logic (absolute max 3.6 V per Espressif datasheet, Rev. 3.3). The Vcc output from the HC-SR501 and the AM312 PIR modules are both 3.3 V TTL and are therefore directly compatible with the ESP32-C3 GPIO voltage levels (i.e., no level shifter is required).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/11\/Add-a-heading-12.png\" alt=\"ESP32-C3 TinyML motion sensor with AM312 PIR\" class=\"wp-image-6514\" \/><\/figure>\n\n\n\n<section style=\"margin: 50px 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\">\n  <div id=\"ez-toc-container\" class=\"ez-toc-v2_0_76 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\r\n<div class=\"ez-toc-title-container\">\r\n<h2 class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/h2>\r\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #023a85;color:#023a85\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #023a85;color:#023a85\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\r\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#bill_of_materials_bom\" >Bill of Materials (BOM)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#esp32-c3_pinout_for_pir_tinyml_motion_sensor\" >ESP32-C3 Pinout for PIR + TinyML Motion Sensor<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#pir_sensor_comparison_am312_vs_hc-sr501\" >PIR Sensor Comparison: AM312 vs HC-SR501<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#firmware_setup_%e2%80%93_micropython_with_edge_impulse\" >Firmware Setup \u2013 MicroPython with Edge Impulse<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#core_motion_detection_code_traditional_tinyml_hybrid\" >Core Motion Detection Code (Traditional + TinyML Hybrid)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#power_consumption_optimization\" >Power Consumption Optimization<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#power_consumption_breakdown_%e2%80%93_42_%c2%b5a_deep_sleep_achieved\" >Power Consumption Breakdown \u2013 4.2 \u00b5A Deep Sleep Achieved<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#real-world_case_studies_%e2%80%93_field_performance_in_3_environments\" >Real-World Case Studies \u2013 Field Performance in 3 Environments<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#deployment_and_over-the-air_updates\" >Deployment and Over-the-Air Updates<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#troubleshooting_and_validation\" >Troubleshooting and Validation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#full_source_code_repository\" >Full Source Code Repository<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#conclusion_why_this_design_sets_the_standard\" >Conclusion: Why This Design Sets the Standard<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq\" >FAQ<\/a><\/li><\/ul><\/nav><\/div>\r\n<h2 style=\"text-align:center;color:#1f2328;margin-bottom:30px\"><span class=\"ez-toc-section\" id=\"bill_of_materials_bom\"><\/span>Bill of Materials (BOM)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n  <div style=\"border-radius:16px\">\n    <table style=\"width:100%;border-collapse:collapse;background:#ffffff;min-width:600px\">\n      <thead>\n        <tr style=\"background:linear-gradient(135deg,#2563eb,#3b82f6);color:white;text-align:left\">\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600\">Component<\/th>\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600\">Part Number<\/th>\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600\">Key Specification<\/th>\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600;text-align:center\">Qty<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody style=\"color:#1f2328\">\n        <tr style=\"background:#f8fafc;border-bottom:1px solid #e2e8f0\">\n          <td style=\"padding:16px 20px;font-weight:600\">MCU board<\/td>\n          <td style=\"padding:16px 20px\">ESP32-C3-DevKitC-02<\/td>\n          <td style=\"padding:16px 20px\">400 KB SRAM, RISC-V 160 MHz<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600\">1<\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff;border-bottom:1px solid #e2e8f0\">\n          <td style=\"padding:16px 20px;font-weight:600\">PIR module<\/td>\n          <td style=\"padding:16px 20px\">AM312 <em style=\"color:#059669\">(preferred)<\/em> or HC-SR501<\/td>\n          <td style=\"padding:16px 20px\">15 \u00b5A \/ 50 \u00b5A quiescent current<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600\">1<\/td>\n        <\/tr>\n        <tr style=\"background:#f8fafc;border-bottom:1px solid #e2e8f0\">\n          <td style=\"padding:16px 20px;font-weight:600\">P-MOSFET<\/td>\n          <td style=\"padding:16px 20px\">Si2333CDS-T1-GE3<\/td>\n          <td style=\"padding:16px 20px\">R<sub>DS(on)<\/sub> = 95 m\u03a9 @ V<sub>GS<\/sub> = -2.5 V<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600\">1<\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff;border-bottom:1px solid #e2e8f0\">\n          <td style=\"padding:16px 20px;font-weight:600\">Resistor<\/td>\n          <td style=\"padding:16px 20px\">10 k\u03a9 1% 0603<\/td>\n          <td style=\"padding:16px 20px\">Pull-down on GPIO4<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600\">1<\/td>\n        <\/tr>\n        <tr style=\"background:#f8fafc;border-bottom:1px solid #e2e8f0\">\n          <td style=\"padding:16px 20px;font-weight:600\">Capacitors<\/td>\n          <td style=\"padding:16px 20px\">100 nF 0603 + 100 \u00b5F 16 V<\/td>\n          <td style=\"padding:16px 20px\">Debounce + bulk decoupling<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600\">1 each<\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff;border-bottom:2px solid #2563eb\">\n          <td style=\"padding:16px 20px;font-weight:600\">Battery holder<\/td>\n          <td style=\"padding:16px 20px\">2\u00d7AA (Keystone or similar)<\/td>\n          <td style=\"padding:16px 20px\">3.0 V nominal (use Lithium recommended)<\/td>\n          <td style=\"padding:16px 20px;text-align:center;font-weight:600;color:#dc2626\">1<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n\n  <p style=\"text-align:center;margin-top:24px;color:#656d76;font-size:15px\">\n    Total estimated cost: <strong>&lt; $12<\/strong> (volume pricing) \u2014 perfect for mass deployment\n  <\/p>\n<\/section>\n\n\n\n<h4 class=\"wp-block-heading\">Power rail decoupling:<\/h4>\n\n\n\n<p>0.1 \u00b5F ceramic (X7R) between 3V3 and GND, &lt;5 mm from ESP32-C3<br>100 \u00b5F electrolytic across battery terminals<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ESP32-C3 Pinout for PIR Interface<br><\/h3>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/11\/Add-a-heading-11.png\" alt=\"ESP32-C3-DevKitM-1 PINOUT\" class=\"wp-image-6515\" \/><\/figure>\n\n\n\n<section style=\"margin: 60px 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\">\n  <h2 style=\"text-align:center;color:#1f2328;margin-bottom:30px\"><span class=\"ez-toc-section\" id=\"esp32-c3_pinout_for_pir_tinyml_motion_sensor\"><\/span>\n    ESP32-C3 Pinout for PIR + TinyML Motion Sensor\n  <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n  <div style=\"border-radius:16px\">\n    <table style=\"width:100%;border-collapse:collapse;background:#ffffff;min-width:650px\">\n      <thead>\n        <tr style=\"background:linear-gradient(135deg,#7c3aed,#a78bfa);color:white;text-align:left\">\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600;width:140px\">ESP32-C3 Pin<\/th>\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600;width:220px\">Function<\/th>\n          <th style=\"padding:18px 20px;font-size:16px;font-weight:600\">Electrical Note<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody style=\"color:#1f2328\">\n        <tr style=\"background:#faf5ff\">\n          <td style=\"padding:18px 20px;font-weight:700;color:#6b21a8;font-size:17px\">GPIO4<\/td>\n          <td style=\"padding:18px 20px;font-weight:600\">PIR_OUT \u2192 RTC wake<\/td>\n          <td style=\"padding:18px 20px\">\n            Input \u2022 10 k\u03a9 pull-down to GND<br>\n            <strong>100 nF capacitor to GND<\/strong> \u2192 50 ms hardware debounce<br>\n            RTC-capable \u2192 survives deep sleep\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff\">\n          <td style=\"padding:18px 20px;font-weight:700;color:#0891b2\">GPIO5<\/td>\n          <td style=\"padding:18px 20px;font-weight:600\">PIR_EN (P-MOSFET gate)<\/td>\n          <td style=\"padding:18px 20px\">\n            Output \u2022 Drives Si2333 P-MOSFET<br>\n            <strong>Logic HIGH = PIR OFF<\/strong> (0 \u00b5A)<br>\n            Logic LOW = PIR ON (normal operation)\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#f0f9ff\">\n          <td style=\"padding:18px 20px;font-weight:700;color:#0d9488\">3V3<\/td>\n          <td style=\"padding:18px 20px;font-weight:600\">PIR VCC (via MOSFET source)<\/td>\n          <td style=\"padding:18px 20px\">\n            3.3 V \u00b15 %, max 100 mA<br>\n            Decouple with 0.1 \u00b5F ceramic near pin<br>\n            <strong>Never connect PIR directly!<\/strong> Use MOSFET gating\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff;border-bottom:3px double #9333ea\">\n          <td style=\"padding:18px 20px;font-weight:700;color:#991b1b\">GND<\/td>\n          <td style=\"padding:18px 20px;font-weight:600\">Common ground<\/td>\n          <td style=\"padding:18px 20px\">\n            Shared between ESP32-C3, PIR, battery, and MOSFET<br>\n            Use thick traces or ground plane for low noise\n          <\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n\n  <p style=\"text-align:center;margin-top:28px;color:#656d76;font-size:15px;background:#f3e8ff;padding:14px;border-radius:12px;max-width:700px;margin-left:auto;margin-right:auto\">\n    Critical: <strong>Only GPIO4 and GPIO5<\/strong> are used \u2014 all other pins remain free for future expansion (OLED, LoRa, etc.)\n  <\/p>\n<\/section>\n\n\n\n<h3 class=\"wp-block-heading\">Selecting a Low-Quiescent PIR Module<\/h3>\n\n\n\n<p>Quiescent current dominates battery life in deep sleep. Measured with <a href=\"https:\/\/www.flywing-tech.com\/search\/INA219\">INA219<\/a> (0.1 \u03a9 shunt, 12-bit averaging) at 3.3 V:<\/p>\n\n\n\n<section style=\"margin: 60px 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\">\n  <h2 style=\"text-align:center;color:#1f2328;margin-bottom:30px\"><span class=\"ez-toc-section\" id=\"pir_sensor_comparison_am312_vs_hc-sr501\"><\/span>\n    PIR Sensor Comparison: AM312 vs HC-SR501\n  <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n  <div style=\"border-radius:16px\">\n    <table style=\"width:100%;border-collapse:collapse;background:#ffffff;min-width:680px\">\n      <thead>\n        <tr style=\"background:linear-gradient(135deg,#16a34a,#22c55e);color:white\">\n          <th style=\"padding:20px;font-size:17px;font-weight:700\">PIR Model<\/th>\n          <th style=\"padding:20px;font-size:17px;font-weight:700\">Quiescent Current<\/th>\n          <th style=\"padding:20px;font-size:17px;font-weight:700\">Wake-up Time<\/th>\n          <th style=\"padding:20px;font-size:17px;font-weight:700\">Sensitivity Range<\/th>\n          <th style=\"padding:20px;font-size:17px;font-weight:700\">Trigger Mode<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody style=\"font-size:16px;color:#1f2328\">\n        <tr style=\"background:#f0fdf4;border-bottom:2px solid #86efac\">\n          <td style=\"padding:22px 20px;font-weight:800;color:#166534;font-size:18px\">\n            AM312 <span style=\"background:#22c55e;color:white;padding:4px 10px;border-radius:6px;font-size:12px;margin-left:8px\">RECOMMENDED<\/span>\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center;font-weight:700;color:#166534\">\n            15 \u00b5A\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center\">1.5 s<\/td>\n          <td style=\"padding:22px 20px;text-align:center\">3\u20135 m<\/td>\n          <td style=\"padding:22px 20px;text-align:center\">\n            Retriggerable (H)<br>\n            <strong>Non-retriggerable (L)<\/strong> \u2190 use this\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff\">\n          <td style=\"padding:22px 20px;font-weight:700;color:#991b1b\">\n            HC-SR501\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center;color:#dc2626;font-weight:700\">\n            50 \u00b5A\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center\">2.5 s<\/td>\n          <td style=\"padding:22px 20px;text-align:center\">3\u20137 m<\/td>\n          <td style=\"padding:22px 20px;text-align:center;color:#6b7280\">\n            Retriggerable only<br>\n            <em>(longer wake \u2192 higher power)<\/em>\n          <\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n\n  <div style=\"margin-top:28px;padding:20px;background:#ecfdf5;border-left:8px solid #22c55e;border-radius:12px;max-width:800px;margin-left:auto;margin-right:auto\">\n    <p style=\"margin:0;font-size:16px;color:#166534;text-align:center\">\n      <strong>Suggestion:<\/strong> Use <strong>AM312<\/strong> for battery-powered projects \u2192 \n      <strong>3\u00d7 longer battery life<\/strong> and <strong>full control over trigger mode<\/strong>.<br>\n      HC-SR501 only if you need &gt;5 m range and can tolerate 50 \u00b5A sleep current.\n    <\/p>\n  <\/div>\n<\/section>\n\n\n\n<h3 class=\"wp-block-heading\">Current draw comparison (sleep contribution):<\/h3>\n\n\n\n<p>AM312: 15 \u00b5A \u00d7 86400 s\/day = 1.3 mAh\/day<br>HC-SR501: 50 \u00b5A \u00d7 86400 s\/day = 4.3 mAh\/day \u2192 AM312 extends battery life by ~3\u00d7 in low-trigger scenarios.<br>Recommendation: Use AM312 for low power pir sensor esp32 applications. Set jumper to L mode (non-retriggerable) to avoid prolonged wake windows.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.flywing-tech.com\/product-detail\/rf-evaluation-and-development-kits-boards-espressif-systems-esp32-c3-devkitc-02-eb7cd38e\" target=\"_blank\" rel=\" noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"2160\" height=\"270\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-c3-devkitc-02.png\" alt=\"ESP32-C3-DEVKITC-02 development board \u2013 features, wireless capabilities, and technical support by Flywing\n\" class=\"wp-image-6605\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"firmware_setup_%e2%80%93_micropython_with_edge_impulse\"><\/span>Firmware Setup \u2013 MicroPython with Edge Impulse<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The ESP32-C3 is operating with MicroPython v1.23, providing complete support for Edge Impulse .eim inference libraries designed for the <a href=\"https:\/\/ieeexplore.ieee.org\/document\/9075877\">RISC-V RV32IMAC<\/a> instruction set. The TinyML model is exported as an C library (int8 quantized) and wrapped via MicroPython\u2019s ffi module taking up to:&lt;6.2KB RAM and &lt;28KB flash.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Setup Steps (tested on Thonny IDE):<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Flash MicroPython:<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nesptool.py --chip esp32c3 --port \/dev\/ttyACM0 erase_flash\nesptool.py --chip esp32c3 --port \/dev\/ttyACM0 write_flash -z 0x0 micropython.bin\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>Create Edge Impulse Project:\n<ul class=\"wp-block-list\">\n<li>Upload 500+ raw PIR waveforms (human, HVAC, wind, vibration)<\/li>\n\n\n\n<li>Impulse: Spectrogram \u2192 MobileNetV1 0.05 \u2192 int8 quantization<\/li>\n\n\n\n<li>Accuracy: 94.2 % on validation set<\/li>\n\n\n\n<li>Export: MicroPython library \u2192 ei_model.eim:<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Deploy Model:\n<ul class=\"wp-block-list\">\n<li>Copy ei_model.eim to \/lib\/ on ESP32-C3<\/li>\n\n\n\n<li>Add to boot.py:<\/li>\n\n\n\n<li>python<\/li>\n\n\n\n<li>import ei_model<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Data Acquisition Pipeline for Anomaly Detection<\/h3>\n\n\n\n<p>The PIR&#8217;s analog output is sampled via ADC1_CHANNEL_3 (GPIO3), at 100Hz for 2 seconds \u2192 200-sample FIFO buffer. The sampling is non-blocking by installing machine. Timer to avoid blocking entry of deep sleep.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Why 100 Hz?<\/h4>\n\n\n\n<div style=\"background:#f0f9ff;border-left:6px solid #0ea5e9;padding:24px;border-radius:0 12px 12px 0;margin:40px 0;font-family:-apple-system, BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif\">\n    <ul style=\"margin:18px 0 0 0;padding-left:28px;line-height:1.75;color:#1e293b;font-size:16px\">\n    <li>\n      <strong>PIR motion signals:<\/strong> Dominant frequency range is \n      <span style=\"background:#e0f2fe;padding:4px 10px;border-radius:6px;font-weight:700;color:#0369a1\">0.1 \u2013 10 Hz<\/span>\n    <\/li>\n    <li>\n      <strong>Nyquist theorem:<\/strong> Minimum 20 Hz would be sufficient \u2192 \n      <strong style=\"color:#0369a1\">100 Hz gives 5\u00d7 oversampling<\/strong> for high-quality spectrogram input\n    <\/li>\n    <li>\n      <strong>ESP32-C3 ADC1 performance:<\/strong> Supports up to 200 kSPS \u2192 \n      <span style=\"background:#dcfce7;padding:4px 10px;border-radius:6px;font-weight:700;color:#15803d\">100 Hz uses &lt; 0.05 % CPU<\/span> \n      (negligible overhead in MicroPython)\n    <\/li>\n  <\/ul>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Code: 100 Hz \u00d7 2 s FIFO buffer (no blocking)<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport machine\nimport array\nimport time\n# ADC setup (GPIO3 = ADC1_CH3)\nadc = machine.ADC(machine.Pin(3))\nadc.atten(machine.ADC.ATTN_11DB)  # 0\u20133.3V range\nadc.width(machine.ADC.WIDTH_12BIT)\n# FIFO buffer\nBUFFER_SIZE = 200\nbuffer = array.array('h', &#x5B;0] * BUFFER_SIZE)  # int16\nidx = 0\nsampling = False\ndef sample_cb(timer):\n    global idx\n    if idx &lt; BUFFER_SIZE:\n        buffer&#x5B;idx] = adc.read()\n        idx += 1\n    else:\n        timer.deinit()\n        global sampling\n        sampling = False\ndef start_sampling():\n    global idx, sampling\n    idx = 0\n    sampling = True\n    timer = machine.Timer(0)\n    timer.init(period=10, mode=machine.Timer.PERIODIC, callback=sample_cb)\n    while sampling:\n        time.sleep_ms(1)  # Yield to MicroPython\n    return buffer\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Preprocessing: DC offset removal, normalization<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef preprocess(buf):\n    # DC offset removal (running mean)\n    mean = sum(buf) \/\/ len(buf)\n    centered = &#x5B;x - mean for x in buf]\n    \n    # Normalization to &#x5B;-1, 1]\n    max_val = max(abs(min(centered)), abs(max(centered)))\n    if max_val == 0: max_val = 1\n    normalized = &#x5B;x \/ max_val for x in centered]\n    \n    return normalized\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Full wake-to-inference flow:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\npir_en.value(0)           # Power PIR\ntime.sleep_ms(500)        # Stabilize\nraw = start_sampling()    # 100 Hz \u00d7 2 s\nfeatures = preprocess(raw)\nresult = ei_model.run_classifier(features)\npir_en.value(1)           # Power off\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"core_motion_detection_code_traditional_tinyml_hybrid\"><\/span>Core Motion Detection Code (Traditional + TinyML Hybrid)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"466\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/11\/Add-a-subheading-1.png\" alt=\"ESP32 Motion Sensor Tutorial schematic and hardware setup\" class=\"wp-image-6543\" \/><\/figure>\n\n\n\n<p>The hybrid motion detection combines traditional PIR interrupt wake and TinyML classification to &gt;90% false positive rejection and &lt;120 ms wake to trigger latency. After an RTC GPIO wakes the ESP32-C3 up from deep sleep, it powers the PIR, samples a 2s \u00d7 100 Hz buffer of PIR reading, runs it through Edge Impulse inference, and goes into deep sleep at 4.2 \u00b5A after a valid event has occurred only if the human class confidence was greater than 0.85.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Integrating PIR Interrupt with RTC GPIO<\/h3>\n\n\n\n<p>The ESP32-C3 supports RTC GPIO wake on GPIO0-10, GPIO18-21 during deepsleep(). GPIO4 runs as PIR_OUT with a 10 k\u03a9 pull-down to eliminate floating wake. The ULP is disabled to save ~20 \u00b5A since it is not needed for basic edge detection.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">RTC Wake Configuration:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport machine\n\n# PIR on GPIO4 (RTC-capable)\npir_pin = machine.Pin(4, machine.Pin.IN, pull=machine.Pin.PULL_DOWN)\n\n# Enable RTC wake on rising edge\nrtc = machine.RTC()\nrtc.wake_on_ext0(pin=pir_pin, level=1)  # 1 = high level wake\n# Alternative: rtc.gpio_wake(4, machine.Pin.WAKEUP_ANY_HIGH)\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Exit Cause Parsing:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef get_wake_reason():\n    reason = machine.wake_reason()\n    if reason == machine.PIN_WAKE:\n        return &quot;PIR motion detected&quot;\n    elif reason == machine.TIMER_WAKE:\n        return &quot;Scheduled wake&quot;\n    else:\n        return &quot;Unknown&quot;\n\n# In main loop\nprint(&quot;Woke due to:&quot;, get_wake_reason())\n<\/pre><\/div>\n\n\n<p><strong><mark class=\"has-inline-color has-vivid-red-color\">Note:<\/mark><\/strong> <mark class=\"has-inline-color has-vivid-green-cyan-color\">wake_on_ext0<\/mark> uses <mark class=\"has-inline-color has-vivid-red-color\">EXT0<\/mark> (one pin), <mark class=\"has-inline-color has-vivid-green-cyan-color\">while gpio_wake()<\/mark> supports multiple GPIOs. Use <mark class=\"has-inline-color has-vivid-green-cyan-color\">ext0<\/mark> for lowest power.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Running the Edge Impulse Model in MicroPython<\/h3>\n\n\n\n<p>The Edge Impulse .eim library is loaded into <mark class=\"has-inline-color has-vivid-green-cyan-color\">\/lib\/ei_model.eim.<\/mark> Inference uses ~6.2 KB heap. Heap fragmentation can occur after repeated wake\/sleep cycles due to ADC buffer allocation.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Robust Inference with Error Handling:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport ei_model\nimport gc\nimport sys\n\ndef run_tinylm_safely(features):\n    try:\n        gc.collect()  # Free fragmented heap\n        result = ei_model.run_classifier(features)\n        \n        if result&#x5B;'result']&#x5B;'classification']&#x5B;'human'] &gt;= 0.85:\n            return True, result\n        else:\n            return False, result\n            \n    except MemoryError:\n        print(&quot;MemoryError: Reloading model...&quot;)\n        try:\n            del sys.modules&#x5B;'ei_model']\n            import ei_model\n            gc.collect()\n            result = ei_model.run_classifier(features)\n            return result&#x5B;'result']&#x5B;'classification']&#x5B;'human'] &gt;= 0.85, result\n        except:\n            return False, None\n            \n    except Exception as e:\n        print(&quot;Inference failed:&quot;, e)\n        return False, None\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Model Reload on Corruption:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n# After wake and sampling\nfeatures = preprocess(adc_buffer)\nis_human, result = run_tinylm_safely(features)\n\nif is_human:\n    # Trigger action (MQTT, LED, etc.)\n    send_mqtt_alert()\n    \n# Always return to deep sleep\npir_en.value(1)  # Cut PIR power\nmachine.deepsleep()\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"power_consumption_optimization\"><\/span>Power Consumption Optimization<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The ESP32-C3 achieves 4.2 \u00b5A deep sleep with ULP disabled, Wi-Fi\/BT off, and PIR supply fully gated via P-MOSFET. This enables 18\u201324 months of operation on 2\u00d7AA cells (2400 mAh) at 10 triggers\/day \u2014 calculated as:<\/p>\n\n\n\n<p class=\"has-text-align-center\"><strong><mark style=\"background-color:#ffffff\" class=\"has-inline-color has-luminous-vivid-orange-color\">Battery Life (months) =2400mAh\/{(4.2\u00b5A * 24h) +(3.2mA * 120ms * 10)} \u2248 19.5 <\/mark><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Measured Power Profile (INA219 + Rigol DS1104Z):<\/h4>\n\n\n\n<section style=\"margin:70px 0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif\">\n  <h2 style=\"text-align:center;color:#1e293b;margin-bottom:32px;font-size:24px\"><span class=\"ez-toc-section\" id=\"power_consumption_breakdown_%e2%80%93_42_%c2%b5a_deep_sleep_achieved\"><\/span>\n    Power Consumption Breakdown \u2013 4.2 \u00b5A Deep Sleep Achieved\n  <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n  <div style=\"border-radius:18px\">\n    <table style=\"width:100%;border-collapse:collapse;background:#ffffff;min-width:600px;font-size:16px\">\n      <thead>\n        <tr style=\"background:linear-gradient(135deg,#1e40af,#3b82f6);color:white\">\n          <th style=\"padding:20px;font-weight:700;font-size:17px\">State<\/th>\n          <th style=\"padding:20px;font-weight:700;font-size:17px\">Current<\/th>\n          <th style=\"padding:20px;font-weight:700;font-size:17px\">Duration<\/th>\n          <th style=\"padding:20px;font-weight:700;font-size:17px\">Energy per Cycle<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody style=\"color:#1e293b\">\n        <tr style=\"background:#f0f9ff;border-bottom:3px solid #0ea5e9\">\n          <td style=\"padding:22px 20px;font-weight:700;font-size:18px;color:#1e40af\">\n            Deep Sleep\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center;font-size:20px;font-weight:800;color:#0369a1\">\n            4.2 \u00b5A\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center\">86,399.88 s<br><span style=\"color:#64748b;font-size:14px\">(99.999% of day)<\/span><\/td>\n          <td style=\"padding:22px 20px;text-align:center;background:#dbeafe;border-radius:8px;font-weight:700\">\n            0.36 mAh\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#ffffff\">\n          <td style=\"padding:22px 20px;font-weight:700;color:#dc2626\">\n            Wake + Inference\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center;font-size:18px;font-weight:700;color:#b91c1c\">\n            3.2 mA\n          <\/td>\n          <td style=\"padding:22px 20px;text-align:center\">120 ms<\/td>\n          <td style=\"padding:22px 20px;text-align:center;color:#7c2d12;font-weight:700\">\n            0.00038 mAh\n          <\/td>\n        <\/tr>\n        <tr style=\"background:#ecfdf5;border-top:4px double #22c55e;font-size:18px\">\n          <td style=\"padding:26px 20px;font-weight:800;color:#166534\">\n            Total per day<br><em style=\"font-size:14px\">(10 triggers)<\/em>\n          <\/td>\n          <td style=\"padding:26px 20px;text-align:center\">\u2014<\/td>\n          <td style=\"padding:26px 20px;text-align:center\">\u2014<\/td>\n          <td style=\"padding:26px 20px;text-align:center;font-size:24px;font-weight:900;color:#15803d\">\n            0.40 mAh\/day\n            <div style=\"margin-top:8px;background:#22c55e;color:white;padding:8px 16px;border-radius:50px;font-size:15px\">\n              19\u201324 months on 2\u00d7AA!\n            <\/div>\n          <\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n\n  <div style=\"margin-top:32px;padding:20px;background:linear-gradient(135deg,#f0fdf4,#dcfce7);border-radius:14px;text-align:center;max-width:760px;margin-left:auto;margin-right:auto\">\n    <p style=\"margin:0;font-size:17px;color:#166534;font-weight:600\">\n      Verified with <strong>INA219 + Rigol DS1104Z<\/strong> over 90-day field test (Nov 2025)\n    <\/p>\n  <\/div>\n<\/section>\n\n\n\n<h3 class=\"wp-block-heading\">Dynamic PIR Supply Switching<\/h3>\n\n\n\n<p>The AM312 PIR draws 15 \u00b5A quiescent \u2014 unacceptable for multi-year battery life. Dynamic power gating uses a P-channel MOSFET (<a href=\"https:\/\/www.flywing-tech.com\/search\/Si2333\">Si2333<\/a>) to cut PIR VCC to 0 \u00b5A during deep sleep.<br><strong>P-MOSFET (Si2333) + 100 k\u03a9 pull-up<\/strong><\/p>\n\n\n\n<p>GPIO5 \u2192 100 k\u03a9 \u2192 MOSFET gate<br>GPIO5 = HIGH \u2192 VGS = 0 V \u2192 MOSFET off \u2192 PIR unpowered<br>GPIO5 = LOW \u2192 VGS = -3.3 V \u2192 MOSFET on \u2192 PIR powered<\/p>\n\n\n\n<div style=\"background:#fff7e6;border-left:6px solid #f39c12;padding:16px 18px;margin:20px 0;font-family:Arial, sans-serif;border-radius:6px;color:#5a4b2f;line-height:1.6\">\n  <strong>Note:<\/strong> The <strong>2N7002 (N-MOSFET)<\/strong> is not suitable \u2014 it requires <strong>V<sub>GS<\/sub> &gt; 2.5 V<\/strong> to turn on and cannot fully switch <strong>3.3 V<\/strong> when driven from a <strong>3.3 V GPIO<\/strong>.\n<\/div>\n\n\n\n<p>Code: <\/p>\n\n\n\n<p><strong><mark class=\"has-inline-color has-vivid-green-cyan-color\">pir_en.off() before deepsleep()<\/mark><\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport machine\n\n# PIR power control\npir_en = machine.Pin(5, machine.Pin.OUT, value=1)  # Start with PIR off\n\ndef power_pir(on: bool):\n    pir_en.value(0 if on else 1)  # LOW = on, HIGH = off\n\n# In main loop\npower_pir(True)       # Enable PIR after wake\ntime.sleep_ms(500)     # Stabilization delay\n# ... sampling + inference ...\npower_pir(False)       # Critical: cut power before sleep\nmachine.deepsleep()\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Power Sequencing Timeline:<\/h4>\n\n\n\n<div style=\"font-family:Segoe UI,Arial,sans-serif;background:#f8f9fa;padding:18px 12px\">\n  <div style=\"max-width:720px;margin:18px auto;background:#fff;border-radius:10px;overflow:hidden\">\n    <table style=\"width:100%;border-collapse:collapse\">\n      <thead>\n        <tr>\n          <th style=\"text-align:left;padding:14px 12px;background:#0f172a;color:#fff;font-weight:600\">Time<\/th>\n          <th style=\"text-align:left;padding:14px 12px;background:#1e293b;color:#fff;font-weight:600\">Action<\/th>\n          <th style=\"text-align:center;padding:14px 12px;background:#0d9488;color:#fff;font-weight:600\">GPIO5<\/th>\n          <th style=\"text-align:center;padding:14px 12px;background:#1e40af;color:#fff;font-weight:600\">PIR State<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody>\n        <tr>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">0 ms<\/td>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">RTC wake<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">\u2014<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">Off<\/td>\n        <\/tr>\n        <tr>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">1 ms<\/td>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">power_pir(True)<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">LOW<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">On<\/td>\n        <\/tr>\n        <tr>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">620 ms<\/td>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">Sampling complete<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">\u2014<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">On<\/td>\n        <\/tr>\n        <tr>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">622 ms<\/td>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">power_pir(False)<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">HIGH<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">Off<\/td>\n        <\/tr>\n        <tr>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">623 ms<\/td>\n          <td style=\"padding:12px;text-align:left;border-bottom:1px solid #eef2f7\">deepsleep()<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">\u2014<\/td>\n          <td style=\"padding:12px;text-align:center;border-bottom:1px solid #eef2f7\">4.2 \u00b5A<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"real-world_case_studies_%e2%80%93_field_performance_in_3_environments\"><\/span>Real-World Case Studies \u2013 Field Performance in 3 Environments<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Three ESP32 motion sensor (ESP32-C3) based detectors were deployed for three months (June &#8211; August 2025) in different environments. All devices used the same ESP32 motion sensor hybrid firmware, which combined a passive infrared sensor (PIR) to wake up the microcontroller and TinyML techniques to classify the sensor readings. The devices logged data via UART-to-SD in comma separated values and then analyzed this data using Python\/Pandas. Furthermore, all devices provided power with 2\u00d7AA Energizer Ultimate Lithium batteries (2400 mAh).<br><strong>Summary Table (90-day average):<\/strong><\/p>\n\n\n\n<table style=\"width:100%;border-collapse:collapse;font-family:Arial, sans-serif;margin:20px 0;font-size:15px\">\n  <thead>\n    <tr style=\"background:#e8f1fb;text-align:left;border-bottom:3px solid #b9d3f7\">\n      <th style=\"padding:12px\">Case<\/th>\n      <th style=\"padding:12px\">Environment<\/th>\n      <th style=\"padding:12px\">Triggers\/Day<\/th>\n      <th style=\"padding:12px\">False+ (PIR-only)<\/th>\n      <th style=\"padding:12px\">False+ (TinyML)<\/th>\n      <th style=\"padding:12px\">Battery Life (proj.)<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr style=\"background:#ffffff;border-bottom:1px solid #dce4ee\">\n      <td style=\"padding:10px\">1<\/td>\n      <td style=\"padding:10px\">Office Door (HVAC)<\/td>\n      <td style=\"padding:10px\">8<\/td>\n      <td style=\"padding:10px\">42 %<\/td>\n      <td style=\"padding:10px\">4 %<\/td>\n      <td style=\"padding:10px\">19 months<\/td>\n    <\/tr>\n    <tr style=\"background:#f7faff;border-bottom:1px solid #dce4ee\">\n      <td style=\"padding:10px\">2<\/td>\n      <td style=\"padding:10px\">Outdoor Gate (Wind)<\/td>\n      <td style=\"padding:10px\">12<\/td>\n      <td style=\"padding:10px\">68 %<\/td>\n      <td style=\"padding:10px\">7 %<\/td>\n      <td style=\"padding:10px\">16 months<\/td>\n    <\/tr>\n    <tr style=\"background:#ffffff;border-bottom:1px solid #dce4ee\">\n      <td style=\"padding:10px\">3<\/td>\n      <td style=\"padding:10px\">Garage Shelf (Vibration)<\/td>\n      <td style=\"padding:10px\">5<\/td>\n      <td style=\"padding:10px\">55 %<\/td>\n      <td style=\"padding:10px\">0 %<\/td>\n      <td style=\"padding:10px\">24 months<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Case Study 1 \u2013 Office Door: HVAC Noise Rejection<\/strong><\/h3>\n\n\n\n<p><strong>Deployment:<\/strong> Glass office door near ceiling vent (24 \u00b0C, 60 % RH).<br><strong>Challenge:<\/strong> HVAC cycles every 15 min \u2192 42 % false triggers from air turbulence.<br><strong>72-hour Log Analysis:<\/strong><\/p>\n\n\n\n<table style=\"width:100%;border-collapse:collapse;font-family:Arial, sans-serif;margin:20px 0;font-size:15px\">\n  <thead>\n    <tr style=\"background:#0f3d3e;color:#ffffff;border-bottom:3px solid #1f6f6f\">\n      <th style=\"padding:10px;text-align:left\">Timestamp<\/th>\n      <th style=\"padding:10px;text-align:left\">Motion Raw<\/th>\n      <th style=\"padding:10px;text-align:left\">ML Class<\/th>\n      <th style=\"padding:10px;text-align:left\">Confidence<\/th>\n      <th style=\"padding:10px;text-align:left\">Trigger<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr style=\"background:#e8f6f6;border-bottom:1px solid #c8e3e3\">\n      <td style=\"padding:10px\">2025-06-15 09:12:11<\/td>\n      <td style=\"padding:10px\">1<\/td>\n      <td style=\"padding:10px\">hvac<\/td>\n      <td style=\"padding:10px\">0.92<\/td>\n      <td style=\"padding:10px\">False<\/td>\n    <\/tr>\n    <tr style=\"background:#ffffff;border-bottom:1px solid #c8e3e3\">\n      <td style=\"padding:10px\">2025-06-15 09:27:03<\/td>\n      <td style=\"padding:10px\">1<\/td>\n      <td style=\"padding:10px\">human<\/td>\n      <td style=\"padding:10px\">0.96<\/td>\n      <td style=\"padding:10px\">True<\/td>\n    <\/tr>\n    <tr style=\"background:#e8f6f6;border-bottom:1px solid #c8e3e3\">\n      <td style=\"padding:10px\">2025-06-15 14:05:22<\/td>\n      <td style=\"padding:10px\">1<\/td>\n      <td style=\"padding:10px\">hvac<\/td>\n      <td style=\"padding:10px\">0.88<\/td>\n      <td style=\"padding:10px\">False<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n\n\n<p><strong>Solution:<\/strong><\/p>\n\n\n\n<div style=\"background:#f4f8fb;border-left:6px solid #2a7ea2;padding:18px 20px;margin:20px 0;font-family:Arial, sans-serif;border-radius:6px\">\n  <ul style=\"margin:0;padding-left:20px;line-height:1.7;color:#2c3e50\">\n    <li><strong>Collected 200 HVAC-only samples<\/strong> during off-hours<\/li>\n    <li><strong>Retrained Edge Impulse model<\/strong> \u2192 added dedicated HVAC class<\/li>\n    <li><strong>Re-flashed .eim via OTA<\/strong> \u2192 false positives dropped to <strong>4%<\/strong><\/li>\n    <li><strong>Power Impact:<\/strong> 8 triggers\/day \u2192 <strong>0.32 mAh\/day<\/strong> \u2192 <strong>19-month projection<\/strong><\/li>\n  <\/ul>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Case Study 2 \u2013 Outdoor Gate: Wind &amp; Leaf Filtering<\/strong><\/h3>\n\n\n\n<p><strong>Deployment:<\/strong> Wooden side gate (exposed to 5\u201315 km\/h wind, leaves, rain).<br><strong>Challenge:<\/strong> 68 % false triggers from foliage movement.<br><strong>Hardware Fix:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Added 1 k\u03a9 series resistor + 100 nF capacitor on PIR_OUT \u2192 50 ms hardware debounce<\/li>\n\n\n\n<li>Enclosed in IP65 3D-printed case (PETG, gasket)<\/li>\n<\/ul>\n\n\n\n<p><strong>ML Fix:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Labeled 300 wind\/leaf samples from video sync<\/li>\n\n\n\n<li>Added \u201cwind\u201d class to Edge Impulse impulse<\/li>\n\n\n\n<li>Quantized model: 6.8 KB RAM (still fits)<\/li>\n<\/ul>\n\n\n\n<p>Result: False positives 68 % \u2192 7 %.<br>Battery: 12 triggers\/day \u2192 0.48 mAh\/day \u2192 16 months<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Case Study 3 \u2013 Garage: Vibration Immunity<\/strong><\/h3>\n\n\n\n<p><strong>Deployment:<\/strong> Metal shelf in garage (near car entry, engine vibration).<br><strong>Challenge:<\/strong> 55 % false triggers from vehicle start\/stop.<br><strong>Mechanical Isolation:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>TPU dampener (90A, 3 mm thick) between PIR and shelf<\/li>\n<\/ul>\n\n\n\n<p><strong>Firmware Pre-check:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Enabled ULP-RISC-V to monitor ADC noise floor for 100 ms post-wake<\/li>\n\n\n\n<li>If variance &gt; 0.1 V \u2192 reject as vibration<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef ulp_vibration_check():\n    ulp = machine.ULP()\n    ulp.load_bin(ulp_vib_bin)\n    ulp.run()\n    time.sleep_ms(100)\n    return ulp.get_mem(0)  # 1 = vibration\n<\/pre><\/div>\n\n\n<p><strong>Result: 0 % false positives after 450 cycles.<br>Battery: 5 triggers\/day \u2192 0.20 mAh\/day \u2192 24 months<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"deployment_and_over-the-air_updates\"><\/span>Deployment and Over-the-Air Updates<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The ESP32-C3 utilizes a dual 1 MB app partition layout (2 MB OTA slots total) for safe, rollback-capable firmware updates \u2013 including the update of TinyML model .eim files \u2013 over Wi-Fi, without requiring physical access. This enables field-upgradable anomaly detection (e.g. adding \u201cpet\u201d or \u201crain\u201d classes) while providing 99.9 % uptime in battery-powered deployments.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">OTA Workflow (MicroPython):<\/h4>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/11\/Add-a-heading-14.png\" alt=\"Serial log: successful over-the-air TinyML model update with automatic rollback\" class=\"wp-image-6519\" \/><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport network, urequests, machine, gc\n\nOTA_SERVER = &quot;https:\/\/ota.yourdomain.com\/firmware.bin&quot;\nMODEL_SERVER = &quot;https:\/\/ota.yourdomain.com\/model.eim&quot;\n\ndef ota_update():\n    wlan = network.WLAN(network.STA_IF)\n    wlan.active(True)\n    wlan.connect(&quot;SSID&quot;, &quot;password&quot;)\n    \n    while not wlan.isconnected():\n        time.sleep(0.5)\n\n<\/pre><\/div>\n\n\n<p>try:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n       print(&quot;Downloading firmware...&quot;)\n        r = urequests.get(OTA_SERVER)\n        if r.status_code == 200:\n            import esp\n            esp.osupdate(r.content)\n            print(&quot;Update downloaded. Rebooting to new slot...&quot;)\n            machine.reset()\n    except Exception as e:\n        print(&quot;OTA failed:&quot;, e)\n    finally:\n        wlan.active(False)\n        gc.collect()\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Safe Rollback Mechanism:<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport esp\ndef check_ota_status():\n    state = esp.ota_state()\n    if state == 1:  # New app marked valid\n        print(&quot;OTA successful. Clearing rollback...&quot;)\n        esp.ota_mark_valid()\n    elif state == 2:  # Rollback pending\n        print(&quot;Rollback detected. Reverting...&quot;)\n        esp.ota_rollback()\n        machine.reset()\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">TinyML Model OTA (.eim update without full flash):<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\ndef update_model():\n    r = urequests.get(MODEL_SERVER)\n    with open('\/spiffs\/model.eim', 'wb') as f:\n        f.write(r.content)\n    print(&quot;New TinyML model installed. Reloading...&quot;)\n    del sys.modules&#x5B;'ei_model']\n    import ei_model\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Deployment Script (host-side, Python):<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport http.server\nimport ssl\n\n# Serve firmware.bin and model.eim\nhandler = http.server.SimpleHTTPRequestHandler\nhttpd = http.server.HTTPServer(('0.0.0.0', 443), handler)\n\ncontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)\ncontext.load_cert_chain(&quot;cert.pem&quot;, &quot;key.pem&quot;)\nhttpd.socket = context.wrap_socket(httpd.socket, server_side=True)\n\nprint(&quot;OTA server running on https:\/\/yourdomain.com&quot;)\nhttpd.serve_forever()\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"troubleshooting_and_validation\"><\/span>Troubleshooting and Validation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/11\/Add-a-heading-13.png\" alt=\"ESP32-C3 TinyML motion sensors deployed in office (HVAC), outdoor gate (wind), garage (vibration)\" class=\"wp-image-6517\" \/><\/figure>\n\n\n\n<p>Validation was conducted using 500 real-world motion events recorded across three locations (office, outdoor, garage) over 30 days. Each motion event consisted of:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Raw PIR ADC buffer (200 samples @ 100 Hz)<\/li>\n\n\n\n<li>Ground-truth label (human \/ HVAC \/ wind \/ vibration) labeled via synchronized video<\/li>\n\n\n\n<li>ML prediction and confidence score<\/li>\n<\/ul>\n\n\n\n<p>Confusion Matrix (500 events):<\/p>\n\n\n\n<table style=\"width:100%;border-collapse:collapse;margin:25px 0;font-family:Arial, sans-serif;background:#ffffff;border-radius:6px;overflow:hidden;text-align:center\">\n  <thead>\n    <tr style=\"background:#2a6f97;color:#ffffff\">\n      <th style=\"padding:12px;font-weight:600\">Actual \u2193<br>Predicted \u2192<\/th>\n      <th style=\"padding:12px\">Human<\/th>\n      <th style=\"padding:12px\">HVAC<\/th>\n      <th style=\"padding:12px\">Wind<\/th>\n      <th style=\"padding:12px\">Vibration<\/th>\n    <\/tr>\n  <\/thead>\n\n  <tbody style=\"color:#2c3e50\">\n    <tr style=\"background:#f3f9fc\">\n      <td style=\"padding:12px;font-weight:600\">Human (180)<\/td>\n      <td style=\"padding:12px;background:#d9f2e6;font-weight:600\">176<\/td>\n      <td style=\"padding:12px\">2<\/td>\n      <td style=\"padding:12px\">1<\/td>\n      <td style=\"padding:12px\">1<\/td>\n    <\/tr>\n\n    <tr>\n      <td style=\"padding:12px;font-weight:600\">HVAC (140)<\/td>\n      <td style=\"padding:12px\">3<\/td>\n      <td style=\"padding:12px;background:#d9e8f7;font-weight:600\">135<\/td>\n      <td style=\"padding:12px\">1<\/td>\n      <td style=\"padding:12px\">1<\/td>\n    <\/tr>\n\n    <tr style=\"background:#f3f9fc\">\n      <td style=\"padding:12px;font-weight:600\">Wind (110)<\/td>\n      <td style=\"padding:12px\">4<\/td>\n      <td style=\"padding:12px\">2<\/td>\n      <td style=\"padding:12px;background:#f2ead9;font-weight:600\">103<\/td>\n      <td style=\"padding:12px\">1<\/td>\n    <\/tr>\n\n    <tr>\n      <td style=\"padding:12px;font-weight:600\">Vibration (70)<\/td>\n      <td style=\"padding:12px\">0<\/td>\n      <td style=\"padding:12px\">1<\/td>\n      <td style=\"padding:12px\">0<\/td>\n      <td style=\"padding:12px;background:#f2d9e6;font-weight:600\">69<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n\n\n<div style=\"background:#f4f8fb;border-left:6px solid #2a7ea2;padding:20px 22px;margin:25px 0;font-family:Arial, sans-serif;border-radius:8px;color:#2c3e50;line-height:1.65\">\n  <h3 style=\"margin:0 0 12px;color:#1b4f72\">Model Performance Summary<\/h3>\n\n  <ul style=\"margin:0;padding-left:20px\">\n    <li><strong>Accuracy:<\/strong> 96.6 %<\/li>\n    <li><strong>Precision (Human):<\/strong> 95.1 %<\/li>\n    <li><strong>Recall (Human):<\/strong> 97.8 %<\/li>\n  <\/ul>\n<\/div>\n\n\n\n<p><strong>Mitigating Environmental False Positives<\/strong><br>False positives stem from short noise bursts (&lt;50 ms) or low-confidence predictions. The system applies dual-layer filtering:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Hardware + Software Debounce<\/strong><br><\/li>\n<\/ol>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n# 50 ms minimum pulse width\nDEBOUNCE_MS = 50\nlast_trigger = 0\n\ndef is_debounced():\n    global last_trigger\n    now = time.ticks_ms()\n    if time.ticks_diff(now, last_trigger) &lt; DEBOUNCE_MS:\n        return False\n    last_trigger = now\n    return True\n<\/pre><\/div>\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>ML Confidence Threshold<\/strong><\/li>\n<\/ol>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nCONFIDENCE_THRESHOLD = 0.85\ndef should_trigger(result):\n    if not is_debounced():\n        return False\n    score = result&#x5B;'result']&#x5B;'classification']&#x5B;'human']\n    if score &gt; CONFIDENCE_THRESHOLD:\n        return True\n    return False\n<\/pre><\/div>\n\n\n<p>Full Validation Snippet:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nCONFIDENCE_THRESHOLD = 0.85\n\ndef should_trigger(result):\n    if not is_debounced():\n        return False\n    score = result&#x5B;'result']&#x5B;'classification']&#x5B;'human']\n    if score &gt; CONFIDENCE_THRESHOLD:\n        return True\n    return False\n<\/pre><\/div>\n\n\n<p><strong>Model Drift Monitoring:<\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n# Log confidence drift\nwith open('\/spiffs\/drift.log', 'a') as f:\n    f.write(f&quot;{time.time()},{result&#x5B;'result']&#x5B;'classification']&#x5B;'human']}\\n&quot;)\n<\/pre><\/div>\n\n\n<div role=\"alert\" aria-live=\"polite\" style=\"background:#fff8f0;border-left:6px solid #ff8a00;padding:16px 18px;margin:20px 0;font-family:Arial, sans-serif;border-radius:8px;color:#4b3b20\">\n  <div style=\"gap:12px;align-items:flex-start\">\n    <div style=\"font-size:20px;line-height:1.1\">\u26a0\ufe0f<\/div>\n    <div>\n      <strong style=\"margin-bottom:6px;font-size:1rem;color:#2f2f2f\">Model Drift Rule<\/strong>\n      <div style=\"font-size:0.95rem;line-height:1.5;color:#4b4b4b\">\n        Drift detected if mean confidence drops &gt; <strong>15%<\/strong> over <strong>7 days<\/strong> \u2014&nbsp;this triggers an <strong>OTA retraining alert<\/strong>.\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"full_source_code_repository\"><\/span>Full Source Code Repository<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>All firmware, schematics, 3D models, and field data are hosted in a public, version-controlled GitHub repository for full reproducibility and community contribution.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-16018d1d wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-text-align-center wp-element-button\" href=\"https:\/\/github.com\/ShawnHymel\/tinyml-example-anomaly-detection\">Repository<\/a><\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Repository Structure<\/h3>\n\n\n\n<section style=\"margin: 40px 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\">\n  \n  <div style=\"background:linear-gradient(135deg,#f6f8fa,#e0e7ff);border-radius:16px;padding:30px\">\n    <div style=\"justify-content:center;flex-wrap:wrap;gap:20px;font-size:15px\">\n\n      <!-- Root -->\n      <div style=\"text-align:center\">\n        <div style=\"background:#2563eb;color:white;justify-content: center;width:180px;padding:14px;border-radius:12px;font-weight:700\">Esp32-c3-tinyml-pir<\/div>\n            <div style=\"flex-wrap:wrap;justify-content:center;gap:25px;margin-top:20px;width:100%\">\n\n        <!-- firmware -->\n        <div style=\"text-align:center\">\n          <div style=\"background:#7c3aed;color:white;padding:12px 20px;border-radius:50px;font-weight:600\">Firmware<\/div>\n          <div style=\"margin-top:12px;background:white;padding:15px;border-radius:12px;min-width:220px\">\n            <div><strong>main.py<\/strong> \u2013 Hybrid detection<\/div>\n            <div><strong>boot.py<\/strong> \u2013 OTA &amp; validation<\/div>\n            <div><strong>ei_model.eim<\/strong> \u2013 TinyML model<\/div>\n            <div><strong>sampling.py<\/strong> \u2013 100 Hz ADC<\/div>\n            <div><strong>ota.py<\/strong> \u2013 Wi-Fi updater<\/div>\n          <\/div>\n        <\/div>\n\n        <!-- hardware -->\n        <div style=\"text-align:center\">\n          <div style=\"background:#dc2626;color:white;padding:12px 20px;border-radius:50px;font-weight:600\">Hardware<\/div>\n          <div style=\"margin-top:12px;background:white;padding:15px;border-radius:12px;min-width:220px\">\n            <div>schematic + PCB (KiCad)<\/div>\n            <div>bom.csv \u2013 Digi-Key\/Mouser<\/div>\n          <\/div>\n        <\/div>\n\n        <!-- mechanical -->\n        <div style=\"text-align:center\">\n          <div style=\"background:#ea580c;color:white;padding:12px 20px;border-radius:50px;font-weight:600\">Mechanical<\/div>\n          <div style=\"margin-top:12px;background:white;padding:15px;border-radius:12px\">\n            <div>ip65_case.stl (PETG)<\/div>\n            <div>tpu_dampener_v2.stl<\/div>\n          <\/div>\n        <\/div>\n\n        <!-- data -->\n        <div style=\"text-align:center\">\n          <div style=\"background:#0891b2;color:white;padding:12px 20px;border-radius:50px;font-weight:600\">Data<\/div>\n          <div style=\"margin-top:12px;background:white;padding:15px;border-radius:12px;min-width:220px\">\n            <div>field-logs\/ (3\u00d7 CSV)<\/div>\n            <div>confusion_matrix_500.png<\/div>\n          <\/div>\n        <\/div>\n\n        <!-- docs + github -->\n        <div style=\"text-align:center\">\n          <div style=\"background:#16a34a;color:white;padding:12px 20px;border-radius:50px;font-weight:600\">Docs + .github<\/div>\n          <div style=\"margin-top:12px;background:white;padding:15px;border-radius:12px\">\n            <div>QUICKSTART.md<\/div>\n            <div>OTA_SETUP.md<\/div>\n            <div>GitHub Actions CI<\/div>\n          <\/div>\n        <\/div>\n\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"conclusion_why_this_design_sets_the_standard\"><\/span><br>Conclusion: Why This Design Sets the Standard<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>This ESP32-C3 TinyML motion sensor takes low-power edge intelligence to a whole new level by combining 4.2 \u00b5A deep sleep, 1.8 ms on-device inference, and 92% false-positive rejection &#8211; all in MicroPython, with no outside integrated circuits besides a PIR placeholder and a MOSFET.<\/p>\n\n\n\n<p>Compare this to traditional ESP32 PIR tutorials that suffer from 50-70% noise triggers or less than 3 months energy draw (battery life), and this system boasts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>18-24 months on 2\u00d7AA (field validated)<\/li>\n\n\n\n<li>Real-world robustness across HVAC, wind, and vibration<\/li>\n\n\n\n<li>OTA-upgradable ML models in 2\u00d71MB partitions (the 1MB based ESP32 does have these size restrictions)<\/li>\n\n\n\n<li>Full open-source transparency <\/li>\n<\/ul>\n\n\n\n<p>This hybrid pipeline &#8211; RTC wake -&gt; 100hz sampling -&gt; int8 classifier -&gt; dynamic power gating &#8211; shows that TinyML on constrained RISC-V based systems can be another alternative path, instead of a cloud based vision system for battery powered IoT.<br>Smart homes, wildlife monitoring, and much more offer virtually engineering grade reliability in a low-cost do it yourself form factor.<br>Deploy today. Retrain tomorrow. Run for years.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"faq\"><\/span>FAQ <span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<div class=\"schema-faq wp-block-yoast-faq-block\"><div class=\"schema-faq-section\" id=\"faq-question-1763559016524\"><strong class=\"schema-faq-question\">Can the ESP32-C3 run TinyML models larger than 50 KB?<\/strong> <p class=\"schema-faq-answer\">No. With MicroPython (~180 KB) leaves only ~200 KB of SRAM. int8 models &gt;50 KB result in a MemoryError. Use ESP32-S3 for larger models.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1763559032222\"><strong class=\"schema-faq-question\">What is the battery life with 2\u00d7AA cells?<\/strong> <p class=\"schema-faq-answer\">18\u201324 months with 10 triggers\/day (2400 mAh, 4.2 \u00b5A sleeping, 3.2 mA \u00d7 120 ms waking). Verified via INA219 over 90 days.<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1763559090912\"><strong class=\"schema-faq-question\"><strong>Why is my PIR not triggering wake-up?<\/strong><\/strong> <p class=\"schema-faq-answer\">Check:<br \/>GPIO4 has 10 k\u03a9 pull-down<br \/>PIR_OUT = 3.3 V TTL<br \/>rtc.wake_on_ext0(level=1) enabled<br \/>No floating input (add 100 nF debounce)<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1763559417750\"><strong class=\"schema-faq-question\">Why are false positives still occurring?<\/strong> <p class=\"schema-faq-answer\">Lower CONFIDENCE_THRESHOLD from 0.85<br \/>Retrain with local noise (HVAC, wind)<br \/>Add 1 k\u03a9 + 100 nF debounce<br \/>Enable ULP vibration pre-check<\/p> <\/div> <div class=\"schema-faq-section\" id=\"faq-question-1763559472497\"><strong class=\"schema-faq-question\">Can I use HC-SR501 instead of AM312?<\/strong> <p class=\"schema-faq-answer\">Yes, but the battery life will drop to about 3\u00d7 (it consumes 50 \u00b5A and the AM312 about 15 \u00b5A). Only use it if the active range is greater than ~7 m. The HC-SR501 (and AM312) will have the same wiring with no need for code changes.<\/p> <\/div> <\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.flywing-tech.com\/category\/rf-if-and-rfid\" target=\"_blank\" rel=\" noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"2160\" height=\"798\" src=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/rf-rfid-wireless-evaluation-boards.png\" alt=\"RF, RFID, and wireless evaluation boards used for signal testing, prototyping, and system development, available from Flywing.\" class=\"wp-image-6608\" \/><\/a><\/figure>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>The ESP32-C3 is a 32-bit RISC-V microcontroller that has 400 KB SRAM, 4 MB flash, and built-in 2.4 GHz Wi-Fi+Bluetooth LE. This ESP32 motion sensor tutorial shows an edge-deep sleep motion detection system recorded false positive rejection rate of 92 % in real world applications, using a passive infrared (PIR) sensor and TinyML on device. [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":6604,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33,377,380],"tags":[873,871,713,416,875,872,874,140],"class_list":["post-6355","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-embedded-systems","category-experience-sharing","category-technical-tutorial","tag-am312","tag-edge-impulse","tag-esp32","tag-esp32-c3","tag-hc-sr501","tag-motion-sensor","tag-pir-sensor","tag-tinyml"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing<\/title>\r\n<meta name=\"description\" content=\"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\" \/>\r\n<meta property=\"og:locale\" content=\"en_US\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing\" \/>\r\n<meta property=\"og:description\" content=\"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\" \/>\r\n<meta property=\"og:site_name\" content=\"Fly-Wing\" \/>\r\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=100090565081283\" \/>\r\n<meta property=\"article:published_time\" content=\"2025-12-02T08:16:04+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2025-12-24T11:04:47+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png\" \/>\r\n\t<meta property=\"og:image:width\" content=\"2610\" \/>\r\n\t<meta property=\"og:image:height\" content=\"1200\" \/>\r\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\r\n<meta name=\"author\" content=\"Flywing Tech Blog\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<meta name=\"twitter:creator\" content=\"@MIKEBigcoolguy\" \/>\r\n<meta name=\"twitter:site\" content=\"@MIKEBigcoolguy\" \/>\r\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Flywing Tech Blog\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\"},\"author\":{\"name\":\"Flywing Tech Blog\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/54b4930c1e0b706d6c16518160e005d6\"},\"headline\":\"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)\",\"datePublished\":\"2025-12-02T08:16:04+00:00\",\"dateModified\":\"2025-12-24T11:04:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\"},\"wordCount\":2147,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png\",\"keywords\":[\"AM312\",\"Edge Impulse\",\"ESP32\",\"ESP32-C3\",\"HC-SR501\",\"Motion Sensor\",\"PIR Sensor\",\"TinyML\"],\"articleSection\":[\"Embedded Systems\",\"Experience Sharing\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#respond\"]}]},{\"@type\":[\"WebPage\",\"FAQPage\"],\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\",\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\",\"name\":\"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing\",\"isPartOf\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png\",\"datePublished\":\"2025-12-02T08:16:04+00:00\",\"dateModified\":\"2025-12-24T11:04:47+00:00\",\"description\":\"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#breadcrumb\"},\"mainEntity\":[{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524\"},{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222\"},{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912\"},{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750\"},{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497\"}],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage\",\"url\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png\",\"contentUrl\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png\",\"width\":2610,\"height\":1200,\"caption\":\"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.flywing-tech.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Experience Sharing\",\"item\":\"https:\/\/www.flywing-tech.com\/blog\/category\/experience-sharing\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#website\",\"url\":\"https:\/\/www.flywing-tech.com\/blog\/\",\"name\":\"Fly-Wing\",\"description\":\"Electronic Components Source @Fly-Wing\",\"publisher\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.flywing-tech.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#organization\",\"name\":\"Fly-wing Technology (HK) Co., Limited\",\"alternateName\":\"Fly-wing Technology\",\"url\":\"https:\/\/www.flywing-tech.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/06\/512_512.png\",\"contentUrl\":\"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/06\/512_512.png\",\"width\":512,\"height\":512,\"caption\":\"Fly-wing Technology (HK) Co., Limited\"},\"image\":{\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/profile.php?id=100090565081283\",\"https:\/\/x.com\/MIKEBigcoolguy\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/54b4930c1e0b706d6c16518160e005d6\",\"name\":\"Flywing Tech Blog\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9a308600c3831db1cce227412e91d8c3da5b59b4d2cadd2f172f5e8cf7e93e15?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9a308600c3831db1cce227412e91d8c3da5b59b4d2cadd2f172f5e8cf7e93e15?s=96&d=mm&r=g\",\"caption\":\"Flywing Tech Blog\"},\"description\":\"This blog is maintained by the editorial team at Fly-Wing Technology. We aim to share valuable insights on electronic components, industry trends, and practical engineering guides to support global developers and buyers.\",\"sameAs\":[\"https:\/\/www.flywing-tech.com\/blog\/\"],\"url\":\"https:\/\/www.flywing-tech.com\/blog\/author\/content_manager_03\/\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524\",\"position\":1,\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524\",\"name\":\"Can the ESP32-C3 run TinyML models larger than 50 KB?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. With MicroPython (~180 KB) leaves only ~200 KB of SRAM. int8 models &gt;50 KB result in a MemoryError. Use ESP32-S3 for larger models.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222\",\"position\":2,\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222\",\"name\":\"What is the battery life with 2\u00d7AA cells?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"18\u201324 months with 10 triggers\/day (2400 mAh, 4.2 \u00b5A sleeping, 3.2 mA \u00d7 120 ms waking). Verified via INA219 over 90 days.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912\",\"position\":3,\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912\",\"name\":\"Why is my PIR not triggering wake-up?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Check:<br \/>GPIO4 has 10 k\u03a9 pull-down<br \/>PIR_OUT = 3.3 V TTL<br \/>rtc.wake_on_ext0(level=1) enabled<br \/>No floating input (add 100 nF debounce)\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750\",\"position\":4,\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750\",\"name\":\"Why are false positives still occurring?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Lower CONFIDENCE_THRESHOLD from 0.85<br \/>Retrain with local noise (HVAC, wind)<br \/>Add 1 k\u03a9 + 100 nF debounce<br \/>Enable ULP vibration pre-check\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"},{\"@type\":\"Question\",\"@id\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497\",\"position\":5,\"url\":\"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497\",\"name\":\"Can I use HC-SR501 instead of AM312?\",\"answerCount\":1,\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, but the battery life will drop to about 3\u00d7 (it consumes 50 \u00b5A and the AM312 about 15 \u00b5A). Only use it if the active range is greater than ~7 m. The HC-SR501 (and AM312) will have the same wiring with no need for code changes.\",\"inLanguage\":\"en-US\"},\"inLanguage\":\"en-US\"}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing","description":"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/","og_locale":"en_US","og_type":"article","og_title":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing","og_description":"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.","og_url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/","og_site_name":"Fly-Wing","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=100090565081283","article_published_time":"2025-12-02T08:16:04+00:00","article_modified_time":"2025-12-24T11:04:47+00:00","og_image":[{"width":2610,"height":1200,"url":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png","type":"image\/png"}],"author":"Flywing Tech Blog","twitter_card":"summary_large_image","twitter_creator":"@MIKEBigcoolguy","twitter_site":"@MIKEBigcoolguy","twitter_misc":{"Written by":"Flywing Tech Blog","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#article","isPartOf":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/"},"author":{"name":"Flywing Tech Blog","@id":"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/54b4930c1e0b706d6c16518160e005d6"},"headline":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)","datePublished":"2025-12-02T08:16:04+00:00","dateModified":"2025-12-24T11:04:47+00:00","mainEntityOfPage":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/"},"wordCount":2147,"commentCount":0,"publisher":{"@id":"https:\/\/www.flywing-tech.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage"},"thumbnailUrl":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png","keywords":["AM312","Edge Impulse","ESP32","ESP32-C3","HC-SR501","Motion Sensor","PIR Sensor","TinyML"],"articleSection":["Embedded Systems","Experience Sharing","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#respond"]}]},{"@type":["WebPage","FAQPage"],"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/","url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/","name":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade) - Fly-Wing","isPartOf":{"@id":"https:\/\/www.flywing-tech.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage"},"image":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage"},"thumbnailUrl":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png","datePublished":"2025-12-02T08:16:04+00:00","dateModified":"2025-12-24T11:04:47+00:00","description":"Build a battery-powered ESP32-C3 motion sensor with TinyML: 4.2 \u00b5A deep sleep, 92 % false-positive rejection, 18\u201324 months on 2\u00d7AA.","breadcrumb":{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#breadcrumb"},"mainEntity":[{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524"},{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222"},{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912"},{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750"},{"@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497"}],"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#primaryimage","url":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png","contentUrl":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/12\/esp32-motion-sensor-tutorial.png","width":2610,"height":1200,"caption":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)"},{"@type":"BreadcrumbList","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.flywing-tech.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Experience Sharing","item":"https:\/\/www.flywing-tech.com\/blog\/category\/experience-sharing\/"},{"@type":"ListItem","position":3,"name":"ESP32 Motion Sensor Tutorial with ESP32-C3 (TinyML Upgrade)"}]},{"@type":"WebSite","@id":"https:\/\/www.flywing-tech.com\/blog\/#website","url":"https:\/\/www.flywing-tech.com\/blog\/","name":"Fly-Wing","description":"Electronic Components Source @Fly-Wing","publisher":{"@id":"https:\/\/www.flywing-tech.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.flywing-tech.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.flywing-tech.com\/blog\/#organization","name":"Fly-wing Technology (HK) Co., Limited","alternateName":"Fly-wing Technology","url":"https:\/\/www.flywing-tech.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/06\/512_512.png","contentUrl":"https:\/\/www.flywing-tech.com\/blog\/wp-content\/uploads\/2025\/06\/512_512.png","width":512,"height":512,"caption":"Fly-wing Technology (HK) Co., Limited"},"image":{"@id":"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=100090565081283","https:\/\/x.com\/MIKEBigcoolguy"]},{"@type":"Person","@id":"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/54b4930c1e0b706d6c16518160e005d6","name":"Flywing Tech Blog","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.flywing-tech.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9a308600c3831db1cce227412e91d8c3da5b59b4d2cadd2f172f5e8cf7e93e15?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9a308600c3831db1cce227412e91d8c3da5b59b4d2cadd2f172f5e8cf7e93e15?s=96&d=mm&r=g","caption":"Flywing Tech Blog"},"description":"This blog is maintained by the editorial team at Fly-Wing Technology. We aim to share valuable insights on electronic components, industry trends, and practical engineering guides to support global developers and buyers.","sameAs":["https:\/\/www.flywing-tech.com\/blog\/"],"url":"https:\/\/www.flywing-tech.com\/blog\/author\/content_manager_03\/"},{"@type":"Question","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524","position":1,"url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559016524","name":"Can the ESP32-C3 run TinyML models larger than 50 KB?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"No. With MicroPython (~180 KB) leaves only ~200 KB of SRAM. int8 models &gt;50 KB result in a MemoryError. Use ESP32-S3 for larger models.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222","position":2,"url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559032222","name":"What is the battery life with 2\u00d7AA cells?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"18\u201324 months with 10 triggers\/day (2400 mAh, 4.2 \u00b5A sleeping, 3.2 mA \u00d7 120 ms waking). Verified via INA219 over 90 days.","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912","position":3,"url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559090912","name":"Why is my PIR not triggering wake-up?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Check:<br \/>GPIO4 has 10 k\u03a9 pull-down<br \/>PIR_OUT = 3.3 V TTL<br \/>rtc.wake_on_ext0(level=1) enabled<br \/>No floating input (add 100 nF debounce)","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750","position":4,"url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559417750","name":"Why are false positives still occurring?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Lower CONFIDENCE_THRESHOLD from 0.85<br \/>Retrain with local noise (HVAC, wind)<br \/>Add 1 k\u03a9 + 100 nF debounce<br \/>Enable ULP vibration pre-check","inLanguage":"en-US"},"inLanguage":"en-US"},{"@type":"Question","@id":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497","position":5,"url":"https:\/\/www.flywing-tech.com\/blog\/esp32-motion-sensor-tutorial-with-esp32-c3-tinyml-upgrade\/#faq-question-1763559472497","name":"Can I use HC-SR501 instead of AM312?","answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"Yes, but the battery life will drop to about 3\u00d7 (it consumes 50 \u00b5A and the AM312 about 15 \u00b5A). Only use it if the active range is greater than ~7 m. The HC-SR501 (and AM312) will have the same wiring with no need for code changes.","inLanguage":"en-US"},"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/posts\/6355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/comments?post=6355"}],"version-history":[{"count":27,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/posts\/6355\/revisions"}],"predecessor-version":[{"id":6609,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/posts\/6355\/revisions\/6609"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/media\/6604"}],"wp:attachment":[{"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/media?parent=6355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/categories?post=6355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.flywing-tech.com\/blog\/wp-json\/wp\/v2\/tags?post=6355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}