Guias · 14 min de leitura

Atualização OTA no ESP32: carregue código sem fio com ArduinoOTA

Com o OTA do ESP32 você carrega código pelo Wi-Fi, sem cabo USB: porta de rede do ArduinoOTA, página web do ElegantOTA, senha, partition scheme e carregamentos interrompidos.

ESP32 OTA (Over-The-Air, atualização “pelo ar”) significa carregar um novo código na sua placa pelo Wi-Fi, em vez de conectar um cabo USB. Imagine um sensor instalado no teto, um controlador de irrigação no jardim ou uma placa de relés guardada dentro de um quadro elétrico: em vez de pegar a escada, desmontar a placa e ligá-la ao computador a cada pequena correção, você envia a nova versão pelo Arduino IDE ou por um navegador em poucos segundos. A placa grava o novo programa na memória em segundo plano, verifica e se reinicia sozinha.

Neste guia você vai aprender, passo a passo, como o OTA funciona dentro do ESP32, três métodos diferentes (ArduinoOTA para o Arduino IDE, ElegantOTA para o navegador e HTTPUpdate para baixar de um servidor), como escolher o partition scheme (tabela de partições) certo, como proteger os carregamentos com senha, o que acontece se um carregamento for interrompido e como não perder o OTA no seu próximo sketch. Todo o código foi compilado e testado com o core ESP32 do Arduino.

Placa de desenvolvimento ESP32 com carregamento de código sem fio por OTA
Placa de desenvolvimento ESP32: depois de carregar o primeiro sketch com OTA uma vez pelo USB, você pode enviar todas as atualizações seguintes pelo Wi-Fi.

O que é OTA no ESP32 e como funciona?

A memória flash do ESP32 não é um bloco único; ela é dividida em partes por um mapa chamado tabela de partições. Uma tabela de partições com suporte a OTA tem duas áreas idênticas para o programa: app0 (ota_0) e app1 (ota_1). Enquanto a placa roda a partir de uma delas, o novo programa é gravado na outra. Uma pequena partição otadata guarda qual área será executada na próxima inicialização. Assim, o programa em execução nunca é tocado durante o carregamento, e a placa só abandona o programa antigo depois que o novo código foi totalmente gravado e verificado.

  1. O novo programa (o arquivo .bin compilado) chega à placa pelo Wi-Fi, pedaço por pedaço.
  2. O ESP32 grava cada pedaço na partição de programa que não está em uso no momento; enquanto isso, o código em execução continua fazendo seu trabalho.
  3. Quando o arquivo inteiro chega, o cabeçalho da imagem e o checksum são verificados; um arquivo corrompido ou incompleto é rejeitado.
  4. Se a verificação der certo, o otadata é atualizado: a nova partição será executada na próxima inicialização.
  5. A placa reinicia e volta com o novo programa. A próxima atualização vai para a outra partição; as duas áreas são usadas alternadamente.
MétodoDe onde você envia o código?Mais indicado paraBiblioteca extra
ArduinoOTAPela porta de rede no Arduino IDEDesenvolvimento na bancada, na mesma redeNenhuma (vem com o core)
ElegantOTAEscolhendo um .bin na página /update do navegadorDeixar alguém sem o IDE atualizar, carregar pelo celularElegantOTA
HTTPUpdateA própria placa baixa um arquivo .bin de um servidorMuitos dispositivos, dispositivos remotos, atualizações automáticasNenhuma (vem com o core)

Partition scheme e tamanho da flash

Este é o requisito mais esquecido do OTA: precisa existir uma segunda área livre para gravar o novo programa. Por isso, o maior programa que você pode carregar por OTA é menor até que a metade da flash. Você escolhe a tabela de partições no Arduino IDE em Ferramentas > Partition Scheme. Nas placas ESP32 DevKit comuns com 4 MB de flash (ESP32 Dev Module), as opções são:

Partition SchemeÁrea de programaOTASistema de arquivos (SPIFFS/LittleFS)
Default 4MB with spiffs2 × 1,25 MB (1.310.720 bytes)Sim1,375 MB
Minimal SPIFFS (1.9MB APP with OTA)2 × 1,875 MB (1.966.080 bytes)Sim128 KB
No OTA (2MB APP/2MB SPIFFS)1 × 2 MBNão1,875 MB
Huge APP (3MB No OTA/1MB SPIFFS)1 × 3 MBNão896 KB
Minimal (1.3MB APP/700KB SPIFFS)1 × 1,25 MB (para placas com 2 MB de flash)Não640 KB
Quando um programa grande que usa Bluetooth e Wi-Fi juntos dá o erro “Sketch too big”, a maioria das pessoas escolhe Huge APP. Esse esquema não tem uma segunda área de programa: o código carrega normalmente pelo USB, mas o OTA não começa e falha com “Not Enough Space” ou “Begin Failed”. Se você quer OTA, escolha Minimal SPIFFS (1.9MB APP with OTA).

Você vê quanto espaço seu programa ocupa na saída da compilação: nosso primeiro exemplo com Wi-Fi e ArduinoOTA mostra “O sketch usa 758685 bytes (57%) ... O máximo são 1310720 bytes”. Esse máximo é o tamanho de uma única área de programa no esquema que você escolheu. Se o uso passar de 90%, conte com a mudança para Minimal SPIFFS em breve. Mais uma regra importante: a tabela de partições não pode ser alterada por OTA. Se você mudou o esquema, esse carregamento precisa ser feito pelo USB; os OTAs seguintes funcionam com a nova tabela. Em placas com 8 MB ou 16 MB de flash (como muitos modelos ESP32-S3), escolher o tamanho correto no menu Flash Size dá áreas de OTA bem maiores.

Se quiser ver de qual partição sua placa está realmente rodando e quanto espaço sobra para o OTA, carregue o pequeno sketch abaixo. Ele não precisa de biblioteca e usa diretamente as funções de OTA do 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}

Com o esquema padrão, depois do primeiro carregamento pelo USB a saída mostra “Running partition: app0 (0x010000)” e “Next OTA partition: app1 (0x150000)”. Rode o código de novo depois de uma atualização OTA e você verá que agora ele roda a partir do app1. O valor de ESP.getFreeSketchSpace() é o tamanho do maior programa que você pode carregar por OTA; se o seu novo arquivo .bin for maior, a atualização não começa.

Método 1: carregamento sem fio pelo Arduino IDE com ArduinoOTA

A biblioteca ArduinoOTA vem com o core do ESP32, então não há nada para instalar. Quando a placa entra na rede, ela se anuncia via mDNS; o Arduino IDE percebe isso e adiciona uma porta de rede ao menu Porta. Ao clicar em Carregar, o IDE envia o programa por essa porta de rede em vez do USB. No ESP32, o serviço OTA escuta por padrão na porta 3232. O sketch abaixo conecta ao Wi-Fi, inicia o OTA com senha e pisca o LED integrado como prova:

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}
LinhaO que faz?
ArduinoOTA.setHostname("bluegrays-esp32") (nome na rede)O nome que aparece na lista de portas do IDE e na rede. Se você tem várias placas, dê um nome diferente a cada uma.
ArduinoOTA.setPassword("ota1234") (senha)Sem senha, qualquer pessoa na sua rede pode carregar código na sua placa. O IDE pede essa senha antes de carregar.
onStart / onEnd (início / fim)Rodam quando a atualização começa e termina. O lugar ideal para parar motores, desligar relés ou mostrar “Atualizando” em um display.
onProgress (progresso)Fornece a porcentagem; você pode desenhar uma barra de progresso em um LCD ou OLED.
onError (erro)Informa o tipo de erro: senha, falta de espaço, conexão, recepção ou finalização.
ArduinoOTA.handle() (verificação)Verifica se chegaram pedidos de OTA. Se não for chamada com frequência dentro de loop(), a placa não consegue responder ao IDE.

Passo a passo: seu primeiro carregamento OTA

  1. Edite os valores de WIFI_SSID e WIFI_PASS no código para a sua rede. O ESP32 só conecta a redes de 2,4 GHz.
  2. No menu Ferramentas, selecione sua placa (por exemplo, ESP32 Dev Module) e um Partition Scheme que inclua OTA (Default ou Minimal SPIFFS).
  3. Faça o primeiro carregamento com um cabo USB. Este passo é obrigatório, porque a placa ainda não tem código de OTA.
  4. Abra o Monitor Serial em 115200 baud e anote o endereço IP que a placa mostra.
  5. Desconecte o cabo USB e, se quiser, alimente a placa com outra fonte. Em 10–20 segundos aparece uma linha como “bluegrays-esp32 at 192.168.1.x” em portas de rede, no menu Ferramentas > Porta; selecione-a.
  6. Mude o atraso do LED no código de 500 para 100 e clique em Carregar. Quando o IDE pedir a senha, digite “ota1234”.
  7. Na janela de saída você verá a porcentagem de progresso e, no fim, uma linha como “Done” ou “Result: OK”. A placa reinicia e o LED agora pisca rápido.
Se a porta de rede não aparecer, primeiro confira se o computador e o ESP32 estão na mesma rede; redes de convidados e alguns sistemas mesh impedem que os dispositivos se enxerguem. Reiniciar o IDE, reiniciar a placa ou simplesmente esperar alguns segundos costuma bastar. No Mac e no Linux o IDE usa o script espota.py para o carregamento, então o Python 3 precisa estar instalado no seu computador.

Há uma limitação que você precisa conhecer: a porta de rede serve apenas para carregar, e o Monitor Serial não funciona sem fio. Para ver as mensagens da placa, conecte o USB de novo ou mostre o status em uma página web, em um display ou por mensagens MQTT. Enquanto desenvolve na bancada, você também pode deixar a placa no USB e ainda assim carregar pela rede; assim você tem carregamentos rápidos e o Monitor Serial ao mesmo tempo.

Proteção por senha e segurança

Uma placa com o serviço OTA aberto é uma porta pela qual qualquer pessoa na sua rede pode carregar código. No ArduinoOTA, setPassword() guarda a senha como texto puro dentro do código; se você vai compartilhar o código, pode passar o hash MD5 da senha com setPasswordHash(), assim a senha em si não aparece no código-fonte. O ElegantOTA protege a página /update com usuário e senha (HTTP Basic Auth).

  • Dê a cada dispositivo uma senha diferente e difícil de adivinhar: valores como “1234” ou “admin” são os primeiros que alguém tenta.
  • Não exponha a placa à internet: abrir o OTA ou a página /update para fora com redirecionamento de portas no roteador é um risco sério; esses métodos funcionam sobre HTTP sem criptografia.
  • Use HTTPUpdate para dispositivos remotos: como a própria placa pede a atualização, basta uma conexão de saída; se você baixa pela internet, prefira HTTPS com WiFiClientSecure.
  • Entre em um estado seguro durante a atualização: pare motores e desligue os relés de aquecedores e bombas dentro de onStart. A reinicialização leva alguns segundos e os pinos podem ficar indefinidos nesse tempo.

Método 2: carregar um .bin pelo navegador com ElegantOTA

O ElegantOTA adiciona uma página /update caprichada ao servidor web que roda no ESP32. Você abre essa página no celular ou em qualquer computador sem o Arduino IDE, escolhe o arquivo .bin compilado e envia; a barra de progresso aparece no navegador. É o método mais prático quando você entrega o dispositivo para outra pessoa ou atualiza em campo.

  1. No Arduino IDE, abra o Gerenciador de Bibliotecas, busque ElegantOTA (Ayush Sharma) e instale.
  2. Edite o código abaixo e carregue pelo USB na primeira vez.
  3. Abra no navegador o endereço mostrado no Monitor Serial (por exemplo, http://192.168.1.42/update) e entre com o usuário “admin” e a senha “ota1234”.
  4. Deixe Firmware selecionado como modo e arraste e solte o arquivo .bin; quando o envio termina, a placa reinicia sozinha.
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}

Por padrão, o ElegantOTA trabalha com a classe síncrona WebServer que vem com o core, por isso as chamadas server.handleClient() e ElegantOTA.loop() dentro de loop() são obrigatórias. Se o seu projeto já usa ESPAsyncWebServer, você pode definir a opção ELEGANTOTA_USE_ASYNC_WEBSERVER da biblioteca como 1 e usá-la com o servidor assíncrono; explicamos como montar o servidor assíncrono no post ESP32 Async Web Server e WebSocket. Já o modo Filesystem da página atualiza a imagem do LittleFS ou SPIFFS (arquivos da página web, arquivos de configuração) em vez do programa.

Obtendo o arquivo .bin: exportando o binário compilado

Para o ElegantOTA e o HTTPUpdate você precisa do arquivo do programa compilado. No Arduino IDE, execute Sketch > Exportar binário compilado. O IDE compila o programa e deixa vários arquivos em uma pasta build dentro da pasta do sketch. Só um deles serve para OTA:

ArquivoConteúdoUsado no OTA?
meuprojeto.ino.binSó a aplicação (o seu programa)Sim, este é o arquivo a enviar
meuprojeto.ino.bootloader.binBootloaderNão
meuprojeto.ino.partitions.binTabela de partiçõesNão
meuprojeto.ino.merged.bin (core 3.x)Bootloader, tabela e programa combinadosNão, ele só é gravado pelo USB no endereço 0x0
Confira se você compilou o programa para o chip da sua placa. Se tentar enviar um .bin compilado para o ESP32 a um ESP32-S3 ou ESP32-C3, ele é rejeitado na verificação final e a placa continua rodando o programa antigo. Se escolher merged.bin ou bootloader.bin por engano, a atualização para com “Wrong Magic Byte”, um erro de falta de espaço ou um erro de ativação.

Método 3: atualizações automáticas a partir de um servidor com HTTPUpdate

Imagine que você tem cinco placas de sensores espalhadas pela casa. Em vez de carregar em cada uma pelo IDE, você coloca o novo arquivo .bin em um servidor e as placas baixam sozinhas. A biblioteca HTTPUpdate, que vem com o core do ESP32, faz isso em uma única linha. No exemplo abaixo, a atualização começa quando você aperta o botão BOOT (GPIO 0) da placa:

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}

Para testar, coloque o arquivo .bin em uma pasta do computador com o nome firmware.bin e rode python3 -m http.server 8000 nessa pasta; troque o endereço IP no código pelo IP local do seu computador. No uso real, você troca o botão por um temporizador: por exemplo, uma vez por hora a placa lê um pequeno arquivo “version.txt” e, se o número de versão no servidor for maior que o dela, inicia a atualização. Assim ela também nunca fica presa baixando o mesmo arquivo e reiniciando sem parar.

O que acontece se a conexão cair durante o OTA?

Resposta curta: sua placa não vai virar tijolo. Como o novo programa é gravado na partição sem uso e a partição de boot só é trocada depois que o arquivo inteiro foi verificado, um carregamento interrompido deixa a placa rodando o programa antigo. Você verá “Error Uploading” no IDE, OTA_RECEIVE_ERROR no ArduinoOTA ou um resultado de falha no ElegantOTA; resolva o problema e repita o carregamento. Se a energia cair no meio do carregamento, é a mesma coisa: a placa inicia com o programa antigo. O único risco real é o próprio programa novo ter defeito: se você carregar um programa que não conecta ao Wi-Fi ou não contém o código de OTA, vai precisar do cabo de novo para a próxima atualização.

  • Sinal de Wi-Fi fraco: se o carregamento trava por volta de 30–60%, aproxime a placa do roteador ou use um módulo com antena externa. Se WiFi.RSSI() no Monitor Serial estiver pior que -75 dBm, espere quedas; veja ESP32 não conecta ao Wi-Fi para mais detalhes.
  • Alimentação insuficiente: gravar na flash e transmitir pelo Wi-Fi puxam corrente ao mesmo tempo. Uma porta USB fraca ou um cabo fino pode resetar a placa durante o carregamento; isso aparece como um erro de brownout. Use uma fonte de 5 V firme, que forneça pelo menos 500 mA.
  • Esperas longas dentro de loop(): esperas como delay(5000) atrasam a chamada de handle() e o IDE mostra “No response from the ESP”. Faça a temporização com millis().
  • Firewall: no ArduinoOTA, quando o carregamento começa, o ESP32 se conecta de volta ao seu computador. Se o Firewall do Windows ou um antivírus bloquear essa conexão de entrada, o carregamento falha logo no início; permita redes privadas na janela de permissão que aparece na primeira tentativa.

Mantendo o OTA em todo sketch novo

A decepção mais comum com OTA é assim: você carrega por OTA um projeto completamente diferente, esse projeto não tem o código ArduinoOTA e a placa não aparece mais como porta de rede. O programa recém-carregado substitui totalmente o antigo; o OTA não é algo que “fica na placa”, cada programa precisa trazê-lo consigo. Para não subir a escada até o dispositivo do teto de novo, transforme estas regras em hábito:

  • Use um modelo: coloque a conexão Wi-Fi e as configurações do ArduinoOTA em uma função separada (por exemplo, setupOTA()) e comece todo projeto novo a partir desse arquivo.
  • Coloque a chamada de handle() bem no início de loop() e nunca deixe dentro de loop() um laço que bloqueie por muito tempo.
  • Reconecte se o Wi-Fi cair: use WiFi.setAutoReconnect(true) ou verifique a conexão regularmente; uma placa sem conexão não recebe atualizações.
  • Adicione uma janela de manutenção a projetos com deep sleep: uma placa dormindo não ouve o pedido de OTA. Escreva um modo que espere 60 segundos pelo OTA antes de dormir se um botão estiver pressionado na inicialização ou se uma flag MQTT tiver chegado. Para os modos de sono, veja o post ESP32 Deep Sleep.
  • Teste primeiro em uma segunda placa na bancada: antes de enviar ao dispositivo em campo, rode o mesmo código em uma placa ao seu lado; se ela conecta ao Wi-Fi e aparece como porta de rede, é seguro enviar.
  • Mostre um número de versão: mantenha uma constante FW_VERSION no topo do código e exiba-a na página web ou no display; assim você sempre sabe qual versão está rodando em qual placa.

OTA no ESP8266 e no Raspberry Pi Pico W

O código de ArduinoOTA e ElegantOTA deste guia também roda em placas ESP8266 (NodeMCU, Wemos D1 Mini) com pequenas mudanças: usa-se ESP8266WiFi.h em vez de WiFi.h, ESP8266WebServer em vez de WebServer, e a porta do OTA é 8266. Em placas ESP8266 com 4 MB de flash, escolha uma opção de Flash Size que deixe espaço de programa suficiente para o OTA. No Raspberry Pi Pico W, o core arduino-pico oferece suporte a ArduinoOTA; nele, o novo programa é primeiro baixado para o sistema de arquivos LittleFS e copiado para o lugar na inicialização. Por isso, no Pico W você precisa reservar no menu Flash Size uma área de sistema de arquivos maior que o seu programa. O MicroPython não tem uma ferramenta de OTA pronta; dá para escrever pequenos scripts que baixam e substituem arquivos .py pela rede, mas isso não é uma atualização verificada com duas partições como no ESP32.

Ideias de projetos com OTA

  • Sensor de teto ou de parede: mude os limites de uma placa que mede temperatura, umidade e movimento sem encostar em nenhum cabo.
  • Relé de casa inteligente: atualize por OTA as regras de horário da placa de relé na caixa elétrica; coloque os relés em estado seguro dentro de onStart.
  • Dispositivos do Home Assistant: adicione novos recursos pela rede a sensores conectados ao Home Assistant via MQTT.
  • Controlador de irrigação do jardim: mude os horários de rega e o limite de umidade do solo sem abrir a caixa à prova d'água.
  • Barra de progresso em um LCD: mostre o valor de onProgress como porcentagem e barra em um LCD 16x2; o projeto ESP32 OTA do BlueGrays faz exatamente isso.
  • Atualização de frota: faça várias placas verificarem toda noite, com HTTPUpdate, se há uma nova versão no mesmo servidor.

Erros comuns e como resolver

SintomaCausa provávelSolução
Nenhuma porta de rede no menu PortaPlaca em outra rede, mDNS bloqueado, o código não chama ArduinoOTA.begin()Use a mesma rede de 2,4 GHz, reinicie o IDE, confira se o IP aparece no Monitor Serial
“No response from the ESP” (sem resposta do ESP)delay() longo em loop(), placa ocupada ou firewallUse millis() em vez de delay(), libere o IDE no firewall
“Authentication Failed” (falha de autenticação)Senha de OTA erradaDigite o valor de setPassword do código; se esqueceu, carregue uma vez pelo USB
“Not Enough Space” / Begin Failed (espaço insuficiente / falha ao iniciar)Esquema Huge APP ou No OTA, o programa não cabe na segunda áreaEscolha Minimal SPIFFS e carregue uma vez pelo USB; reduza o programa
O carregamento para no meioSinal fraco, queda de alimentação, conexão perdidaAproxime a placa do roteador, use uma fonte de 5 V firme, tente de novo; o programa antigo continua rodando
A placa não entra na rede depois do carregamentoO código novo não tem Wi-Fi ou OTA, SSID erradoCorrija pelo USB; daqui em diante, comece todo projeto pelo modelo com OTA
“Wrong Magic Byte” ou erro de ativação no ElegantOTAFoi escolhido o merged.bin, o bootloader.bin ou um arquivo compilado para outro chipEnvie apenas o arquivo meuprojeto.ino.bin
Ao configurar o OTA pela primeira vez, deixe a placa no USB com o Monitor Serial aberto; a saída de onError diz em uma única linha se o problema é a senha, o espaço ou a conexão. Quando tudo funcionar, você pode tirar o cabo.

O OTA é a chave para tirar os projetos com ESP32 da bancada e instalá-los em um lugar de verdade: depois de bem configurado, correções, novos recursos e mudanças de limites chegam sem fio em segundos. No app BlueGrays você encontra o projeto ESP32 OTA Update (com IP e progresso mostrados em um LCD), junto com os projetos de conexão Wi-Fi, Async Web Server, mDNS e NTP, cada um com esquema do circuito e código, passo a passo.

Perguntas frequentes

O que é OTA no ESP32?

OTA (Over-The-Air) significa carregar um novo programa no ESP32 pelo Wi-Fi em vez de um cabo USB. A placa grava o novo código em uma partição de programa sem uso, verifica e reinicia com a nova versão. Você precisa carregar o primeiro sketch com OTA uma vez pelo USB.

Por que a porta de rede do ESP32 não aparece no Arduino IDE?

Os motivos mais comuns são o computador e a placa estarem em redes diferentes, uma rede de convidados isolando os dispositivos e a chamada ArduinoOTA.begin() do código nunca ser executada. Confira no Monitor Serial se a placa recebeu um IP, reinicie o IDE e espere alguns segundos.

Qual partition scheme devo escolher para OTA no ESP32?

Em placas com 4 MB de flash, escolha Default (área de programa de 2 × 1,25 MB) ou, se o seu programa for grande, Minimal SPIFFS (2 × 1,875 MB). Huge APP e No OTA não têm segunda área de programa, então o OTA não funciona com eles. O primeiro carregamento depois de mudar o esquema precisa ser feito pelo USB.

Um carregamento OTA interrompido pode estragar o ESP32?

Não. O novo programa é gravado em uma partição vazia e a partição de boot só é trocada depois que o arquivo inteiro foi verificado. Se a conexão ou a energia cair, a placa inicia com o programa antigo e você pode simplesmente tentar o carregamento de novo.

Dá para usar o Monitor Serial ao carregar por OTA?

A porta de rede serve apenas para carregar código; o Monitor Serial não funciona sem fio. Para ver as mensagens, você pode deixar a placa conectada ao USB ou mostrar as informações em uma página web, em um display ou via MQTT.

Como definir uma senha de OTA no ESP32?

No ArduinoOTA, você chama ArduinoOTA.setPassword("senha") antes de begin(); o IDE pede essa senha antes de carregar. Se não quiser a senha visível no código, passe o hash MD5 dela com setPasswordHash(). No ElegantOTA, você protege a página com begin(&server, "usuario", "senha").

Ver todos os projetos
Atualização OTA no ESP32 — firmware sem fio (com display LCD)
#132Avançado
Premium

Atualização OTA no ESP32 — firmware sem fio (com display LCD)

Neste projeto, você aprende a carregar um novo firmware no ESP32 por Wi-Fi, sem cabo USB. Com a biblioteca ArduinoOTA, o ESP32 fica visível na rede e aparece automaticamente como bluegrays-ota na aba "Porta de rede" do Arduino IDE. A primeira carga é feita por USB, e todas as atualizações seguintes são sem fio — um recurso essencial para dispositivos em campo. O LCD I2C mostra o estado do Wi-Fi, o IP, o nome do host, a porcentagem de envio por OTA e as mensagens de erro em turco e inglês.

4.6(14)
22
Conexão Wi-Fi STA com ESP32 e display LCD
#127Avançado

Conexão Wi-Fi STA com ESP32 e display LCD

Neste projeto, conectamos o ESP32 à rede Wi-Fi de casa no modo estação (STA), de forma segura e tolerante a falhas. Guardamos o SSID e a senha em um arquivo secrets.h separado para evitar que sejam expostos no Git, configuramos um mecanismo de reconexão automática e exibimos o nível de sinal RSSI e o endereço IP em um display LCD I2C. O LED integrado funciona como indicador de status. É um gerenciador de conexão pronto para uso em produção.

4.4(21)
34
Servidor web assíncrono com ESP32 (IP no LCD)
#129Avançado

Servidor web assíncrono com ESP32 (IP no LCD)

Neste projeto, transformamos o ESP32 em um servidor web assíncrono que você pode controlar pelo celular ou computador na mesma rede Wi-Fi. Com a biblioteca ESPAsyncWebServer, o servidor processa várias requisições ao mesmo tempo (ao contrário do WebServer.h tradicional, que bloqueia a execução ao processar uma requisição). Você poderá abrir o endereço IP do ESP32 no navegador e clicar nos botões ON/OFF (as rotas /on e /off) para ligar e desligar o LED conectado ao GPIO 5.

4.8(16)
26
ESP32 mDNS: acesso via bluegrays-esp.local (com display LCD)
#131Avançado
Premium

ESP32 mDNS: acesso via bluegrays-esp.local (com display LCD)

Neste projeto, adicionamos o serviço mDNS (Multicast DNS) ao ESP32 para acessar o dispositivo por um nome de host fácil de ler, como bluegrays-esp.local, em vez de um endereço IP. Mesmo que o IP atribuído pelo DHCP mude, você pode acessar o dispositivo pelo mesmo nome. Também executamos um pequeno servidor web no dispositivo para enviar comandos de controle do LED pelo navegador no endereço http://bluegrays-esp.local/.

4.8(14)
23
Contador WebSocket com ESP32 (2 botões + LCD + web em tempo real)
#130Avançado
Premium

Contador WebSocket com ESP32 (2 botões + LCD + web em tempo real)

Neste projeto, dois botões conectados ao ESP32 (GPIO 5 aumenta, GPIO 4 diminui) alteram um contador. O valor é atualizado instantaneamente no LCD I2C 16x2 e, com o suporte a WebSocket do ESPAsyncWebServer, em todos os navegadores conectados, sem recarregar a página. Em vez de consultas periódicas por HTTP, usamos uma única conexão persistente, e o ESP32 envia o novo valor a todos com ws.textAll().

4.5(14)
23
Hora exata pela internet com ESP32 e NTP
#128Avançado
Premium

Hora exata pela internet com ESP32 e NTP

O ESP32 se conecta ao Wi-Fi e obtém a hora exata com precisão de milissegundos de servidores NTP (Network Time Protocol). Com a função configTzTime(), o fuso horário configurado (TRT, UTC+3 neste exemplo) é aplicado automaticamente, e as mudanças nos países que adotam horário de verão são gerenciadas automaticamente por uma string POSIX TZ. A hora é exibida tanto no Monitor Serial quanto em um LCD I2C opcional.

4.5(13)
21
ESP32 HTTPClient + API de clima (com display LCD)
#133Avançado
Premium

ESP32 HTTPClient + API de clima (com display LCD)

Neste projeto, usamos o ESP32 para fazer uma chamada REST à API de clima wttr.in pela internet com HTTPClient.h. Interpretamos a resposta JSON com ArduinoJson v7 e mostramos cidade, temperatura, umidade, condições do tempo e vento em um display LCD I2C, em turco e inglês. O wttr.in é grátis e não exige chave de API — basta informar o nome de uma cidade para consultar o clima atual. Os dados se atualizam sozinhos a cada 30 minutos, e o LED dá um retorno visual diferente para cada tela.

4.7(13)
20
ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)
#135Avançado
Premium

ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)

O modo de sono profundo do ESP32 reduz o consumo de corrente para ~10 µA ao desligar a CPU e a maioria dos periféricos, sendo essencial para dispositivos IoT alimentados por bateria. Neste projeto, usamos três fontes de despertar: um temporizador (15 segundos), um pino externo (EXT0 — botão no GPIO 33) e toque (T0/GPIO 4). As variáveis marcadas com RTC_DATA_ATTR são preservadas durante o sono profundo, mantendo os contadores de inicializações e de despertares por temporizador e botão.

4.3(10)
16