JXCT Soil Sensor 7-in-1 v3.4.9 (June 2025)
Professional IoT soil monitoring system with ESP32, Modbus RTU, MQTT, and advanced compensation algorithms
Загрузка...
Поиск...
Не найдено
ota_manager.cpp
См. документацию.
1#include "ota_manager.h"
2#include <Update.h>
3#include "logger.h"
4#include "jxct_config_vars.h"
5#include <HTTPClient.h>
6#include <ArduinoJson.h>
7#include <mbedtls/sha256.h>
8#include <esp_ota_ops.h>
9#include <strings.h>
10#include "version.h"
11#include <Arduino.h>
12#include <esp_task_wdt.h>
13
14// Глобальные переменные для OTA 2.0
15// Сначала статусный буфер (расширен до 128 байт), чтобы возможное переполнение НЕ затирало URL.
16static char statusBuf[128] = "Ожидание";
17static char guardGap[8] = "BEFORE"; // часовой между statusBuf и URL
18static char manifestUrlGlobal[512] = ""; // Буфер URL манифеста (512 байт)
19static WiFiClient* clientPtr = nullptr;
20static bool urlInitialized = false; // Флаг инициализации для защиты от перезаписи
21
22// Переменные для двухэтапного OTA (проверка -> установка)
23static bool updateAvailable = false;
24static String pendingUpdateUrl = "";
25static String pendingUpdateSha256 = "";
26static String pendingUpdateVersion = "";
27
28static char guardSentinel[8] = "GUARD!"; // часовой после URL, как раньше
29
30static void _printGuard(const char* name, const char* tag, const char* current) {
31 logError("[GUARD] Повреждение (%s) после %s: '%s'", name, tag, current);
32}
33
34void checkGuard(const char* tag) {
35 if (strncmp(guardGap, "BEFORE", 6) != 0) {
36 _printGuard("GAP", tag, guardGap);
37 strncpy(guardGap, "BEFORE", 7);
38 }
39 if (strncmp(guardSentinel, "GUARD!", 6) != 0) {
40 _printGuard("AFTER", tag, guardSentinel);
41 strncpy(guardSentinel, "GUARD!", 7);
42 }
43}
44
45const char* getOtaStatus() { return statusBuf; }
46
47void setupOTA(const char* manifestUrl, WiFiClient& client)
48{
49 checkGuard("setupOTA:entry");
50 // КРИТИЧЕСКАЯ ЗАЩИТА: Проверяем повторную инициализацию
51 if (urlInitialized) {
52 logWarn("[OTA] [SETUP DEBUG] ⚠️ OTA уже инициализирован, пропускаем повторную инициализацию");
53 return;
54 }
55
56 // ДОБАВЛЕНО: Детальная диагностика инициализации
57 logSystem("[OTA] [SETUP DEBUG] Инициализация OTA 2.0...");
58 logSystem("[OTA] [SETUP DEBUG] Входные параметры:");
59 logSystem("[OTA] [SETUP DEBUG] manifestUrl: %s", manifestUrl ? manifestUrl : "NULL");
60 logSystem("[OTA] [SETUP DEBUG] client: %s", &client ? "OK" : "NULL");
61
62 // КРИТИЧЕСКАЯ ПРОВЕРКА: Валидация входного URL
63 if (!manifestUrl || strlen(manifestUrl) < 20 || strstr(manifestUrl, "github.com") == nullptr) {
64 logError("[OTA] [SETUP DEBUG] ❌ Неверный URL манифеста!");
65 return;
66 }
67
68 // КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Защищенное копирование с проверкой целостности
69 memset(manifestUrlGlobal, 0, sizeof(manifestUrlGlobal)); // Очищаем буфер
70 strlcpy(manifestUrlGlobal, manifestUrl, sizeof(manifestUrlGlobal));
71
72 // ПРОВЕРКА ЦЕЛОСТНОСТИ после копирования
73 if (strlen(manifestUrlGlobal) != strlen(manifestUrl) ||
74 strstr(manifestUrlGlobal, "github.com") == nullptr) {
75 logError("[OTA] [SETUP DEBUG] ❌ URL поврежден при копировании!");
76 memset(manifestUrlGlobal, 0, sizeof(manifestUrlGlobal));
77 return;
78 }
79
80 clientPtr = &client;
81 strlcpy(statusBuf, "Готов", sizeof(statusBuf));
82 urlInitialized = true; // Защищаем от повторной инициализации
83
84 // Сброс состояния обновлений
85 updateAvailable = false;
89
90 logSystem("[OTA] [SETUP DEBUG] Глобальные переменные установлены:");
91 logSystem("[OTA] [SETUP DEBUG] manifestUrlGlobal: %s", manifestUrlGlobal);
92 logSystem("[OTA] [SETUP DEBUG] clientPtr: %p", clientPtr);
93 logSystem("[OTA] [SETUP DEBUG] statusBuf: '%s'", statusBuf);
94 logSystem("[OTA] [SETUP DEBUG] urlInitialized: %s", urlInitialized ? "ДА" : "НЕТ");
95
96 logSuccess("[OTA] [SETUP DEBUG] ✅ OTA инициализирован успешно с защитой памяти");
97 checkGuard("setupOTA:exit");
98}
99
100static bool verifySha256(const uint8_t* calcDigest, const char* expectedHex)
101{
102 char calcHex[65];
103 for (int i = 0; i < 32; ++i)
104 sprintf(&calcHex[i * 2], "%02x", calcDigest[i]);
105 return strcasecmp(calcHex, expectedHex) == 0;
106}
107
108// Вспомогательная функция для инициализации загрузки
109static bool initializeDownload(HTTPClient& http, const String& binUrl, int& contentLen)
110{
111 esp_task_wdt_reset();
112 strcpy(statusBuf, "Подключение");
113
114 // КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Проверяем память перед началом
115 size_t freeHeap = ESP.getFreeHeap();
116 logSystem("[OTA] Свободная память перед HTTP: %d байт", freeHeap);
117
118 // УВЕЛИЧИВАЕМ ТРЕБОВАНИЯ К ПАМЯТИ для безопасности
119 if (freeHeap < 70000) {
120 strcpy(statusBuf, "Мало памяти");
121 logError("[OTA] Недостаточно памяти для HTTP: %d байт", freeHeap);
122 return false;
123 }
124
125 // ИСПРАВЛЕНО: Защита от повреждения памяти - копируем URL в статический буфер
126 static char urlBuffer[256];
127 strlcpy(urlBuffer, binUrl.c_str(), sizeof(urlBuffer));
128 logSystem("[OTA] Загрузка: %s", urlBuffer);
129
130 // ДОПОЛНИТЕЛЬНАЯ ЗАЩИТА: Проверяем целостность URL
131 if (strlen(urlBuffer) < 10 || strstr(urlBuffer, "github.com") == nullptr) {
132 strcpy(statusBuf, "Поврежденный URL");
133 logError("[OTA] URL поврежден или некорректен: %s", urlBuffer);
134 return false;
135 }
136
137 // ИСПРАВЛЕНО: Добавляем проверку инициализации HTTP
138 logSystem("[OTA] Инициализация HTTP клиента...");
139 if (!http.begin(*clientPtr, binUrl)) {
140 strcpy(statusBuf, "Ошибка HTTP init");
141 logError("[OTA] Не удалось инициализировать HTTP клиент");
142 return false;
143 }
144
145 http.setTimeout(65000); // Максимум для uint16_t ~65 секунд
146 http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
147
148 logSystem("[OTA] Выполняем HTTP GET запрос...");
149 esp_task_wdt_reset();
150
151 int code = http.GET();
152 esp_task_wdt_reset();
153
154 logSystem("[OTA] HTTP ответ: %d", code);
155
156 if (code != HTTP_CODE_OK)
157 {
158 snprintf(statusBuf, sizeof(statusBuf), "Ошибка HTTP %d", code);
159 logError("[OTA] HTTP ошибка %d", code);
160 return false;
161 }
162
163 contentLen = http.getSize();
164 if (contentLen == -1)
165 {
166 logSystem("[OTA] Chunked transfer, размер неизвестен");
167 contentLen = UPDATE_SIZE_UNKNOWN;
168 }
169 else
170 {
171 logSystem("[OTA] Размер файла: %d байт", contentLen);
172 }
173
174 if (!Update.begin(contentLen))
175 {
176 strcpy(statusBuf, "Нет места");
177 logError("[OTA] Update.begin() failed");
178 Update.printError(Serial);
179 return false;
180 }
181
182 return true;
183}
184
185// Вспомогательная функция для загрузки данных
186static bool downloadData(HTTPClient& http, int contentLen, mbedtls_sha256_context& shaCtx)
187{
188 strcpy(statusBuf, "Загрузка");
189
190 // КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Проверяем память перед загрузкой
191 size_t heapBeforeDownload = ESP.getFreeHeap();
192 logSystem("[OTA] Память перед загрузкой: %d байт", heapBeforeDownload);
193
194 // УВЕЛИЧИВАЕМ ТРЕБОВАНИЯ К ПАМЯТИ для безопасности
195 if (heapBeforeDownload < 60000) {
196 strcpy(statusBuf, "Мало памяти для загрузки");
197 logError("[OTA] Недостаточно памяти для загрузки: %d байт", heapBeforeDownload);
198 return false;
199 }
200
201 WiFiClient* stream = http.getStreamPtr();
202 if (!stream) {
203 strcpy(statusBuf, "Ошибка потока");
204 logError("[OTA] Не удалось получить поток данных");
205 return false;
206 }
207
208 // ИСПРАВЛЕНО: Оптимальный размер буфера для стабильной загрузки
209 uint8_t buf[512]; // Увеличиваем буфер для более быстрой загрузки
210 size_t totalDownloaded = 0;
211 unsigned long lastProgress = millis();
212 unsigned long lastActivity = millis();
213 const unsigned long TIMEOUT_MS = 120000; // 2 минуты паузы между пакетами допускаются
214 bool isChunked = (contentLen == UPDATE_SIZE_UNKNOWN);
215
216 while (http.connected())
217 {
218 esp_task_wdt_reset();
219
220 size_t avail = stream->available();
221 if (avail > 0)
222 {
223 lastActivity = millis();
224 size_t toRead = (avail > sizeof(buf)) ? sizeof(buf) : avail;
225 int readBytes = stream->readBytes(buf, toRead);
226
227 if (readBytes <= 0)
228 {
229 strcpy(statusBuf, "Ошибка чтения");
230 logError("[OTA] Ошибка чтения данных");
231 Update.abort();
232 return false;
233 }
234
235 if (Update.write(buf, readBytes) != (size_t)readBytes)
236 {
237 strcpy(statusBuf, "Ошибка записи");
238 logError("[OTA] Ошибка записи во flash");
239 Update.printError(Serial);
240 Update.abort();
241 return false;
242 }
243
244 mbedtls_sha256_update_ret(&shaCtx, buf, readBytes);
245 totalDownloaded += readBytes;
246
247 // Прогресс каждые 3 секунды
248 if (millis() - lastProgress > 3000)
249 {
250 if (isChunked)
251 {
252 snprintf(statusBuf, sizeof(statusBuf), "Загружено %dКБ", (int)(totalDownloaded / 1024));
253 }
254 else
255 {
256 int percent = (totalDownloaded * 100) / contentLen;
257 snprintf(statusBuf, sizeof(statusBuf), "Загружено %d%%", percent);
258 }
259 logSystem("[OTA] Загружено: %d байт", totalDownloaded);
260 lastProgress = millis();
261 }
262 }
263 else
264 {
265 // Если весь файл уже получен, прекращаем ожидание доп.данных
266 if (!isChunked && totalDownloaded == (size_t)contentLen)
267 {
268 logSystem("[OTA] Загрузка завершена, получено %d байт", totalDownloaded);
269 break;
270 }
271
272 if (millis() - lastActivity > TIMEOUT_MS)
273 {
274 strcpy(statusBuf, "Таймаут");
275 logError("[OTA] Таймаут загрузки (нет данных %lu мс)", TIMEOUT_MS);
276 Update.abort();
277 return false;
278 }
279
280 if (isChunked && !http.connected())
281 {
282 logSystem("[OTA] Chunked transfer завершён, загружено %d байт", totalDownloaded);
283 break;
284 }
285
286 esp_task_wdt_reset();
287 delay(10);
288 }
289 }
290
291 if (!isChunked && totalDownloaded != (size_t)contentLen)
292 {
293 snprintf(statusBuf, sizeof(statusBuf), "Неполная загрузка %d/%d", totalDownloaded, contentLen);
294 logError("[OTA] Неполная загрузка: %d из %d байт", totalDownloaded, contentLen);
295 Update.abort();
296 return false;
297 }
298
299 return true;
300}
301
302// Основная функция загрузки и обновления (упрощенная)
303static bool downloadAndUpdate(const String& binUrl, const char* expectedSha256)
304{
305 logSystem("[OTA] Начинаем загрузку и обновление");
306
307 // КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Проверяем общее состояние системы
308 size_t initialHeap = ESP.getFreeHeap();
309 logSystem("[OTA] Начальная память: %d байт", initialHeap);
310
311 // УВЕЛИЧИВАЕМ ТРЕБОВАНИЯ К ПАМЯТИ для безопасности
312 if (initialHeap < 80000) {
313 strcpy(statusBuf, "Критически мало памяти");
314 logError("[OTA] Критически мало памяти: %d байт", initialHeap);
315 return false;
316 }
317
318 // ИСПРАВЛЕНО: Создаем HTTP клиент в куче для экономии стека
319 HTTPClient* http = new HTTPClient();
320 if (!http) {
321 strcpy(statusBuf, "Ошибка создания HTTP клиента");
322 logError("[OTA] Не удалось создать HTTP клиент");
323 return false;
324 }
325
326 int contentLen;
327
328 // Инициализация загрузки с дополнительными проверками
329 logSystem("[OTA] Инициализация загрузки...");
330 if (!initializeDownload(*http, binUrl, contentLen))
331 {
332 logError("[OTA] Ошибка инициализации загрузки");
333 http->end();
334 delete http;
335 return false;
336 }
337
338 logSystem("[OTA] Инициализация успешна, размер контента: %d", contentLen);
339
340 // ИСПРАВЛЕНО: Создаем SHA256 контекст в куче
341 mbedtls_sha256_context* shaCtx = new mbedtls_sha256_context();
342 if (!shaCtx) {
343 strcpy(statusBuf, "Ошибка создания SHA256 контекста");
344 logError("[OTA] Не удалось создать SHA256 контекст");
345 http->end();
346 delete http;
347 return false;
348 }
349
350 mbedtls_sha256_init(shaCtx);
351 mbedtls_sha256_starts_ret(shaCtx, 0);
352
353 // Загрузка данных
354 bool downloadSuccess = downloadData(*http, contentLen, *shaCtx);
355 http->end();
356 delete http;
357
358 if (!downloadSuccess)
359 {
360 mbedtls_sha256_free(shaCtx);
361 delete shaCtx;
362 return false;
363 }
364
365 // Проверка SHA256
366 strcpy(statusBuf, "Проверка");
367 uint8_t digest[32];
368 mbedtls_sha256_finish_ret(shaCtx, digest);
369 mbedtls_sha256_free(shaCtx);
370 delete shaCtx;
371
372 // Проверяем SHA256
373 if (!verifySha256(digest, expectedSha256))
374 {
375 strcpy(statusBuf, "Неверная контрольная сумма");
376 logError("[OTA] SHA256 не совпадает");
377 Update.abort();
378 return false;
379 }
380
381 // Завершение обновления
382 strcpy(statusBuf, "Завершение установки");
383 if (!Update.end(true))
384 {
385 strcpy(statusBuf, "Ошибка завершения");
386 logError("[OTA] Update.end() failed");
387 Update.printError(Serial);
388 return false;
389 }
390
391 // ИСПРАВЛЕНО: Устанавливаем позитивный статус для пользователя
392 strcpy(statusBuf, "✅ Обновление завершено!");
393 logSystem("[OTA] ✅ Обновление успешно завершено. Перезагрузка через 3 секунды...");
394
395 // Даем время веб-интерфейсу получить финальный статус
396 delay(1000);
397
398 // Дополнительное уведомление для пользователя
399 strcpy(statusBuf, "🔄 Перезагрузка...");
400 delay(2000);
401
402 ESP.restart();
403 return true;
404}
405
406// Принудительная проверка OTA (игнорирует таймер)
408{
409 static bool isChecking = false;
410
411 if (isChecking) {
412 logWarn("[OTA] Проверка уже выполняется, пропускаем");
413 return;
414 }
415
416 isChecking = true;
417 logSystem("[OTA] Принудительная проверка OTA запущена");
418 handleOTA();
419 isChecking = false;
420}
421
422// Принудительная установка найденного обновления
424{
425 if (!updateAvailable || pendingUpdateUrl.isEmpty())
426 {
427 logError("[OTA] Нет доступных обновлений для установки");
428 strcpy(statusBuf, "Нет обновлений");
429 return;
430 }
431
432 logSystem("[OTA] Принудительная установка обновления %s", pendingUpdateVersion.c_str());
433 logSystem("[OTA] URL: %s", pendingUpdateUrl.c_str());
434 logSystem("[OTA] SHA256: %.16s...", pendingUpdateSha256.c_str());
435
436 // ИСПРАВЛЕНО: Устанавливаем статус успешного обновления ДО перезагрузки
437 strcpy(statusBuf, "Обновление успешно!");
438
440
441 // ИСПРАВЛЕНО: Этот код выполнится только если обновление действительно не удалось
442 // (функция downloadAndUpdate перезагружает устройство при успехе)
443 if (!result)
444 {
445 logError("[OTA] Установка обновления не удалась");
446 strcpy(statusBuf, "Ошибка установки");
447 // Сбрасываем информацию об обновлении при ошибке
448 updateAvailable = false;
449 pendingUpdateUrl = "";
452 }
453}
454
456{
457 // ДОБАВЛЕНО: Детальная диагностика для отладки
458 static unsigned long debugCallCount = 0;
459 debugCallCount++;
460
461 // Сброс watchdog перед началом проверки
462 esp_task_wdt_reset();
463
464 // КРИТИЧЕСКАЯ ПРОВЕРКА: Проверяем инициализацию и целостность URL
465 if (!urlInitialized || strlen(manifestUrlGlobal) == 0) {
466 logError("[OTA] [DEBUG] OTA не инициализирован или URL пуст - выходим");
467 return;
468 }
469
470 // КРИТИЧЕСКАЯ ПРОВЕРКА: Проверяем целостность URL перед использованием
471 if (strstr(manifestUrlGlobal, "github.com") == nullptr) {
472 logError("[OTA] [DEBUG] ❌ URL поврежден в памяти: %s", manifestUrlGlobal);
473 logError("[OTA] [DEBUG] Переинициализируем OTA...");
474 urlInitialized = false; // Сбрасываем флаг для переинициализации
475 return;
476 }
477
478 logSystem("[OTA] [DEBUG] handleOTA() вызов #%lu, URL проверен: %.50s...",
479 debugCallCount, manifestUrlGlobal);
480
481 if (!clientPtr)
482 {
483 logError("[OTA] [DEBUG] clientPtr не задан - выходим");
484 return;
485 }
486
487 logSystem("[OTA] [DEBUG] Начинаем проверку обновлений...");
488 logSystem("[OTA] [DEBUG] URL манифеста: %s", manifestUrlGlobal);
489 strcpy(statusBuf, "Проверка обновлений");
490
491 HTTPClient http;
492 logSystem("[OTA] [DEBUG] Инициализируем HTTP клиент...");
493 http.begin(*clientPtr, manifestUrlGlobal);
494 http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
495 http.setTimeout(15000); // 15 секунд таймаут
496
497 logSystem("[OTA] [DEBUG] Отправляем GET запрос...");
498 int code = http.GET();
499 esp_task_wdt_reset();
500
501 logSystem("[OTA] [DEBUG] HTTP ответ: %d", code);
502
503 if (code != HTTP_CODE_OK)
504 {
505 snprintf(statusBuf, sizeof(statusBuf), "Ошибка манифеста %d", code);
506 logError("[OTA] [DEBUG] Ошибка загрузки манифеста: HTTP %d", code);
507
508 // Дополнительная диагностика для популярных ошибок
509 if (code == HTTP_CODE_NOT_FOUND) {
510 logError("[OTA] [DEBUG] 404 - файл манифеста не найден на сервере");
511 } else if (code == HTTP_CODE_MOVED_PERMANENTLY || code == HTTP_CODE_FOUND) {
512 logError("[OTA] [DEBUG] %d - редирект, но followRedirects включен", code);
513 } else if (code == -1) {
514 logError("[OTA] [DEBUG] -1 - ошибка подключения/DNS");
515 } else if (code == -11) {
516 logError("[OTA] [DEBUG] -11 - таймаут подключения");
517 }
518
519 http.end();
520 return;
521 }
522
523 String manifestContent = http.getString();
524 int contentLength = manifestContent.length();
525 http.end();
526
527 logSystem("[OTA] [DEBUG] Манифест получен: %d байт", contentLength);
528
529 // Показываем первые 200 символов для диагностики
530 String preview = manifestContent.substring(0, min(200, contentLength));
531 logSystem("[OTA] [DEBUG] Начало манифеста: '%s'%s",
532 preview.c_str(), contentLength > 200 ? "..." : "");
533
534 // Проверяем что это JSON
535 if (!manifestContent.startsWith("{")) {
536 logError("[OTA] [DEBUG] Манифест не начинается с '{' - возможно HTML ошибка");
537 strcpy(statusBuf, "Неверный формат");
538 return;
539 }
540
541 const size_t capacity = JSON_OBJECT_SIZE(3) + 300; // Увеличиваем буфер
542 StaticJsonDocument<capacity> doc;
543 DeserializationError err = deserializeJson(doc, manifestContent);
544 if (err)
545 {
546 strcpy(statusBuf, "Ошибка JSON");
547 logError("[OTA] [DEBUG] Ошибка парсинга JSON: %s", err.c_str());
548 logError("[OTA] [DEBUG] JSON содержимое: '%s'", manifestContent.c_str());
549 return;
550 }
551
552 const char* newVersion = doc["version"] | "";
553 const char* binUrl = doc["url"] | "";
554 const char* sha256 = doc["sha256"] | "";
555
556 logSystem("[OTA] [DEBUG] Парсинг JSON успешен:");
557 logSystem("[OTA] [DEBUG] version: '%s'", newVersion);
558 logSystem("[OTA] [DEBUG] url: '%s'", binUrl);
559 logSystem("[OTA] [DEBUG] sha256: '%s'", sha256);
560 logSystem("[OTA] [DEBUG] текущая версия: '%s'", JXCT_VERSION_STRING);
561
562 // Детальная валидация полей
563 if (strlen(newVersion) == 0) {
564 logError("[OTA] [DEBUG] Поле 'version' пустое или отсутствует");
565 strcpy(statusBuf, "Нет версии в манифесте");
566 return;
567 }
568 if (strlen(binUrl) == 0) {
569 logError("[OTA] [DEBUG] Поле 'url' пустое или отсутствует");
570 strcpy(statusBuf, "Нет URL в манифесте");
571 return;
572 }
573 if (strlen(sha256) != 64) {
574 logError("[OTA] [DEBUG] Поле 'sha256' неверной длины: %d (ожидается 64)", strlen(sha256));
575 strcpy(statusBuf, "Неверная подпись");
576 return;
577 }
578
579 // Проверка версий
580 logSystem("[OTA] [DEBUG] Сравниваем версии: '%s' vs '%s'", newVersion, JXCT_VERSION_STRING);
581
582 if (strcmp(newVersion, JXCT_VERSION_STRING) == 0)
583 {
584 strcpy(statusBuf, "Актуальная версия");
585 updateAvailable = false;
586 pendingUpdateUrl = "";
589 logSystem("[OTA] [DEBUG] Версии совпадают - обновление не требуется");
590 return;
591 }
592
593 // Сохраняем информацию об обновлении
594 updateAvailable = true;
595 pendingUpdateUrl = String(binUrl);
596 pendingUpdateSha256 = String(sha256);
597 pendingUpdateVersion = String(newVersion);
598
599 snprintf(statusBuf, sizeof(statusBuf), "Доступно обновление: %s", newVersion);
600 logSystem("[OTA] [DEBUG] ✅ ОБНОВЛЕНИЕ НАЙДЕНО!");
601 logSystem("[OTA] [DEBUG] Текущая: %s", JXCT_VERSION_STRING);
602 logSystem("[OTA] [DEBUG] Доступна: %s", newVersion);
603 logSystem("[OTA] [DEBUG] URL: %s", binUrl);
604 logSystem("[OTA] [DEBUG] SHA256: %.16s...", sha256);
605 logSystem("[OTA] [DEBUG] Ожидаем подтверждения установки через веб-интерфейс");
606}
void logWarn(const char *format,...)
Определения logger.cpp:78
void logSuccess(const char *format,...)
Определения logger.cpp:129
void logError(const char *format,...)
Определения logger.cpp:61
void logSystem(const char *format,...)
Определения logger.cpp:213
Система логгирования с красивым форматированием
static bool updateAvailable
Определения ota_manager.cpp:23
void triggerOtaInstall()
Определения ota_manager.cpp:423
static bool verifySha256(const uint8_t *calcDigest, const char *expectedHex)
Определения ota_manager.cpp:100
static String pendingUpdateVersion
Определения ota_manager.cpp:26
void handleOTA()
Определения ota_manager.cpp:455
static String pendingUpdateSha256
Определения ota_manager.cpp:25
const char * getOtaStatus()
Определения ota_manager.cpp:45
static char manifestUrlGlobal[512]
Определения ota_manager.cpp:18
static char statusBuf[128]
Определения ota_manager.cpp:16
static bool downloadData(HTTPClient &http, int contentLen, mbedtls_sha256_context &shaCtx)
Определения ota_manager.cpp:186
static WiFiClient * clientPtr
Определения ota_manager.cpp:19
static void _printGuard(const char *name, const char *tag, const char *current)
Определения ota_manager.cpp:30
static bool downloadAndUpdate(const String &binUrl, const char *expectedSha256)
Определения ota_manager.cpp:303
void setupOTA(const char *manifestUrl, WiFiClient &client)
Определения ota_manager.cpp:47
static bool initializeDownload(HTTPClient &http, const String &binUrl, int &contentLen)
Определения ota_manager.cpp:109
static char guardGap[8]
Определения ota_manager.cpp:17
void triggerOtaCheck()
Определения ota_manager.cpp:407
static String pendingUpdateUrl
Определения ota_manager.cpp:24
void checkGuard(const char *tag)
Определения ota_manager.cpp:34
static char guardSentinel[8]
Определения ota_manager.cpp:28
static bool urlInitialized
Определения ota_manager.cpp:20
#define JXCT_VERSION_STRING
Определения version.h:12