Como utilizar uma API no ESP8266 NodeMCU: boas práticas para integrações Deixe um comentário

O ESP8266 é um dos módulos com conexão WiFi mais utilizados por makers em soluções de IoT nos dias de hoje. Destaca-se pelo baixo custo, tamanho reduzido e a facilidade de integração com outras soluções. A sua utilização é simples: para fazê-lo consumir um serviço externo basta escrever algumas linhas de códigos e pronto! Você já consegue receber dados, por exemplo, de um serviço de monitoramento do clima. Entretanto, fazer está integração direta com um serviço de terceiro pode causar algum trabalho caso queira fazer a troca do provedor ou os parâmetros fornecidos por ele se modifiquem. A intenção deste post é mostrar que criar uma API intermediária, entre o ESP8266 e serviços externos, pode ser uma opção para evitar alguns transtornos, entre eles, mudanças no projeto e alterações feitas pelo provedor do serviço. Como exemplo de aplicação, desenvolveremos uma API para o ESP8266 que consome dados de clima de dois fornecedores diferentes.

Imagem 1- Uso de uma API no ESP8266

Dentre os diversos módulos ESP existentes, vamos utilizar neste post o ESP8266 NodeMCU ESP-12. Este módulo é uma placa que tem o ESP8266 embutido e possui uma interface USB/Serial, o que facilita bastante o programação do dispositivo via USB. Para saber mais sobre o NodeMCU e como programá-lo, acesse o post Como programar o NodeMCU com IDE Arduino.

Materiais Necessários

Você sabe o que é uma API?

Application Programming Interface, ou API é um conjunto de padrões de programação que permite a construção de aplicativos. Basicamente é um sistema que você utiliza para integrar a um sistema maior. Por exemplo, imagine que você esteja construindo um formulário de cadastro de endereço e gostaria que após preencher o CEP todos os outros campos, Rua/Cidade/Bairro, fossem preenchidos automaticamente. Bom, existe um sistema chamado ViaCep que informando o CEP da rua ele retorna os dados da Rua, cidade, bairro e outros relacionados. Seria interessante fazer com que a sua aplicação se comunicasse com o ViaCep, certo? O ViaCep disponibiliza um serviço, uma API, que permite que você faça estas consultas diretamente pelo seu sistema e assim preenchendo os campos que você deseja.

Uso de uma API no ESP8266 NodeMCU – Problema

Para exemplificar nossa problemática com o uso de API em um ESP8266, apresentaremos uma aplicação convencional cuja intenção é consumir dados climáticos de uma cidade.

Imagem 2 - Problema

A verdade é que não há nada de errado nesta implementação. Mas imagine que um dia você queira mudar o provider da API (quem disponibiliza o serviço), o Apikey (dado para validação de acesso ao serviço) de acesso ou algum dado que a API espera. Ou ainda, que por algum motivo, o response seja alterado e seu código não saiba como lidar com a resposta. Vamos ter que mudar o firmware, correto?

Nestes casos a quantidade de trabalho pode variar muito, pois irá depender de diversos fatores. Mas você não concorda que ficar alterando o firmware devido a mudanças que estão fora do nosso controle não é um pouco desconfortável?

Uso de uma API no ESP8266 NodeMCU – Solução

A sugestão para contornar este problema é desenvolver a sua própria API. Esta seria responsável por comunicar com uma ou várias API’s externas, formatar os dados e disponibilizá-los da maneira que seu firmware aguarda. Desta forma, você pode optar por qual API vai querer utilizar, seja por custo ou qualidade do serviço, e trabalhar os dados recebidos de acordo com suas regras de negócios e evitar realizar mudanças no firmware. A imagem abaixo representa a proposta.

Imagem 3 - Uso de uma API no ESP8266

Para exemplificar a proposta, desenvolvi uma API em NodeJs que consome dados de clima de dois fornecedores, o Open Weather e o AccurWeather. Estas API’s fornecem dados climáticos de diversas cidades do mundo. Para ativar a conta e obter o token/appID para utilização basta seguir a documentação que é bem completa.

Desenvolvimento da API intermedária

Vamos começar construindo nossa API em NodeJs. Se você não tem familiaridade com o Node, pode começar a estudar por este post “Criando uma API Node em 10 passos com Express.js”.

Tendo em vista que você já implementou o básico da API como o server, rota e controller, criaremos o nosso modelo. Ele será a estrutura da resposta para o nosso firmware. O nosso modelo possui apenas o nome da cidade, temperatura e umidade.

WeatherModel.Js

class Wheater {
    constructor(city, temperature, humidity){
        this.city = city;
        this.temperature = temperature;
        this.humidity = humidity;
    }
}
module.exports = Wheater;

Agora serão criados os nossos serviços, são dois: um para o OpenWeather e outro AccurWeather.

AccurWeatherService.js

const axios = require('axios');
var fahrenheitToCelsius = require('fahrenheit-to-celsius');
const Weather = require("../models/weatherModel");
const { API_KEY_ACCUR_WEATHER, URL_ACCUR_WEATHER } = require('../../config');

exports.getWeather = () => {
    let token = API_KEY_ACCUR_WEATHER;
    let url = URL_ACCUR_WEATHER;
    
    return axios.get(url.concat(token))
        .then(response => {
            let weather = new Weather("Belo Horizonte",
                parseInt(fahrenheitToCelsius(response.data.DailyForecasts[0].Temperature.Maximum.Value)),
                0);
            return weather;
        })
        .catch(error => {
            console.log(error);
        });
}

OpenWeatherMapService.js

const axios = require('axios');
const kelvinToCelsius = require('kelvin-to-celsius');
const Weather = require("../models/weatherModel");
const { API_KEY_OPEN_WEATHER, URL_OPEN_WEATHER } = require('../../config');

exports.getWeather = () => {
    let token = API_KEY_OPEN_WEATHER;
    let url = URL_OPEN_WEATHER;    
    return axios.get(url.concat(token))
        .then(response => {
            let code = response.data.cod;
            if (code == 200) {
                let weather = new Weather(response.data.name,
                    parseInt(kelvinToCelsius(response.data.main.temp)),
                    response.data.main.humidity);
                return weather;
            }
        })
        .catch(error => {
            console.log(error);
        });
}

Para determinar qual fornecedor será utilizado passaremos o nome por parâmetro na nossa função, assim será instanciado o serviço desejado.

O código está na classe abaixo:

WeatherService.js

const weatherFactory = require("./weatherFactory");

exports.getWeather = () => {
    return weatherFactory("open_weather_map"); 
}

No controller importamos a classe WeatherService e nela definimos que desejamos obter os dados da API AccurWeather. Neste instante, quando a chamada chegar ao nosso controller, ele irá direcionar para o serviço escolhido e aguardará a resposta. Caso seja obtido uma resposta válida, os dados do clima da cidade serão usados para formatar uma resposta que contenha apenas o nome da cidade, temperatura e umidade. Se ocorrer alguma falha na comunicação ou erro na resposta da API do Accur, será criada uma resposta específica informando que não foi possível obter as informações.

WeatherController.js

'use scrict'

const service = require('../service/weather');
const express = require('express');
const router = express.Router();

router.get('/temperatures', service.getWeather);

module.exports = router;

Pronto, nossa API está implementada. Execute e verifique o resultado, o Postman irá nos ajudar nesta parte.

Imagem 4 - Uso de uma API no ESP8266

Código fonte do para uso da API no ESP8266 NodeMCU

Inicialmente, estamos utilizando um display LCD, logo devemos importar a library LiquidCrystal.

O código fonte completo está aqui:

#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h> 
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

const char * ssid = "SEU_SSID";
const char * password = "SENHA_DA_REDE";
byte grau[8] ={ B00001100,
                B00010010,
                B00010010,
                B00001100,
                B00000000,
                B00000000,
                B00000000,
                B00000000,};
                
void setup() {

  //configura a comunicação serial, LCD 
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  Wire.begin(D2, D1); 
  lcd.begin(20, 4);

  lcd.backlight();
  lcd.home();
  //Cria o caractere customizado com o simbolo do grau
  lcd.createChar(0, grau);
  

  //limpa o tela e escreve os textos iniciais 
  lcd.clear();    
  lcd.setCursor(1,1);
  lcd.print("SSID: ");

  lcd.setCursor(7,1);
  lcd.print(ssid);
    
  lcd.setCursor(1,2);
  lcd.print("Conectando...");  

  //Verifica se o esp está conectado na rede, caso contrário realiza a tentaiva a cada 2 seg.
  while (WiFi.status() != WL_CONNECTED) {  
    delay(2000);
    Serial.println(WiFi.status());
  }
}

void loop() {

  // Verifica se o esp está conectado
  if (WiFi.status() == WL_CONNECTED) {  
    
    //cria a requisição http passando o URL da api node
    HTTPClient http;
    http.begin("URL_DA_API");    
    int httpCode = http.GET();    
    if (httpCode > 0) {      

      //difinindo o tamanho do buffer para o objeto json
      const size_t bufferSize = JSON_OBJECT_SIZE(3);
    
      //realizando o parse do json para um JsonObject
      DynamicJsonBuffer jsonBuffer(bufferSize);
      JsonObject & root = jsonBuffer.parseObject(http.getString());

      //carregando os valores nas variaveis 
      const char * temp = root["temperature"];
      const char * city = root["city"];
      const char * humidity = root["humidity"];

      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(city);
     
      //criando a string que irá exibir os dados da temperatura e umidade
      String message = "Temperatura: ";
      message += temp;
      lcd.setCursor(0, 2);
      lcd.print(message);
      //Mostra o simbolo do grau formado pelo array
      lcd.write((byte)0);      
      message = "Umidade: ";
      message += humidity;
      message += "%";
      lcd.setCursor(0, 3);
      lcd.print(message);
    }    
    //fechando a conexão
    http.end();
  }
  delay(60000);
}

No setup, iniciamos a conexão do ESP8266 com a rede WiFI informando o SSID e a senha (é necessário alterar tais parâmetros com as informações da sua rede WiFi). Em seguida, configuramos o LCD para realizar as operações de escrita.

No loop, verificamos se a conexão com a rede WiFi é válida, se sim, realizamos o request para a nossa API e aguardamos o resultado. A resposta vem no formato Json, logo devemos deserializar para um objeto. Neste momento utilizamos a library ArduinoJson, ela permite definir do tamanho do buffer e realiza o parse do json recebido para um objeto ou array.

Após o processo de deserialização podemos extrair os dados do objeto, lembrando que os mesmos nomes que foi definido no modelo da API Node devem ser utilizados neste ponto. Em seguida, escrevemos estes valores no display LCD e encerramos a conexão HTTP.

Conclusão

É isto pessoal, espero que esta sugestão seja útil para vocês. Existem diversas formas para esta implementação e como eu disse no início, não existe o modo certo, mas utilizar boas práticas torna nossa vida bem mais fácil para manter a manutenção e evolução do código.

Gostou? Ajude-nos a melhorar o Blog comentando abaixo sobre este tutorial. Não se esqueça de visitar nosso Fórum para dúvidas e compartilhar suas ideias de projetos com a comunidade.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

{"cart_token":"","hash":"","cart_data":""}