feat: initial
This commit is contained in:
1 parent
d9828464bf
commit
e33153af38
9 files changed
+272
-3
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
|
||||
idf_component_register(SRCS "zh_ota_server.c" INCLUDE_DIRS "include" REQUIRES app_update esp_http_server EMBED_FILES "zh_ota_server.html")
|
||||
@@ -1,3 +1,64 @@
|
||||
# esp_component_template
|
||||
# ESP32 ESP-IDF component for simple OTA server
|
||||
|
||||
esp_component_template
|
||||
## Tested on
|
||||
|
||||
1. [ESP32 ESP-IDF v5.5.3](https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32/index.html)
|
||||
|
||||
## SAST Tools
|
||||
|
||||
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
|
||||
|
||||
## Using
|
||||
|
||||
In an existing project, run the following command to install the component:
|
||||
|
||||
```text
|
||||
cd ../your_project/components
|
||||
git clone http://git.zh.com.ru/esp_components/zh_ota_server
|
||||
```
|
||||
|
||||
In the application, add the component:
|
||||
|
||||
```c
|
||||
#include "zh_ota_server.h"
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
OTA server by address http://192.168.4.1/ota
|
||||
|
||||
```c
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "zh_ota_server.h"
|
||||
|
||||
#define WIFI_SSID "SSID"
|
||||
#define WIFI_PASS "password"
|
||||
|
||||
static httpd_handle_t http_server_handle = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_ota_server", ESP_LOG_ERROR);
|
||||
nvs_flash_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_init();
|
||||
esp_netif_create_default_wifi_ap();
|
||||
wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_wifi_init(&wifi_config);
|
||||
wifi_config_t ap_config = {
|
||||
.ap = {
|
||||
.ssid = WIFI_SSID,
|
||||
.password = WIFI_PASS,
|
||||
.max_connection = 4,
|
||||
.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
},
|
||||
};
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
esp_wifi_set_config(WIFI_IF_AP, &ap_config);
|
||||
esp_wifi_start();
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
httpd_start(&http_server_handle, &config);
|
||||
zh_ota_server_init(http_server_handle, "/ota");
|
||||
}
|
||||
```
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file zh_ota_server.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_http_server.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Start OTA server.
|
||||
*
|
||||
* @param[in] server HTTP server handle.
|
||||
* @param[in] path OTA server path.
|
||||
*
|
||||
* @return ESP_OK if success or an error code otherwise.
|
||||
*/
|
||||
esp_err_t zh_ota_server_init(httpd_handle_t server, const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#include "zh_ota_server.h"
|
||||
|
||||
static const char *TAG = "zh_ota_server";
|
||||
|
||||
#define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__)
|
||||
#define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__)
|
||||
|
||||
#define ZH_ERROR_CHECK(cond, err, cleanup, msg, ...) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
ZH_LOGE(msg, err, ##__VA_ARGS__); \
|
||||
cleanup; \
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
static char *_ota_path = NULL;
|
||||
static char *_ota_ws_path = "/zh_ota_server";
|
||||
|
||||
static esp_err_t _ota_page_handler(httpd_req_t *req);
|
||||
static esp_err_t _ota_ws_page_handler(httpd_req_t *req);
|
||||
|
||||
extern const uint8_t zh_ota_server_html_start[] asm("_binary_zh_ota_server_html_start");
|
||||
extern const uint8_t zh_ota_server_html_end[] asm("_binary_zh_ota_server_html_end");
|
||||
|
||||
httpd_uri_t _ota_page = {
|
||||
.uri = NULL,
|
||||
.method = HTTP_GET,
|
||||
.handler = _ota_page_handler,
|
||||
.user_ctx = NULL};
|
||||
|
||||
httpd_uri_t _ota_page_ws = {
|
||||
.uri = NULL,
|
||||
.method = HTTP_POST,
|
||||
.handler = _ota_ws_page_handler,
|
||||
.user_ctx = NULL};
|
||||
|
||||
esp_err_t zh_ota_server_init(httpd_handle_t server, const char *path)
|
||||
{
|
||||
ZH_LOGI("OTA server initialization started.");
|
||||
ZH_ERROR_CHECK(server != NULL && path != NULL, ESP_ERR_INVALID_ARG, NULL, "OTA server initialization failed. Invalid argument.");
|
||||
_ota_path = (char *)heap_caps_calloc(1, strlen(path), MALLOC_CAP_8BIT);
|
||||
strcpy(_ota_path, path);
|
||||
_ota_page.uri = _ota_path;
|
||||
_ota_page_ws.uri = _ota_ws_path;
|
||||
esp_err_t err = httpd_register_uri_handler(server, &_ota_page);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "OTA server initialization failed. Register uri handler failed.");
|
||||
err = httpd_register_uri_handler(server, &_ota_page_ws);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "OTA server initialization failed. Register uri handler failed.");
|
||||
ZH_LOGI("OTA server initialization completed successfully.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t _ota_page_handler(httpd_req_t *req)
|
||||
{
|
||||
esp_err_t err = httpd_resp_send(req, (const char *)zh_ota_server_html_start, zh_ota_server_html_end - zh_ota_server_html_start);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t _ota_ws_page_handler(httpd_req_t *req) // -V2008
|
||||
{
|
||||
char buf[1000] = {0};
|
||||
esp_ota_handle_t ota_handle = {0};
|
||||
int remaining = req->content_len;
|
||||
const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ZH_ERROR_CHECK(ota_partition != NULL, ESP_FAIL, NULL, "OTA get next update partition failed.");
|
||||
esp_err_t err = esp_ota_begin(ota_partition, OTA_SIZE_UNKNOWN, &ota_handle);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "OTA begin failed.");
|
||||
while (remaining > 0)
|
||||
{
|
||||
int recv_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
|
||||
if (recv_len == HTTPD_SOCK_ERR_TIMEOUT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (recv_len <= 0)
|
||||
{
|
||||
err = httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Protocol Error");
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_ota_write(ota_handle, (const void *)buf, recv_len);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, err = httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Flash error.");
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error."), "OTA write failed.");
|
||||
remaining -= recv_len;
|
||||
}
|
||||
err = esp_ota_end(ota_handle);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, err = httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.");
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error."), "OTA end failed.");
|
||||
err = esp_ota_set_boot_partition(ota_partition);
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, err = httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.");
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error."), "OTA set boot partition failed.");
|
||||
err = httpd_resp_sendstr(req, "Firmware update complete, rebooting now!\n");
|
||||
ZH_ERROR_CHECK(err == ESP_OK, err, NULL, "HTTP server internal error.");
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ESP32 OTA Server</title>
|
||||
<script>
|
||||
function startUpload() {
|
||||
var otafile = document.getElementById("file").files;
|
||||
if (otafile.length == 0) {
|
||||
alert("No file selected!");
|
||||
} else {
|
||||
document.getElementById("file").disabled = true;
|
||||
document.getElementById("upload").disabled = true;
|
||||
var file = otafile[0];
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
document.open();
|
||||
document.write(xhr.responseText);
|
||||
document.close();
|
||||
} else if (xhr.status == 0) {
|
||||
alert("Server closed the connection abruptly!");
|
||||
location.reload()
|
||||
} else {
|
||||
alert(xhr.status + " Error!\n" + xhr.responseText);
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open("POST", "/zh_ota_server", true);
|
||||
xhr.send(file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
max-width: 360px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
display: block;
|
||||
width: 90%;
|
||||
background: #3df;
|
||||
padding: 30px;
|
||||
color: #FFF;
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
font-family: Tahoma;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #3ce;
|
||||
}
|
||||
|
||||
.button div {
|
||||
color: #FFF;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>ESP32 OTA Server</h1>
|
||||
<div>
|
||||
<label for="file" class="button">Firmware file:<div></div></label>
|
||||
<input type="file" accept=".bin" name="upload" id="file" hidden="true"
|
||||
onchange='document.querySelector(".button div").innerHTML = Array.from(this.files).map(f => f.name).join("<br />")' />
|
||||
<p></p>
|
||||
<button style="width:107%" id="upload" type="button" class="button" onclick="startUpload()">Upload</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in new issue
Block a user