fbpixel
Etiquetas: ,

Um recurso muito prático no campo da IoT é a capacidade de programar um microcontrolador conectado, como um ESP32, via WiFi. Esta técnica é designada por Programação Over-The-Air (OTA).

Hardware

  • NodeMCU ESP32
  • Computador
  • Rede WiFi

 

Princípio de funcionamento

Em princípio, descarrega-se um programa para o ESP32 NodeMCU através de uma ligação em série à porta USB. O computador comunica o programa ao microcontrolador. Quando dois dispositivos estão ligados à mesma rede WiFi, podem comunicar entre si. Assim, é possível carregar o código através da rede WiFi sem ter de se ligar a cada microcontrolador.

É igualmente possível carregar um código no NodeMCU quando este está configurado como ponto de acesso WiFi. Neste caso, o computador utilizado para a programação deve estar ligado à rede do NodeMCU. O código completo é apresentado no final deste tutorial.

 

 

Adicionar firmware ArduinoOTA

A biblioteca ArduinoOTA está disponível quando se instala o gestor do ESP32. Para configurar a programação OTA, é necessário instalar o firmware. Para o fazer, pode utilizar o exemplo BasicOTA.ino disponível no IDE Arduino.

Certifique-se de que substitui ssid e password pelos identificadores da sua rede WiFi.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid  = "*******";
const char* password  = "********";

unsigned long previousMillis;

void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  
    // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  //ArduinoOTA.setHostname("esp3232");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
      else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
      else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
      else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
      else if (error == OTA_END_ERROR) Serial.println("End Failed");
    });

  ArduinoOTA.begin();
  
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();
}

ota-programming-basicota-firmware-result Programar um ESP32 via WiFi com o IDE Arduino (OTA)

Anote o endereço IP local do aparelho (aqui: 192.168.1.33).

Quando este código tiver sido carregado através de comunicação série, poderá carregar um código através de Wifi. Prima o botão RST ou EN no seu ESP e reinicie o Arduino IDE.

 

Modificar o seu programa antes de o carregar

Colocamos o código de inicialização na função initOTA(). Assim, será fácil copiá-lo para outros projectos. E acrescentamos um ecrã na porta série para verificar se o código foi modificado.

ATENÇÃO: O código de inicialização do ArduinoOTA deve estar presente em todos os códigos que carregar, caso contrário perderá a capacidade de efetuar transmissões aéreas.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid  = "******";
const char* password  = "********";

unsigned long previousMillis;

void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  initOTA();

  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();

  if (millis() - previousMillis >= 500) {
    previousMillis = millis();
    Serial.println(F("Code has been update"));
  }
}


void initOTA() {
  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  ArduinoOTA.setHostname("ESP32");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
  .onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  })
  .onEnd([]() {
    Serial.println("\nEnd");
  })
  .onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  })
  .onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  ArduinoOTA.begin();
}

Una vez que haya modificado su código, en “Herramienta> Puerto”, seleccione su tarjeta entre los puertos de red. (si no aparece el puerto de red, consulte el capítulo siguiente).

ota-programming-network-port Programar um ESP32 via WiFi com o IDE Arduino (OTA)

Pode então carregar o seu código como faria com a comunicação em série.

ota-programming-arduino-uploading Programar um ESP32 via WiFi com o IDE Arduino (OTA)

Seleccione novamente a porta série e abra o monitor série para verificar se o código foi modificado.

ota-programming-result Programar um ESP32 via WiFi com o IDE Arduino (OTA)

 

Se a porta de rede não for exibida no IDE do Arduino

Se a porta de rede não estiver presente nas opções do Arduino IDE, terá de fazer um pequeno trabalho. É necessário ir ao Centro de Rede e Partilha

windows-network-center Programar um ESP32 via WiFi com o IDE Arduino (OTA)

Em seguida, aceda a “Modificar parâmetros do cartão”.

Clique com o botão direito do rato na sua rede e vá para propriedades

Pode então desmarcar a opção “Protocolo Internet versão 6 (TCP)”.

windows-network-adapter-disable-ipv6 Programar um ESP32 via WiFi com o IDE Arduino (OTA)

Se voltar ao Arduino IDE depois de o reiniciar, em “Tool > Port” (Ferramenta > Porta) deve ver a opção de porta de rede apresentada.

Pode verificar novamente o IPv6 assim que a sua porta de rede for reconhecida.

 

Bónus: visualização de mensagens de série OTA

A possibilidade de carregar um programa via Wi-Fi é muito útil, mas é preciso notar que se perde a possibilidade de depurar com o monitor de série. É possível criar uma interface Web que permita visualizar as informações do ESP32 NodeMCU.

Aqui veremos como usar a biblioteca RemoteDebug para se conectar ao microcontrolador via telnet e recuperar as mensagens enviadas.

  • Comece por instalar a biblioteca descarregando o ficheiro ZIP ou através do gestor de bibliotecas.
arduino-ide-ota-prorgamming-remotedebug Programar um ESP32 via WiFi com o IDE Arduino (OTA)
  • Criar um objeto RemoteDebug Debug; depois de incluir o objeto
  • Inicializar a depuração com Debug.begin(“ESP32”);
  • Em seguida, substitua todos os Serial.print por Debug.print
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <RemoteDebug.h>

const char* ssid  = "*******";
const char* password  = "*******";

RemoteDebug Debug;

unsigned long previousMillis;

void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // init remote debug
  Debug.begin("ESP32");  
  
  initOTA();

  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();
  Debug.handle();
  
  if (millis() - previousMillis >= 500) {
    previousMillis = millis();
    Debug.println(F("Code has been update"));
  }
}


void initOTA() {
  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  ArduinoOTA.setHostname("ESP32");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
  .onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  })
  .onEnd([]() {
    Serial.println("\nEnd");
  })
  .onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  })
  .onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  ArduinoOTA.begin();
}

Para visualizar as mensagens enviadas via RemoteDebug, vamos utilizar o software PuTTy que já vimos várias vezes. Para esta aplicação, vamos configurar o PuTTy no telnet usando o endereço IP obtido do monitor serial.

putty-telnet Programar um ESP32 via WiFi com o IDE Arduino (OTA)
putty-telnet-ota-esp-result Programar um ESP32 via WiFi com o IDE Arduino (OTA)

 

Bónus 2: Programação OTA em modo de ponto de acesso

É possível utilizar a rede criada pelo ESP32 NodeMCU em modo Access Point. Para isso, pode descarregar o código seguinte através da porta série. Em seguida, é necessário ligar o computador à rede do NodeMCU para poder descarregar um novo código via WiFi.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char *ssid = "AC-ESP32";
const char *passphrase = "123456789";

IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);

unsigned long previousMillis;
void setup() {
 Serial.begin(115200);
 Serial.println("Booting");

 //AP config
 Serial.print("Setting soft-AP configuration ... ");
 Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");
 Serial.print("Setting soft-AP ... ");
 Serial.println(WiFi.softAP(ssid,passphrase) ? "Ready" : "Failed!");
   
 initOTA();
 Serial.println("Ready");
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
}
void loop() {
 ArduinoOTA.handle();
 if (millis() - previousMillis >= 500) {
   previousMillis = millis();
   Serial.println(F("Code has been update via Wifi"));
 }
}
void initOTA() {
 // Port defaults to 3232
 // ArduinoOTA.setPort(3232);
 // Hostname defaults to esp3232-[MAC]
 ArduinoOTA.setHostname("ESP32");
 // No authentication by default
 // ArduinoOTA.setPassword("admin");
 // Password can be set with it's md5 value as well
 // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
 ArduinoOTA
 .onStart([]() {
   String type;
   if (ArduinoOTA.getCommand() == U_FLASH)
     type = "sketch";
   else // U_SPIFFS
     type = "filesystem";
   // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
   Serial.println("Start updating " + type);
 })
 .onEnd([]() {
   Serial.println("\nEnd");
 })
 .onProgress([](unsigned int progress, unsigned int total) {
   Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
 })
 .onError([](ota_error_t error) {
   Serial.printf("Error[%u]: ", error);
   if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
   else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
   else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
   else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
   else if (error == OTA_END_ERROR) Serial.println("End Failed");
 });
 ArduinoOTA.begin();
}

Fontes