本文介绍: 1)本节主要是介绍基于 ESP-IDF 的原始 API,实现综合项目1- 通过Web Server 实现 WiFi 配网网页 OTA 更新。2)示例设计了多网页菜单实现管理 WiFi 配网、OTA 固件更新设备重启功能。更多综合项目敬请期待。

ESP32-Web-Server编程综合项目1-结合 Web Server 实现 WiFi 配网和网页 OTA 更新

概述

前述的内容多是一个个功能演示,本章节讲述一些实际项目中使用到的综合项目。

首先要讲述的案例是通过ESP32 上的 Web Server 实现对 ESP32 的 WiFi 配网和网页 OTA 更新功能

需求功能解析

项目的主要功能有:

WiFi 配网

用户初次使用设备时,设备完全不知道连接路由器信息,此时可以通过建立一个 SoftAP (什么是 SoftAP 参考AP、STA的概念以及AP+STA的实现),让用户向连接路由器一样连接默认的 AP,然后打开配网网页,让 ESP32 连接指定路由器,最终使得 ESP32 设备能够正常上网

设备重启

设备配网信息下发后,可以通过网页或者设备的按钮重启设备(重新上电也行),设备将在重启后检测到已经下发的配网信息网络名称密码),然后使用该配网信息进行联网

此外,当设备 OTA 结束时,也需要设备重启以加载新的固件

OTA 更新

OTA 是更新设备固件技术可以认为是通过网络方式传输固件进行软件更新的一种方法

路由器设计中,经常看到路由器本地网页支持通过网页上的输入框选中一个新的固件下发给路由器,对路由器的固件进行更新

在这里插入图片描述

示例解析

目录结构

├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c                 User application
├── components
│   └── fs_image
		└── index.html
		└── ...
|	└── url_handlers
		└── url_handlers.c
		└── ...
└── README.md                  This is the file you are currently reading

前端代码

多网页菜单设计

components/fs_image/web_image/index.html设计三个子菜单wifimanagerota、Home:

<div class="topnav-right">
  &lt;a href="wifimanager"&gt;WiFi Manager</a>
  <a href="ota">OTA</a>
  <a href="/">Home</a>
</div>

点击对应href 时,浏览器会向 ESP32 Web Server 发送对该 href 的 Get 请求

SoftAP 配网界面

components/fs_image/web_image/wifimanager_softap.html 中设计 SoftAP 配网的界面

<form action="/wifi_config" method="POST">
  <p>
    <label for="ssid">SSID</label>
    <input type="text" id ="ssid" name="ssid"><br>
    <label for="pass">Password</label>
    <input type="text" id ="pass" name="pass"><br>
    <label for="ip">IP Address</label>
    <input type="text" id ="ip" name="ip" value="192.168.1.xxx">
    <input type ="submit" value ="Submit">
  </p>
</form>

上述配网界面非常简单,提供了输入路由器 WiFi 名称和密码的输入框

当用户输入 WiFi 名称和密码后,将通过 http 的 POST 方法向 /wifi_config 推送配网信息

WiFi Manager 界面

在配网成功,设备连接路由器后,我们可以像往常一样。让手机或者电脑连接到同一个路由器,然后通过局域网打开 ESP32 设备上的控制网页。

components/fs_image/web_image/wifimanager.html 界面我们可以重新下发设备要连接的路由器的信息

<form action="/wifi_config" method="POST">
  <p>
    <label for="ssid">SSID</label>
    <input type="text" id ="ssid" name="ssid"><br>
    <label for="pass">Password</label>
    <input type="text" id ="pass" name="pass"><br>
    <!-- <label for="ip">IP Address</label>
    <input type="text" id ="ip" name="ip" value="192.168.1.200"> -->
    <input type ="submit" value ="Submit">
  </p>
</form>
OTA 更新界面

可以通过子菜单跳转到 OTA 更新的网页。

components/fs_image/web_image/ota.html 中,建立了一个 input file 的输入框。,并设置一个 submit 按钮

<h2>ESP32 Firmware Update Page</h2>
    <h4 id="latest_firmware"></h4>

<input type="file" id="selectedFile" accept=".bin" style="display: none;" onchange="myFunction()" />
<input type="button" value="Browse..." onclick="document.getElementById('selectedFile').click();" />
<h3 id="file_info"></h3>
<input type='submit' id="upload_button" value='Update Firmware' onclick="updateFirmware()"><br>
<p>
<button onclick="rebootButton()">Reboot</button>
</p>

选中文件后,通过ota.html 中的 updateFirmware() 实现/update 以 POST 方法推送新固件数据

function updateFirmware() {
    // Form Data
    var formData = new FormData();

    var fileSelect = document.getElementById("selectedFile");

    if (fileSelect.files &amp;&amp; fileSelect.files.length == 1) {
        var file = fileSelect.files[0];
        formData.set("file", file, file.name);
        document.getElementById("status").innerHTML = "Uploading " + file.name + " , Please Wait...";

        // Http Request
        var request = new XMLHttpRequest();

        request.upload.addEventListener("progress", updateProgress);

        request.open('POST', "/update");
        request.responseType = "blob";
        request.send(formData);
    } else {
        window.alert('Select A File First')
    }
}

此外,为了反映 OTA 更新过程进度,在 ota.html 中的 script添加了更新进度的函数

// progress on transfers from the server to the client (downloads)
function updateProgress(oEvent) {
    if (oEvent.lengthComputable) {
        getstatus();
    } else {
        window.alert('total size is unknown')
    }
}

后端代码

后端代码除建立前述基于 spiffs 的 Web Server 外,重点是设计对应前端文件各个 URL 的 handlers.

这里的 URL 分为两组,一组是还未进行 WiFi 配网(比如设备刚出厂,用户首次使用时)时手机通过连接 ESP32 的 SoftAP 时打开的网页 httpd_uri_array_softap_only[]一组是配网后,ESP32 设备连接到路由器后,用户通过手机或者电脑连接至同一个路由器时通过局域网打开的网页httpd_uri_array[]

httpd_uri_t httpd_uri_array_softap_only[] = {
        {"/wifi_config", HTTP_POST, wifi_config_post_handler, rest_context},
        {"/*", HTTP_GET, softap_wifi_html_get_handler,rest_context}
    };

httpd_uri_t httpd_uri_array[] = {
    {"/wifimanager", HTTP_GET, wifi_manage_html_get_handler, rest_context},
    {"/ota", HTTP_GET, ota_html_get_handler, rest_context},
    {"/wifi_config", HTTP_POST, wifi_config_post_handler, rest_context},
    {"/update", HTTP_POST, OTA_update_post_handler, rest_context},
    {"/status", HTTP_POST, OTA_update_status_handler, rest_context},
    {"/reboot", HTTP_GET, reboot_html_get_handler, rest_context},
    {"/*", HTTP_GET, rest_common_get_handler,rest_context}//Catch-all callback function for the filesystem, this must be set to the array last one
};

if (!s_sta_connected) { // 首次配网时
    uris = httpd_uri_array_softap_only;
    uris_len = sizeof(httpd_uri_array_softap_only)/sizeof(httpd_uri_t);
} else { // 已经配过网时
    uris = httpd_uri_array;
    uris_len = sizeof(httpd_uri_array)/sizeof(httpd_uri_t);
}

for(int i = 0; i < uris_len; i++){
    if (httpd_register_uri_handler(server, &amp;uris[i]) != ESP_OK) {
        ESP_LOGE(TAG, "httpd register uri_array[%d] fail", i);
    }
}
配网处理

配网处理main/main.c 中的 wifi_config_post_handler() 中,接收网页端 POST 的路由器 WiFi 的名称和密码,并使用 wifi_config_store() 将其存储在设备上。设备重新上电后将检测到已经存储了WiFi 的名称和密码,然后触发向该路由器的 WiFi 连接。

if (recv_post_data(req, buf) != ESP_OK) {
    ESP_LOGE(TAG, "recv post data error");
    return ESP_FAIL;
}

str_len = httpd_find_arg(buf, PARAM_INPUT_1, temp_str, sizeof(temp_str));
if ((str_len != -1) &amp;&amp; (strlen((char *)temp_str) != 0)) {
    // snprintf((char *)wifi_config.sta.ssid, 32, "%s", temp_str);
    memcpy((char *)wifi_config.sta.ssid, temp_str, 32);
    ESP_LOGI(TAG, "ssid:%s", (char *)wifi_config.sta.ssid);
}

memset(temp_str, '', sizeof(temp_str));

str_len = httpd_find_arg(buf, PARAM_INPUT_2, temp_str, sizeof(temp_str));
if ((str_len != -1) &amp;& (strlen((char *)temp_str) != 0)) {
    memcpy((char *)wifi_config.sta.password, temp_str, 64);
    ESP_LOGI(TAG, "pwd:%s", (char *)wifi_config.sta.password);
}
if(!wifi_config_store(&wifi_config)) {
    return ESP_FAIL;
}
关于 WiFi Manager

WiFI 管理主要是负责管理 WiFi 的工作模式,WiFi 配网信息

默认情况下,设备将建立一个称为 IoT_Old_Wang、密码为 123456789 、IP 地址默认192.168.4.1的 SoftAP,用户可以通过手机或者电脑搜索热点然后连接到该默认 AP,并使用浏览器打开 192.168.4.1 网址查看配网界面。

如果需要更改默认 SoftAP 的信息,可以在编译程序时通过 idf.py menuconfig 打开配置菜单配置下述信息

在这里插入图片描述

OTA 的后端处理

OTA 更新的后端处理main/web_ota.c 中,在 OTA_update_post_handler() 内,主要是接收从网页下发的固件数据

if (esp_ota_end(ota_handle) == ESP_OK)
{
    // Lets update the partition
    if(esp_ota_set_boot_partition(update_partition) == ESP_OK) 
    {
        const esp_partition_t *boot_partition = esp_ota_get_boot_partition();

        // Webpage will request status when complete 
        // This is to let it know it was successful
        flash_status = 1;
        ESP_LOGI("OTA", "Next boot partition subtype %d at offset 0x%x", boot_partition->subtype, boot_partition->address);
        ESP_LOGI("OTA", "Please Restart System...");
    }
    else
    {
        ESP_LOGI("OTA", "rnrn !!! Flashed Error !!!");
    }
}

接收完固件将设置标志位 flash_status=1,当网页端重新请求固件的 status 时,将在 OTA_update_status_handler()触发创建一个定时重启设备的定时器

if (flash_status == 1)
{
    // We cannot directly call reboot here because we need the 
    // browser to get the ack back. Send message to another task or create a 
    create_a_restart_timer();
    // xEventGroupSetBits(reboot_event_group, REBOOT_BIT);		
}

示例效果

连接 SoftAP 执行配网

下图分别为连接对应热点,浏览器输入 192.168.4.1 打开配网界面:

在这里插入图片描述

注意:

重启设备后,连接电脑同一个路由器,打开多网页菜单

设备连接到路由器后,登录路由器查看 ESP32 设备的 IP 地址如下该设备的 IP 地址192.168.47.100:

在这里插入图片描述

打开 wifi manager 界面

点击菜单栏的 WiFi Manager 打开wifi manager 界面:

在这里插入图片描述

打开 OTA 界面

点击菜单栏的 OTA 打开 OTA 界面:

在这里插入图片描述

OTA 完成后重启设备

点击 OTA 更新界面的 Browse 按钮选择需要上传的 OTA 新固件,然后点击 Updata Firmware,开始 OTA 更新。OTA 更新后可以重启设备以便于在下次重启设备时加载新的固件。

在这里插入图片描述

流程示意图:

在这里插入图片描述

讨论

1)示例程序中有 version.txt 文件,该文件可以用于控制固件的版本信息。读者可以更改该文件的内容记录固件的版本信息

2)示例使用的 ota APIs 可以参考 ESP-IDF OTA 开发指南

总结

1)本节主要是介绍基于 ESP-IDF 的原始 API,实现综合项目1- 通过Web Server 实现 WiFi 配网和网页 OTA 更新。

2)示例设计了多网页子菜单,实现管理 WiFi 配网、OTA 固件更新、设备重启的功能。更多综合项目敬请期待。

资源链接

1)ESP32-Web-Server ESP-IDF系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库

3)下一篇

(码字不易感谢点赞收藏)

原文地址:https://blog.csdn.net/wangyx1234/article/details/134772311

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_36914.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注