Localizador ISS: uso de uma API Web com o ESP8266 Deixe um comentário

Neste tutorial vamos montar um display que mostra a posição atual (latitude e longitude) da Estação Espacial Internacional (ISS), usando o WeMos D1 mini e dois módulos MAX7219 com display de 8 dígitos. Para isto vamos ver como acessar uma API Web com o ESP8266 e como cascatear dois módulos MAX7219.

Localização através da API Web com o ESP8266

Material necessário

Neste projeto utilizamos os seguintes componentes:

Obtendo a Posição da ISS

Existem vários serviços na web que fornecem informações sobre satélites, planetas e outros objetos no espaço. Neste tutorial vamos usar um site para obter a posição da ISS, que possui uma interface bastante simples.

Conforme descrito no site, basta navegar para http://api.open-notify.org/iss-now.json para receber a posição no formato JSON:

{
  "message": "success", 
  "timestamp": 1555508500, 
  "iss_position": 
  {
    "latitude": "-36.2235", 
    "longitude": "-143.7500"
  }
}

A primeira linha (“message”: “success”) indica que conseguimos obter a posição. timestamp é a hora atual no formato Unix (segundos desde as 0 horas GMT do dia 1/1/1970). O que nos interessa são as informações de “latitude” e “longitude”.

Acessando a API Web com o ESP8266 (WeMos D1 Mini)

O WeMos D1 Mini utiliza um ESP8266 que é capaz de se conectar à internet via WiFi. Para acessar um site ou uma API web precisamos realizar uma requisição http get. Isto é feito usando a biblioteca ESP8266HTTPClient:

#include <ESP8266HTTPClient.h>

HTTPClient http;

    http.begin("http://api.open-notify.org/iss-now.json");
    int httpCode = http.GET();
    if (httpCode == HTTP_CODE_OK) {
      String resposta = http.getString();
    }
    http.end();

Extraindo Informações do Formato JSON

No código acima “resposta” conterá um texto em formato JSON semelhante ao que vimos no começo. Poderíamos extrair a latitude e longitude “na marra”, porém esta solução provavelmente seria frágil e difícil de adaptar para outras respostas. Felizmente existem várias bibliotecas para processamento de JSON.

Vamos usar a ArduinoJson (https://arduinojson.org/). Ela pode ser instalada diretamente da IDE do Arduino:

JSON da API Web com o ESP8266

Com esta biblioteca fica muito simples extrair a posição:

StaticJsonDocument<500> doc;

  DeserializationError err = deserializeJson(doc, resposta.c_str());
  if (err == DeserializationError::Ok) {
    bool sucesso;
    double latitude, longitude;
    
    sucesso = strcmp(doc["message"], "success") == 0;
    latitude = doc["iss_position"]["latitude"];
    longitude = doc["iss_position"]["longitude"];
  }

Cascateando Módulos MAX7219

O módulo de display neste projeto usa um MAX7219 para controlar 8 dígitos de sete segmentos (8 segmentos se contarmos o ponto decimal), o seu funcionamento com arduino pode ser visto neste post. O uso de um módulo isolado pode ser resumido da seguinte forma:

  • Cada escrita no MAX7219 consiste em 16 bits; os primeiros 8 selecionam um registrador (ou comando) e os 8 seguintes o dado a ser escrito nele.
  • Os 16 bits são enviados serialmente através do pino DIN, com o pino CLK sendo pulsado a cada bit
  • Um pulso no sinal CS (ou LOAD) executa a escrita

Para reduzir o número de conexões ao se utilizar múltiplos MAX7219 é possível cascateá-los. Para isto existe o sinal DOUT, que deve ser conectado ao DIN do módulo seguinte. O processo de escrita é semelhante ao de um módulo isolado, porém enviamos serialmente “n” blocos de 16 bits e depois pulsamos o CS. Quando o sinal CS é pulsado o primeiro bloco é usado pelo último módulo, o segundo pelo penúltimo e assim por diante. Repare que sempre será feita escrita em todos os módulo, existe um registrador “fantasma” (NOP) para ser usado nos módulos onde não temos nada a fazer.

A Montagem

O desenho abaixo mostra a montagem.

Montagem da API Web com o ESP8266

Os sinais GND, CS e CLK do segundo módulo de display podem também ser obtidos do conector da direita do primeiro. No caso do sinal VCC é preferível ligar direto, pois existe um diodo entre os conectores da esquerda e direita que reduz a tensão na saída.

O Código

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

// Conexões ao MAX7219
#define PIN_CLK  14
#define PIN_LOAD 16
#define PIN_DIN  2

// Registradores do MAX7219
#define MAX7219_NOP   0x00
#define MAX7219_DIG0  0x01
#define MAX7219_DIG1  0x02
#define MAX7219_DIG2  0x03
#define MAX7219_DIG3  0x04
#define MAX7219_DIG4  0x05
#define MAX7219_DIG5  0x06
#define MAX7219_DIG6  0x07
#define MAX7219_DIG7  0x08
#define MAX7219_MODE  0x09
#define MAX7219_INT   0x0A
#define MAX7219_LIM   0x0B
#define MAX7219_SHUT  0x0C
#define MAX7219_TEST  0x0F

// CONEXOES DOS LEDS
#define DIG1   MAX7219_DIG7
#define DIG2   MAX7219_DIG6
#define DIG3   MAX7219_DIG5
#define DIG4   MAX7219_DIG4
#define DIG5   MAX7219_DIG3
#define DIG6   MAX7219_DIG2
#define DIG7   MAX7219_DIG1
#define DIG8   MAX7219_DIG0

// Para determinar o valor a escrever
const uint8_t digito[] = { DIG1, DIG2, DIG3, DIG4, DIG5, DIG6, DIG7, DIG8 };
uint8_t valor [2][8];

// Coloque aqui os dados do seu roteador
const char* ssid = "MEU_ROTEADOR";
const char* password = "segredo";

HTTPClient http;

/*
 * Exemplos de resposta
 * 
 * {
 *   "message": "success", 
 *   "timestamp": 1554985387, 
 *   "iss_position": {
 *     "latitude": -47.9204, 
 *     "longitude": -111.3012
 *   }
 * }
 * 
 */
StaticJsonDocument<500> doc;

// Iniciação
void setup ()
{
  // Configura os pinos
  pinMode (PIN_LOAD, OUTPUT);
  pinMode (PIN_DIN, OUTPUT);
  pinMode (PIN_CLK, OUTPUT);
  digitalWrite (PIN_LOAD, HIGH);
  digitalWrite (PIN_CLK, LOW);
  digitalWrite (PIN_DIN, LOW);

  // Configura o MAX7219
  write7219 (MAX7219_SHUT, 0x00);
  write7219 (MAX7219_TEST, 0x00);
  write7219 (MAX7219_MODE, 0xFF);
  write7219 (MAX7219_INT, 0x0F);
  write7219 (MAX7219_LIM, 0x07);

  // Iniciar apresentando zeros
  for (int i = 0; i < 2; i++)
    for (int j = 0; j < 8; j++)
      valor[i][j] = 0;
  atlDisplay();
  write7219 (MAX7219_SHUT, 0x01);

  // Conectar ao WiFi
  WiFi.begin (ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay (100);
  }
}

// Laco principal
void loop ()
{
  if (WiFi.status() == WL_CONNECTED) {
    // Consulta posição atual
    http.begin("http://api.open-notify.org/iss-now.json");
    int httpCode = http.GET();
    if (httpCode == HTTP_CODE_OK) {
      String resposta = http.getString();
      trataPosicao(resposta.c_str());
    }
    http.end();
  }

  // 30 segundos entre as consultas
  delay (30000);
}

// Extrai a posição da resposta e mostra no display
void trataPosicao(const char *resp) {
  DeserializationError err = deserializeJson(doc, resp);
  if (err == DeserializationError::Ok) {
    bool sucesso;
    double latitude, longitude;
    
    sucesso = strcmp(doc["message"], "success") == 0;
    latitude = doc["iss_position"]["latitude"];
    longitude = doc["iss_position"]["longitude"];
    if (sucesso) {
      fmtval (1, (long) (latitude * 10000.0));
      fmtval (0, (long) (longitude * 10000.0));
      atlDisplay();
    }
  }
}

// Formato o valor para apresentação no display
// -999.9999
void fmtval (int disp, long val) {
  if (val < 0L) {
    // Apresentar '-' na frente se negativo
    val = -val;
    valor[disp][0] =  10;
  } else {
    valor[disp][0] =  15;
  }
  for (int i = 7; i > 0; i--) {
    valor[disp][i] = val % 10L;
    val = val / 10L;
  }
  // Coloca o ponto
  valor[disp][3] |= 0x80;
}

// Atualiza o display
void atlDisplay(void) {
  for (int i = 0; i < 8; i++) {
    write7219b (digito[i], valor[0][i], valor[1][i]);
  }
}

// Escreve o mesmo valor no mesmo registrador nos dois módulos
void write7219 (uint8_t addr, uint8_t data)
{
  digitalWrite (PIN_LOAD, LOW);
  for (int i = 0; i < 2; i++) {
    shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, addr);
    shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, data);
  }
  digitalWrite (PIN_LOAD, HIGH);
}

// Escreve valores diferentes no mesmo registrador nos dois módulos
void write7219b (uint8_t addr, uint8_t data1, uint8_t data2)
{
  digitalWrite (PIN_LOAD, LOW);
  shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, addr);
  shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, data1);
  shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, addr);
  shiftOut (PIN_DIN, PIN_CLK, MSBFIRST, data2);
  digitalWrite (PIN_LOAD, HIGH);
}

Gostou do localizador ISS? Ajude-nos a melhorar o blog comentando abaixo sobre ele.

Faça seu comentário

Acesse sua conta e participe