【ESP32日记003】EPS32S3上使用自身WiFi热点,用于OTA升级的代码实现

最近客户突然新增一个本地OTA需求,要求能实现让用户通过手机按需对设备进行程序更新,经评估后,考虑直接使用芯片自带的WiFi功能,以实现上电后,5分钟内开启WiFi热点,用于进行OTA升级,具体实现代码如下:
1、WiFi热点配置以及简易网页效果优化(居于esp32 ota demo修改,手机页面效果见下方图片),先贴代码段
wifi_conf.h:

#ifndef _WIFI_CONF_H_
#define _WIFI_CONF_H_

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>

const char* AP_SSID = "FO1_PLUS";  //热点名称
const char* host = "esp32";
const char* ssid = "ssid";       //用户名
const char* password = "password";    //密码

static char web_ack_buf[40];

WebServer server(80);

/*
 * Login page
 */

const char* loginIndex =
 "<!DOCTYPE html> "
 "<html>"
    "<head>"
		"<title>FO1_PLUS Login</title>"
		"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> "
	"</head>"
    "<body>"
        "<form name='loginForm'>"
            "<div></div>"
            "<table width='50%' bgcolor='A09F9F' align='center'>"
                "<tr>"
                    "<td colspan=2>"
                        "<center><font size=4><b>FO1 PLUS Login Page</b></font></center>"
                        "<br>"
                    "</td>"
                    "<br>"
                    "<br>"
                "</tr>"
                "<tr>"
                    "<td>Username:</td>"
                    "<td><input type='text' size=15 name='userid'><br></td>"
                    "<br>"
                    "<br>"
                "</tr>"
                "<tr>"
                    "<td>Password:</td>"
                    "<td><input type='Password' size=15 name='pwd'><br></td>"
                    "<br>"
                    "<br>"
                "</tr>"
                "<tr>"
                    "<td colspan=2>"
                        "<center><input type='submit' size=15 onclick='check(this.form)' value='Login'></center>"
                        "<br>"
                    "</td>"
                    "<br>"
                    "<br>"   
                "</tr>"
            "</table>"
        "</form>"
    "</body>"
"</html>"
"<script>"
    "function check(form)"
    "{"
        "if(form.userid.value=='admin' && form.pwd.value=='admin')"
        "{"
            "window.open('/serverIndex')"
        "}"
        "else"
        "{"
            " alert('Error Password or Username')/*displays error message*/"
        "}"
    "}"
"</script>";

/*
 * Server Index Page
 */

const char* serverIndex =
"<!DOCTYPE html> "
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<html>"
    "<head>"
		"<title>FO1_PLUS OTA</title>"
		"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> "
	"</head>"
    "<body>"
        "<form method='POST' action='/update' enctype='multipart/form-data' id='upload_form'>"
            "<div> </div>"
            "<table width='50%' bgcolor='A09F9F' align='center'>"
                "<tr>"
                    "<td colspan=2>"
                        "<center><font size=4><b>FO1 PLUS OTA</b></font></center>"
                        "<br>"
                    "</td>"
                    "<br>"
                    "<br>"
                "</tr>"
                "<tr>"
                    "<td colspan=2>"
                        "<center><input type='file' size=15 name='update'></center>"
                        "<br>"
                    "</td>"
                    "<br>"
                    "<br>"
                "</tr>"
                "<tr>"
                    "<td colspan=2>"
                        "<center><input type='submit' size=15 value='Update'></center>"
                        "<br>"
                    "</td>"
                    "<br>"
                    "<br>"   
                "</tr>"
            "</table>"
            "<div id='prg'>progress: 0% 请先选择文件,然后点击“Update”上传更新程序。</div>"
            "<div id='prg'><b>注意:</b></div>"
            "<div id='prg'>1、升级过程中请勿操作水龙头!</div>"
            "<div id='prg'>2、升级过程中请勿操作当前页面,勿息屏,等待文件传输完成!</div>"
        "</form>"
    "</body>"
"</html>"
 "<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>";


#endif /* _WIFI_CONF_H_ */

2、功能实现

2.1、WiFi初始化,开启热点

//wifi热点初始化
void WiFi_OTA_AP_init(){
    //WiFi.begin();
    //Serial.println("wifi begin");
    WiFi.mode(WIFI_AP); //WiFi配置为AP热点模式
    WiFi_data.status = 1;   //模式状态   0:关闭WiFi   1:AP热点    2:STA终端  3:AP+STA   4:未知
    WiFi_data.flag_wifi_symbol = 1;
    WiFi_data.flag_ap = 0;
    WiFi_data.flag_mdns = 0;
    WiFi_data.flag_server = 0;
    #if PRINT_U0 != 0   //#endif
    Serial.println("WiFi enble by AP");
    #endif
}

2.2、关闭热点
 

//关闭WiFi AP OTA
void closeWiFi() {
  WiFi.softAPdisconnect(true);
  WiFi.mode(WIFI_OFF); 
}

2.3、WiFi热点参数设置与调试代码

//设置wifi热点
uint8_t WiFi_OTA_AP_set(const char* apssid){
    if (WiFi.softAP(apssid, ""))
    {
        //热点开启成功*****************
        //WiFi_data.flag_ap = 1;   //AP模式下,配网状态   0: 未配置成功   1:配网成功
        IPAddress myIP = WiFi.softAPIP();
        //打印相关信息
        #if PRINT_U0 != 0   //#endif
        Serial.print("Soft-AP IP address = ");
        Serial.println(myIP);
        Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
        Serial.println("waiting ...");
        #endif
        return 1;
    }else
    {
        return 0;
    }
    
}

2.4、开启mdns服务

//MDNS服务设置
uint8_t WiFi_MDNS_set(const char* mhost){
    /* mdns服务未开启*/
    if (!MDNS.begin(mhost))
    { //http://esp32.local
        #if PRINT_U0 != 0   //#endif
        Serial.println("MDNS begin Error! Try again.");
        #endif
        return 0;
    }
    else
    {
        #if PRINT_U0 != 0   //#endif
        Serial.println("mDNS responder started");
        #endif
        return 1;
    }
}

2.5、开启WiFi server,用于对网页操作进行处理,OTA实现的主要代码段:

//Web Server 任务配置
void Server_OTA_set(){
    /*return index page which is stored in serverIndex */
    server.on(
        "/", HTTP_GET, []()
        {
            server.sendHeader("Connection", "close");
            server.send(200, "text/html", loginIndex);
        });
    server.on(
        "/serverIndex", HTTP_GET, []()
        {
            server.sendHeader("Connection", "close");
            server.send(200, "text/html", serverIndex);
        });

    /*handling uploading firmware file */
    server.on(
        "/update", HTTP_POST, []()
        {
            server.sendHeader("Connection", "close");
            //server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
            if (Update.hasError())
            {
                //升级出错
                OTA_data.status = 3;
                OTA_data.cur_ota_time = 10;

                server.send(200, "text/plain", "升级失败!");
            }
            else
            {
                //升级成功
                OTA_data.status = 2;
                OTA_data.cur_ota_time = 10;

                server.send(200, "text/plain", "升级成功,请关闭页面,等待水龙头重新启动!");
            }

            //ESP.restart();
        },
        []()
        {
            HTTPUpload &upload = server.upload();
            if (upload.status == UPLOAD_FILE_START)
            {
                //开始升级
                OTA_data.status = 1;
                OTA_data.cur_ota_time = 100;
                #if PRINT_U0 != 0   //#endif
                Serial.printf("Update: %s\n", upload.filename.c_str());
                #endif
                if (!Update.begin(UPDATE_SIZE_UNKNOWN))
                { //start with max available size
                    Update.printError(Serial);
                }
            }
            else if (upload.status == UPLOAD_FILE_WRITE)
            {
                /* flashing firmware to ESP*/
                if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)
                {
                    OTA_data.status = 3;
                    OTA_data.cur_ota_time = 10;

                    Update.printError(Serial);
                    #if PRINT_U0 != 0   //#endif
                    Serial.printf("Update writed: %u\n", upload.currentSize);
                    #endif
                }
            }
            else if (upload.status == UPLOAD_FILE_END)
            {
                if (Update.end(true))
                { //true to set the size to the current progress
                    //sprintf(web_ack_buf, "上传成功,传输%u字节,等待重启...", upload.totalSize);
                    OTA_data.status = 2;
                    OTA_data.cur_ota_time = 10;

                    server.send(200, "text/plain", "升级成功,请关闭页面,等待水龙头重新启动!");
                    #if PRINT_U0 != 0   //#endif
                    Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
                    #endif
                    //ESP.restart();
                }
                else
                {
                    OTA_data.status = 3;
                    OTA_data.cur_ota_time = 10;
                    Update.printError(Serial);
                }
            }
        });
    server.begin();
}

2.6、超时处理机制
没有执行ota操作,超时后关闭WiFi;

//WiFi OTA 超时处理
void WiFi_OTA_timeout(){
    if (OTA_data.cur_ota_time>0) OTA_data.cur_ota_time--;
    
    if (WiFi_data.ota_time_out>0)
    {
        WiFi_data.ota_time_out--;
    }else
    {
        WiFi_data.ota_time_out = 0;    
    }   
}

2.7、在核心core0创建一个task任务,让任务跑起来,以及超时结束任务;

//WIFI任务
void Task_WiFi_Sub(void *pvParameters)
{
    (void)pvParameters;
    // Connect to WiFi network
    //WiFi.begin(ssid, password);
    WiFi_OTA_AP_init(); //开启WiFi热点,用于OTA升级
    for (;;)            // A Task shall never return or exit.
    {
        if (WiFi_data.ota_time_out == 0)
        {
            if (WiFi_data.status == 1 && OTA_data.status != 1)
            {
                //wifi开启,且现在没有在执行OTA任务

                server.close(); //server.stop();  //关闭WebServer;
                MDNS.end();
                closeWiFi();
                WiFi_data.status = 0;
                WiFi_data.flag_wifi_symbol = 0;
                #if PRINT_U0 != 0 //#endif
                Serial.println("timeout close WiFi");
                #endif

                vTaskDelete(NULL); // 删除当前任务
            }
        }
        else
        {
            if (WiFi_data.flag_server == 0)
            {
                /* web server 未开启 */
                if (WiFi_data.flag_mdns == 0)
                {
                    /* mdns 服务未开启*/
                    if (WiFi_data.flag_ap == 0)
                    {
                        /* AP热点未开启成功 */
                        WiFi_data.flag_ap = WiFi_OTA_AP_set(AP_SSID);
                    }
                    else
                    {
                        /* AP热点已开启 */
                        WiFi_data.flag_mdns = WiFi_MDNS_set(host); //开启MDNS
                    }
                }
                else
                {
                    /* mdns 服务未开启已开启 */
                    Server_OTA_set();          //设置并启用OTA Server
                    WiFi_data.flag_server = 1; //置位标志位
                    WiFi_data.flag_wifi_symbol = 2;
                }
                vTaskDelay(1000); //N ms检测一次
            }
            else
            {
                /* web server 已开启 */
                server.handleClient();
                vTaskDelay(10); //N ms检测一次
            }
        }
    }
}

实现功能代码如上,代码中注释信息比较详细,代码段功能就不再多讲了,看下运行效果:
 

   

      

设备上效果演示,小视频(部分涉及客户信息的演示截取掉了):

esp32s3

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ESP32-S3支持通过OTA(Over-The-Air)方式进行固件升级,以下是ESP-IDF中实现OTA的基本步骤: 1. 配置OTA服务器和升级包生成工具,可以使用ESP-IDF中提供的ota服务和espsecure.py工具。 2. 在应用程序中添加OTA服务的客户端代码,通过WiFi接口连接OTA服务器并下载升级包。 3. 将升级包写入ESP32-S3的闪存中,可以使用ESP-IDF中提供的OTA API。 以下是基本的OTA升级流程: 1. 配置OTA服务器和升级包生成工具 在ESP-IDF中,可以使用ota服务和espsecure.py工具来搭建OTA服务器和生成升级包。具体步骤请参考ESP-IDF官方文档。 2. 添加OTA客户端代码 在应用程序中,可以使用ESP-IDF提供的OTA客户端代码来连接OTA服务器并下载升级包。以下是基本的OTA客户端代码: ```c #include "esp_http_client.h" #include "esp_https_ota.h" esp_err_t ota_task(void) { esp_http_client_config_t config = { .url = "http://ota_server_address/image.bin", .cert_pem = (char *)server_cert_pem_start, }; esp_err_t ret = esp_https_ota(&config); if (ret == ESP_OK) { esp_restart(); } return ret; } ``` 在以上代码中,url参数指定OTA服务器的地址和升级包的文件名。server_cert_pem_start是一个指向OTA服务器证书的指针,可以使用ESP-IDF提供的certs.c文件来存储证书。 3. 写入升级包 下载完成升级包后,可以使用OTA API将升级包写入ESP32-S3的闪存中。以下是基本的OTA API代码: ```c #include "esp_ota_ops.h" esp_err_t update_firmware(void) { esp_err_t ret; const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); if (update_partition == NULL) { return ESP_FAIL; } ret = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, NULL); if (ret != ESP_OK) { return ret; } // Write OTA data to flash // ... ret = esp_ota_end(update_partition); if (ret != ESP_OK) { return ret; } ret = esp_ota_set_boot_partition(update_partition); if (ret != ESP_OK) { return ret; } esp_restart(); return ESP_OK; } ``` 在以上代码中,esp_ota_get_next_update_partition函数用于获取下一个可用的OTA分区。esp_ota_begin函数用于开始OTA更新过程,esp_ota_end函数用于结束OTA更新过程。在OTA更新结束后,可以使用esp_ota_set_boot_partition函数将新的固件分区设置为启动分区,并重启设备以完成更新。 需要注意的是,OTA更新的过程中需要保证设备的可靠性和稳定性,尤其是在写入升级包的过程中,需要确保闪存写操作的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值