Internet de les coses amb ESP32 i ESP8266

Exemples Referència Plaques   Recursos CITCEA
Projectes Programació Perifèrics   Inici

Visualitzador de dades o avisos

Programa del grup 4

Aquest grup va fer un visualitzador del preu de criptomonedes. Amb aquest dispositiu, l'usuari pot veure el preu actual i la variació en un determinat període de les criptomonedes que triï.

Pantalla del dispositiu

Les dades s'obtenen en format JSON d'una API pública de CoinPaprika.

El programa del microcontrolador és el següent:

#include <WiFiNINA.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define NUM_CRYPTOS 3
#define POLS_BTC 7
#define POLS_ETH 5
#define POLS_BNB 4
#define DEBOUNCE_MS 300
#define PERIODE_REFRESH 15000UL
//Wi-Fi
const char idXarxa[]     = "iPhone de Pepe"; //Idenitifació de xarxa Wi-Fi
const char contrasenya[] = "12345678";
char server[] = "api.coinpaprika.com";
// Les 3 criptomonedes 
struct Crypto { const char* nom; const char* id; }; //API Criptomonedes
Crypto cryptos[NUM_CRYPTOS] = {
  { "Bitcoin",  "btc-bitcoin"      },
  { "Ethereum", "eth-ethereum"     },
  { "BNB",      "bnb-binance-coin" }
};
// Intervals: 0=1h  1=24h  2=7d
int intervalCripto[NUM_CRYPTOS] = { 0, 0, 0 };
const char* nomInterval[]       = { "1 h", "24 h", "7 d" }; 
int criptoActual = -1;
//Pantalla OLED 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//  Botons, un per cripto 
unsigned long darreraPremuda = 0;
//Dades guardades
float preuActual = 0.0; //Preu actual de la criptomoneda
float canvis[3]  = { 0.0, 0.0, 0.0 };  // 0=1 h, 1=24 h, 2=7 d
// Xarxa 
int status = WL_IDLE_STATUS;
WiFiSSLClient client;
String line        = "";
bool esperantDades = false;
unsigned long darreraConnexio = 0;
// SETUP, selecció de polsadors
void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;
  }
  pinMode(POLS_BTC, INPUT_PULLUP);
  pinMode(POLS_ETH, INPUT_PULLUP);
  pinMode(POLS_BNB, INPUT_PULLUP);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Error OLED");
    while (true);
  }
  display.clearDisplay(); //Mostrar "Inici..." a la pantalla
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.println("Inici...");
  display.display();
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("No s'ha trobat el dispositiu Wi-Fi");
    while (true);
  }
  String versio = WiFi.firmwareVersion();
  if (versio < "1.0.0") {
    Serial.println("Convindria actualitzar el firmware");
  }
  while (status != WL_CONNECTED) {
    Serial.print("Connectant a la xarxa ");
    Serial.println(idXarxa);
    status = WiFi.begin(idXarxa, contrasenya);
    delay(10000);
  }
  Serial.print("Connectat a ");
  Serial.println(WiFi.SSID());
  Serial.print("Estat de la connexió: ");
  Serial.println(WiFi.status());
  Serial.print("Adreça IP del dispositiu: ");
  Serial.println(WiFi.localIP());
  Serial.print("Intensitat del senyal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.println();
  Serial.println("Anem a connectar al servidor");
  mostrarIdle();
}
// LOOP
void loop() {
  if (botoPremat(POLS_BTC)) seleccionarCripto(0); //Es criden les funcions per escollir el interval
  if (botoPremat(POLS_ETH)) seleccionarCripto(1);
  if (botoPremat(POLS_BNB)) seleccionarCripto(2);
  if (esperantDades) processarResposta();
  if (criptoActual >= 0 && !esperantDades && darreraConnexio > 0 &&
    millis() - darreraConnexio > PERIODE_REFRESH) {
    mostrarCarregant();
    fetchDades(criptoActual);
  }
}
// LÒGICA PRINCIPAL
void seleccionarCripto(int idx) {
  if (criptoActual == idx) {
    // Mateixa cripto, cicla l'interval sense haver de tornar a cridar l'API
    intervalCripto[idx] = (intervalCripto[idx] + 1) % 3;
    mostrarDetall();
  } else {
    // Cripto diferent, cal demanar dades noves
    criptoActual = idx;
    mostrarCarregant();
    fetchDades(idx);
  }
}
// COMUNICACIÓ API
void fetchDades(int idx) {
  char pagina[70];
  snprintf(pagina, sizeof(pagina), "/v1/tickers/%s?quotes=EUR", cryptos[idx].id); //Accedeix a la API
  client.stop();
  if (client.connect(server, 443)) {
    Serial.print("Connexio OK: ");
    Serial.println(cryptos[idx].id);
    client.print("GET ");
    client.print(pagina);
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();
    darreraConnexio = millis();
    esperantDades   = true;
  } else {
    Serial.println("Connexio fallida");
    mostrarError();
    delay(2000);
    if (criptoActual >= 0) mostrarDetall();
    else                   mostrarIdle();
  }
}
void processarResposta() {
  while (client.available()) {
    line = client.readStringUntil('\r');
    if (line.indexOf('{') >= 0) {
      DynamicJsonDocument doc(8000);
      DeserializationError err = deserializeJson(doc, line);
      if (!err) {
        preuActual = doc["quotes"]["EUR"]["price"].as<float>(); //Extreiem la informació necessària del JSON
        canvis[0]  = doc["quotes"]["EUR"]["percent_change_1h"].as<float>();
        canvis[1]  = doc["quotes"]["EUR"]["percent_change_24h"].as<float>();
        canvis[2]  = doc["quotes"]["EUR"]["percent_change_7d"].as<float>();
        esperantDades = false;
        mostrarDetall();
      } else {
        Serial.print("JSON error: ");
        Serial.println(err.c_str());
        esperantDades = false;
        mostrarError();
        delay(2000);
        mostrarIdle();
      }
    }
  }
}
// PANTALLES
void mostrarIdle() {
  display.clearDisplay();
  display.setTextColor(WHITE); //S'ha d'especificar el color perquè sinó no apareix
  // Zona groga
  display.setTextSize(1);
  display.setCursor(22, 4);
  display.println("CRYPTOTRACKER");
  display.drawLine(0, 15, 127, 15, WHITE);
  // Zona blava, instruccions inicials
  display.setCursor(0, 20);
  display.println("Prem un boto:");
  display.setCursor(0, 32);
  display.println("1: Bitcoin");
  display.setCursor(0, 42);
  display.println("2: Ethereum");
  display.setCursor(0, 52);
  display.println("3: BNB");
  display.display();
}
void mostrarDetall() {
  display.clearDisplay();
  display.setTextColor(WHITE); 
  // Zona groga: nom cripto + interval actiu
  display.setTextSize(1);
  display.setCursor(0, 4);
  display.print(cryptos[criptoActual].nom);
  display.print(" [");
  display.print(nomInterval[intervalCripto[criptoActual]]);
  display.print("]");
  display.drawLine(0, 15, 127, 15, WHITE);
  // Zona blava: preu en EUR
  display.setTextSize(1);
  display.setCursor(0, 18);
  display.print("Preu: ");
  if      (preuActual >= 10000) display.print(preuActual, 0);
  else if (preuActual >= 1)     display.print(preuActual, 2);
  else                          display.print(preuActual, 5);
  display.print(" EUR");   
  display.drawLine(0, 30, 127, 30, WHITE);
  // Canvi de punt a coma
  float val = canvis[intervalCripto[criptoActual]];
  display.setTextSize(2);
  display.setCursor(0, 38);
  if (val >= 0) display.print("+");
  else          display.print("-");
  String s = String(abs(val), 2);
  s.replace(".", ",");
  display.print(s);
  display.print(" %");
  display.display();
}
void mostrarCarregant() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  // Zona groga
  display.setTextSize(1);
  display.setCursor(28, 4);
  display.println("Carregant...");
  display.drawLine(0, 15, 127, 15, WHITE);
  // Zona blava
  if (criptoActual >= 0) {
    display.setCursor(0, 22);
    display.print("Cripto: ");
    display.println(cryptos[criptoActual].nom);
    display.setCursor(0, 38);
    display.print("Interval: ");
    display.println(nomInterval[intervalCripto[criptoActual]]);
  }
  display.display();
}
void mostrarError() {  // mostrar error si no es conecta 
  display.clearDisplay();
  display.setTextColor(WHITE);
  // Zona groga
  display.setTextSize(1);
  display.setCursor(20, 4);
  display.println("!! ERROR !!");
  display.drawLine(0, 15, 127, 15, WHITE);
  // Zona blava
  display.setCursor(4, 22);
  display.println("Error de connexio");
  display.setCursor(4, 38); 
  display.display();
}
// utilitats del polsadors 
bool botoPremat(int pin) {
  if (digitalRead(pin) == LOW) {
    unsigned long ara = millis();
    if (ara - darreraPremuda > DEBOUNCE_MS) {
      darreraPremuda = ara;
      return true;
    }
  }
  return false;
}

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.