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 }
3 Comentários
Teria como deixar as bibliotecas usadas para downloads ?
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluircomo consigo as bibliotecas webserver e update?
ResponderExcluir