Manual
do
Maker
.
com
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 (link). se você não leu os outros artigos sobre a atualização via OTA, sugiro a leitura:
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 canal Manual do Maker Brasil no YouTube.
Próximo post a caminho!
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.