ESP8266 – Como configurar atualização OTA por MQTT (2 de 2)

mosquitto

Atualização OTA

Na parte 1 de 2 expliquei em detalhes as configurações de todos os serviços necessários para criar sua estrutura de gerenciamento IoT sem depender de um broker público ou da contratação de um serviço (parte 1 sobre atualização OTA). O broker nesse caso que estou criando, é um Raspberry Pi 2. Estar com o broker dentro da mesma rede que os dispositivos tem seus prós e contras. O ideal é sim ter um broker na nuvem e utilizando SSL no canal de comunicação. Aqui estou fazendo uma configuração doméstica para exemplo de  e também para meus propósitos.

A necessidade de ter um broker na nuvem é principalmente pelo fato de disponibilidade. Se ficar sem internet em casa, nem broker, nem nada.

Se você seguiu o exemplo de configuração do broker com as ACLs especificadas aqui, basta seguir adiante. Em outro caso, volte ao post anterior e verifique se não esqueceu algum detalhe.

O propósito agora é interagir com um ESP8266 – no caso, o Wemos mesmo – e isso, utilizando as ACLs definidas no meu artigo sobre configuração do broker. Nesse post utilizaremos apenas o tópico ‘/mcu’, mas não remova o tópico ‘casa/’ se pretende implementar os controles domésticos que iniciarei tão logo haja tempo (não era pra contar, era surpresa…).

Só pra finalizar, eu sei que estamos em uma verdadeira pandemia com o coquetel de doenças oferecido pelo Aedes Aegypti e falar de mosquito nesse momento pode causar um certo desconforto (já me cocei umas 3 vezes só nesse parágrafo), mas tenha bons pensamentos, esse mosquitto vai te deixar feliz!

Programação do ESP8266

O MQTT é apenas um carteiro; ele recebe a mensagem e entrega ao destinatário (quando o destinatário vier checar se tem algo pra ele). Nesse caso, devemos nos ater na implementação do código no ESP8266, que deverá reagir a cada mensagem lida. Vamos criar duas condições diferentes, depois explico o motivo.

Controle de um LED

Vamos implementar um controle básico de um LED. Eu sei que é um verdadeiro sacão usar sempre LEDs em provas de conceito, mas se você consegue ligar um LED, consegue acionar um relê. Cada pino de GPIO pode suprir até 20mA, dá pra usar LED de 5mm tranquilamente, mas vou ficar com o de 3mm que já estava jogado na mesa quando tive a ideia.

O jeito mais simples de programar é planejar previamente e, se não ficar adequado, reescrever. Se não quiser pensar, é só pegar o código e compilar, depois quando entregar curriculo, torça pra que ninguém te ponha à prova quanto aos seus conhecimentos de programação </psicologico>.

Planejamento

O propósito é informar via MQTT ao ESP8266 que há atualização para ele. O bom em utilizar MQTT é que você pode mandar o nome do arquivo que ele deve pegar. Pensando desse modo, só precisamos de 2 estados da informação; vamos chamar a ausência de atualização de “NULL” e a disponibilidade de atualização de “firmware-123.bin”.  Não sei se você já reparou, mas utilizar MQTT é tão flexivel que permitirá a você despejar montes de firmwares no mesmo diretório e depois informar a cada dispositivo qual lhe pertence. Isso também permite fazer um rollback caso a nova implementação precise ser recuada.




O LED está aí para mostrar  a alternância entre as tarefas, mas também pra amadurecer a ideia do conceito.

  • Enviar comando para o ESP8266 manipular o LED e receber seu status.
  • Informar o ESP8266 a respeito de atualizações.

São duas tarefas distintas e para ambas, os pontos extremos não se conhecem; não há interação direta entre os dispositivos que se comunicam com o broker e o client que faz consultas.

Em relação à atualização OTA, o ESP8266  mantém a informação no tópico /mcu/fw_update  e escreve a mudança de estado para /mcu/fw_version. As tarefas se dividem então em:

LED

/mcu/LED – ON/OFF, enviando 1 para ON e 0 para OFF

O client manda ON/OFF para para o tópico /mcu/LED quando desejar ligar ou desligar o LED.

LED_status

Aqui fica guardado o status real atual do LED, exibindo “ON” ou “OFF”.

/mcu/fw_version

Exibe a informação do firmware atual. Por exemplo, você pode mandar um update e a MCU resetar antes de fazer a atualização por algum bug. Pouco provável? – Não, não. Infelizmente não estou conseguindo fazer o update desse modo porque o WDT reseta a MCU mesmo quando não encontra um firmware. Já tentei o sketch puro, mas nessa board Wemos não funciona, não tem jeito. Até o firmware eu compilei o meu próprio (você verá no próximo post). Enfim, dá pra ver no video quando o objeto de atualização é chamado e a mensagem de despedida do firmware velho é exibida. Bem bonitinho.

OTA

/mcu/fw_update – nome_do_firmware.bin

A atualização via OTA nesse caso é executada pela MCU, porém ela só vai buscar atualização se receber um nome no tópico e esse nome for diferente da versão já instalada. Aqui será necessário implementar um pequeno conjunto de regras. O firmware deverá ter um define com o nome do firmware. Por exemplo:




Do lado client poderá ser implementada qualquer lógica desejada em relação ao informe da atualização com sucesso. Mas e se durante a atualização do firmware o dispositivo não voltar? – Nesse caso, pareceria que até o momento o dispositivo não viu que existia atualização para ser feita. Essa condição precisa obrigatoriamente ser tratada, mas nessa prova de conceito não me darei ao trabalho.

/mcu/reconnection

Esse tópico guarda o timestamp da reconexão. Pode ser bom para saber os momentos que acontecer a desconexão, já que a reconexão é automática.

Client MQTT

Será necessário um client que possa se comunicar com o broker. Por aí o comum é utilizar um web service para interação do usuário, mas para debug e deploy não há nada melhor que um aplicativo que permita interagir sem muito compromisso. Eu utilizo (e recomendo) o MyMQTT. A configuração é simples, vou mostrar no video como configurar, subescrever e publicar no broker com ele. Só tem uma questão que poderá impedí-lo de participar dessa configuração; esse app é para Android. Se você tem outra coisa que não seja Android, procure algum app para a tarefa.

Codificando

Para fazer a verificação periódica consultando via web, eu escrevi esse post. Nele, utilizo também um recurso da API do ESP8266, que é o timer, de forma que a verificação é assincrona. Podemos fazer o intervalo de duas maneiras; uma é através de millis(), que funciona bem se você não tem nenhum compromisso com precisão. Cito isso porque se houver algum delay no loop, certamente haverá diferença de tempo também na comunicação com o broker. Por outro lado, se estiver acontecendo uma tarefa de maior prioridade que a comunicação com o broker, uma interrupção pode ser prejudicial. Tentei usar o timer, mas não funcionou bem em conjunção com as demais bibliotecas, acabei usando algo parecido com o millis(), mas da própria API do ESP8266. O millis também deu vários WDT. Delay, nem pensar, só um yield muito humilde no final do loop, senão, de novo reset pelo WDT. Gostaria de acrescentar que a função de timer utilizado está disponível através da API do ESP8266 e a estrutura do método contempla a chamada de uma função de callback, mas além de não ter sido possível usar o timer, o MQTT tem um callback, ia virar uma zona. E por falar nisso, estou fortemente inclinado a escrever um post só sobre a API do ESP8266, mas por enquanto, vamos fazer “o mais Arduino possível”.

PubSubClient MQTT

Você encontrará várias bibliotecas para comunicação MQTT procurando pouco. Eu optei por essa, que você encontra no Library Manager da IDE do seu Arduino. Nesse caso, não poderei abstrair porque o comportamento da biblioteca certamente varia das demais. A documentação completa da API você encontra aqui.  A biblioteca oferece vários exemplos, mas eu estava na dúvida de como funcionava o tratamento da resposta de um subscribe, por isso procurei pela documentação. A documentação discorre que se o client se subescrever para tópicos no broker, obrigatoriamente será necessário declarar uma função callback() no seguinte formato:

Não gosto de descrever métodos e funções, mas nesse caso será realmente necessário fazê-lo para não ter que explicar isso durante a exibição do código.

Parâmetros

  • topic – O tópico à qual a mensagem será entregue.
  • payload – payload da mensagem (dã).
  • lenght – o comprimento do payload da mensagem.

A documentação descreve que internamente o client utiliza o mesmo buffer para mensagem de entrada e de saída. Quando do retorno da função de callback (ou se uma chamada publish/subscribe ocorrer) , topic e payload passados para a função serão sobrescritos. Por isso, é necessário que façamos uma cópia destes valores na aplicação, caso a interação com o retorno seja desejado (e obviamente, o é).




Enfim, o código ficou grande, mas o plugin do WordPress permite copiar facilmente o código, ele tem uma barrinha no começo, toque com o mouse a primeira linha do código, então copie para um novo sketch. Eu não colocaria um código tão grande em um post, mas o código tem comentários das funcionalidades. Nesse caso, acho que o código por aqui será necessário.

No vídeo você vê o upload do sketch e a comunicação acontecendo com informativos na serial. Em dado momento eu mostro algumas das mensagens que são tratadas sendo exibidas no console e finalizo informando ao ESP8266 que tem um firmware novo pra ele.

Prometo que vou fazer o mesmo processo com o NodeMCU, percebi vários comportamentos estranhos com o Wemos, mesmo tendo compilado meus próprios firmwares. Não sei se por alguma razão a alimentação na USB não está sendo suficiente ou outro problema que seja, mas não importa, certeza que o video vai te deixar animado para reproduzir esse processo.

Inscreva-se no nosso newsletter, alí em cima à direita e receba novos posts por email.

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!

 euler_banner

Comments

comments

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