<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>固件 on Tech Snippets - 嵌入式技术笔记</title><link>https://tech-snippets.xyz/tags/%E5%9B%BA%E4%BB%B6/</link><description>Recent content in 固件 on Tech Snippets - 嵌入式技术笔记</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 03:00:00 +0800</lastBuildDate><atom:link href="https://tech-snippets.xyz/tags/%E5%9B%BA%E4%BB%B6/index.xml" rel="self" type="application/rss+xml"/><item><title>IoT 固件开发最佳实践 2026</title><link>https://tech-snippets.xyz/posts/iot-firmware-development/</link><pubDate>Mon, 13 Apr 2026 03:00:00 +0800</pubDate><guid>https://tech-snippets.xyz/posts/iot-firmware-development/</guid><description>IoT 固件开发完整指南，涵盖架构设计、OTA 升级、低功耗和安全通信</description><content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>本文基于 2026 年最新行业资料整理，涵盖 IoT firmware development 的核心概念、开发流程和实战技巧。</p>
<h2 id="iot-固件架构">IoT 固件架构</h2>
<h3 id="11-现代-iot-固件分层设计">1.1 现代 IoT 固件分层设计</h3>
<p>现代 IoT 固件采用模块化分层架构，每一层都有明确的职责和接口，便于维护、测试和升级。</p>
<figure>
<svg viewBox="0 0 700 320" xmlns="http://www.w3.org/2000/svg" style="max-width:100%;height:auto;background:#f8f9fa;border-radius:8px;padding:20px;">
  <defs>
    <style>
      .layer { fill: #e3f2fd; stroke: #1976d2; stroke-width: 2; rx: 4; }
      .layer-title { font-family: Arial; font-size: 13px; font-weight: bold; fill: #1976d2; }
      .layer-text { font-family: Arial; font-size: 11px; fill: #333; }
      .module { fill: #fff; stroke: #4a9eff; stroke-width: 1.5; rx: 3; }
      .module-text { font-family: Arial; font-size: 10px; fill: #333; }
    </style>
  </defs>
  <!-- 应用层 -->
  <rect x="50" y="20" width="600" height="50" class="layer"/>
  <text x="70" y="40" class="layer-title">应用层（Application）</text>
  <rect x="70" y="50" width="100" height="35" class="module"/>
  <text x="120" y="72" text-anchor="middle" class="module-text">MQTT 客户端</text>
  <rect x="180" y="50" width="100" height="35" class="module"/>
  <text x="230" y="72" text-anchor="middle" class="module-text">HTTP 客户端</text>
  <rect x="290" y="50" width="100" height="35" class="module"/>
  <text x="340" y="72" text-anchor="middle" class="module-text">CoAP 客户端</text>
  <!-- 中间件层 -->
  <rect x="50" y="85" width="600" height="50" class="layer" style="fill:#c8e6c9;stroke:#388e3c;"/>
  <text x="70" y="105" class="layer-title" style="fill:#388e3c;">中间件层（Middleware）</text>
  <rect x="70" y="115" width="100" height="35" class="module"/>
  <text x="120" y="130" text-anchor="middle" class="module-text">FreeRTOS</text>
  <text x="120" y="143" text-anchor="middle" class="module-text" font-size="9">(RTOS)</text>
  <rect x="180" y="115" width="100" height="35" class="module"/>
  <text x="230" y="130" text-anchor="middle" class="module-text">LwIP</text>
  <text x="230" y="143" text-anchor="middle" class="module-text" font-size="9">(网络栈)</text>
  <rect x="290" y="115" width="100" height="35" class="module"/>
  <text x="340" y="130" text-anchor="middle" class="module-text">mbedTLS</text>
  <text x="340" y="143" text-anchor="middle" class="module-text" font-size="9">(加密)</text>
  <!-- 驱动层 -->
  <rect x="50" y="150" width="600" height="50" class="layer" style="fill:#fff3e0;stroke:#f57c00;"/>
  <text x="70" y="170" class="layer-title" style="fill:#f57c00;">驱动层（Driver）</text>
  <rect x="70" y="180" width="70" height="30" class="module"/>
  <text x="105" y="200" text-anchor="middle" class="module-text">GPIO</text>
  <rect x="150" y="180" width="70" height="30" class="module"/>
  <text x="185" y="200" text-anchor="middle" class="module-text">SPI</text>
  <rect x="230" y="180" width="70" height="30" class="module"/>
  <text x="265" y="200" text-anchor="middle" class="module-text">I2C</text>
  <rect x="310" y="180" width="70" height="30" class="module"/>
  <text x="345" y="200" text-anchor="middle" class="module-text">UART</text>
  <rect x="390" y="180" width="70" height="30" class="module"/>
  <text x="425" y="200" text-anchor="middle" class="module-text">ADC</text>
  <rect x="470" y="180" width="70" height="30" class="module"/>
  <text x="505" y="200" text-anchor="middle" class="module-text">PWM</text>
  <!-- HAL 层 -->
  <rect x="50" y="215" width="600" height="45" class="layer" style="fill:#f3e5f5;stroke:#7b1fa2;"/>
  <text x="70" y="235" class="layer-title" style="fill:#7b1fa2;">硬件抽象层（HAL）</text>
  <text x="350" y="255" text-anchor="middle" class="layer-text">STM32 HAL / ESP-IDF / Nordic SDK</text>
  <!-- 硬件层 -->
  <rect x="50" y="270" width="600" height="40" class="layer" style="fill:#ffebee;stroke:#c62828;"/>
  <text x="70" y="290" class="layer-title" style="fill:#c62828;">硬件层（Hardware）</text>
  <text x="350" y="310" text-anchor="middle" class="layer-text">Cortex-M / ESP32 / nRF52 / RISC-V</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">现代 IoT 固件分层架构图</figcaption>
</figure>
<h3 id="12-各层职责详解">1.2 各层职责详解</h3>
<table>
<thead>
<tr>
<th>层级</th>
<th>职责</th>
<th>典型组件</th>
<th>更换频率</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>应用层</strong></td>
<td>业务逻辑、用户功能</td>
<td>MQTT 客户端、传感器数据处理</td>
<td>高（频繁迭代）</td>
</tr>
<tr>
<td><strong>中间件层</strong></td>
<td>通用服务、协议栈</td>
<td>FreeRTOS、LwIP、mbedTLS</td>
<td>中（版本升级）</td>
</tr>
<tr>
<td><strong>驱动层</strong></td>
<td>外设驱动、硬件抽象</td>
<td>GPIO、SPI、I2C、UART</td>
<td>低（稳定）</td>
</tr>
<tr>
<td><strong>HAL 层</strong></td>
<td>芯片厂商提供</td>
<td>STM32 HAL、ESP-IDF</td>
<td>低（跟随 SDK）</td>
</tr>
<tr>
<td><strong>硬件层</strong></td>
<td>物理芯片</td>
<td>Cortex-M4、ESP32</td>
<td>极低（产品周期）</td>
</tr>
</tbody>
</table>
<h3 id="13-模块化设计原则">1.3 模块化设计原则</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 1. 接口与实现分离（头文件定义接口）
</span></span></span><span class="line"><span class="cl"><span class="c1">// sensor_interface.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifndef SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp">#define SENSOR_INTERFACE_H
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">read</span><span class="p">)(</span><span class="kt">float</span> <span class="o">*</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">calibrate</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">sensor_ops_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 外部声明具体实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">dht22_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">sensor_ops_t</span> <span class="n">bmp280_ops</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2. 依赖注入（便于测试和替换）
</span></span></span><span class="line"><span class="cl"><span class="c1">// main.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">sensor_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 通过指针传入具体实现，而非硬编码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">sensor_ops_t</span> <span class="o">*</span><span class="n">sensor</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">dht22_ops</span><span class="p">;</span>  <span class="c1">// 可轻松切换为 bmp280_ops
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">float</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">sensor</span><span class="o">-&gt;</span><span class="nf">read</span><span class="p">(</span><span class="o">&amp;</span><span class="n">value</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">publish_data</span><span class="p">(</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 3. 配置与代码分离
</span></span></span><span class="line"><span class="cl"><span class="c1">// config.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define CONFIG_MQTT_BROKER      &#34;mqtt.example.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_PORT        1883
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_MQTT_KEEPALIVE   60
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_SSID        &#34;MyNetwork&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp">#define CONFIG_WIFI_PASSWORD    &#34;SecretPassword&#34;
</span></span></span></code></pre></div><h2 id="ota-升级firmware-over-the-air">OTA 升级（Firmware Over-The-Air）</h2>
<h3 id="21-ota-架构设计">2.1 OTA 架构设计</h3>
<figure>
<svg viewBox="0 0 700 400" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      .box { fill: #1a1a2e; stroke: #4a9eff; stroke-width: 2; rx: 6; }
      .text { fill: #e0e0e0; font-family: Arial, sans-serif; font-size: 13px; text-anchor: middle; }
      .label { fill: #4a9eff; font-family: Arial, sans-serif; font-size: 15px; font-weight: bold; }
      .arrow { stroke: #4a9eff; stroke-width: 2; fill: none; marker-end: url(#arrow); }
      .dashed { stroke: #e74c3c; stroke-width: 2; stroke-dasharray: 5,3; }
    </style>
    <marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a9eff"/>
    </marker>
  </defs>
  <!-- 云端 -->
  <rect class="box" x="50" y="20" width="200" height="100"/>
  <text class="label" x="150" y="55">OTA 服务器</text>
  <text class="text" x="150" y="85">固件存储 + 版本管理</text>
  <text class="text" x="150" y="105">签名服务</text>
  <!-- 通信 -->
  <line class="arrow" x1="250" y1="70" x2="350" y2="70"/>
  <text class="text" x="300" y="60" font-size="11">HTTPS 下载</text>
  <!-- 设备 -->
  <rect class="box" x="350" y="20" width="300" height="150"/>
  <text class="label" x="500" y="50">IoT 设备</text>
  <!-- Bootloader -->
  <rect class="box" x="370" y="65" width="120" height="40"/>
  <text class="text" x="430" y="90">Bootloader</text>
  <!-- A/B 分区 -->
  <rect class="box" x="510" y="65" width="60" height="40" style="fill:#2a3a2e;stroke:#388e3c;"/>
  <text class="text" x="540" y="85">A 区</text>
  <text class="text" x="540" y="100" fill="#388e3c">运行中</text>
  <rect class="box" x="580" y="65" width="60" height="40" style="fill:#3a2a2e;stroke:#e74c3c;"/>
  <text class="text" x="610" y="85">B 区</text>
  <text class="text" x="610" y="100" fill="#e74c3c">更新中</text>
  <!-- 箭头回云端 -->
  <line class="dashed" x1="350" y1="100" x2="250" y2="100"/>
  <text class="text" x="300" y="120" font-size="11">状态上报</text>
  <!-- 设备内部流程 -->
  <line class="arrow" x1="490" y1="85" x2="510" y2="85"/>
  <text class="text" x="500" y="75" font-size="10">验证</text>
</svg>
<figcaption style="text-align:center;color:#888;font-size:12px;margin-top:8px;">OTA 升级架构图</figcaption>
</figure>
<h3 id="22-ab-分区升级策略">2.2 A/B 分区升级策略</h3>
<p>A/B 分区（也称为双分区）是最安全的 OTA 升级方案：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>A 分区</th>
<th>B 分区</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>当前状态</strong></td>
<td>运行中（Active）</td>
<td>空闲/更新中（Inactive）</td>
</tr>
<tr>
<td><strong>升级时</strong></td>
<td>保持运行</td>
<td>写入新固件</td>
</tr>
<tr>
<td><strong>升级后</strong></td>
<td>变为备份</td>
<td>变为活动</td>
</tr>
<tr>
<td><strong>回滚</strong></td>
<td>可回滚到 A</td>
<td>可回滚到 B</td>
</tr>
</tbody>
</table>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Flash 分区表示例（ESP32 风格）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 假设 Flash 总大小 4MB
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>         <span class="c1">// 分区表魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>       <span class="c1">// 分区表版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">ota_seq</span><span class="p">;</span>       <span class="c1">// OTA 序列号（判断哪个分区是活动的）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">ota_data</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>  <span class="c1">// OTA 状态数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">crc</span><span class="p">;</span>           <span class="c1">// CRC32 校验
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">ota_data_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 分区布局
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x0000 - 0x1000:   引导加载程序（Bootloader）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1000 - 0x2000:   分区表
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x2000 - 0x3000:   OTA 数据（ota_data）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x3000 - 0x100000: 应用 A 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x100000 - 0x1FD000: 应用 B 区（约 988KB）
</span></span></span><span class="line"><span class="cl"><span class="c1">// 0x1FD000 - 0x200000: NVS（非易失存储）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 升级流程
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_IDLE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VERIFYING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_FLASHING</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_VALIDATED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">OTA_STATE_INVALID</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">ota_state_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">ota_state_t</span> <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// OTA 任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">ota_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 1. 检查是否有新版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nf">check_new_firmware_version</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_DOWNLOADING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="c1">// 2. 下载新固件到非活动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kt">uint32_t</span> <span class="n">inactive_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">download_firmware</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VERIFYING</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                
</span></span><span class="line"><span class="cl">                <span class="c1">// 3. 验证签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">if</span> <span class="p">(</span><span class="nf">verify_firmware_signature</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_VALIDATED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 4. 更新 OTA 数据，标记下次从新分区启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">inactive_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    
</span></span><span class="line"><span class="cl">                    <span class="c1">// 5. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_INVALID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_ota_state</span> <span class="o">=</span> <span class="n">OTA_STATE_IDLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">report_error</span><span class="p">(</span><span class="s">&#34;Download failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 6 小时检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">6</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="23-安全-ota-实现">2.3 安全 OTA 实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 完整的 OTA 升级流程（包含安全验证）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/sha256.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;mbedtls/pk.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span>             <span class="c1">// 魔数：0xABCDEF00
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>           <span class="c1">// 固件版本号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">timestamp</span><span class="p">;</span>         <span class="c1">// 编译时间戳
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">image_size</span><span class="p">;</span>        <span class="c1">// 固件大小
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">image_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>    <span class="c1">// SHA-256 哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span>  <span class="n">signature</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>     <span class="c1">// ECDSA 签名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="kt">firmware_header_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">perform_ota_update</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">firmware_url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">firmware_header_t</span> <span class="n">header</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">download_buffer</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">total_downloaded</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 下载并解析头部
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to download header&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 验证魔数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">magic</span> <span class="o">!=</span> <span class="mh">0xABCDEF00</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Invalid firmware magic&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 验证签名（使用固化在设备中的公钥）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">ret</span> <span class="o">=</span> <span class="nf">verify_signature_with_public_key</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="o">&amp;</span><span class="n">header</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">.</span><span class="n">signature</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">g_device_public_key</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Signature verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 擦除目标分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint32_t</span> <span class="n">target_partition</span> <span class="o">=</span> <span class="nf">get_inactive_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">spi_flash_erase_range</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 下载固件主体并写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Downloading firmware...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">header</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">&lt;</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint32_t</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="nf">MIN</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">download_buffer</span><span class="p">),</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span> <span class="o">-</span> <span class="n">total_downloaded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="nf">http_download_range</span><span class="p">(</span><span class="n">firmware_url</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Download failed at offset %lu&#34;</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 写入 Flash
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">spi_flash_write</span><span class="p">(</span><span class="n">target_partition</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span> <span class="n">download_buffer</span><span class="p">,</span> <span class="n">chunk_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">offset</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">total_downloaded</span> <span class="o">+=</span> <span class="n">chunk_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 报告进度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">progress</span> <span class="o">=</span> <span class="p">(</span><span class="n">total_downloaded</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span> <span class="o">/</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Progress: %d%%&#34;</span><span class="p">,</span> <span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">report_ota_progress</span><span class="p">(</span><span class="n">progress</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 验证下载的哈希
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">uint8_t</span> <span class="n">calculated_hash</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="nf">calculate_flash_hash</span><span class="p">(</span><span class="n">target_partition</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_size</span><span class="p">,</span> <span class="n">calculated_hash</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">memcmp</span><span class="p">(</span><span class="n">calculated_hash</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">image_hash</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;Hash verification failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 设置下次启动分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">set_next_boot_partition</span><span class="p">(</span><span class="n">target_partition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;OTA&#34;</span><span class="p">,</span> <span class="s">&#34;OTA update successful, rebooting...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重启
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span>  <span class="c1">// 等待日志输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_restart</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 回滚机制：如果新固件启动失败，自动回滚到旧版本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">app_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 检查启动原因
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">esp_reset_reason_t</span> <span class="n">reset_reason</span> <span class="o">=</span> <span class="nf">esp_reset_reason</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_PANIC</span> <span class="o">||</span> <span class="n">reset_reason</span> <span class="o">==</span> <span class="n">ESP_RST_INT_WDT</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 异常重启，可能是新固件有问题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;BOOT&#34;</span><span class="p">,</span> <span class="s">&#34;Abnormal reset detected, rolling back...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 回滚到之前的分区
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">rollback_to_previous_partition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 标记当前分区为有效（看门狗机制）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">mark_current_partition_valid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 正常运行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">run_application</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="低功耗设计">低功耗设计</h2>
<h3 id="31-功耗分析">3.1 功耗分析</h3>
<table>
<thead>
<tr>
<th>模式</th>
<th>ESP32 典型电流</th>
<th>STM32L4 典型电流</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>运行（全速）</strong></td>
<td>80-240mA</td>
<td>10-50mA</td>
<td>数据处理、通信</td>
</tr>
<tr>
<td><strong>运行（降频）</strong></td>
<td>40-100mA</td>
<td>5-20mA</td>
<td>常规任务</td>
</tr>
<tr>
<td><strong>睡眠（Light Sleep）</strong></td>
<td>0.8mA</td>
<td>5-10μA</td>
<td>等待事件</td>
</tr>
<tr>
<td><strong>深度睡眠（Deep Sleep）</strong></td>
<td>10-20μA</td>
<td>1-3μA</td>
<td>电池供电</td>
</tr>
<tr>
<td><strong>休眠（Hibernate）</strong></td>
<td>2.5μA</td>
<td>0.5μA</td>
<td>超长待机</td>
</tr>
</tbody>
</table>
<h3 id="32-动态电压频率调节dvfs">3.2 动态电压频率调节（DVFS）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// ESP32 DVFS 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_pm.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_dvfs</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">esp_pm_config_esp32_t</span> <span class="n">pm_config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">max_freq_mhz</span> <span class="o">=</span> <span class="mi">240</span><span class="p">,</span>      <span class="c1">// 最大频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">min_freq_mhz</span> <span class="o">=</span> <span class="mi">80</span><span class="p">,</span>       <span class="c1">// 最小频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">.</span><span class="n">light_sleep_enable</span> <span class="o">=</span> <span class="nb">true</span>  <span class="c1">// 启用 Light Sleep
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">esp_pm_configure</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pm_config</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 根据负载动态调整频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">adjust_frequency_based_on_load</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">is_system_idle</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">idle_count</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 空闲时间长，降低频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_80M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">idle_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 有任务，提升频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">esp_pm_cpu_freq_set</span><span class="p">(</span><span class="n">ESP_PM_CPU_FREQ_MAX_240M</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="33-深度睡眠实现">3.3 深度睡眠实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// STM32 深度睡眠配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stm32l4xx_hal.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">enter_deep_sleep</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">wake_time_ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 配置唤醒源（RTC、外部中断等）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">RTC_HandleTypeDef</span> <span class="n">hrtc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 RTC 唤醒定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">hrtc</span><span class="p">.</span><span class="n">Instance</span> <span class="o">=</span> <span class="n">RTC</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">HAL_RTCEx_SetWakeUpTimer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hrtc</span><span class="p">,</span> <span class="n">wake_time_ms</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">,</span> <span class="n">RTC_WAKEUPCLOCK_CK_SPRE_16BITS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 GPIO 为低功耗模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 所有未使用引脚设为模拟输入（最低功耗）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">is_pin_used</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pin</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">GPIO_MODE_ANALOG</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">Pull</span> <span class="o">=</span> <span class="n">GPIO_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nf">HAL_GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 关闭未使用外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_I2C1_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_ADC_CLK_DISABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 禁用 SysTick 中断（睡眠期间不需要）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">SysTick</span><span class="o">-&gt;</span><span class="n">CTRL</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">SysTick_CTRL_TICKINT_Msk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 进入 STOP2 模式（最低功耗，保留 SRAM 内容）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">HAL_PWREx_EnterSTOP2Mode</span><span class="p">(</span><span class="n">PWR_STOPENTRY_LOWPOWER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 唤醒后从这里继续执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 6. 恢复系统时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">SystemClock_Config</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 恢复外设时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_RCC_USART2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__HAL_RCC_SPI2_CLK_ENABLE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 8. 重新配置 GPIO
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">gpio_init_all</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 9. 清除唤醒标志
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">__HAL_PWR_CLEAR_FLAG</span><span class="p">(</span><span class="n">PWR_FLAG_WU</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 电池供电设备的功耗优化任务
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">power_management_task</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">pvParameters</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">battery_voltage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 读取电池电压
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">battery_voltage</span> <span class="o">=</span> <span class="nf">read_battery_voltage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3300</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.3V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 进入省电模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_power_saving_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">battery_voltage</span> <span class="o">&lt;</span> <span class="mi">3000</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 低于 3.0V
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 紧急模式：只保留基本功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_emergency_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 正常模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">enter_normal_mode</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 每 5 分钟检查一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">vTaskDelay</span><span class="p">(</span><span class="nf">pdMS_TO_TICKS</span><span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="34-无线通信功耗优化">3.4 无线通信功耗优化</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// WiFi 功耗优化（ESP32）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;esp_wifi.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_wifi_power_save</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 启用 WiFi 节能模式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">WIFI_PS_MAX_MODEM</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置 DTIM 监听间隔（越大越省电，但延迟越高）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// DTIM = 3 表示每 3 个信标帧唤醒一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">wifi_ps_type_t</span> <span class="n">ps_type</span> <span class="o">=</span> <span class="n">WIFI_PS_MAX_MODEM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">esp_wifi_set_ps</span><span class="p">(</span><span class="n">ps_type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// BLE 广播功耗优化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;nimble/nimble_port.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">configure_ble_advertising</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 配置广播参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">ble_gap_adv_params</span> <span class="n">adv_params</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">adv_params</span><span class="p">.</span><span class="n">conn_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_CONN_MODE_NON</span><span class="p">;</span>  <span class="c1">// 不可连接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">disc_mode</span> <span class="o">=</span> <span class="n">BLE_GAP_DISC_MODE_GEN</span><span class="p">;</span>  <span class="c1">// 可发现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_min</span> <span class="o">=</span> <span class="mh">0x0640</span><span class="p">;</span>  <span class="c1">// 1 秒（0x0640 = 1000ms）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">adv_params</span><span class="p">.</span><span class="n">itvl_max</span> <span class="o">=</span> <span class="mh">0x0C80</span><span class="p">;</span>  <span class="c1">// 2 秒
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    
</span></span><span class="line"><span class="cl">    <span class="c1">// 较长的广播间隔 = 更低的功耗
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">ble_gap_adv_start</span><span class="p">(</span><span class="n">OWN_ADDR_TYPE_PUBLIC</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">BLE_HS_FOREVER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="o">&amp;</span><span class="n">adv_params</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="无线通信协议对比">无线通信协议对比</h2>
<h3 id="41-主流-iot-无线协议">4.1 主流 IoT 无线协议</h3>
<table>
<thead>
<tr>
<th>协议</th>
<th>频率</th>
<th>范围</th>
<th>速率</th>
<th>功耗</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>WiFi 4 (802.11n)</strong></td>
<td>2.4/5GHz</td>
<td>50m</td>
<td>150Mbps</td>
<td>高</td>
<td>视频、大数据</td>
</tr>
<tr>
<td><strong>WiFi 6 (802.11ax)</strong></td>
<td>2.4/5/6GHz</td>
<td>50m</td>
<td>600Mbps</td>
<td>中高</td>
<td>高密度 IoT</td>
</tr>
<tr>
<td><strong>Bluetooth 5</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>2Mbps</td>
<td>低</td>
<td>可穿戴、信标</td>
</tr>
<tr>
<td><strong>BLE Mesh</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>1Mbps</td>
<td>低</td>
<td>智能家居</td>
</tr>
<tr>
<td><strong>Zigbee</strong></td>
<td>2.4GHz</td>
<td>10-100m</td>
<td>250kbps</td>
<td>低</td>
<td>工业、家居</td>
</tr>
<tr>
<td><strong>Thread</strong></td>
<td>2.4GHz</td>
<td>多跳</td>
<td>250kbps</td>
<td>低</td>
<td>Matter 生态</td>
</tr>
<tr>
<td><strong>LoRaWAN</strong></td>
<td>Sub-1GHz</td>
<td>2-15km</td>
<td>0.3-50kbps</td>
<td>极低</td>
<td>广域 IoT</td>
</tr>
<tr>
<td><strong>NB-IoT</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>20-250kbps</td>
<td>低</td>
<td>蜂窝 IoT</td>
</tr>
<tr>
<td><strong>LTE-M</strong></td>
<td>授权频段</td>
<td>1-10km</td>
<td>1Mbps</td>
<td>中</td>
<td>移动 IoT</td>
</tr>
</tbody>
</table>
<h3 id="42-mqtt-协议详解">4.2 MQTT 协议详解</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// MQTT 客户端实现（使用 Paho MQTT）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;MQTTClient.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">broker_url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">client_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">publish_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">subscribe_topic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">mqtt_config_t</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 全局 MQTT 客户端
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">MQTTClient</span> <span class="n">client</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">mqtt_config_t</span> <span class="n">config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">broker_url</span> <span class="o">=</span> <span class="s">&#34;mqtt.example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">port</span> <span class="o">=</span> <span class="mi">1883</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">client_id</span> <span class="o">=</span> <span class="s">&#34;iot_device_001&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="s">&#34;device_user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">&#34;device_pass&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">publish_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/data&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">subscribe_topic</span> <span class="o">=</span> <span class="s">&#34;devices/001/command&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">keepalive</span> <span class="o">=</span> <span class="mi">60</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">clean_session</span> <span class="o">=</span> <span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connected</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connected to broker&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 订阅命令主题
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_subscribe</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">subscribe_topic</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 发布上线消息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">&#34;devices/001/status&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="nf">strlen</span><span class="p">(</span><span class="s">&#34;online&#34;</span><span class="p">),</span> <span class="s">&#34;online&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 消息到达回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_message_arrived</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">topicName</span><span class="p">,</span> <span class="kt">int</span> <span class="n">topicLen</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">MQTTClient_message</span> <span class="o">*</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGI</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Message arrived on topic %s&#34;</span><span class="p">,</span> <span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理命令
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="o">-&gt;</span><span class="n">payload</span><span class="p">,</span> <span class="n">message</span><span class="o">-&gt;</span><span class="n">payloadlen</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_freeMessage</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_free</span><span class="p">(</span><span class="n">topicName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 连接丢失回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">on_connection_lost</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cause</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ESP_LOGW</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Connection lost: %s&#34;</span><span class="p">,</span> <span class="n">cause</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 自动重连
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">MQTTClient_reconnect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="mi">5000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// MQTT 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">broker_url</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">client_id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">MQTTCLIENT_PERSISTENCE_NONE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_callbacks</span> <span class="n">callbacks</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connect</span> <span class="o">=</span> <span class="n">on_connected</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_message</span> <span class="o">=</span> <span class="n">on_message_arrived</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">on_connection_lost</span> <span class="o">=</span> <span class="n">on_connection_lost</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">MQTTClient_setCallbacks</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callbacks</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_connectOptions</span> <span class="n">conn_opts</span> <span class="o">=</span> <span class="n">MQTTClient_connectOptions_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">keepAliveInterval</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">keepalive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">cleansession</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">clean_session</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">username</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="n">config</span><span class="p">.</span><span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">conn_opts</span><span class="p">.</span><span class="n">automaticReconnect</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="nf">MQTTClient_connect</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">conn_opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">!=</span> <span class="n">MQTTCLIENT_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ESP_LOGE</span><span class="p">(</span><span class="s">&#34;MQTT&#34;</span><span class="p">,</span> <span class="s">&#34;Failed to connect: %d&#34;</span><span class="p">,</span> <span class="n">rc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 发布数据
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">mqtt_publish_data</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MQTTClient_message</span> <span class="n">pubmsg</span> <span class="o">=</span> <span class="n">MQTTClient_message_initializer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payload</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">payloadlen</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pubmsg</span><span class="p">.</span><span class="n">qos</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// QoS 1：至少送达一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">pubmsg</span><span class="p">.</span><span class="n">retained</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">MQTTClient_publish</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">publish_topic</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                              <span class="n">len</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="43-coap-协议轻量级替代">4.3 CoAP 协议（轻量级替代）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// CoAP 客户端实现（使用 libcoap）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;coap3/coap.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 资源定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resources</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">NULL</span>  <span class="c1">// 资源列表结束标记
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 请求处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">helloworld_get</span><span class="p">(</span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="k">struct</span> <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">resource</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_session_t</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">pdu</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_binary_t</span> <span class="o">*</span><span class="n">token</span><span class="p">,</span> <span class="kt">coap_string_t</span> <span class="o">*</span><span class="n">query</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">coap_pdu_t</span> <span class="o">*</span><span class="n">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="s">&#34;Hello, World!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_data</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">payload</span><span class="p">),</span> <span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CoAP 服务器初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">coap_context_t</span> <span class="o">*</span><span class="nf">coap_server_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_context_t</span> <span class="o">*</span><span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">coap_address_t</span> <span class="n">listen_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 绑定到 5683 端口（CoAP 标准端口）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">coap_address_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">INADDR_ANY</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">listen_addr</span><span class="p">.</span><span class="n">addr</span><span class="p">.</span><span class="n">sin</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="nf">htons</span><span class="p">(</span><span class="mi">5683</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">ctx</span> <span class="o">=</span> <span class="nf">coap_new_context</span><span class="p">(</span><span class="o">&amp;</span><span class="n">listen_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 注册资源
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kt">coap_resource_t</span> <span class="o">*</span><span class="n">r</span> <span class="o">=</span> <span class="nf">coap_resource_init</span><span class="p">(</span><span class="nf">coap_make_str_const</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">),</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_register_handler</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">COAP_REQUEST_GET</span><span class="p">,</span> <span class="n">helloworld_get</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">coap_add_resource</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>IoT 固件开发的核心要点：</p>
<ol>
<li><strong>分层架构</strong>：清晰的模块划分，便于维护和升级</li>
<li><strong>OTA 升级</strong>：A/B 分区、安全验证、回滚机制</li>
<li><strong>低功耗设计</strong>：DVFS、睡眠模式、无线优化</li>
<li><strong>无线通信</strong>：根据场景选择合适的协议（WiFi/BLE/LoRa/NB-IoT）</li>
<li><strong>安全性</strong>：安全启动、加密通信、安全存储</li>
</ol>
<p>掌握这些技能，你可以开发出可靠、安全、低功耗的 IoT 设备！</p>
<hr>
<p><em>本文基于 ESP32、STM32、nRF52 等平台实际项目经验整理，结合 2026 年最新 IoT 技术趋势编写。</em></p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>ESP-IDF Programming Guide - <a href="https://docs.espressif.com/">https://docs.espressif.com/</a></li>
<li>STM32CubeMX User Manual</li>
<li>Nordic nRF5 SDK Documentation</li>
<li>MQTT Version 5.0 Specification - OASIS Standard</li>
<li>The IoT Hacker&rsquo;s Handbook (2025)</li>
<li>Building Secure IoT Devices (2026)</li>
</ol>
<h2 id="参考资料-1">参考资料</h2>
<ol>
<li><a href="https://www.meegle.com/en_us/topics/firmware-development/firmware-development-for-iot">Firmware Development For IoT - Meegle</a></li>
<li><a href="https://webbylab.com/blog/firmware-development-iot/">IoT Firmware Development: Process, Challenges &amp; Best Practices</a></li>
<li><a href="https://rapidlab.io/firmware-development/">Embedded firmware development for IoT devices - RapidLab</a></li>
</ol>
<hr>
<p><em>本文基于网络公开资料整理，结合嵌入式开发实践经验编写。</em></p>
]]></content:encoded></item></channel></rss>