[IoT] Como fazer upload de códigos para a ESP8266 via Wi-Fi

O módulo ESP8266 da Espressif, que vem com Wi-Fi integrado, permite o rápido desenvolvimento de protótipos que necessitam conectividade à internet, o que o torna adequado para projetos de IoT (Internet das Coisas). Além disso, uma outra vantagem da conectividade Wi-Fi é a possibilidade de enviar o código a ser executado no módulo sem a necessidade de fios, como apontado pelo blog do Pedro Minatel.

Módulo Wi-Fi NodeMcu ESP8266
Módulo Wi-Fi NodeMcu ESP8266

Neste tutorial, será explicado passo a passo como descarregar um firmware para a ESP8266 sem fios (over-the-air).

Instalando o PlatformIO

O PlatformIO é uma IDE open-source voltado para o desenvolvimento de aplicações IoT. É desenvolvida em Python e roda em cima do editor de texto Atom.

Caso você já tenha o Atom instalado, pode instalar o PlataformIO seguindo as instruções na página oficial.

Criar novo projeto

Caso a instalação ocorra corretamente, após reiniciar o Atom, você será recebido com uma janela de boas-vindas. Selecione a opção New Project para criar um novo projeto para a ESP8266.

Janela de Boas-Vindas do PlatformIO
Janela de Boas-Vindas do PlatformIO

Na janela de configurações que se abrir, seleciona a placa que será utilizada no projeto.

PlatformIO - Janela de Configuração
PlatformIO - Janela de Configuração

Para este tutorial, estou utilizando um Módulo ESP8266 NodeMCU v3, que é uma placa de desenvolvimento que possui a ESP8266 integrada. Você deve escolher o modelo da placa que irá utilizar.

Upload do firmware inicial

Na primeira vez que for configurar a sua ESP8266 para receber as atualizações via Wi-Fi, será necessário descarregar um código via USB. O código a ser utilizado utilizará a biblioteca ArduinoOTA. A biblioteca deverá ser colocada dentro da pasta lib do projeto, da seguinte forma:

Organização das pastas do projeto
Organização das pastas do projeto

Você pode usar o código de exemplo fornecido pela biblioteca para fazer a gravação na ESP8266. O arquivo deve ser salvo dentro da pasta src com a extensão .ino. Por exemplo, src/esp8266_ota.ino.

/* Código de Exemplo da biblioteca ArduinoOTA*/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "SSID";
const char* password = "SENHA";

void setup() {

  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  ArduinoOTA.setHostname("esp8266");
  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready OTA ESP8266");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();
}

Para configurar corretamente o código de exemplo, substitua SSID pelo nome da sua rede Wi-Fi:

const char* ssid = "SSID";

E SENHA pela senha da sua rede Wi-Fi:

const char* password = "SENHA";

Descarregue o código de exemplo para a ESP8266.

Caso não consiga fazer upload do código no Ubuntu, tente rodar o Atom como usuário root, usando o comando sudo atom em um terminal, ou adicione o seu usuário à lista de permissão de acesso às portas seriais com o comando sudo usermod -a -G dialout $USER e reinicie o seu computador.

Abra o Serial Monitor para consultar o IP local do seu módulo.

Serial Monitor
Serial Monitor

Configurando o upload sem fio no PlatformIO

Será necessário alterar o arquivo platformio.ini. Adicione ao arquivo a linha:

upload_port = 192.168.0.22

Alterando o valor para o endereço de IP mostrado no Serial Monitor.

Caso você deseje dar um nome à placa, para que não seja necessário consultar o endereço de IP constantemente, adicione um nome à sua ESP266 na linha:

ArduinoOTA.setHostname("nome_personalizado");

Substitua o endereço IP no arquivo platformio.ino pelo nome definido, como no exemplo a seguir:

upload_port = nome_personalizado.local

Lembrando que é necessário que o seu computador e a placa estejam conectados à mesma rede Wi-Fi.

Código de teste (Piscar LED)

Agora, mesmo se você desconectar a placa do computador e ligar a uma alimentação externa, será possível descarregar os seus códigos.

Caso você queira testar se o envio do código está funcionando corretamente, altere o void loop () do código de exemplo, adicionando o comando de piscar o LED interno, e faça upload do código:

void loop() {
  pinMode(2, OUTPUT);

  ArduinoOTA.handle();

  digitalWrite(2, HIGH);
  delay(500);
  digitalWrite(2, LOW);
  delay(500);
}

No módulo NodeMCU, o LED interno está ligado à porta GPIO 2. Você deve consultar o GPIO correspondente para a sua placa.

O código final a ser utilizado deve conter as funções e chamadas do código de exemplo da biblioteca ArduinoOTA, pois o novo código que for descarregado irá sobrescrever o código presente na memória flash. Ou seja, caso você não adicione a biblioteca ArduinoOTA e as suas funções no novo código descarregado, não será mais possível fazer upload de códigos sem fio e o próximo upload deverá ser feito novamente via porta USB.