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