文章目录
前言
前提:需要先设置好分区表,下载固件到设定的分区空间中,此文升级不开启http文件服务器,使用webserver进行直接升级。
一、创建web_server.c
#include <include.h>
static const char *TAG = "example";
/* html转成文本*/
const char head_char[]="<!DOCTYPE html>\
<html>\
<head>\
<meta charset=\"UTF-8\">\
<style type=\"text/css\">\
li {list-style: none;}\
a {text-decoration: none;color: #444444;display: block;}\
a:hover {color: white;}\
ul {width: 1100px;height: 40px;background-color: #e7e7e2;margin: 40px auto;}\
ul > li {float: left;height: 40px;line-height: 40px;width:150px;text-align: center;}\
ul > li:hover {background-color: #403f40;}\
form {width: 800px;margin: 40px auto;}\
</style>\
</head>\
<body>\
<script type=\"text/javascript\"> \
function post(url, params) {\
var temp = document.createElement(\"form\");\
temp.action = url;\
temp.method = \"post\";\
temp.style.display = \"none\";\
for (var x in params) {\
var opt = document.createElement(\"textarea\");\
opt.name = x;\
opt.value = params[x];\
temp.appendChild(opt);\
}\
document.body.appendChild(temp);\
temp.submit();\
return temp;\
}\
</script>\
<ul>\
<li><a onclick=\"javascript:post('/index', {id:1,name:'index'})\">主页</a></li>\
<li><a onclick=\"javascript:post('/update', {id:6,name:'update'})\">固件升级</a></li>\
</ul>";
const char tail_char[]="</body></html>";
void index_send(httpd_req_t *req)
{
httpd_resp_send_chunk(req, head_char, strlen(head_char));
httpd_resp_send_chunk(req, tail_char, strlen(tail_char));
}
const char update_html_char[]="<form method='POST' action='/update' enctype='multipart/form-data' id='upload_form'>\
<input type='file' name='update' accept='.bin,.BIN'>\
<input type='submit' value='Update'>\
<div id='prg'>progress: 0%</div>\
</form>\
<script>\
$('form').submit(function(e){\
e.preventDefault();\
var form = $('#upload_form')[0];\
var data = new FormData(form);\
$.ajax({\
url: '/update',\
type: 'POST',\
data: data,\
contentType:false,\
processData:false,\
xhr: function() {\
var xhr = new window.XMLHttpRequest();\
xhr.upload.addEventListener('progress', function(evt) {\
if (evt.lengthComputable) {\
var per = evt.loaded / evt.total;\
$('#prg').html('progress: ' + Math.round(per*100) + '%');\
}\
}, false);\
return xhr;\
},\
success:function(d, s) {\
console.log('success!')\
},\
error: function (a, b, c) {\
}\
});\
});\
</script>;";
void update_send(httpd_req_t *req)
{
httpd_resp_send_chunk(req, head_char, strlen(head_char));
httpd_resp_send_chunk(req, update_html_char, strlen(update_html_char));
httpd_resp_send_chunk(req, tail_char, strlen(tail_char));
}
/* An HTTP GET handler */
static esp_err_t hello_get_handler(httpd_req_t *req)
{
char* buf;
size_t buf_len;
/* Read URL query string length and allocate memory for length + 1,
* extra byte for null termination */
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = malloc(buf_len);
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Found URL query => %s", buf);
char param[32];
/* Get value of expected key from query string */
if (httpd_query_key_value(buf, "query1", param, sizeof(param)) == ESP_OK) {
ESP_LOGI(TAG, "Found URL query parameter => query1=%s", param);
}
if (httpd_query_key_value(buf, "query3", param, sizeof(param)) == ESP_OK) {
ESP_LOGI(TAG, "Found URL query parameter => query3=%s", param);
}
if (httpd_query_key_value(buf, "query2", param, sizeof(param)) == ESP_OK) {
ESP_LOGI(TAG, "Found URL query parameter => query2=%s", param);
}
}
free(buf);
}
index_send(req);
return ESP_OK;
}
static const httpd_uri_t hello = {
.uri = "/",
.method = HTTP_GET,
.handler = hello_get_handler,
/* Let's pass response string in user
* context to demonstrate it's usage */
.user_ctx = NULL
};
/* An HTTP POST handler */
static esp_err_t index_post_handler(httpd_req_t *req)
{
char buf[1024];
int ret, remaining = req->content_len;
while (remaining > 0) {
/* Read the data for the request */
if ((ret = httpd_req_recv(req, buf,
MIN(remaining, sizeof(buf)))) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry receiving if timeout occurred */
continue;
}
return ESP_FAIL;
}
/* Send back the same data */
// httpd_resp_send_chunk(req, buf, ret);
remaining -= ret;
/* Log data received */
ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
ESP_LOGI(TAG, "%.*s", ret, buf);
ESP_LOGI(TAG, "====================================");
}
index_send(req);
// End response
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
static const httpd_uri_t index_post = {
.uri = "/index",
.method = HTTP_POST,
.handler = index_post_handler,
.user_ctx = NULL,
};
int strpos(char *str,int strlen)
{
int i=0;
for(i=0;i<strlen;i++)
{
if(str[i] == 0xe9)
{
if(str[i+1] == 0x06)
{
return i;
}
}
}
return -1;
}
void datancpy(char *drc,char *src,int len)
{
for(int i=0;i<len;i++)
{
drc[i] = src[i];
}
}
//异常处理,连接http服务器失败等异常
static void __attribute__((noreturn)) task_fatal_error()
{
ESP_LOGE("ota", "Exiting task due to fatal error...");
xEventGroupSetBits(tcp_mqtt_ntp_event_group,TASK_BIT_2);
// if((xEventGroupGetBits(tcp_mqtt_ntp_event_group) & 0x01))
// {
// led_mode_int = Led_mode_normal;//切换LED灯状态
// }
// else
// {
// led_mode_int = Led_mode_network_fail;//切换LED灯状态
// }
sys_sta.sys_ota_update_flag=0;
}
static void infinite_loop(void)
{
int i = 0;
ESP_LOGI("ota", "When a new firmware is available on the server, press the reset button to download it");
while(1) {
ESP_LOGI("ota", "Waiting for a new firmware ... %d", ++i);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
/* An HTTP POST handler */
static esp_err_t update_post_handler(httpd_req_t *req)
{
char buf[1024];
int ret, remaining = req->content_len;
int start_recv=0;
bool recv_head=0;
char * fir_addr;
int i=0;
esp_err_t err;
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
esp_ota_handle_t update_handle = 0;
const esp_partition_t *update_partition = NULL;
int binary_file_length = 0;
bool image_header_was_checked = false;
ESP_LOGI("ota", "Starting OTA example");
unsigned int par_addr=0;
while (remaining > 0) {
if(start_recv==0)
{
if ((ret = httpd_req_recv(req, buf, MIN(remaining, 149) )) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry receiving if timeout occurred */
continue;
}
return ESP_FAIL;
}
if(((fir_addr=strstr(buf,"name="))!=NULL) && ((fir_addr=strstr(buf,"filename="))!=NULL) )
{
start_recv=1;
// ESP_LOGI(TAG, "=========== RECEIVED DATA =1=========\n");
// for(i=0;i<ret;i++)
// {
// printf("%02x ",buf[i]);
// }
// ESP_LOGI(TAG, "====================================\n");
}
remaining -= ret;
}
else if(start_recv==1) //开始接收固件
{
const esp_partition_t *configured = esp_ota_get_boot_partition();
const esp_partition_t *running = esp_ota_get_running_partition();
if (configured != running) {
ESP_LOGI("ota", "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
configured->address, running->address);
}
ESP_LOGI("ota", "Running partition type %d subtype %d (offset 0x%08x)",running->type, running->subtype, running->address);
update_partition = esp_ota_get_next_update_partition(NULL);
assert(update_partition != NULL);
while(remaining > 0)
{
if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)) )) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry receiving if timeout occurred */
continue;
}
return ESP_FAIL;
}
sys_sta.sys_ota_update_flag=1;
remaining -= ret;
// ESP_LOGI(TAG, "=========== RECEIVED DATA =2====addr=%05X=====\n",par_addr);
// for(i=0;i<ret;i++)
// {
// printf("%02x ",buf[i]);
// }
// ESP_LOGI(TAG, "====================================\n");
if(recv_head == 0) //还未收到固件头
{
int recv_head_addr = strpos(buf,ret); //查找固件头首位置
if(recv_head_addr < 0)
{
httpd_resp_send_chunk(req, "升级失败 ", 13);
update_send(req);
return false;
}
else
{
char recv_head_buf[1024]={0};
datancpy(recv_head_buf,buf+recv_head_addr,(ret-recv_head_addr));
recv_head=1;
memset(buf,0,1024);
datancpy(buf,recv_head_buf,(ret-recv_head_addr));
ret =ret-recv_head_addr ;
}
}
int data_read = ret;
if (data_read < 0)
{
ESP_LOGE("ota", "Error: SSL data read error");
task_fatal_error();
}
else if (data_read > 0)
{
if (image_header_was_checked == false)
{
esp_app_desc_t new_app_info;
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t))
{
// check current version with downloading
memcpy(&new_app_info, &buf[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
ESP_LOGI("ota", "New firmware version: %s", new_app_info.version);
esp_app_desc_t running_app_info;
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK)
{
ESP_LOGI("ota", "Running firmware version: %s", running_app_info.version);
}
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
esp_app_desc_t invalid_app_info;
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK)
{
ESP_LOGI("ota", "Last invalid firmware version: %s", invalid_app_info.version);
}
// check current version with last invalid partition
if (last_invalid_app != NULL)
{
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0)
{
ESP_LOGI("ota", "New version is the same as invalid version.");
ESP_LOGI("ota", "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
ESP_LOGI("ota", "The firmware has been rolled back to the previous version.");
infinite_loop();
}
}
image_header_was_checked = true;
err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
if (err != ESP_OK)
{
ESP_LOGE("ota", "esp_ota_begin failed (%s)", esp_err_to_name(err));
esp_ota_abort(update_handle);
task_fatal_error();
}
}
else
{
ESP_LOGE("ota", "received package is not fit len");
esp_ota_abort(update_handle);
task_fatal_error();
}
}
debug_print("esp_ota_write\r\n");
err = esp_ota_write( update_handle, (const void *)buf, data_read);
if (err != ESP_OK) {
esp_ota_abort(update_handle);
task_fatal_error();
}
binary_file_length += data_read;
ESP_LOGI("ota", "Written image length %d", binary_file_length);
}
else if (data_read == 0)
{
if (errno == ECONNRESET || errno == ENOTCONN) {
ESP_LOGE("ota", "Connection closed, errno = %d", errno);
break;
}
if(remaining<=0)
{
debug_print("remaining =0 \n");
}
}
}
ESP_LOGI("ota", "Total Write binary data length: %d", binary_file_length);
err = esp_ota_end(update_handle);
if (err != ESP_OK) {
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
ESP_LOGE("ota", "Image validation failed, image is corrupted");
}
ESP_LOGE("ota", "esp_ota_end failed (%s)!", esp_err_to_name(err));
task_fatal_error();
}
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE("ota", "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
task_fatal_error();
}
// ESP_LOGI("ota", "Prepare to restart system!");
other_info.mode = NORMAL_MODE; //长按进入配置 模式
other_info_write(); //写入flash
ss_delay_ms(1000);
httpd_resp_send_chunk(req, " 升级 OK ", 13);
update_send(req);
ss_delay_ms(10);
esp_restart();
}
else
{
if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)) )) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry receiving if timeout occurred */
continue;
}
return ESP_FAIL;
}
ESP_LOGI(TAG, "=========== RECEIVED DATA =3=========\n");
for(i=0;i<ret;i++)
{
printf("%02x ",buf[i]);
}
ESP_LOGI(TAG, "====================================\n");
remaining -= ret;
}
}
start_recv=0;
update_send(req);
// End response
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
static const httpd_uri_t update_post = {
.uri = "/update",
.method = HTTP_POST,
.handler = update_post_handler,
.user_ctx = NULL,
};
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{
if (strcmp("/", req->uri) == 0) {
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/hello URI is not available");
/* Return ESP_OK to keep underlying socket open */
return ESP_OK;
} else if (strcmp("/index", req->uri) == 0) {
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/index URI is not available");
/* Return ESP_FAIL to close underlying socket */
return ESP_FAIL;
}else if (strcmp("/update", req->uri) == 0) {
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/update URI is not available");
/* Return ESP_FAIL to close underlying socket */
return ESP_FAIL;
}
/* For any other URI send 404 and close socket */
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message");
return ESP_FAIL;
}
#define HTTPD_BASE_CONFIG() { \
.task_priority = tskIDLE_PRIORITY+5, \
.stack_size = 10240, \
.core_id = tskNO_AFFINITY, \
.server_port = 80, \
.ctrl_port = 32768, \
.max_open_sockets = 7, \
.max_uri_handlers = 8, \
.max_resp_headers = 8, \
.backlog_conn = 5, \
.lru_purge_enable = false, \
.recv_wait_timeout = 5, \
.send_wait_timeout = 5, \
.global_user_ctx = NULL, \
.global_user_ctx_free_fn = NULL, \
.global_transport_ctx = NULL, \
.global_transport_ctx_free_fn = NULL, \
.open_fn = NULL, \
.close_fn = NULL, \
.uri_match_fn = NULL \
}
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_BASE_CONFIG();
config.lru_purge_enable = true;
// Start the httpd server
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
if (httpd_start(&server, &config) == ESP_OK) {
// Set URI handlers
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &hello);
httpd_register_uri_handler(server, &index_post);
httpd_register_uri_handler(server, &update_post);
return server;
}
ESP_LOGI(TAG, "Error starting server!");
return NULL;
}
static void stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
httpd_stop(server);
}
static void disconnect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server) {
ESP_LOGI(TAG, "Stopping webserver");
stop_webserver(*server);
*server = NULL;
}
}
static void connect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server == NULL) {
ESP_LOGI(TAG, "Starting webserver");
*server = start_webserver();
}
}
void init_webserver(void)
{
static httpd_handle_t server = NULL;
/* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
* and re-start it upon connection.
*/
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
/* Start the server for the first time */
server = start_webserver();
}
二、创建web_server.h
#ifndef WEB_SERVICE_H
#define WEB_SERVICE_H
extern void init_webserver(void);
#endif /* WEB_SERVICE_H */
三、wifi,nvs函数调用
void nvs_data_init()
{
//打印系统信息
ESP_LOGI("ESP32","[APP] Startup..\n");
ESP_LOGI("ESP32","[APP] Free memory: %d bytes\n", esp_get_free_heap_size());
ESP_LOGI("ESP32","[APP] IDF version: %s\n", esp_get_idf_version());
//初始化NVS,如果失败则先擦除再初始化
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
}
/*
* wifi_config.h
*
* Created on: 2022年8月24日
* Author: wst
*/
#include "include.h"
#include "esp_wifi.h"
#include "lwip/sys.h"
//#include "WiFi.h"
/*
*
* WIFI AP 模式
*
*/
#define EXAMPLE_MAX_STA_CONN 4 //最大连接数量
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
// debug_print(" wifi ap event_id = %d \n",event_id);
if (event_id == WIFI_EVENT_AP_STACONNECTED)
{
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI("wifi_AP", "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
}
else if (event_id == WIFI_EVENT_AP_STADISCONNECTED)
{
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI("wifi_AP", "station "MACSTR" leave, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
/*
* 函数:开启wifi ap模式
* 参数:
* ap_ssid:wifi 名
* ap_pwd :wifi 密码
* ap_channel: wifi通道 1-13
*/
void wifi_init_softap(char *softap_ssid,char *ap_pwd,unsigned char ap_channel)
{
esp_netif_ip_info_t netif_soft_ap_ip = { //注意:ip和gw必须一致,否则会出现频繁掉线
.ip = { .addr = ESP_IP4TOADDR(192,168,10,251) },
.gw = { .addr = ESP_IP4TOADDR(192,168,10,251) },
.netmask = { .addr = ESP_IP4TOADDR(255,255,255,0) },
};
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
//create customized access point
esp_netif_inherent_config_t esp_netif_config2 = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
esp_netif_config2.if_desc = "jala wifi ap";
esp_netif_config2.route_prio = 10;
esp_netif_config2.ip_info = &netif_soft_ap_ip;
esp_netif_create_wifi(WIFI_IF_AP, &esp_netif_config2);
esp_wifi_set_default_wifi_ap_handlers();
wifi_init_config_t cfg2 = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg2));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,
&wifi_event_handler,NULL,NULL));
wifi_config_t wifi_config = {
.ap = {
// .ssid = softap_ssid,
.ssid_len = strlen(softap_ssid),
.channel = ap_channel,
// .password = ap_pwd,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA2_PSK,
},
};
strcpy((char*)wifi_config.ap.ssid,softap_ssid);
strcpy((char*)wifi_config.ap.password,ap_pwd);
if (strlen(ap_pwd) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI("ap", "wifi_init_softap finished. SSID:%s password:%s channel:%d",
softap_ssid, ap_pwd, ap_channel);
}
void wifi_start()
{
wifi_init_softap("jala_wst",(char *)"",2);
}
四、main函数调用
void app_main(void)
{
wifi_start();//启动wifi
init_webserver();//开启http
nvs_data_init();
while(1)
{
vTaskDelay(1000/ portTICK_PERIOD_MS);
}
}
总结
web_server.c 和web_server.h生成文件后,加载到工程中直接调用就可以了