基于ESP-IDF的4G网络OTA升级
·
一,硬件使用: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;
}
更多推荐



所有评论(0)