ESP8266 buscando atualização de firmware em servidor web

webserverEstamos 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.

Sistemas Embarcados

BR-Arduino.org

Próximo post a caminho!





euler_banner

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.

Deixe uma resposta