banner

Gravando o ESP32 pela Internet “OTA”


Já falei aqui antes sobre OTA, mas como este é um assunto bastante pedido por vocês e as mudanças estão ocorrendo de forma muito rápida, resolvi voltar neste vídeo a falar sobre métodos de distribuição de novas atualizações Over-The-Air (OTA). Vou, então, te oferecer uma opção de OTA com um nível de segurança um pouco superior aos exemplos gravando um ESP32 pelo WiFi.





RECURSOS USADOS

  • 1x ESP Wroom 32
  • 1x WiFi
  • Arduino IDE






SEQUÊNCIA DE VERIFICAÇÃO

Neste exemplo de OTA, aplicaremos uma sequência de verificação antes de iniciar a atualização de fato. Ela será baseada na verificação de algum código de autorização, enviado através da página html inicial, e verificado pelo próprio ESP.
Se a verificação falhar, o ESP não servirá a página para seleção de arquivo e impedirá a continuidade do processo.




GERANDO O .BIN A SER GRAVADO






CONEXÃO EXTERNA


É possível também efetuar uma conexão através da internet. Para isso, mantenha o ESP com um endereço fixo e redirecione seu endereço IP e sua porta usando os recursos disponíveis no seu roteador.
Para se conectar, basta utilizar o endereço fornecido pelo seu provedor, para sua conexão WAN, e apontar para a porta roteada.
Nem todos os roteadores possuem essa função e as particularidades da rede em questão devem ser levadas em consideração.




SEQUÊNCIA DE VERIFICAÇÃO

Ao acessar o endereço IP do ESP a ser atualizado, ele servirá a primeira página.
No código de exemplo, introduzimos nesta página algumas informações sobre o chip e a versão do código gravado. Aqui elas servem simplesmente para exemplificar que isso é possível e pode ser bastante útil.
Em seguida, observamos a solicitação de uma chave de autorização.



Se uma chave incorreta for introduzida, o ESP servirá uma página indicando o um ERRO no processo de atualização.
Isso ocorre porque a chave de verificação enviada não correspondeu com a esperada pelo código do ESP.



A chave de verificação dependerá do algoritmo de verificação implementado pelo desenvolvedor. Neste exemplo colocamos uma verificação bastante simples. Basta que a chave seja ”123456” para ser aceita.
Uma forma interessante seria usar, por exemplo, uma verificação que evolvesse o id do chip. Isso traria variedade a proteção.
Outro detalhe é que neste exemplo, usamos uma caixa de texto comum, para facilitar a visualização. Mas poderíamos ter utilizado uma caixa de senha.



Se a verificação da chave foi positiva, o ESP servirá a página de seleção do arquivo para upload.
Se um arquivo inválido for enviado, a página de ERRO será servida. Caso contrário, uma página de conclusão deverá ser enviada.
Em alguns casos, devido ao reinicio do ESP (reset), a conexão com o navegador pode ser perdida e a página de resposta pode não ser recebida. Na dúvida, reenvie o arquivo.



Esta é a página recebida depois da conclusão bem sucedida da atualização.
Note que a versão indica por ela ainda é a mesma. Isso porque ela é enviada antes do reinicio do ESP.



Mas, se carregarmos novamente a página inicial, poderemos observar a alteração da versão que introduzimos nas informações do código.



Atenção: observe o código fonte a seguir para perceber que essa atualização do número da versão foi introduzida para que fosse possível verificar que um novo código foi gravado no ESP, mas não existe nenhum gerenciamento automático de versão.



Podemos acompanhar todo processo também pela serial, para debug.
Ao lado vemos as mensagens iniciais do ESP. Além das informações padrão, enviamos também a versão e o id do chip.



Em seguida, podemos acompanhar pela serial:
  • O recebimento do número de verificação que enviamos através da página.
  • O início da atualização, indicando o nome do arquivo selecionado.
  • Uma mensagem de atualização bem sucedida.
  • Uma mensagem indicando que o ESP será reiniciado.
  • E um novo conjunto de informações iniciais indicando a nova versão do firmware.







CÓDIGO-FONTE

Declarações

// Exemplo de WebOTA 
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <Update.h>

//Parâmetros da rede WiFi
const char* ssid = "xxxxxxxxxx";
const char* password = "yyyyyyy";

//Parâmetros de rede
IPAddress local_ip(192, 168, 0, 121);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
const uint32_t PORTA = 80; //A porta que será utilizada (padrão 80)

//Algumas informações que podem ser interessantes
const uint32_t chipID = (uint32_t)(ESP.getEfuseMac() >> 32); //um ID exclusivo do Chip...
const String CHIP_ID = "<p> Chip ID: " + String(chipID) + "</p>"; // montado para ser usado no HTML
const String VERSION = "<p> Versão: 3.0 </p>"; //Exemplo de um controle de versão

//Informações interessantes agrupadas
const String INFOS = VERSION + CHIP_ID;

//Sinalizador de autorização do OTA
boolean OTA_AUTORIZADO = false;

//inicia o servidor na porta selecionada
//aqui testamos na porta 3000, ao invés da 80 padrão
WebServer server(PORTA);

//Páginas HTML utilizadas no procedimento OTA
String verifica = "<!DOCTYPE html><html><head><title>ESP32 webOTA</title><meta charset='UTF-8'></head><body><h1>ESP32 webOTA</h1><h2>Digite a chave de verificação.<p>Clique em ok para continuar. . .</p></h2>" + INFOS + "<form method='POST' action='/avalia 'enctype='multipart/form-data'> <p><label>Autorização: </label><input type='text' name='autorizacao'></p><input type='submit' value='Ok'></form></body></html>";
String serverIndex = "<!DOCTYPE html><html><head><title>ESP32 webOTA</title><meta charset='UTF-8'></head><body><h1>ESP32 webOTA</h1><h2>Selecione o arquivo para a atualização e clique em atualizar.</h2>" + INFOS + "<form method='POST' action='/update' enctype='multipart/form-data'><p><input type='file' name='update'></p><p><input type='submit' value='Atualizar'></p></form></body></html>";
String Resultado_Ok = "<!DOCTYPE html><html><head><title>ESP32 webOTA</title><meta charset='UTF-8'></head><body><h1>ESP32 webOTA</h1><h2>Atualização bem sucedida!</h2>" + INFOS + "</body></html>";
String Resultado_Falha = "<!DOCTYPE html><html><head><title>ESP32 webOTA</title><meta charset='UTF-8'></head><body><h1>ESP32 webOTA</h1><h2>Falha durante a atualização. A versão anterior será recarregado.</h2>" + INFOS + "</body></html>";


A páginas HTML (verifica)



A páginas HTML (serverIndex)



A páginas HTML (Resultado_Ok)



A páginas HTML (Resultado_Falha)



Setup()

//Setup
void setup(void)
{
  Serial.begin(115200); //Serial para debug

  WiFi.mode(WIFI_AP_STA); //Comfigura o ESP32 como ponto de acesso e estação

  WiFi.begin(ssid, password);// inicia a conexão com o WiFi

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (WiFi.status() == WL_CONNECTED) //aguarda a conexão
  {
    //atende uma solicitação para a raiz
    // e devolve a página 'verifica'
    server.on("/", HTTP_GET, []() //atende uma solicitação para a raiz
    {
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", verifica);
    });

    //atende uma solicitação para a página avalia
    server.on("/avalia", HTTP_POST, [] ()
    {
      Serial.println("Em server.on /avalia: args= " + String(server.arg("autorizacao"))); //somente para debug

      if (server.arg("autorizacao") != "123456") // confere se o dado de autorização atende a avaliação
      {
        //se não atende, serve a página indicando uma falha
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", Resultado_Falha);
        //ESP.restart();
      }
      else
      {
        //se atende, solicita a página de índice do servidor
        // e sinaliza que o OTA está autorizado
        OTA_AUTORIZADO = true;
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", serverIndex);
      }
    });

    //serve a página de indice do servidor
    //para seleção do arquivo
    server.on("/serverIndex", HTTP_GET, []()
    {
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", serverIndex);
    });

    //tenta iniciar a atualização . . .
    server.on("/update", HTTP_POST, []()
    {
      //verifica se a autorização é false.
      //Se for falsa, serve a página de erro e cancela o processo.
      if (OTA_AUTORIZADO == false)
      {
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", Resultado_Falha);
        return;
      }
      //Serve uma página final que depende do resultado da atualização
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", (Update.hasError()) ? Resultado_Falha : Resultado_Ok);
      delay(1000);
      ESP.restart();
    }, []()
    {
      //Mas estiver autorizado, inicia a atualização
      HTTPUpload& upload = server.upload();
      if (upload.status == UPLOAD_FILE_START)
      {
        Serial.setDebugOutput(true);
        Serial.printf("Atualizando: %s\n", upload.filename.c_str());
        if (!Update.begin())
        {
          //se a atualização não iniciar, envia para serial mensagem de erro.
          Update.printError(Serial);
        }
      }
      else if (upload.status == UPLOAD_FILE_WRITE)
      {
        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)
        {
          //se não conseguiu escrever o arquivo, envia erro para serial
          Update.printError(Serial);
        }
      }
      else if (upload.status == UPLOAD_FILE_END)
      {
        if (Update.end(true))
        {
          //se finalizou a atualização, envia mensagem para a serial informando
          Serial.printf("Atualização bem sucedida! %u\nReiniciando...\n", upload.totalSize);
        }
        else
        {
          //se não finalizou a atualização, envia o erro para a serial.
          Update.printError(Serial);
        }
        Serial.setDebugOutput(false);
      }
      else
      {
        //se não conseguiu identificar a falha no processo, envia uma mensagem para a serial
        Serial.printf("Atualização falhou inesperadamente! (possivelmente a conexão foi perdida.): status=%d\n", upload.status);
      }
    });

    server.begin(); //inicia o servidor

    Serial.println(INFOS); //envia as informações armazenadas em INFOS, para debug

    //Envia ara a serial o IP atual do ESP
    Serial.print("Servidor em: ");
    Serial.println( WiFi.localIP().toString() + ":" + PORTA);
  }
  else
  {
    //avisa se não onseguir conectar no WiFi
    Serial.println("Falha ao conectar ao WiFi.");
  }
}


Loop()

void loop(void)
{
  //manipula clientes conectados
  server.handleClient();

  delay(1);//somente um instante
}






FAÇA O DOWNLOAD DOS ARQUIVOS

PDF

INO




Um comentário:

  1. Teria como deixar as bibliotecas usadas para downloads ?

    ResponderExcluir

Tecnologia do Blogger.