doc: added wiki
This commit is contained in:
1 parent
51fdf710b8
commit
bcd5d5c3f8
1 file changed
+196
+196
@@ -0,0 +1,196 @@
|
||||
# Описание компонента 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()` для перезагрузки.
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Базовый пример
|
||||
|
||||
```c
|
||||
#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 в режиме точки доступа
|
||||
|
||||
```c
|
||||
#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.
|
||||
|
||||
```c
|
||||
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)
|
||||
|
||||
## Установка
|
||||
|
||||
```bash
|
||||
cd ваш_проект/components
|
||||
git clone https://github.com/aZholtikov/zh_ota_server
|
||||
```
|
||||
|
||||
```c
|
||||
#include "zh_ota_server.h"
|
||||
```
|
||||
Reference in new issue
Block a user