Estamos no penúltimo artigo sobre “Over-The-Air”, e esse post descreve a configuração client e server para atualização de firmware por WiFi, mas dessa vez o ESP8266 consultará o server de forma automática.
Esse procedimento é adequado para quando se tem um parque de dispositivos grande o suficiente para necessitar de um centralizador. Existem diversos modos de fazê-lo, mas exemplificar o conceito poderá fazê-lo refletir sobre a melhor forma para sua necessidade. Assim como nos outros artigos, utilizarei aqui o Wemos. se você não leu os outros artigos sobre a atualização via OTA, sugiro que o faça:
Requisitos
Você pode utilizar qualquer ESP8266, estou exemplificando com o Wemos porque gostei bastante dele, mas tenho vários e todos funcionam, desde o ESP01.
O segundo requisito (porque obviamente o ESP8266 é o primeiro) é um servidor web. Vou instalar um nginx em meu Raspberry Pi para esse artigo. Nesse Raspberry também tenho um servidor DNS configurado para resolver nomes públicos e para os dispositivos da rede local. Se você não leu esse artigo, não é fundamental, mas garanto que é uma boa leitura, por isso recomendo que o faça através desse link.
Procedimentos iniciais
Se você não desejar configurar um DNS na sua rede, invés de uma URL você poderá utilizar o endereço IP. Eu optei por instalar o servidor web no Raspberry porque tenho mais planos para ele e no final terei toda uma coleção de links para o processo. De qualquer modo, esse tutorial não exemplifica a instalação de um servidor web em Windows e esse tutorial serve para distribuições Linux que tenham o gerenciador de pacote apt.
Instalando nginx no Raspberry Pi / Orange Pi / Banana Pi / Linux
Esse é o processo mais simples, não tem trabalho nenhum. Simplesmente execute:
su apt-get install nginx && service nginx start
No video você vê o processo e os sintomas de tentar acessar um serviço não inicializado.
Código na IDE do Arduino
Esse é outro processo simples. Abra File→Examples→ESP8266httpUpdate→httpUpdate.
Toda a mágica se dá por 1 linha que faz todo o trabalho quando invocada:
ESPhttpUpdate.update("ns1.dobitaobyte.lan",80,"/fw.bin");
No exemplo da httpUpdate essa linha de código tem uma diferença significativa:
ESPhttpUpdate.update("http://ns1.dobitaobyte.lan/fw.bin");
Esse sketch é puramente um exemplo, não serve para mais nada além de atualizar o firmware. Ou pelo menos deveria. Passei muitas horas implementando uma boa lógica para tornar o sketch mais útil, e peguei em todos os casos um reset por watchdog. Não sei exatamente a causa, mas pela mensagem parece um reset na interrupção do pino. Tentei de tudo, testei documentação, tentei desabilitar watchdog, tentei aumentar o tempo do watchdog de 1 para 5 segundos, troquei yield() por delay(), mas nada resolveu. Não vou deixar de mostrar o código porque pode ser algum bug relacionado ao firmware que estou utilizando, ou ainda, um bug relacionado à board Wemos. Confira primeiro o sketch de exemplo da própria IDE:
/** * httpUpdate.ino * * Created on: 27.11.2015 * */ #include <Arduino.h> #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266HTTPClient.h> #include <ESP8266httpUpdate.h> #define USE_SERIAL Serial ESP8266WiFiMulti WiFiMulti; void setup() { USE_SERIAL.begin(115200); // USE_SERIAL.setDebugOutput(true); USE_SERIAL.println(); USE_SERIAL.println(); USE_SERIAL.println(); for(uint8_t t = 4; t > 0; t--) { USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); USE_SERIAL.flush(); delay(1000); } WiFiMulti.addAP("SSID", "PASSWORD"); } void loop() { // wait for WiFi connection if((WiFiMulti.run() == WL_CONNECTED)) { t_httpUpdate_return ret = ESPhttpUpdate.update("http://server/file.bin"); //t_httpUpdate_return ret = ESPhttpUpdate.update("https://server/file.bin"); switch(ret) { case HTTP_UPDATE_FAILED: USE_SERIAL.println("HTTP_UPDATE_FAILD"); break; case HTTP_UPDATE_NO_UPDATES: USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES"); break; case HTTP_UPDATE_OK: USE_SERIAL.println("HTTP_UPDATE_OK"); break; } } }
E agora, o sketch que eu escrevi implementando duas funcionalidades. Primeiro, invés de prender o programa no loop dedicando-o a ser um atualizador de firmware, coloquei uma interrupção para fazer verificação periódica. Assim, quando tiver estouro do timer, uma flag é setada e o loop trata de chamar uma função que analisa se tem uma nova versão e se tiver, inicia o processo. A segunda funcionalidade foi a consulta prévia de versão disponível. Caso a versão disponível seja maior do que a versão local, então inicia o processo de atualização. Não funcionou com o firmware padrão, mas escrevi esse post que relata o meio de atualizar o firmware e já comprovado, resolveu o bug causado que acontecia por conta do firmware antigo da Wemos. Esse código está 100% funcional:
#define S Serial #define mSSID "DobtiAoByte" #define mPASS "segredo" #define TIMEOUT_MS 3000 #include <Arduino.h> #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266HTTPClient.h> #include <ESP8266httpUpdate.h> extern "C"{ #include "user_interface.h" } os_timer_t mTimer; ESP8266WiFiMulti WiFiMulti; HTTPClient http; bool _timeout = false; bool canGo = true; String VERSION = "1.2.3"; void tCallback(void *tCall){ _timeout = true; } void usrInit(void){ os_timer_setfn(&mTimer, tCallback, NULL); os_timer_arm(&mTimer, TIMEOUT_MS, true); } void checkUpdate(void){ if (canGo){ canGo = false; } else{ return; } if((WiFiMulti.run() == WL_CONNECTED)){ http.begin("ns1.dobitaobyte.lan", 80, "/version.html"); // inicio da conexao HTTP e envio do header int httpCode = http.GET(); if(httpCode){ //S.printf("[HTTP] GET... code: %d\n", httpCode); if(httpCode == 200) { String payload = http.getString(); http.end(); payload.replace(".",""); VERSION.replace(".",""); S.print("Local Version: "); S.println(VERSION); S.print("Remote Version: "); S.println(payload); if (VERSION.toInt() < payload.toInt()){ Serial.println("New version"); payload = ""; t_httpUpdate_return ret = ESPhttpUpdate.update("http://ns1.dobitaobyte.lan/fw.bin"); switch(ret) { case HTTP_UPDATE_FAILED: S.println("HTTP_UPDATE_FAILED"); break; case HTTP_UPDATE_NO_UPDATES: S.println("HTTP_UPDATE_NO_UPDATES"); break; case HTTP_UPDATE_OK: S.println("HTTP_UPDATE_OK"); break; } } else{ S.println("No updates available for now... "); } } } else{ S.print("[HTTP] GET... failed, no connection or no HTTP server\n"); } } canGo = true; } void setup() { //ESP.wdtDisable(); //ESP.wdtEnable(5000); usrInit(); S.begin(115200); S.println(); for(char a = 'd'; a > 'a'; a--) { S.printf("[SETUP] WAIT %d...\n", a-97); S.flush(); delay(1000); } S.println("Starting connection, please wait..."); WiFiMulti.addAP(mSSID, mPASS); } void loop() { if (_timeout){ //S.println(system_get_free_heap_size()); S.println("cuco!"); checkUpdate(); _timeout = false; } yield(); }
Se quiser entender a respeito da interrupção do ESP8266, leia esse post que fiz exclusivamente para tal.
Inscreva-se no nosso newsletter, alí em cima à direita e receba novos posts por email.
Ou ainda, no rodapé, pelo disqus.
Siga-nos no Do bit Ao Byte no Facebook.
Prefere twitter? @DobitAoByte.
Inscreva-se no nosso canal Do bit Ao Byte Brasil no YouTube.
Próximo post a caminho!