feat: initial

This commit is contained in:
aZholtikov committed 2026-03-01 18:24:35 +03:00
1 parent d9828464bf
commit e33153af38
9 files changed
+272 -3

No files matched your search

+1 -1
View File
@@ -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")
+63 -2
View File
@@ -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");
}
```
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+28
View File
@@ -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
View File
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
1.0.0
+100
View File
@@ -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;
}
+79
View File
@@ -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>