Neste post, será demonstrado como criar um visualizador automático (sem a interação do usuário para troca de telas) da cotação das seguintes moedas para o Real:
As cotações destas 6 moedas serão visualizadas em um display OLED e obtidas de um serviço gratuito de cotações financeiras, o que necessita de uma conexão com a Internet. Também será demonstrado como exibir caracteres do conjunto de caracteres Code page 437, da IBM PC, no display OLED. As cotações financeiras serão obtidas através da biblioteca HTTPClient, que será usada para fazer as requisições GET para o servidor. Todo o sistema será controlado pelo microcontrolador ESP32, que já conta com WiFi integrado para conexão com a Internet.
Para este post, foram utilizados os seguintes materiais:
Além destes materiais, será necessário um Ponto de acesso sem fio (popularmente conhecido como WiFi) com Internet.
Para a obtenção das cotações das moedas, será utilizada a API AwesomeAPI. Seu uso é gratuito e não demanda cadastro.
Está API permite fazer cotações de mais de 150 moedas. A lista completa de moedas pode ser vista neste link: economia.awesomeapi.com.br/xml/available/uniq. A combinação das cotações de moedas pode ser vista neste outro link: economia.awesomeapi.com.br/xml/available. A documentação completa pode ser vista em https://docs.awesomeapi.com.br/api-de-moedas. Para obter a cotação de uma moeda para outra, deve-se fazer uma requisição HTTP com o método GET.
O Hypertext Transfer Protocol (HTTP) foi projetado para permitir a comunicação entre clientes e servidores. Ele funciona como um protocolo de solicitação-resposta entre um cliente e um servidor. Exemplo: Um cliente (navegador) envia uma solicitação HTTP ao servidor; em seguida, o servidor retorna uma resposta ao cliente. A resposta contém informações de status sobre a solicitação e pode, também, conter o conteúdo solicitado.
O método GET é usado para solicitar dados de um recurso especificado no servidor.
A sintaxe da Requisição para obter a cotação ou cotações de uma ou mais moedas é a seguinte:
https://economia.awesomeapi.com.br/:format/last/:moedas
:format
: o formato de resposta;:moedas
: as moedas selecionadas, separado por vírgula (,) Ex.: USD-BRL, EUR-BRLA resposta do servidor pode ter dois formatos:
XML (sigla de Extensible Markup Language: Linguagem de Marcação Extensível) é uma recomendação da W3C (organização de padronização da World Wide Web) para gerar linguagens de marcação para necessidades especiais. O XML é um formato para a criação de documentos com dados organizados de forma hierárquica. Pela sua portabilidade, já que é um formato que não depende das plataformas de hardware ou de software, um banco de dados pode, por exemplo, através de uma aplicação, escrever em um arquivo XML, e um outro banco, distinto, pode ler, então, estes mesmos dados.
<?xml version="1.0"?> <quiz> <questao> Qual o segundo nome de Thomas Edison? </questao> <resposta> Edison </resposta> <!-- Nota: Precisamos perguntar mais.--> </quiz>
JSON (acrônimo de JavaScript Object Notation: Notação de Objeto JavaScript), é um formato leve e compacto, para armazenar e trocar dados de forma rápida e simples entre sistemas. Este formato foi especificado por Douglas Crockford, em 2000, e é “autodescritivo” e fácil de entender. O JSON é frequentemente usado quando os dados são enviados de um servidor para uma página da web.
"Funcionarios":[ {"nome":"John", "sobrenome":"Doe"}, {"nome":"Anna", "sobrenome":"Smith"}, {"nome":"Peter", "sobrenome":"Jones"} ]
code
: o código da moeda para cotação;codein
: o código da moeda de destino para cotar;name
: o nome das moedas;high
: o valor Máximo da moeda;low
: o valor Mínimo da moeda.varBid
: o valor de Variação da moeda;pctChange
: a Porcentagem de Variação da moeda;bid
: o valor da moeda para Compra;ask
: o valor da moeda para Venda;timestamp
: a cadeia de caracteres denotando a hora e data em que a cotação ocorreu;create_date
: a data em que ocorreu a cotação;Para cotar o Dólar para o Real e ter a resposta no formato JSON, é utilizada a seguinte requisição:
https://economia.awesomeapi.com.br/json/last/USD-BRL
A resposta retornada da requisição é:
{ "USDBRL": { "code": "USD", "codein": "BRL", "name": "Dólar Americano/Real Brasileiro", "high": "4.8059", "low": "4.7477", "varBid": "0.0221", "pctChange": "0.46", "bid": "4.7947", "ask": "4.7963", "timestamp": "1654531611", "create_date": "2022-06-06 13:06:51" } }
Esta resposta nos diz que, no dia 06 de junho de 2022, às 13h06m51, (create_date
) a cotação do Dólar para o Real (name
) estava a R$ 4,79 (bid
).
Para fazer com que o ESP32 faça requisições GET, será necessária uma biblioteca. Esta biblioteca é a HTTPClient.h. HTTPClient é uma classe que executa solicitações HTTP, tanto no ESP8266 quanto no ESP32 . Esta biblioteca permite realizar solicitações HTTP ou HTTPS.
Através do seguinte algoritmo, é feita a Requisição GET:
/****************************************************************************** Cotação do Dólar em Tempo Real com ESP32 Sketch Auxiliar: Exemplo de Requisição GET Criado em 30 de Maio de 2022 por Michel Galvão Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão da(s) biblioteca(s) #include <WiFi.h> // Biblioteca nativa do ESP32 #include <HTTPClient.h> // Biblioteca nativa do ESP32 // Configurações da rede WiFi à se conectar const char* ssid = "SSID"; const char* password = "PASSWORD"; void setup() { // Inicia Serial Serial.begin(115200); Serial.println(); delay(1000); WiFi.disconnect(); // Desconecta do WiFI se já houver alguma conexão WiFi.mode(WIFI_STA); // Configura o ESP32 para o modo de conexão WiFi Estação Serial.println("[SETUP] Tentando conexão com o WiFi..."); WiFi.begin(ssid, password); // Conecta-se à rede if (WiFi.waitForConnectResult() == WL_CONNECTED) // aguarda até que o módulo se // conecte ao ponto de acesso { Serial.println("[SETUP] WiFi iniciado com sucesso!"); } else { Serial.println("[SETUP] Houve falha na inicialização do WiFi. Reiniciando ESP."); ESP.restart(); } HTTPClient http; // o objeto da classe HTTPClient Serial.println("[HTTP] começar..."); http.begin("http://example.com/index.html"); // configura o URL para fazer requisição no servidor Serial.println("[HTTP] GET..."); int httpCode = http.GET(); // inicia uma conexão e envia um cabeçalho HTTP para o // URL do servidor configurado Serial.print("[HTTP] GET... código: "); Serial.println(httpCode); if (httpCode == HTTP_CODE_OK) // se o cabeçalho HTTP foi enviado e o cabeçalho de // resposta do servidor foi tratado, ... { Serial.println("[HTTP] GET... OK! Resposta: "); String payload = http.getString(); // armazena a resposta da requisição Serial.println(payload); // imprime a resposta da requisição } else // se não, ... { Serial.print("HTTP GET... Erro. Mensagem de Erro: "); Serial.println(http.errorToString(httpCode).c_str()); // Imprime a mensagem de erro da requisição } http.end();// Fecha a requisição HTTP // entra em um laço de repetição infinito while (1); } void loop() {}
Para fazer uma requisição GET, primeiro devemos incluir a biblioteca HTTPClient.h.
// Inclusão da(s) biblioteca(s) #include <WiFi.h> // Biblioteca nativa do ESP32 #include <HTTPClient.h> // Biblioteca nativa do ESP32
Em seguida, fazemos a configuração das credenciais de WiFi. Neste trecho, deve-se substituir SSID
e PASSWORD
pelo nome e senha, respectivamente, de sua rede WiFi:
// Configurações da rede WiFi à se conectar const char* ssid = "SSID"; const char* password = "PASSWORD";
Em void setup()
, inicializamos a Serial:
// Inicia Serial Serial.begin(115200); Serial.println(); delay(1000);
Ainda em void setup()
, inicializamos o WiFi e aguardamos, por meio da WiFi.waitForConnectResult()
, o ESP32 se conectar. Se não conseguir, reiniciamo-lo:
WiFi.disconnect(); // Desconecta do WiFI se já houver alguma conexão WiFi.mode(WIFI_STA); // Configura o ESP32 para o modo de conexão WiFi Estação Serial.println("[SETUP] Tentando conexão com o WiFi..."); WiFi.begin(ssid, password); // Conecta-se à rede if (WiFi.waitForConnectResult() == WL_CONNECTED) // aguarda até que o módulo se // conecte ao ponto de acesso { Serial.println("[SETUP] WiFi iniciado com sucesso!"); } else { Serial.println("[SETUP] Houve falha na inicialização do WiFi. Reiniciando ESP."); ESP.restart(); }
Em seguida, criamos um objeto da classe HTTPClient, que irá realizar as requisições:
HTTPClient http; // o objeto da classe HTTPClient
Após, configuramos o URL que irá fazer as requisições ao servidor. Este URL é o http://example.com/index.html:
Serial.println("[HTTP] começar..."); http.begin("http://example.com/index.html"); // configura o URL para fazer requisição no servidor
Logo após, iniciamos a conexão com o servidor enviando a requisição a ele e armazenando o código HTTP de resposta obtido. A lista de todos os códigos HTTP pode ser vista em Lista de códigos de estado HTTP:
Serial.println("[HTTP] GET..."); int httpCode = http.GET(); // inicia uma conexão e envia um cabeçalho HTTP para o // URL do servidor configurado Serial.print("[HTTP] GET... código: "); Serial.println(httpCode);
Posteriormente, verificamos se o código HTTP de resposta obtido é o 200 (HTTP_CODE_OK
). O código 200 indica que a ação solicitada pelo cliente foi recebida, compreendida, aceita e processada com êxito. Se o código for o 200, armazenamos a resposta da requisição na variável payload
, passando a resposta através da função http.getString()
:
if (httpCode == HTTP_CODE_OK) // se o cabeçalho HTTP foi enviado e o cabeçalho de // resposta do servidor foi tratado, ... { Serial.println("[HTTP] GET... OK! Resposta: "); String payload = http.getString(); // armazena a resposta da requisição Serial.println(payload); // imprime a resposta da requisição }
Caso o código HTTP de resposta obtido não seja o 200, mostramos na Serial a mensagem de erro através da função http.errorToString(int error)
, informando o código HTTP obtido (httpCode
):
else // se não, ... { Serial.print("HTTP GET... Erro. Mensagem de Erro: "); Serial.println(http.errorToString(httpCode).c_str()); // Imprime a mensagem de erro da requisição }
Por fim, fechamos a requisição HTTP através do método http.end()
e entramos em um loop infinito (while(1)
):
http.end();// Fecha a requisição HTTP // entra em um laço de repetição infinito while (1);
Em void loop()
, não temos nenhum comando, pois fazemos tudo em void setup()
:
void loop() {}
Como utilizamos uma interface gráfica de interação com o usuário, é importante mostrar a informação da melhor forma para o usuário. Para isso, utilizaremos caracteres Code page 437.
É o conjunto de caracteres do IBM PC original. Também conhecido como CP437, OEM-US, OEM 437, PC-8, ou DOS Latin US. O conjunto inclui todos os caracteres ASCII imprimíveis, códigos estendidos para letras acentuadas (diacríticos), algumas letras gregas, ícones e símbolos de desenho de linha. Às vezes, é referido como a “fonte OEM” ou “high ASCII”, ou como “Extended ASCII”(uma das muitas extensões ASCII mutuamente incompatíveis). Este conjunto de caracteres continua a ser o conjunto principal no núcleo de qualquer placa gráfica compatível com EGA e VGA . Assim, o texto mostrado quando um PC é inicializado, antes que as fontes possam ser carregadas e renderizadas, normalmente é composto por esse conjunto de caracteres. Muitos formatos de arquivo desenvolvidos na época do IBM PC também são baseados na página de código 437.
A tabela a seguir contém os caracteres Code page 437:
A biblioteca de controle do display OLED, a Adafruit_SSD1306.h
, possui uma fonte interna padrão baseada no conjunto de caracteres Code Page 437. Estes caracteres podem ser impressos no display OLED através da função write
, da classe Adafruit_SSD1306
. Mas, quando os caracteres Code Page 437 foram transcritos para a biblioteca, um símbolo foi omitido acidentalmente. Na prática, o código funciona bem, mas todos os símbolos subsequentes ao que foi omitido foram deslocados por um em comparação com o conjunto de caracteres Code Page 437 “reais”. De acordo com o autor da biblioteca (Adafruit),
“No momento em que isso foi descoberto, tanto código havia sido escrito – projetos compartilhados online, mas também em mídias fixas como livros e revistas – que corrigir o bug quebraria todos os projetos existentes que dependiam desses caracteres estendidos!“.
Portanto, o erro foi deixado, de propósito, no lugar. Mas, isso cria um problema diferente se alguém estiver adaptando o código de outro lugar, que depende dos valores corretos dos caracteres Code Page 437. A solução que encontraram foi a criação de uma função que habilita ou desabilita a sequência de caracteres Code Page 437 “reais”. Por padrão, a sequência de caracteres Code Page 437 “reais” está desativada, para que todos os projetos antigos funcionem sem modificação. A sequência “real” pode ser habilitada com:
display.cp437(true);
Abaixo, encontra-se o mapa do conjunto de caracteres, tanto na versão padrão, errônea, quanto na versão corrigida, usada quando alguém chama cp437(true)
. Observe que isso afeta apenas as últimas cinco linhas de símbolos. Tudo antes do caractere 0xB0
não é afetado:
Para os dois exemplos a seguir, monte o seu circuito de acordo com o seguinte esquemático:
Para mais detalhes do uso de um display OLED, veja os seguintes artigos, do blog Eletrogate:
O algoritmo a seguir mostra os caracteres Code Page 437 na versão errônea:
/****************************************************************************** Cotação do Dólar em Tempo Real com ESP32 Sketch Auxiliar: Exemplo de Impressão de caracteres Code page 437 no display OLED Criado em 03 de Junho de 2022 por Michel Galvão Blog Eletrogate - Veja este e outros projetos e tutoriais no blog Eletrogate https://blog.eletrogate.com/ Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão da(s) biblioteca(s) #include <Wire.h> // Biblioteca nativa do ESP32 #include <Adafruit_SSD1306.h> // Necessária Instalação #define SCREEN_WIDTH 128 // Largura da tela OLED, em pixels #define SCREEN_HEIGHT 64 // Altura da tela OLED, em pixels #define OLED_RESET -1 // Pino de Reset (ou -1 se compartilhar o pino de reset do Arduino) #define SCREEN_ADDRESS 0x3C // Endereço I2C: 0x3D para 128x64 ou 0x3C para 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // objeto para // controle do display OLED. void setup() { if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) // Inicializa o display { Serial.println("Falha na alocação de SSD1306"); for (;;); // Não prossiga, faça um loop para sempre } display.clearDisplay(); // Limpa a tela display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.display(); // Atualiza a tela display.cp437(false); // define se usa a versão corrigida de caracteres Code // Page 437 ou não. true para versão corrigida, false // para versão errônea. for (int c = 0; c <= 0xFF; c++) // Estrutura de repetição para mostrar todos // os caracteres Code Page 437. { display.clearDisplay(); // Limpa a tela display.setCursor(0, 0); // configura o cursor para ir na coluna 0 e linha 0 display.setTextSize(1); // multiplica a escala do texto por 1 // escreve no display o endereço atual do caractere mostrado display.print("0x"); display.print(c, HEX); display.print(": "); // escreve no display a quantidade de caracteres já mostrado e o total de // caracteres. display.print(int(c)); display.print(" de "); display.print(int(0xFF)); display.setTextSize(3); // multiplica a escala do texto por 3 display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.write(char(c)); // escreve o caractere Code Page 437 display.display(); // Atualiza a tela delay(200); // pausa de 200 milissegundos } display.clearDisplay(); // Limpa a tela display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.setTextSize(3); // multiplica a escala do texto por 3 display.println("FIM"); display.display(); // Atualiza a tela while (1); // entra em um laço de repetição infinito } void loop() {}
O vídeo à seguir demonstra a execução do algoritmo que mostra os caracteres Code Page 437 na versão padrão, errônea:
O algoritmo a seguir mostra os caracteres Code Page 437 da versão corrigida:
/****************************************************************************** Cotação do Dólar em Tempo Real com ESP32 Sketch Auxiliar: Exemplo de Impressão de caracteres Code page 437 no display OLED Criado em 03 de Junho de 2022 por Michel Galvão Blog Eletrogate - Veja este e outros projetos e tutoriais no blog Eletrogate https://blog.eletrogate.com/ Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão da(s) biblioteca(s) #include <Wire.h> // Biblioteca nativa do ESP32 #include <Adafruit_SSD1306.h> // Necessária Instalação #define SCREEN_WIDTH 128 // Largura da tela OLED, em pixels #define SCREEN_HEIGHT 64 // Altura da tela OLED, em pixels #define OLED_RESET -1 // Pino de Reset (ou -1 se compartilhar o pino de reset do Arduino) #define SCREEN_ADDRESS 0x3C // Endereço I2C: 0x3D para 128x64 ou 0x3C para 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // objeto para // controle do display OLED. void setup() { if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) // Inicializa o display { Serial.println("Falha na alocação de SSD1306"); for (;;); // Não prossiga, faça um loop para sempre } display.clearDisplay(); // Limpa a tela display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.display(); // Atualiza a tela display.cp437(true); // define se usa a versão corrigida de caracteres Code // Page 437 ou não. true para versão corrigida, false // para versão errônea. for (int c = 0; c <= 0xFF; c++) // Estrutura de repetição para mostrar todos // os caracteres Code Page 437. { display.clearDisplay(); // Limpa a tela display.setCursor(0, 0); // configura o cursor para ir na coluna 0 e linha 0 display.setTextSize(1); // multiplica a escala do texto por 1 // escreve no display o endereço atual do caractere mostrado display.print("0x"); display.print(c, HEX); display.print(": "); // escreve no display a quantidade de caracteres já mostrado e o total de // caracteres. display.print(int(c)); display.print(" de "); display.print(int(0xFF)); display.setTextSize(3); // multiplica a escala do texto por 3 display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.write(char(c)); // escreve o caractere Code Page 437 display.display(); // Atualiza a tela delay(200); // pausa de 200 milissegundos } display.clearDisplay(); // Limpa a tela display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.setTextSize(3); // multiplica a escala do texto por 3 display.println("FIM"); display.display(); // Atualiza a tela while (1); // entra em um laço de repetição infinito } void loop() {}
Para o projeto de Cotação de Moedas em Tempo real com HTTPClient e ESP32, monte seu circuito de acordo com o seguinte esquemático:
O projeto possui o seguinte sketch:
/****************************************************************************** Cotação do Dólar em Tempo Real com ESP32 Sketch Principal Criado em 03 de Junho de 2022 por Michel Galvão Blog Eletrogate - Veja este e outros projetos e tutoriais no blog Eletrogate https://blog.eletrogate.com/ Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <WiFi.h> // Biblioteca nativa do ESP32 #include <HTTPClient.h> // Biblioteca nativa do ESP32 #include <Wire.h> // Biblioteca nativa do ESP32 #include <Adafruit_SSD1306.h> // Necessária Instalação #include <ArduinoJson.h> // Necessária Instalação // Configurações da rede WiFi à se conectar const char* host = "esp32"; const char* ssid = "<SSID>"; const char* password = "<SENHA>"; #define SCREEN_WIDTH 128 // Largura da tela OLED, em pixels #define SCREEN_HEIGHT 64 // Altura da tela OLED, em pixels #define OLED_RESET -1 // Pino de Reset (ou -1 se compartilhar o pino de reset do Arduino) #define SCREEN_ADDRESS 0x3C // Endereço I2C: 0x3D para 128x64 ou 0x3C para 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // objeto para // controle do display OLED. float dolar, euro, libra, peso, bitcoin, iene; // armazenará os valores das moedas float varDolar, varEuro, varLibra, varPeso, varBitcoin, varIene; // armazenará as // variações dos valores das moedas. bool invalido = true; // armazenará se a requisição de cotação é ou não inválida // https://economia.awesomeapi.com.br/json/last/USD-BRL,EUR-BRL,GBP-BRL,ARS-BRL,BTC-BRL,JPY-BRL/ String url = "https://economia.awesomeapi.com.br/json/last/"; String moedas = "USD-BRL,EUR-BRL,GBP-BRL,ARS-BRL,BTC-BRL,JPY-BRL"; int telaAtual = 5; // índice de tela OLED // Timers: unsigned long timerRequisicao; const int periodoRequisicao = 30000; // de requisição HTTP unsigned long timerExibicao; const int periodoExibicao = 5000; // de Troca de Tela unsigned long timerProgress; const int periodoProgress = 50; // de Barra de Progresso int larguraDisplay; // armazenará a largura do display (em Pixels) int alturaDisplay; // armazenará a altura do display (em Pixels) void setup() { url.concat(moedas); // concatena a String url com a String moedas Serial.begin(115200); // Configura a taxa de transferência em bits por // segundo (baud rate) para transmissão serial. Serial.println(); delay(1000); if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) // Inicializa o display { Serial.println("Falha na alocação de SSD1306"); for (;;); // Não prossiga, faça um loop para sempre } larguraDisplay = display.width() - 1; alturaDisplay = display.height() - 1; display.clearDisplay(); // Limpa a tela display.setTextSize(1); // multiplica a escala do texto por 1 display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.setCursor(10, 0); // configura o cursor para ir na coluna 10 e linha 0 display.println("Cotacao do Dolar"); display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.println("Valor Compra"); display.drawLine(0, 42, display.width() - 1, 42, SSD1306_WHITE); // desenha uma // linha, partindo da coluna 0 e linha 42 até // a coluna final e linha 42, utilizando a cor // branca correspondente (azul). display.setCursor(0, 46); // configura o cursor para ir na coluna 0 e linha 46 display.setTextSize(2); // multiplica a escala do texto por 2 display.write(0x12); // escreve o caractere ↕ (Code Page 437) de endereço 0x12 display.setTextSize(1); // multiplica a escala do texto por 1 display.setCursor(20, 46); // configura o cursor para ir na coluna 20 e linha 46 display.println("Variacao em %"); display.display();// Atualiza o display WiFi.disconnect(); // desconecta de algum WiFi conectado anteriormente WiFi.mode(WIFI_STA); // define o ESP32 para o modo estação de WiFi WiFi.setHostname(host); // define o hostname do ESP32 para com a rede WiFi WiFi.begin(ssid, password); // se conecta no WiFi com SSID e a senha informada Serial.println("[SETUP] Iniciando o WiFi"); if (WiFi.waitForConnectResult() == WL_CONNECTED) // Espera o ESP32 se conectar // na rede informada e retorna o resultado após um tempo esperando. { Serial.println("[SETUP] WiFi iniciado com sucesso!"); } else { Serial.println("[SETUP] Houve falha na inicialização do WiFi. A placa será reiniciada."); ESP.restart(); } display.clearDisplay(); // Limpa a tela display.setTextSize(1); // multiplica a escala do texto por 1 display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.setCursor(10, 0); display.println("Cotacao do Dolar"); display.setTextSize(2); display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.println("Busca..."); display.display(); // Atualiza a tela } void loop() { // Faz a requisição e atualiza os dados if (millis() - timerRequisicao >= periodoRequisicao || timerRequisicao == 0) { HTTPClient http; // Cria o objeto da classe HTTPClient Serial.println("[HTTP] begin..."); http.begin(url); // se conecta com o servidor através da UR fornecida Serial.println("[HTTP] GET..."); int httpCode = http.GET(); // envia uma solicitação GET e obtém o código de resposta if (httpCode == 200) // se o código de resposta for igual à 200 (OK), ... { invalido = false; // armazena que a requisição de cotação não é inválida DynamicJsonDocument doc(2048); // Bytes necessários para armazenar os objetos // e matrizes JSON na memória. 2048 Bytes com base no // cálculo em http://arduinojson.org/v6/assistant. // Analisa o objeto JSON DeserializationError error = deserializeJson(doc, http.getString()); if (error) // se houve erros na análise do JSON, ... { Serial.print("deserializeJson() falhou: "); Serial.println(error.f_str()); invalido = true; // armazena que a requisição de cotação é inválida } else // se não houve erros na análise do JSON, ... { // Extrai os valores do tempo e repassa para as variáveis globais dolar = String((const char*)doc["USDBRL"]["bid"]).toFloat(); varDolar = String((const char*)doc["USDBRL"]["pctChange"]).toFloat(); euro = String((const char*)doc["EURBRL"]["bid"]).toFloat(); varEuro = String((const char*)doc["EURBRL"]["pctChange"]).toFloat(); libra = String((const char*)doc["GBPBRL"]["bid"]).toFloat(); varLibra = String((const char*)doc["GBPBRL"]["pctChange"]).toFloat(); peso = String((const char*)doc["ARSBRL"]["bid"]).toFloat(); varPeso = String((const char*)doc["ARSBRL"]["pctChange"]).toFloat(); bitcoin = String((const char*)doc["BTCBRL"]["bid"]).toFloat(); varBitcoin = String((const char*)doc["BTCBRL"]["pctChange"]).toFloat(); iene = String((const char*)doc["JPYBRL"]["bid"]).toFloat(); varIene = String((const char*)doc["JPYBRL"]["pctChange"]).toFloat(); } } else { invalido = true; // armazena que a requisição de cotação é inválida } http.end(); // finaiza a conexão HTTP Serial.println("[HTTP] GET END!\n"); timerRequisicao = millis(); } // Muda a tela if (millis() - timerExibicao >= periodoExibicao || timerExibicao == 0) { telaAtual++; // a cada passada neste ponto, a tela muda if (telaAtual > 5) // se o índice da tela atual for maior // que a quantidade de telas máxima,... { telaAtual = 0; // volta para a tela inicial } if (invalido) // se a requisição anterior for inválida, ... { timerRequisicao = millis() + periodoRequisicao; // adiciona o tempo de periodo // entre as exibições + o tempo atual de millis() // para que uma nova requisição seja feita agora // e não após espera o período periodoRequisicao // terminar. } // exibe a cotação de uma determinada moeda de acordo com a tela atual // à que o programa se encontra. switch (telaAtual) { case 0: // Dólar printMoeda("Dolar", dolar, varDolar); break; case 1: // Euro printMoeda("Euro", euro, varEuro); break; case 2: // Libra Esterlina printMoeda("Libra Est.", libra, varLibra); break; case 3: // Peso Argentino printMoeda("Peso Arge.", peso, varPeso); break; case 4: // Bitcoin printMoeda("Bitcoin", bitcoin, varBitcoin); break; case 5: // Iene printMoeda("Iene", iene, varIene); break; } display.display(); // Atualiza o display timerExibicao = millis(); } // Exibe a barra de progresso if (millis() - timerProgress >= periodoProgress || timerProgress == 0) { // exibe o progresso atual de acordo com a tela atual à que o programa se // encontra e então com converte a faixa de periodo para porcentagem switch (telaAtual) { case 0: // Dólar printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 0, 16) ); // 0% a 16% break; case 1: // Euro printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 16, 32) ); // 16% a 32% break; case 2: // Libra Esterlina printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 32, 48) ); // 32% a 48% break; case 3: // Peso Argentino printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 48, 64) ); // 48% a 64% break; case 4: // Bitcoin printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 64, 80) ); // 64% a 80% break; case 5: // Iene printProgressBar( map(millis() - timerExibicao, 0, periodoExibicao, 80, 100) ); // 80% a 100% break; } display.display(); // Atualiza Display timerProgress = millis(); } delay(1); // pausa de 1 milissegundo } /** Função que mostra na tela o valor, a variação e o nome da moeda informada @param nomeMoeda - O nome da moeda à ser mostrada. @param moeda - O valor de cotação da moeda. @param variacaoMoeda - O valor de variação da moeda. */ void printMoeda(String nomeMoeda, float moeda, float variacaoMoeda) { if (invalido) // se a requisição anterior for inválida, ... { Serial.println("Requisição inválida"); // imprime que a requisição é inválida display.clearDisplay(); // Limpa a tela display.setTextSize(1); // multiplica a escala do texto por 1 display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.setCursor(10, 0); // configura o cursor para ir na coluna 10 e linha 0 display.println("Cotacao do Dolar"); display.setTextSize(2); // multiplica a escala do texto por 2 display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.println("Erro Rede"); } else // se não, ... { display.setTextWrap(false); display.clearDisplay(); // Limpa a tela display.setTextSize(2); // multiplica a escala do texto por 2 display.setTextColor(SSD1306_WHITE); // define a cor dos objetos // (texto, linhas, círculos, etc.) do // display para a cor branca equivalente // do display (azul). display.setCursor(0, 0); // configura o cursor para ir na coluna 0 e linha 0 display.println("Cotacao do Dolar"); display.println(nomeMoeda); // imprime o nome da moeda display.setTextSize(2); // multiplica a escala do texto por 2 display.setCursor(0, 16); // configura o cursor para ir na coluna 0 e linha 16 display.print("R$ "); if (moeda > 99) // se o valor da moeda for maior que 99, ... { display.setTextSize(2); // multiplica a escala do texto por 2 } else // se não, ... { display.setTextSize(3); // multiplica a escala do texto por 3 } display.println(String(moeda, 2)); // imprime o valor da moeda com 2 dígitos decimais display.drawLine(0, 40, display.width() - 1, 40, SSD1306_WHITE); // desenha uma // linha, partindo da coluna 0 e linha 42 até // a coluna final e linha 42, utilizando a cor // branca correspondente (azul). display.setCursor(0, 42); // configura o cursor para ir na coluna 0 e linha 42 display.setTextSize(2); // multiplica a escala do texto por 2 if (variacaoMoeda > 0) // se o valor de variação da moeda for maior que 0, ... { display.write(0x18); // escreve o caractere ↑ (Code Page 437) de endereço 0x18 } else if (variacaoMoeda < 0) { display.write(0x19); // escreve o caractere ↓ (Code Page 437) de endereço 0x19 } else { display.write(0x12); // escreve o caractere ↕ (Code Page 437) de endereço 0x12 } display.setCursor(20, 42); // configura o cursor para ir na coluna 20 e linha 42 variacaoMoeda = abs(variacaoMoeda); // define o valor de variação da moeda // para o valor absoluto do mesmo. display.print(String(variacaoMoeda, 2)); // imprime a variação da moeda display.println("%"); } } /** Função que mostra na tela a barra de progresso @param percent - O valor em porcentagem da barra de progresso. */ void printProgressBar(int percent) { if (!invalido) // se a requisição anterior for inválida, ... { // Desenha o quadrado externo display.drawRect(0, alturaDisplay - 5, larguraDisplay, 5, SSD1306_WHITE); // desenha um retângulo sem cor de preenchimento, partindo // da coluna '0' e linha 'alturaDisplay - 5' com uma // largura de 'larguraDisplay' e com uma altura de '5', // utilizando a cor branca correspondente (azul). //Variáveis locais de controle int larguraInternaMinima = 1; int larguraInternaMaxima = larguraDisplay - 1; int larguraInterna; larguraInterna = map(percent, 0, 100, larguraInternaMinima, larguraInternaMaxima); // Desenha o quadrado interno (preenchimento) display.fillRect(1, alturaDisplay - 5 + 1, larguraInterna, 3, SSD1306_WHITE); //Desenha as barras de separação das telas display.fillRect(21, alturaDisplay - 5, 1, 5, SSD1306_BLACK); display.fillRect(41, alturaDisplay - 5, 1, 5, SSD1306_BLACK); display.fillRect(61, alturaDisplay - 5, 1, 5, SSD1306_BLACK); display.fillRect(81, alturaDisplay - 5, 1, 5, SSD1306_BLACK); display.fillRect(101, alturaDisplay - 5, 1, 5, SSD1306_BLACK); } }
Para o sistema funcionar, deve-se alterar o valor das seguintes variáveis:
ssid
: alterar para o nome de sua rede WiFi;password
: alterar para a senha de sua rede WiFi.O ESP32 começa inicializando o display OLED. Se o OLED falhar na inicialização, o ESP32 entrará em um loop infinito até que haja um reset. Caso o OLED tenha inicializado corretamente, o ESP32 irá tentar se conectar à rede WiFi informada. Se a conexão WiFi for bem-sucedida, o programa continuará a execução. Caso a conexão WiFi for falha, o ESP32 irá se auto-resetar. A cada 30 segundos, o ESP32 irá buscar dados no servidor das cotações das moedas. A cada 5 segundos, a tela de moeda do display OLED é mudada para a próxima tela de moeda e é verificado se a busca de dados no servidor foi falha. Caso haja falha, o ESP32 irá realizar uma nova busca no servidor das cotações das moedas. A cada 0.05 segundos, o ESP32 atualiza a posição da barra de progresso.
Veja, no vídeo abaixo, a demonstração de funcionamento do projeto:
Nota: Valores de cotação para o dia 04/06/2022
Com isto, concluímos o post. Curtiu o post? Avalie e deixe um comentário! Siga-nos também no Instagram e nos marque quando fizer algum projeto nosso: @eletrogate. Até a próxima!
Conheça a Metodologia Eletrogate e ofereça aulas de robótica em sua escola!
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!