OTA远程升级固件

作者:追风剑情 发布于:2026-6-23 14:40 分类:物联网

一、新建分区配置文件

在项目根目录下创建 partitions.csv,内容如下:

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000,
otadata,  data, ota,     0xf000,  0x2000,
phy_init, data, phy,     0x11000, 0x1000,
ota_0,    app,  ota_0,   0x20000, 0x180000,
ota_1,    app,  ota_1,   0x1A0000,0x180000,
storage,  data, fat,     0x320000,0x200000,  

ota_0 与 ota_1 分区大小需要根据ESP32芯片的实际容量来分配。可用命令 python -m esptool --port COM3 flash_id 来查看ESP32芯片闪存容量。

二、修改 sdkconfig

字段修饰符
字段名 说明
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP =y 允许使用 HTTP 协议进行 OTA(而非强制 HTTPS)
CONFIG_PARTITION_TABLE_CUSTOM =y 启用自定义分区表
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME ="partitions.csv" 指定自定义分区表文件名
CONFIG_PARTITION_TABLE_FILENAME ="partitions.csv" 覆盖默认分区表文件名
CONFIG_ESPTOOLPY_FLASHSIZE ="8MB" 适配实际 8MB 闪存容量,为双 OTA 分区提供空间。注意:这里需要根据手上的ESP32开发板提供的实际闪存容量来配置。作者手上的 ESP32-WROOM-32 是 8MB 闪存。

三、OTA固件服务器

编译后的固件在: {项目文件夹}/build/项目文件名.bin。

可以在build目录下启动个http服务器供ESP32下载.bin文件。python -m http.server 8070

四、ESP32端OTA升级模块

ota_control.h

#ifndef OTA_CONTROL_H
#define OTA_CONTROL_H

/**
 * @brief 初始化 OTA 模块(创建互斥锁)
 * 应在系统启动时调用一次。
 */
void ota_control_init(void);

/**
 * @brief 执行 OTA 升级的主任务
 * @param pvParameter 未使用
 */
void ota_task(void *pvParameter);

#endif


ota_control.c

#include "ota_control.h"
#include "esp_log.h"
#include "esp_https_ota.h"
#include "esp_system.h"
#include "esp_crt_bundle.h"
#include <string.h>
#include "mqtt_control.h"
#include "freertos/semphr.h"

static const char *TAG = "OTA";
static bool ota_in_progress = false;
static SemaphoreHandle_t ota_mutex = NULL;

// 固件下载地址(根据实际情况修改)
#define OTA_URL "http://192.168.31.227:8070/LedFlash.bin"

// 用于嵌入证书(如果使用自定义证书)
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");

// 初始化 OTA 模块(创建互斥锁)
void ota_control_init(void) {
    if (ota_mutex == NULL) {
        ota_mutex = xSemaphoreCreateMutex();
        if (ota_mutex != NULL) {
            ESP_LOGI(TAG, "OTA mutex created successfully");
        } else {
            ESP_LOGE(TAG, "Failed to create OTA mutex");
        }
    } else {
        ESP_LOGW(TAG, "OTA mutex already exists");
    }
}

// OTA 升级任务
void ota_task(void *pvParameter) {
    // 检查互斥锁是否已初始化
    if (ota_mutex == NULL) {
        ESP_LOGE(TAG, "OTA mutex not initialized, call ota_control_init() first");
        vTaskDelete(NULL);
        return;
    }

    // 获取互斥锁,检查并设置标志
    if (xSemaphoreTake(ota_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        if (ota_in_progress) {
            xSemaphoreGive(ota_mutex);
            ESP_LOGW(TAG, "OTA already in progress, ignoring duplicate request.");
            vTaskDelete(NULL);
            return;
        }
        ota_in_progress = true;
        xSemaphoreGive(ota_mutex);
    } else {
        ESP_LOGE(TAG, "Failed to take OTA mutex");
        vTaskDelete(NULL);
        return;
    }

    ESP_LOGI(TAG, "Starting OTA upgrade from: %s", OTA_URL);
    mqtt_publish_status("{\"action\":\"OTA_START\"}");

    // 配置 HTTP 客户端
    esp_http_client_config_t http_config = {
        .url = OTA_URL,
        .crt_bundle_attach = esp_crt_bundle_attach,   // 使用内置证书包
        .timeout_ms = 10000,                          // 10秒超时
        .keep_alive_enable = true,
    };

    esp_https_ota_config_t ota_config = {
        .http_config = &http_config,
    };

    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "OTA Succeed! Rebooting...");
        mqtt_publish_status("{\"action\":\"OTA_SUCCESS\"}");
        // 成功时设备重启,无需重置标志
        esp_restart();
    } else {
        ESP_LOGE(TAG, "OTA Failed: %s", esp_err_to_name(ret));
        mqtt_publish_status("{\"action\":\"OTA_FAILED\"}");
        // 失败时重置标志,允许重试
        if (xSemaphoreTake(ota_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
            ota_in_progress = false;
            xSemaphoreGive(ota_mutex);
        }
    }

    // 任务结束(仅当失败时执行到此处)
    vTaskDelete(NULL);
}

标签: IoT

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号