一,硬件使用:BG95-M3(4G模块),ESP32-U4WDH单片机。

二,升级文件下载平台示例:https://amazon.com/ota/4G/ble_env_1.0.3.bin

三,代码。

ota.c文件:

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "nvs_flash.h"
#include "esp_ota_ops.h"
#include "esp_system.h"
#include "ota.h"
#include "nvs_topic.h"
#include "bg95.h"

#define UART_PORT_NUM          UART_NUM_2
#define UART_TX_PIN            GPIO_NUM_21
#define UART_RX_PIN            GPIO_NUM_22
#define POWER_KEY_PIN          GPIO_NUM_5
#define POWER_PIN              GPIO_NUM_25
#define UART_RX_BUFFER_SIZE    1024*2
#define UART_TX_BUFFER_SIZE    512


#define OTA_URL "https://amazon.com/ota/4G/ble_env_1.0.3.bin"
#define OTA_BUF_SIZE 1024  //OTA更新缓冲区
#define TAG     "BG95"

char rx_buffer[UART_RX_BUFFER_SIZE];
char response_buffer[UART_RX_BUFFER_SIZE];

// 初始化NVS
void nvs_init(void){
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
}

// // 发送AT命令并等待响应
int32_t send_at_command(const char *cmd, const char *expected, uint32_t timeout_ms) {
    uart_flush(UART_PORT_NUM);
    uart_write_bytes(UART_PORT_NUM, cmd, strlen(cmd));
    ESP_LOGI(TAG, "Sent: %s", cmd);

    int received = 0;
    uint64_t timeout = esp_timer_get_time() + (timeout_ms * 1000);
    memset(response_buffer, 0, sizeof(response_buffer));
	
    while (esp_timer_get_time() < timeout) {
        vTaskDelay(pdMS_TO_TICKS(10));
        int len = uart_read_bytes(UART_PORT_NUM, rx_buffer, sizeof(rx_buffer) - 1, 10 / portTICK_PERIOD_MS);
        if (len > 0) {
            memcpy(response_buffer + received, rx_buffer, len);
            received += len;
            response_buffer[received] = '\0';
			ESP_LOGI(TAG,"RX:%s",response_buffer);
            if (strstr(response_buffer, expected)) {
                return 1;
            }
            if (strstr(response_buffer, "ERROR")) {
                return 0;
            }
        }
    }
    
    return 2;
	
}

void BG95_init_ATE(void)
{
	//AT测试
	send_at_command("AT\r\n","OK",100);
	//关闭回显
	send_at_command("ATE0\r\n","OK",100);
	//查询卡状态
	send_at_command("AT+CPIN?\r\n","OK",1000);
	//查询注册状态
	send_at_command("AT+CREG?\r\n","OK",1000);
	//完全模式
	send_at_command("AT+CFUN=1\r\n","OK",1000);
	//附着到网络
	send_at_command("AT+CGATT=1\r\n","OK",1000);
	//设置APN
	//send_at_command("AT+QICSGP=1,1,\"CMNET\","","",1\r\n","OK",1000);
	//激活PDP上下文
	send_at_command("AT+QIACT=1\r\n","OK",1000);
	send_at_command("AT+QIACT?\r\n","+QIACT:",1000);
}


// //https连接
uint8_t bg95_https(char *ota_url)
{
	//https验证
	send_at_command("AT+QHTTPCFG=\"contextid\",1\r\n","OK",2000);
	//send_at_command("AT+QHTTPCFG=\"responseheader\",1\r\n","OK",1000);//是否输出https响应体
	send_at_command("AT+QHTTPCFG=\"sslctxid\",1\r\n","OK",2000);
	send_at_command("AT+QSSLCFG=\"sslversion\",1,4\r\n","OK",2000);
	send_at_command("AT+QSSLCFG=\"ciphersuite\",1,0xFFFF\r\n","OK",2000);
	//设置URL
	char url_cmd[128];
    snprintf(url_cmd, sizeof(url_cmd), "AT+QHTTPURL=%d,80\r\n", strlen(ota_url));
	send_at_command(url_cmd,"CONNECT",2000);
    vTaskDelay(10 / portTICK_PERIOD_MS);
	send_at_command(ota_url,"OK",2000);
	//发起GET请求
	vTaskDelay(3000 / portTICK_PERIOD_MS);
	return 0;
}
	

// OTA更新任务
void ota_update_task(void) {
    ESP_LOGI(TAG, "Starting OTA update");
    int data_len = 0;
    uint8_t *ota_buffer = NULL;
    esp_ota_handle_t update_handle = 0;
    const esp_partition_t *update_partition = NULL;
	
    // 发起GET请求
	while(data_len==0){
    send_at_command("AT+QHTTPGET=1000\r\n", "+QHTTPGET:", 15000);
	// 获取数据长度
	if (strstr(response_buffer, "+QHTTPGET: 0,200,")) {
		char *last_comma = strrchr(response_buffer, ',');
		if (last_comma) {
			data_len = atoi(last_comma + 1);
			ESP_LOGI(TAG, "BIN_LEN (strrchr method): %d", data_len);
		    }
		}
        else if(strstr(response_buffer, "+CME")){
            bg95_send_at("+++");
            break;
        }
	}
	// 获取 OTA 更新分区
    update_partition = esp_ota_get_next_update_partition(NULL);
    if (update_partition == NULL) {
        ESP_LOGE(TAG, "Failed to get OTA update partition");
        return;
    }
	
    //准备OTA更新
    if (esp_ota_begin(update_partition, data_len, &update_handle) != ESP_OK) {
        ESP_LOGE(TAG, "OTA begin failed");
        goto ota_fail;
    }
    
	uart_write_bytes(UART_PORT_NUM, "AT+QHTTPREAD=150\r\n", strlen("AT+QHTTPREAD=150\r\n"));
	
	//清除串口缓冲区中的CONNECT\r\n\r\n
    char connect_ack[28];
    uart_read_bytes(UART_PORT_NUM, connect_ack, strlen("CONNECT\r\n\r\n"), 100);
    ESP_LOGI(TAG, "Skipped: %.*s", strlen("CONNECT\r\n\r\n"), connect_ack);
	
    //分配缓冲区
    ota_buffer = malloc(OTA_BUF_SIZE);
    if (!ota_buffer) {
        ESP_LOGE(TAG, "Malloc failed");
        goto ota_fail;
    }

    // 数据接收循环(分块校验)
    int total_read = 0;
    while (total_read < data_len) {
        int read_size = (data_len - total_read > OTA_BUF_SIZE) ? 
                       OTA_BUF_SIZE : data_len - total_read;
	
        int len = uart_read_bytes(UART_PORT_NUM, ota_buffer, read_size, 5000);
        if (len <0) {
            ESP_LOGE(TAG, "Read failed");
            break;
        }
		//写入分区
        if (esp_ota_write(update_handle, ota_buffer, len) != ESP_OK) {
            ESP_LOGE(TAG, "Write failed at %d bytes", total_read);
            break;
        }
        total_read += len;
        //设备进度上报 
    ESP_LOGI(TAG, "Progress: %d/%d", total_read, data_len);
    }
	//准备重启
    if (total_read == data_len) 
    {
        ESP_LOGI(TAG, "OTA download complete, validating...");
        set_triplet_flag(VERSION_DE,version);
        if (esp_ota_end(update_handle) == ESP_OK && 
            esp_ota_set_boot_partition(update_partition) == ESP_OK) {
            ESP_LOGI(TAG, "OTA success, rebooting...");
            esp_restart();
        }
    }
		
ota_fail:
    if (ota_buffer) free(ota_buffer);
    if (update_handle) esp_ota_abort(update_handle);
    vTaskDelete(NULL);
}



直接带入编写会出错,缺少"nvs_topic.h","bg95.h"中的一点内容。

报错一:bg95_send_at("+++");无定义,这个函数在"bg95.h"中:

/**
 * @description: 串口发送一个字节
 * @param {uint8_t} byte 要发送的字节
 * @return {*}
 */
void Uart_SendByte(uint8_t byte)
{
    uart_write_bytes(USER_UART_NUM,&byte,sizeof(byte));
}
/**
 * @description: 通过USART发送字符串
 * @param {char} *String 要发送的字符串
 * @return {*}
 */ 
void bg95_send_at(char *String)
{
	uint8_t i;
	for(i=0;String[i]!='\0';i++)
	{
		Uart_SendByte(String[i]);
	}
}

报错二:VERSION_DE和version未定义,在"nvs_topic.h"中:

#define VERSION_DE "VERSION"      //存储在nvs里面的标志位
extern char *version;            //设备版本号存储

//三元组及设备主题订阅数据

/**
  * @brief  设置设备初始化订阅主题
  * @param  void
  * @retval 成功返回0,失败返回-1
  */
int set_triplet_flag(char *tri,char *triplet)
{
    esp_err_t ret1;
    nvs_handle_t nvs_handle;
    //2、打开NVS
    ret1 = nvs_open("list", NVS_READWRITE, &nvs_handle);
    if(ret1 != ESP_OK)
    {
        ESP_LOGI("nvs", "nvs open failed");
        nvs_close(nvs_handle);
        return -1;
    }
    //3、写入数据
    ret1 = nvs_set_str(nvs_handle/*句柄*/, tri/*键名*/, triplet/*键值*/); //写一个数据(写到键中,调用nvs_commit后才会写入NVS)
    if(ret1 != ESP_OK)
    {
        ESP_LOGI("nvs", "nvs write init flag failed");
        nvs_close(nvs_handle);
        return -1;
    }
    //4、写完提交数据
    ret1 = nvs_commit(nvs_handle);
    if(ret1 != ESP_OK)
    {
        ESP_LOGI("nvs", "nvs commit failed");
        nvs_close(nvs_handle);
        return -1;
    }
    //4、关闭NVS
    nvs_close(nvs_handle);
    return 0;
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐