Files
zh_ota_server/wiki_ru.md
T
2026-10-17 20:41:24 +03:00

12 KiB
Raw Blame History

Описание компонента zh_ota_server

Общие сведения

zh_ota_server — это компонент для ESP-IDF, реализующий сервер обновлений прошивки по воздуху (Over-The-Air, OTA) для микроконтроллеров ESP32.

Компонент предоставляет веб-интерфейс для загрузки бинарных файлов прошивки через браузер и автоматически записывает их в свободный OTA-раздел ESP32 с последующей активацией и перезагрузкой устройства.

Основные возможности

  1. Веб-интерфейс для загрузки — встроенная HTML-страница для выбора и отправки файла прошивки через браузер.
  2. Автоматическое определение раздела — компонент сам находит следующий доступный OTA-раздел через API ESP-IDF.
  3. Построчная запись прошивки — данные принимаются блоками по 1000 байт для экономии оперативной памяти.
  4. Автоматическая активация и перезагрузка — после успешной записи прошивки раздел становится главным, и устройство перезагружается.
  5. Интеграция с ESP-IDF HTTP server — компонент использует встроенный HTTP-сервер ESP-IDF и регистрирует свои обработчики как стандартные URI-хендлеры.
  6. Встроенная HTML-страница — веб-интерфейс хранится как встроенный массив байт (EMBED_FILES), не требуя внешней файловой системы.
  7. Детальное логирование ошибок — при ошибках логируются файл, строка и код возврата ESP-IDF.

Внутреннее устройство

Глобальные переменные

Переменная Тип Описание
_ota_path char * Выделенная динамически память для URI-пути OTA-сервера (не освобождается)
_ota_ws_path const char * URI-путь для POST-обработчика ("/zh_ota_server")
_is_initialized volatile bool Флаг инициализации компонента, предотвращающий повторный вызов zh_ota_server_init()

Структуры обработчиков

Поле Тип Описание
_ota_page.uri char * URI-путь для GET-запроса (устанавливается при инициализации)
_ota_page.method httpd_method_t HTTP-метод: HTTP_GET
_ota_page.handler httpd_req_handler_t Указатель на _ota_page_handler()
_ota_page_ws.uri char * URI-путь для POST-запроса: "/zh_ota_server"
_ota_page_ws.method httpd_method_t HTTP-метод: HTTP_POST
_ota_page_ws.handler httpd_req_handler_t Указатель на _ota_ws_page_handler()

Встроенные ресурсы

HTML-страница zh_ota_server.html встраивается в прошивку через механизм EMBED_FILES CMake. Доступна через внешние символы:

Символ Описание
zh_ota_server_html_start[] Начало встроенного HTML-массива
zh_ota_server_html_end[] Конец встроенного HTML-массива

Размер страницы вычисляется как zh_ota_server_html_end - zh_ota_server_html_start.

Управление памятью

При инициализации zh_ota_path выделяется через heap_caps_calloc() с флагом MALLOC_CAP_8BIT. Память не освобождается никогда — это осознанный выбор, так как функция инициализации может быть вызвана только один раз за время работы приложения.

API (интерфейс)

Инициализация

Функция Описание
zh_ota_server_init() Инициализирует и регистрирует обработчики OTA-сервера в предоставленном экземпляре HTTP-сервера. Принимает handle сервера (httpd_handle_t) и URI-путь (например, "/ota"). Возвращает ESP_OK при успехе, ESP_ERR_INVALID_ARG при некорректных аргументах, ESP_ERR_NO_MEM при нехватке памяти, ESP_FAIL при ошибке регистрации обработчика.

Потокобезопасность

Компонент не является потокобезопасным. Глобальные переменные _ota_path и _is_initialized защищены только проверкой на повторную инициализацию (_is_initialized == false), но не мьютексами. Обработчики HTTP-запросов вызываются в контексте потока HTTP-сервера и не используют механизмы синхронизации.

Алгоритмы

Обработка OTA-обновления (_ota_ws_page_handler)

  1. Выделение буфера размером 1000 байт для приёма данных.
  2. Определение следующего доступного OTA-раздела через esp_ota_get_next_update_partition(NULL).
  3. Начало записи в раздел через esp_ota_begin() с размером OTA_SIZE_UNKNOWN.
  4. Цикл приёма данных:
    • Приём данных из HTTP-запроса через httpd_req_recv() блоками до sizeof(buf) (1000 байт).
    • При таймауте сокета (HTTPD_SOCK_ERR_TIMEOUT) цикл продолжается без действия.
    • При ошибке приёма отправляется HTTP 500 и вызывается esp_ota_abort().
    • Запись данных в flash через esp_ota_write(); при ошибке отправляется HTTP 500 и вызывается esp_ota_abort().
    • Уменьшение счётчика оставшихся байт (remaining -= recv_len).
  5. Завершение записи через esp_ota_end() (валидация образа).
  6. Установка раздела как загрузочного через esp_ota_set_boot_partition().
  7. Отправка HTTP-ответа "Firmware update complete, rebooting now!\n".
  8. Задержка 500 мс через vTaskDelay() и вызов esp_restart() для перезагрузки.

Примеры использования

Базовый пример

#include "nvs_flash.h"
#include "esp_wifi.h"
#include "zh_ota_server.h"

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_netif_init();
    esp_event_loop_run(NULL, NULL);

    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_start(&http_server_handle, &config);

    zh_ota_server_init(http_server_handle, "/ota");
}

Пример с Wi-Fi в режиме точки доступа

#include "nvs_flash.h"
#include "esp_wifi.h"
#include "zh_ota_server.h"

#define WIFI_SSID "ESP32-OTA"
#define WIFI_PASS "password123"

static httpd_handle_t http_server_handle = NULL;

void app_main(void)
{
    esp_log_level_set("zh_ota_server", ESP_LOG_DEBUG);
    nvs_flash_init();
    esp_netif_init();
    esp_event_loop_create_default();

    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");
}

Логирование

Компонент использует ESP-IDF логирование с тегом "zh_ota_server".

Макрос ZH_LOGI — для информационных сообщений. Макрос ZH_LOGE — для ошибок, автоматически добавляет имя файла, номер строки и строковое представление кода ошибки ESP-IDF.

esp_log_level_set("zh_ota_server", ESP_LOG_DEBUG);

Ошибки

Код Описание
ESP_OK Успешное выполнение
ESP_ERR_INVALID_ARG Неверный аргумент: NULL-указатель на сервер или путь
ESP_ERR_INVALID_STATE Повторная инициализация: компонент уже был инициализирован
ESP_ERR_NO_MEM Нехватка памяти при выделении для URI-пути
ESP_FAIL Ошибка регистрации URI-обработчика, записи в flash, завершения OTA или установки загрузочного раздела

Ограничения

  1. Однократная инициализация — zh_ota_server_init() можно вызвать только один раз за время работы приложения. Повторный вызов вернёт ESP_ERR_INVALID_STATE.
  2. Утечка памяти URI-пути — память, выделенная для _ota_path, никогда не освобождается.
  3. Риск «окирпичивания» устройства — запись невалидного образа прошивки сделает устройство не загружаемым. Необходимо иметь механизм восстановления (например, USB-прошивку).
  4. Отсутствие потокобезопасности — компонент не защищён мьютексами; одновременный доступ из нескольких потоков может привести к неопределённому поведению.
  5. Размер буфера 1000 байт — фиксированный размер буфера приёма может быть недостаточен для оптимальной производительности при больших объёмах данных.
  6. Отсутствие контрольной суммы — валидация образа выполняется только через esp_ota_end(), что не гарантирует корректность прошивки на уровне содержимого.

Требования для сборки

  • ESP-IDF (фреймворк для ESP32)
  • esp_http_server — встроенный HTTP-сервер ESP-IDF
  • app_update — модуль ESP-IDF для управления OTA-разделами и загрузкой
  • FreeRTOS (входит в состав ESP-IDF)

Установка

cd ваш_проект/components
git clone https://github.com/aZholtikov/zh_ota_server
#include "zh_ota_server.h"