Руководства · 12 мин чтения

Обновление ESP32 по воздуху (OTA): загрузка кода по Wi-Fi через ArduinoOTA

ESP32 OTA позволяет загружать код по Wi-Fi без USB-кабеля: сетевой порт ArduinoOTA, веб-страница ElegantOTA, пароль, схема разделов и прерванные загрузки.

ESP32 OTA (Over-The-Air, обновление по воздуху) — это загрузка нового кода в плату по Wi-Fi вместо подключения USB-кабеля. Представь датчик под потолком, контроллер полива в саду или релейную плату, спрятанную в электрощитке: вместо того чтобы ради каждой мелкой правки тащить стремянку, снимать плату и подключать её к компьютеру, новая версия отправляется из Arduino IDE или браузера за несколько секунд. Плата в фоне записывает новую программу в память, проверяет её и сама перезагружается.

В этом руководстве шаг за шагом разберём, как OTA устроено внутри ESP32, три разных способа (ArduinoOTA для Arduino IDE, ElegantOTA для браузера и HTTPUpdate для загрузки с сервера), как выбрать правильную схему разделов, как защитить загрузку паролем, что будет, если загрузка прервётся, и как не потерять OTA в следующем скетче. Весь код скомпилирован и проверен на ядре ESP32 для Arduino.

Отладочная плата ESP32 с беспроводной загрузкой кода по OTA
Отладочная плата ESP32: достаточно один раз загрузить первый OTA-скетч по USB, и все следующие обновления можно отправлять по Wi-Fi.

Что такое ESP32 OTA и как оно работает?

Флеш-память ESP32 — не один сплошной блок: она разбита на части по карте, которая называется таблицей разделов. В таблице разделов с поддержкой OTA есть две одинаковые области для программы: app0 (ota_0) и app1 (ota_1). Пока плата работает из одной из них, новая программа записывается в другую. Маленький раздел otadata хранит, какая область запустится при следующей загрузке. Благодаря этому работающая программа во время загрузки не затрагивается, и плата не расстаётся со старой программой, пока новый код не будет полностью записан и проверен.

  1. Новая программа (скомпилированный файл .bin) приходит на плату по Wi-Fi, кусок за куском.
  2. ESP32 записывает каждый кусок в раздел программы, который сейчас не используется; работающий код тем временем продолжает делать своё дело.
  3. Когда весь файл получен, проверяются заголовок образа и контрольная сумма; повреждённый или неполный файл отклоняется.
  4. Если проверка прошла успешно, обновляется otadata: при следующей загрузке запустится новый раздел.
  5. Плата перезагружается и стартует с новой программой. Следующее обновление пойдёт в другой раздел; две области используются по очереди.
СпособОткуда отправляется код?Лучше всего подходит дляДополнительная библиотека
ArduinoOTAЧерез сетевой порт в Arduino IDEРазработки за рабочим столом в той же сетиНе нужна (входит в ядро)
ElegantOTAВыбором .bin на странице /update в браузереОбновления без IDE (другим человеком), загрузки с телефонаElegantOTA
HTTPUpdateПлата сама скачивает файл .bin с сервераМногих устройств, удалённых устройств, автообновленийНе нужна (входит в ядро)

Схема разделов и объём флеш-памяти

Это самое недооценённое требование OTA: нужна вторая свободная область, куда записывать новую программу. Поэтому самая большая программа, которую можно загрузить по OTA, меньше даже половины флеш-памяти. Таблица разделов выбирается в Arduino IDE в меню Инструменты › Partition Scheme (схема разделов). На распространённых платах ESP32 DevKit с 4 МБ флеш-памяти (ESP32 Dev Module) доступны такие варианты:

Partition Scheme (схема разделов)Область программыOTAФайловая система (SPIFFS/LittleFS)
Default 4MB with spiffs2 × 1,25 МБ (1 310 720 байт)Да1,375 МБ
Minimal SPIFFS (1.9MB APP with OTA)2 × 1,875 МБ (1 966 080 байт)Да128 КБ
No OTA (2MB APP/2MB SPIFFS)1 × 2 МБНет1,875 МБ
Huge APP (3MB No OTA/1MB SPIFFS)1 × 3 МБНет896 КБ
Minimal (1.3MB APP/700KB SPIFFS)1 × 1,25 МБ (для плат с 2 МБ флеш-памяти)Нет640 КБ
Когда большая программа, использующая одновременно Bluetooth и Wi-Fi, выдаёт «Sketch too big» (скетч слишком большой), большинство выбирает Huge APP. В этой схеме нет второй области для программы: по USB код загружается нормально, но OTA не запускается и падает с ошибкой «Not Enough Space» (недостаточно места) или «Begin Failed». Если нужно OTA, выбирай Minimal SPIFFS (1.9MB APP with OTA).

Сколько места занимает программа, видно в выводе компиляции: наш первый пример с Wi-Fi и ArduinoOTA выводит «Sketch uses 758685 bytes (57%) … Maximum is 1310720 bytes» (скетч занимает 758685 байт из максимума 1310720). Максимум здесь — размер одной области программы в выбранной схеме. Если занято больше 90%, готовься скоро переходить на Minimal SPIFFS. И ещё одно важное правило: таблицу разделов нельзя изменить по OTA. Если схема изменилась, эту загрузку нужно сделать по USB; следующие обновления по OTA работают уже с новой таблицей. На платах с 8 МБ или 16 МБ флеш-памяти (например, на многих моделях ESP32-S3) правильный объём в меню Flash Size даёт гораздо более просторные области для OTA.

Чтобы увидеть, из какого раздела плата на самом деле работает и сколько места остаётся для OTA, загрузи небольшой скетч ниже. Библиотеки ему не нужны, он напрямую использует OTA-функции ESP-IDF:

C++ (Arduino IDE)
1#include <esp_ota_ops.h>
2
3void setup() {
4  Serial.begin(115200);
5  delay(1000);
6  const esp_partition_t* running = esp_ota_get_running_partition();
7  const esp_partition_t* next = esp_ota_get_next_update_partition(NULL);
8
9  Serial.printf("Flash boyutu / Flash size: %u KB\n", ESP.getFlashChipSize() / 1024);
10  Serial.printf("Program boyutu / Sketch size: %u KB\n", ESP.getSketchSize() / 1024);
11  Serial.printf("OTA icin bos alan / Free space for OTA: %u KB\n", ESP.getFreeSketchSpace() / 1024);
12  Serial.printf("Calisan bolum / Running partition: %s (0x%06x)\n", running->label, running->address);
13  if (next != NULL) {
14    Serial.printf("Siradaki OTA bolumu / Next OTA partition: %s (0x%06x)\n", next->label, next->address);
15  } else {
16    Serial.println("OTA bolumu yok: bolum tablosunu degistir / No OTA partition: change the partition scheme");
17  }
18}
19
20void loop() {
21}

Со схемой по умолчанию после первой загрузки по USB в выводе будет «Running partition: app0 (0x010000)» (текущий раздел) и «Next OTA partition: app1 (0x150000)» (следующий раздел OTA). Запусти код ещё раз после обновления по OTA, и будет видно, что теперь он работает из app1. Значение ESP.getFreeSketchSpace() — это размер самой большой программы, которую можно загрузить по OTA; если новый файл .bin больше, обновление не начнётся.

Способ 1: беспроводная загрузка из Arduino IDE через ArduinoOTA

Библиотека ArduinoOTA входит в ядро ESP32, так что устанавливать ничего не нужно. Подключившись к сети, плата сообщает о себе через mDNS; Arduino IDE это видит и добавляет в меню «Порт» сетевой порт. При нажатии «Загрузка» (Upload) IDE отправляет программу через этот сетевой порт вместо USB. На ESP32 служба OTA по умолчанию слушает порт 3232. Скетч ниже подключается к Wi-Fi, запускает OTA с паролем и в доказательство мигает встроенным светодиодом:

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <ArduinoOTA.h>
3
4const char* WIFI_SSID = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
5const char* WIFI_PASS = "AgSifresi";      // Wi-Fi şifren / your Wi-Fi password
6const int LED_PIN = 2;                    // çoğu DevKit'te dahili LED / onboard LED on most DevKits
7
8unsigned long lastBlink = 0;
9
10void setup() {
11  Serial.begin(115200);
12  pinMode(LED_PIN, OUTPUT);
13
14  WiFi.mode(WIFI_STA);
15  WiFi.begin(WIFI_SSID, WIFI_PASS);
16  while (WiFi.status() != WL_CONNECTED) {   // bağlanana kadar bekle / wait until connected
17    delay(500);
18    Serial.print(".");
19  }
20  Serial.println();
21  Serial.print("IP: ");
22  Serial.println(WiFi.localIP());
23
24  ArduinoOTA.setHostname("bluegrays-esp32");  // IDE'de görünen ad / name shown in the IDE
25  ArduinoOTA.setPassword("ota1234");          // yükleme parolası / upload password
26
27  ArduinoOTA.onStart([]() {
28    // Güncelleme başlıyor: motorları, röleleri güvenli konuma al / update starting: put motors and relays in a safe state
29    String type = (ArduinoOTA.getCommand() == U_FLASH) ? "program" : "dosya sistemi / filesystem";
30    Serial.println("OTA basladi / started: " + type);
31  });
32  ArduinoOTA.onEnd([]() {
33    Serial.println("\nOTA bitti, yeniden baslatiliyor / done, rebooting");
34  });
35  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
36    Serial.printf("Ilerleme / Progress: %u%%\r", progress * 100 / total);
37  });
38  ArduinoOTA.onError([](ota_error_t error) {
39    Serial.printf("Hata / Error[%u]: ", error);
40    if (error == OTA_AUTH_ERROR) Serial.println("parola / auth failed");
41    else if (error == OTA_BEGIN_ERROR) Serial.println("baslatilamadi, yer yok / begin failed, no space");
42    else if (error == OTA_CONNECT_ERROR) Serial.println("baglanti / connect failed");
43    else if (error == OTA_RECEIVE_ERROR) Serial.println("veri alinamadi / receive failed");
44    else if (error == OTA_END_ERROR) Serial.println("bitirilemedi / end failed");
45  });
46
47  ArduinoOTA.begin();                         // OTA hizmetini başlat / start the OTA service
48}
49
50void loop() {
51  ArduinoOTA.handle();                        // her turda çağır, unutma / call on every pass, never forget
52
53  // delay() yerine millis(): OTA isteklerini kaçırmamak için / millis() instead of delay() so OTA requests aren't missed
54  if (millis() - lastBlink >= 500) {
55    lastBlink = millis();
56    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
57  }
58}
СтрокаЧто делает?
ArduinoOTA.setHostname("bluegrays-esp32")Имя, которое видно в списке портов IDE и в сети. Если плат несколько, дай каждой своё имя.
ArduinoOTA.setPassword("ota1234")Без пароля любой в твоей сети может загрузить код в твою плату. IDE спрашивает этот пароль перед загрузкой.
onStart / onEndВызываются в начале и в конце обновления. Идеальное место, чтобы остановить моторы, выключить реле или вывести «Updating» на дисплей.
onProgressСообщает процент выполнения; по нему можно рисовать индикатор прогресса на LCD или OLED.
onErrorСообщает тип ошибки: пароль, нехватка места, соединение, приём или завершение.
ArduinoOTA.handle()Проверяет входящие OTA-запросы. Если не вызывать её часто внутри loop(), плата не сможет ответить IDE.

Шаг за шагом: первая загрузка по OTA

  1. Впиши в код значения WIFI_SSID и WIFI_PASS своей сети. ESP32 подключается только к сетям 2,4 GHz.
  2. В меню «Инструменты» выбери плату (например, ESP32 Dev Module) и Partition Scheme с поддержкой OTA (Default или Minimal SPIFFS).
  3. Первую загрузку сделай по USB-кабелю. Этот шаг обязателен, потому что в плате ещё нет кода OTA.
  4. Открой монитор порта на скорости 115200 бод и запиши IP-адрес, который выводит плата.
  5. Отключи USB-кабель и при желании запитай плату от другого источника. Через 10–20 секунд в меню «Инструменты › Порт» в разделе сетевые порты (network ports) появится строка вроде «bluegrays-esp32 at 192.168.1.x»; выбери её.
  6. Измени в коде задержку светодиода с 500 на 100 и нажми «Загрузка». Когда IDE спросит пароль, введи «ota1234».
  7. В панели «Вывод» (Output) появится процент выполнения, а в конце строка вроде «Done» или «Result: OK». Плата перезагрузится, и светодиод начнёт мигать быстро.
Если сетевой порт не появляется, сначала убедись, что компьютер и ESP32 находятся в одной сети: гостевые сети и некоторые mesh-системы не дают устройствам видеть друг друга. Обычно помогает перезапуск IDE, перезагрузка платы или просто несколько секунд ожидания. На Mac и Linux IDE загружает код через скрипт espota.py, поэтому на компьютере должен быть установлен Python 3.

Есть одно ограничение, о котором стоит знать: сетевой порт служит только для загрузки, и монитор порта по беспроводной связи не работает. Чтобы видеть сообщения платы, либо снова подключи USB, либо выводи состояние на веб-страницу, дисплей или в MQTT-сообщения. При разработке за столом плату можно оставить на USB и всё равно загружать код по сети; так получаются и быстрые загрузки, и монитор порта одновременно.

Защита паролем и безопасность

Плата с открытой службой OTA — это дверь, через которую любой в твоей сети может загрузить код. В ArduinoOTA функция setPassword() хранит пароль в коде открытым текстом; если код будет выложен в общий доступ, можно передать MD5-хеш пароля через setPasswordHash(), и самого пароля в исходнике не будет. ElegantOTA защищает страницу /update именем пользователя и паролем (HTTP Basic Auth).

  • Дай каждому устройству свой сложный пароль: значения вроде «1234» или «admin» пробуют первыми.
  • Не открывай плату в интернет: выставлять OTA или страницу /update наружу через проброс портов на роутере — серьёзный риск; эти способы работают по незашифрованному HTTP.
  • Для удалённых устройств используй HTTPUpdate: плата сама запрашивает обновление, поэтому достаточно исходящего соединения; если файл скачивается через интернет, лучше использовать HTTPS с WiFiClientSecure.
  • На время обновления переходи в безопасное состояние: внутри onStart останавливай моторы и выключай реле нагревателей и насосов. Перезагрузка занимает несколько секунд, и всё это время состояние пинов может быть неопределённым.

Способ 2: загрузка .bin из браузера через ElegantOTA

ElegantOTA добавляет к веб-серверу на ESP32 аккуратную страницу /update. Её можно открыть с телефона или с любого компьютера без Arduino IDE, выбрать скомпилированный файл .bin и загрузить его; индикатор прогресса показывается прямо в браузере. Это самый практичный способ, когда устройство передаётся другому человеку или обновляется на месте установки.

  1. В Arduino IDE открой «Менеджер библиотек» (Library Manager), найди ElegantOTA (Ayush Sharma) и установи её.
  2. Отредактируй код ниже и в первый раз загрузи его по USB.
  3. Открой в браузере адрес, который выводится в монитор порта (например, http://192.168.1.42/update), и войди с именем пользователя «admin» и паролем «ota1234».
  4. Оставь режим Firmware (прошивка) и перетащи файл .bin; когда загрузка закончится, плата сама перезагрузится.
C++ (Arduino IDE)
1#include <WiFi.h>
2#include <WebServer.h>
3#include <ElegantOTA.h>
4
5const char* WIFI_SSID = "AgAdi";          // Wi-Fi adın / your Wi-Fi name
6const char* WIFI_PASS = "AgSifresi";      // Wi-Fi şifren / your Wi-Fi password
7
8WebServer server(80);
9unsigned long otaProgressMillis = 0;
10
11void onOTAStart() {
12  Serial.println("OTA basladi / OTA started");     // röleleri kapat / switch relays off
13}
14
15void onOTAProgress(size_t current, size_t final) {
16  if (millis() - otaProgressMillis > 1000) {        // saniyede bir yaz / print once per second
17    otaProgressMillis = millis();
18    Serial.printf("%u / %u bayt / bytes\n", current, final);
19  }
20}
21
22void onOTAEnd(bool success) {
23  Serial.println(success ? "OTA basarili / OTA successful" : "OTA hatasi / OTA failed");
24}
25
26void setup() {
27  Serial.begin(115200);
28  WiFi.mode(WIFI_STA);
29  WiFi.begin(WIFI_SSID, WIFI_PASS);
30  while (WiFi.status() != WL_CONNECTED) {
31    delay(500);
32    Serial.print(".");
33  }
34  Serial.println();
35  Serial.print("Adres / Address: http://");
36  Serial.print(WiFi.localIP());
37  Serial.println("/update");
38
39  server.on("/", []() {
40    server.send(200, "text/plain", "BlueGrays ESP32 - surum / version 1.0");
41  });
42
43  ElegantOTA.begin(&server, "admin", "ota1234");   // /update sayfası + kullanıcı/parola / /update page + user/password
44  ElegantOTA.onStart(onOTAStart);
45  ElegantOTA.onProgress(onOTAProgress);
46  ElegantOTA.onEnd(onOTAEnd);
47
48  server.begin();
49}
50
51void loop() {
52  server.handleClient();     // web isteklerini işle / handle web requests
53  ElegantOTA.loop();         // başarılı yüklemeden sonra yeniden başlatır / reboots after a successful upload
54}

По умолчанию ElegantOTA работает с синхронным классом WebServer, который входит в ядро, поэтому вызовы server.handleClient() и ElegantOTA.loop() в loop() обязательны. Если в проекте уже используется ESPAsyncWebServer, можно выставить опцию библиотеки ELEGANTOTA_USE_ASYNC_WEBSERVER в 1 и работать с асинхронным сервером; как его настроить, рассказано в статье асинхронный веб-сервер и WebSocket на ESP32. Режим Filesystem (файловая система) на странице обновляет не программу, а образ LittleFS или SPIFFS (файлы веб-страницы, файлы настроек).

Где взять файл .bin: экспорт скомпилированного бинарного файла

Для ElegantOTA и HTTPUpdate нужен скомпилированный файл программы. В Arduino IDE выбери Скетч › Экспорт скомпилированного бинарного файла (Sketch › Export Compiled Binary). IDE скомпилирует программу и положит несколько файлов в папку build внутри папки скетча. Для OTA подходит только один из них:

ФайлСодержимоеДля OTA?
myproject.ino.bin (файл приложения)Только приложение (твоя программа)Да, загружать нужно именно его
myproject.ino.bootloader.bin (файл загрузчика)ЗагрузчикНет
myproject.ino.partitions.bin (файл таблицы разделов)Таблица разделовНет
myproject.ino.merged.bin (ядро 3.x, объединённый файл)Загрузчик, таблица и программа вместеНет, его записывают только по USB по адресу 0x0
Убедись, что программа скомпилирована для чипа, который стоит на плате. Если попытаться загрузить .bin, скомпилированный для ESP32, в ESP32-S3 или ESP32-C3, он будет отклонён на финальной проверке, и плата продолжит работать со старой программой. Если по ошибке выбран merged.bin или bootloader.bin, обновление остановится с ошибкой «Wrong Magic Byte» (неверный магический байт), нехваткой места или ошибкой активации.

Способ 3: автоматические обновления с сервера через HTTPUpdate

Представь, что по дому расставлены пять плат с датчиками. Вместо того чтобы загружать код в каждую из IDE, новый файл .bin кладётся на сервер, а платы скачивают его сами. Библиотека HTTPUpdate, которая входит в ядро ESP32, делает это одной строкой. В примере ниже обновление начинается по нажатию кнопки BOOT (GPIO 0) на плате:

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <HTTPUpdate.h>
3
4const char* WIFI_SSID = "AgAdi";
5const char* WIFI_PASS = "AgSifresi";
6// Bilgisayarındaki ya da sunucundaki .bin dosyası / the .bin file on your computer or server
7const char* FIRMWARE_URL = "http://192.168.1.50:8000/firmware.bin";
8const int BUTTON_PIN = 0;               // karttaki BOOT butonu / the BOOT button on the board
9
10void setup() {
11  Serial.begin(115200);
12  pinMode(BUTTON_PIN, INPUT_PULLUP);
13  WiFi.begin(WIFI_SSID, WIFI_PASS);
14  while (WiFi.status() != WL_CONNECTED) delay(500);
15  Serial.println("Hazir: guncellemek icin BOOT'a bas / Ready: press BOOT to update");
16}
17
18void loop() {
19  if (digitalRead(BUTTON_PIN) == LOW) {      // butona basıldı / button pressed
20    WiFiClient client;
21    httpUpdate.rebootOnUpdate(true);          // başarılıysa yeniden başlat / reboot on success
22    t_httpUpdate_return ret = httpUpdate.update(client, FIRMWARE_URL);
23
24    switch (ret) {
25      case HTTP_UPDATE_FAILED:
26        Serial.printf("Hata / Error (%d): %s\n", httpUpdate.getLastError(),
27                      httpUpdate.getLastErrorString().c_str());
28        break;
29      case HTTP_UPDATE_NO_UPDATES:
30        Serial.println("Guncelleme yok / No update");
31        break;
32      case HTTP_UPDATE_OK:
33        Serial.println("Tamam / OK");        // buraya genelde gelinmez, kart yeniden başlar / usually not reached, the board reboots
34        break;
35    }
36    delay(1000);
37  }
38}

Чтобы попробовать, положи файл .bin в папку на компьютере под именем firmware.bin и запусти в этой папке команду python3 -m http.server 8000; IP-адрес в коде замени на локальный IP своего компьютера. В реальном применении кнопку заменяют таймером: например, раз в час плата читает небольшой файл «version.txt» и, если номер версии на сервере выше её собственного, запускает обновление. Так она ещё и не застрянет, раз за разом скачивая один и тот же файл и перезагружаясь.

Что будет, если связь оборвётся во время OTA?

Короткий ответ: плата не превратится в кирпич. Новая программа записывается в неиспользуемый раздел, а загрузочный раздел переключается только после проверки всего файла, поэтому прерванная загрузка оставляет плату работать со старой программой. В IDE появится «Error Uploading», в ArduinoOTA — OTA_RECEIVE_ERROR, в ElegantOTA — неудачный результат; устрани причину и повтори загрузку. Если посреди загрузки пропадёт питание, история та же: плата загрузит старую программу. Единственный реальный риск — ошибка в самой новой программе: если загрузить программу, которая не подключается к Wi-Fi или не содержит кода OTA, для следующего обновления снова понадобится кабель.

  • Слабый сигнал Wi-Fi: если загрузка зависает где-то на 30–60%, поднеси плату ближе к роутеру или возьми модуль с внешней антенной. Если WiFi.RSSI() в мониторе порта хуже -75 dBm, жди обрывов; подробности в статье ESP32 не подключается к Wi-Fi.
  • Не хватает питания: запись во флеш и передача по Wi-Fi потребляют ток одновременно. Слабый USB-порт или тонкий кабель могут перезагрузить плату во время загрузки; это проявляется как ошибка просадки питания (brownout). Используй надёжный источник 5 V, способный выдать не меньше 500 mA.
  • Долгие ожидания внутри loop(): задержки вроде delay(5000) откладывают вызов handle(), и IDE сообщает «No response from the ESP» (ESP не отвечает). Отсчитывай время через millis().
  • Брандмауэр: в ArduinoOTA при старте загрузки ESP32 сама подключается обратно к компьютеру. Если брандмауэр Windows или антивирус блокирует это входящее соединение, загрузка сразу же срывается; в окне разрешений, которое появляется при первой попытке, разреши доступ для частных сетей.

Как сохранить OTA в каждом новом скетче

Самое частое разочарование с OTA выглядит так: по OTA загружается совсем другой проект, в нём нет кода ArduinoOTA, и плата больше не появляется как сетевой порт. Новая программа полностью заменяет старую; OTA — не что-то, что «остаётся на плате», каждая программа должна нести его сама. Чтобы снова не лезть на стремянку к устройству под потолком, сделай привычкой эти правила:

  • Используй шаблон: вынеси подключение к Wi-Fi и настройки ArduinoOTA в отдельную функцию (например, setupOTA()) и начинай каждый новый проект с этого файла.
  • Ставь вызов handle() в самое начало loop() и никогда не оставляй внутри loop() циклов, которые надолго всё блокируют.
  • Переподключайся, если Wi-Fi пропал: используй WiFi.setAutoReconnect(true) или регулярно проверяй соединение; плата без связи не может получить обновление.
  • Добавь окно обслуживания в проекты с глубоким сном: спящая плата не слышит OTA-запрос. Напиши режим, в котором плата перед сном 60 секунд ждёт OTA, если при старте зажата кнопка или пришёл флаг по MQTT. О режимах сна читай в статье глубокий сон ESP32 (Deep Sleep).
  • Сначала проверь на второй плате на столе: перед отправкой на устройство на месте установки запусти тот же код на плате рядом; если она подключается к Wi-Fi и появляется как сетевой порт, отправлять безопасно.
  • Выводи номер версии: держи константу FW_VERSION в начале кода и показывай её на веб-странице или дисплее; так всегда будет понятно, какая версия работает на какой плате.

OTA на ESP8266 и Raspberry Pi Pico W

Код ArduinoOTA и ElegantOTA из этого руководства с небольшими изменениями работает и на платах ESP8266 (NodeMCU, Wemos D1 Mini): вместо WiFi.h используется ESP8266WiFi.h, вместо WebServer — ESP8266WebServer, а порт OTA — 8266. На платах ESP8266 с 4 МБ флеш-памяти выбирай в Flash Size вариант, который оставляет достаточно места для программы под OTA. На Raspberry Pi Pico W поддержку ArduinoOTA даёт ядро arduino-pico; там новая программа сначала скачивается в файловую систему LittleFS и при загрузке копируется на своё место. Поэтому на Pico W в меню Flash Size нужно зарезервировать под файловую систему область больше, чем программа. В MicroPython готового инструмента для OTA нет; можно написать небольшие скрипты, которые скачивают и заменяют файлы .py по сети, но это не проверяемое обновление с двумя разделами, как на ESP32.

Идеи проектов с OTA

  • Датчик на потолке или стене: меняй пороги платы, которая измеряет температуру, влажность и движение, не прикасаясь к кабелю.
  • Реле для умного дома: обновляй по OTA правила расписания платы реле в электрощитке; внутри onStart переводи реле в безопасное состояние.
  • Устройства для Home Assistant: добавляй по сети новые функции датчикам, подключённым к Home Assistant по MQTT.
  • Контроллер полива в саду: меняй время полива и порог влажности почвы, не открывая водонепроницаемый корпус.
  • Индикатор прогресса на LCD: выводи значение onProgress в процентах и полоской на LCD 16x2; именно это делает проект «Обновление ESP32 по воздуху (OTA)» в BlueGrays.
  • Обновление парка устройств: пусть несколько плат каждую ночь проверяют через HTTPUpdate один и тот же сервер на наличие новой версии.

Частые ошибки и как их исправить

СимптомВероятная причинаРешение
В меню «Порт» нет сетевого портаПлата в другой сети, mDNS заблокирован, в коде не вызывается ArduinoOTA.begin()Используй одну и ту же сеть 2,4 GHz, перезапусти IDE, проверь, что IP выводится в монитор порта
«No response from the ESP» (ESP не отвечает)Долгий delay() в loop(), плата занята или мешает брандмауэрИспользуй millis() вместо delay(), разреши IDE в брандмауэре
«Authentication Failed» (ошибка аутентификации)Неверный пароль OTAВведи значение setPassword из кода; если пароль забыт, один раз загрузи код по USB
«Not Enough Space» / Begin FailedСхема Huge APP или No OTA, программа не помещается во вторую областьВыбери Minimal SPIFFS и один раз загрузи код по USB; уменьши программу
Загрузка останавливается на полпутиСлабый сигнал, просадка питания, обрыв соединенияПоднеси плату ближе к роутеру, возьми надёжный источник 5 V, попробуй ещё раз; старая программа продолжает работать
После загрузки плата не подключается к сетиВ новом коде нет Wi-Fi или OTA, неверный SSIDИсправь по USB; дальше начинай каждый проект с OTA-шаблона
«Wrong Magic Byte» или ошибка активации в ElegantOTAВыбран merged.bin, bootloader.bin или файл, скомпилированный для другого чипаЗагружай только файл myproject.ino.bin
При первой настройке OTA оставь плату на USB с открытым монитором порта: вывод onError одной строкой покажет, в чём проблема — в пароле, месте или соединении. Когда всё заработает, кабель можно отключить.

OTA — ключ к тому, чтобы снять проекты на ESP32 со стола и установить их в реальном месте: если один раз всё правильно настроить, исправления, новые функции и изменения порогов приходят по воздуху за секунды. В приложении BlueGrays есть проект «Обновление ESP32 по воздуху (OTA)» (с выводом IP и прогресса на LCD), а также проекты с подключением к Wi-Fi, асинхронным веб-сервером, mDNS и NTP — у каждого есть схема подключения и код, шаг за шагом.

Частые вопросы

Что такое ESP32 OTA?

OTA (Over-The-Air, по воздуху) — это загрузка новой программы в ESP32 по Wi-Fi вместо USB-кабеля. Плата записывает новый код в неиспользуемый раздел программы, проверяет его и перезагружается с новой версией. Первый скетч с поддержкой OTA нужно один раз загрузить по USB.

Почему сетевой порт ESP32 не появляется в Arduino IDE?

Чаще всего причина в том, что компьютер и плата в разных сетях, гостевая сеть изолирует устройства или вызов ArduinoOTA.begin() в коде так и не выполняется. Проверь в мониторе порта, что плата получила IP, перезапусти IDE и подожди несколько секунд.

Какую схему разделов выбрать для ESP32 OTA?

На платах с 4 МБ флеш-памяти выбирай Default (область программы 2 × 1,25 МБ) или, если программа большая, Minimal SPIFFS (2 × 1,875 МБ). В Huge APP и No OTA нет второй области программы, поэтому OTA с ними не работает. Первую загрузку после смены схемы нужно сделать по USB.

Может ли прерванная загрузка по OTA превратить ESP32 в кирпич?

Нет. Новая программа записывается в пустой раздел, а загрузочный раздел переключается только после проверки всего файла. Если пропадёт связь или питание, плата загрузит старую программу, и загрузку можно просто повторить.

Можно ли пользоваться монитором порта при загрузке по OTA?

Сетевой порт служит только для загрузки кода; монитор порта по беспроводной связи не работает. Чтобы видеть сообщения, можно оставить плату подключённой к USB или выводить информацию на веб-страницу, дисплей или через MQTT.

Как задать пароль OTA на ESP32?

В ArduinoOTA перед begin() вызывается ArduinoOTA.setPassword("password"); IDE спрашивает этот пароль перед загрузкой. Если пароль не должен быть виден в коде, передай его MD5-хеш через setPasswordHash(). В ElegantOTA страница защищается вызовом begin(&server, "user", "password").

Все проекты
Обновление ESP32 по воздуху (OTA): беспроводная установка прошивки (с экраном LCD)
#132Продвинутый
Premium

Обновление ESP32 по воздуху (OTA): беспроводная установка прошивки (с экраном LCD)

В этом проекте ты узнаешь, как загружать новую прошивку в ESP32 по Wi-Fi без USB-кабеля. С библиотекой ArduinoOTA ESP32 становится видна в сети и автоматически появляется как bluegrays-ota на вкладке «Сетевой порт» в Arduino IDE. Первая загрузка выполняется по USB, а все следующие обновления — без проводов: это важно для устройств в эксплуатации. I2C LCD показывает состояние Wi-Fi, IP-адрес, имя хоста, процент загрузки OTA и сообщения об ошибках.

4.6(14)
22
Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)
#127Продвинутый

Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)

В этом проекте ESP32 надёжно и устойчиво к сбоям подключается к домашней сети Wi-Fi в режиме станции (STA). SSID и пароль хранятся в отдельном файле secrets.h, чтобы не утекли в Git; настроено автоматическое переподключение, а уровень сигнала RSSI и IP-адрес выводятся на I2C LCD-экран. Встроенный светодиод служит индикатором состояния. Это менеджер подключения уровня реальных устройств.

4.4(21)
34
Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)
#129Продвинутый

Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)

В этом проекте ESP32 превращается в асинхронный веб-сервер, которым можно управлять с телефона или компьютера в той же сети Wi-Fi. Благодаря библиотеке ESPAsyncWebServer сервер может обрабатывать несколько запросов одновременно (в отличие от классического WebServer.h, который блокируется на одном запросе). Открыв IP-адрес ESP32 в браузере, ты нажимаешь кнопки ON/OFF (маршруты /on и /off) и включаешь и выключаешь светодиод на GPIO 5.

4.8(16)
26
mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)
#131Продвинутый
Premium

mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)

В этом проекте к ESP32 добавляется служба mDNS (Multicast DNS), и к устройству можно обращаться по понятному имени вроде bluegrays-esp.local вместо IP-адреса. Даже если IP, выданный DHCP, меняется, устройство всегда доступно по одному имени. На устройстве также работает маленький веб-сервер, через который по адресу http://bluegrays-esp.local/ из браузера отправляются команды светодиоду. I2C LCD показывает имя хоста, IP-адрес и состояние светодиода.

4.8(14)
23
Счётчик на WebSocket с ESP32 (2 кнопки + LCD + веб в реальном времени)
#130Продвинутый
Premium

Счётчик на WebSocket с ESP32 (2 кнопки + LCD + веб в реальном времени)

В этом проекте две кнопки, подключённые к ESP32 (GPIO 5 увеличивает, GPIO 4 уменьшает), меняют счётчик. Значение сразу обновляется и на I2C LCD 16x2, и — через поддержку WebSocket в ESPAsyncWebServer — во всех подключённых браузерах без перезагрузки страницы. Вместо опроса по HTTP используется одно постоянное соединение, и ESP32 отправляет новое значение всем через ws.textAll().

4.5(14)
23
Точное время из интернета через NTP на ESP32
#128Продвинутый
Premium

Точное время из интернета через NTP на ESP32

ESP32 подключается к Wi-Fi и получает точное время с точностью до миллисекунд от серверов NTP (Network Time Protocol). Благодаря функции configTzTime() автоматически применяется часовой пояс Турции (TRT, UTC+3), а в странах с летним временем переходы обрабатываются автоматически по строке POSIX TZ. Время выводится в монитор порта и на необязательный I2C LCD. configTzTime() запускает службу SNTP, которая периодически пересинхронизирует часы.

4.5(13)
21
ESP32 HTTPClient + API погоды (с экраном LCD)
#133Продвинутый
Premium

ESP32 HTTPClient + API погоды (с экраном LCD)

В этом проекте ESP32 через HTTPClient.h делает вызов REST API к сервису погоды (wttr.in) через интернет. Полученный ответ в JSON разбирается с помощью ArduinoJson v7, а город, температура, влажность, погода и ветер выводятся на I2C LCD на турецком и английском. wttr.in бесплатен и не требует ключа API — достаточно указать любое название города, чтобы получить текущую погоду. Данные автоматически обновляются каждые 30 минут.

4.7(13)
20
Глубокий сон ESP32: экономия батареи (OLED + счётчик в RTC RAM)
#135Продвинутый
Premium

Глубокий сон ESP32: экономия батареи (OLED + счётчик в RTC RAM)

Режим глубокого сна (Deep Sleep) у ESP32 снижает потребление до ~10 µA, выключая процессор и большую часть периферии, — это важно для IoT-устройств на батарейках. В этом проекте используются три источника пробуждения: таймер (15 секунд), внешний пин (EXT0 — кнопка GPIO 33) и касание (T0/GPIO 4). Переменные с RTC_DATA_ATTR сохраняются во время глубокого сна — счётчики загрузок, пробуждений по таймеру и по кнопке ведутся непрерывно.

4.3(10)
16