Select Your Favourite
Category And Start Learning.

Mini TV con ESP32

En esta entrada realizaremos un mini TV utilizando la ya conocida ESP32 y una pantalla TFT de 1.65 pulgadas de la marca WavesShare. Esta mini TV es ideal para ver los resultados de los partidos de futbol como la Eurocopa, Copa America, Liga MX y mas torneos o deportes, así como para otras aplicaciones como monitorear el clima de tu ciudad entre otras muchas aplicaciones mas.

Componentes.

Pare este proyecto ocupamos los siguientes componentes.

  • Pantalla TFT 1.69″ de la marca WavesShare
  • ESP32
  • Stand o Carcasa para la pantalla (opcional)

Carcasa para Mini TV (Opcional).

Como primer paso diseñamos un stand para la pantalla en nuestro caso lo diseñaremos para que se vea como una televisión antigua gracias a la forma rectangular de esta, este paso es opcional nosotros lo haremos así para una mejor presentación de este proyecto. En la pagina de WavesShare obtuvimos las medidas de la pantalla, buscamos una referencia de TV antigua y con esto realizamos un modelo para imprimir.

Modelo de referencia.

Modelo 3D para imprimir.

Desarrollo de la Aplicación.

En este ejemplo desarrollaremos una aplicación para ver en la mini TV los resultados de los partidos de futbol del dia en curso, para esto nuestra aplicación constara de tres partes:

  • Servidor NTP, para obtener la fecha y hora.
  • API-Football, para obtener los resultados de los partidos del día y los próximos partidos.
  • Función Main Page, para imprimir en la pantalla la información obtenida como banderas de los equipos, resultados etc,…

Teniendo los tres puntos explicados anteriormente juntaremos todos para obtener nuestra aplicación.

Componentes de la aplicación.

Servidor NTP

A continuación explicaremos el código de programación utilizado para nuestro servidor NTP.

Iniciamos declarando las librerías Wifi y Time y enseguida ingresamos las claves de internet para la conexión por wifi del ESP32.

#include "WiFi.h"
#include "time.h"

// Claves internet
const char* ssid = "SSID";
const char* password = "Your Password";

Enseguida configuramos el servidor para obtener la fecha y la hora, en este caso utilizaremos “pool.ntp.org” en esta parte lo mas importante es configurar el Offset en segundos de la región en la que nos encontramos utilizando el uso horario en nuestro caso la ciudad de monterrey tiene el uso horario GTM-6 para definir el Offset basta con una simple multiplicación, (-6 * 3600), -6 por el uso horario de nuestra ubicación y 3600 por los segundos que tiene una hora, esta multiplicación nos da un resultado de -21600 que es el valor que le daremos a nuestro Offset.

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = -21600;
const int   daylightOffset_sec = 0;

Por ultimo declaramos dos variables tipo string “todayDate” para almacenar la fecha actual y “fixtureReq” para almacenar el request que queremos solicitar a la API-Football.

String todayDate;
String fixtureReq;

En el void setup iniciamos el serial, nos conectamos al Wifi y configuramos el servidor.

void setup() {
  // Iniciar serial
  Serial.begin(115200);

  // Conectar al WiFi
  Serial.print("Intentando conectarse a la red: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  // Esperar conectarse a la red:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    // Esperar un segundo para reintentar
    delay(1000);
  }
  Serial.print("\nConectado a: ");
  Serial.println(ssid);

  // Configurar tiempo
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

En el void loop estaremos obteniendo el tiempo cada cinco segundos con la función “getLocalTime” dentro de esta función se declara una estructura del tipo “tm timeinfo” la cual es una estructura estándar en ANSI C con la cual obtenemos la fecha y la hora con el formato que necesitamos. Esta fecha obtenida nos servirá como parámetro en nuestro request para API-Football y así obtendremos los resultados de los partidos del día en curso.

void loop() {
  // Obtener fecha y tiempo actual
  getLocalTime();
  delay(5000);
}

void getLocalTime(){
  // Crear una estructura tm llamda timeinfo
  struct tm timeinfo;

  // Esperar para obtener el tiempo local
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }

  Serial.println("Fecha y tiempo actual:");

  // Obtener la fecha
  char formattedDate[16];
  strftime(formattedDate, sizeof(formattedDate), "%Y-%m-%d", &timeinfo);
  Serial.print("Fecha: ");
  Serial.println(formattedDate);

  // Obtener la hora
  char formattedTime[16];
  strftime(formattedTime, sizeof(formattedTime), "%H:%M", &timeinfo);
  Serial.print("Hora: ");
  Serial.println(formattedTime);

  // Imprimir request
  fixtureReq = String("/v3/fixtures?date=") + formattedDate + String("&league=4&season=2024&timezone=America%2FMonterrey");
  Serial.print("Url: ");
  Serial.println(fixtureReq);
}

Cargamos el codigo a la ESP32 y al final deberíamos de ver como se imprime la fecha, enseguida la hora y por ultimo el request que utilizaremos para la API-Football con la fecha que generamos con el servidor NTP.

Fecha y hora obtenida con servidor NTP.

API-Football

A continuación se muestra el código de programación para obtener los resultados de los partidos de la API-Football, por ser este muy extenso se recomiendo que vean la explicación de este en el video que mostraremos mas adelante.

#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "WiFi.h"

// Claves internet
const char* ssid = "SSID";
const char* password = "PASSWORD";

// Server api-football
const char* serverName = "api-football-v1.p.rapidapi.com";
String host = "api-football-v1.p.rapidapi.com";
String apiKey = "Your-Api-Key";

//Certificado del server
const char* test_root_ca =
  "-----BEGIN CERTIFICATE-----\n"
  "MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV\n"
  "BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw\n"
  "MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0\n"
  "eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV\n"
  "UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE\n"
  "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp\n"
  "ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi\n"
  "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/\n"
  "y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N\n"
  "Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo\n"
  "Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C\n"
  "zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J\n"
  "Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB\n"
  "AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O\n"
  "BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV\n"
  "rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u\n"
  "c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud\n"
  "HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG\n"
  "BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G\n"
  "VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1\n"
  "l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt\n"
  "8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ\n"
  "59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu\n"
  "VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w=\n"
  "-----END CERTIFICATE-----\n";

// Datos de los requests
String fixtureReq = "/v3/fixtures?date=2024-06-30&league=4&season=2024&timezone=America/Monterrey";
String nextGamesReq = "/v3/fixtures?&league=4&season=2024&next=4&timezone=America/Monterrey";
String eventReq;

// Constantes
const int maxFixtures = 6;
const int maxGoals = 20;

// Varibles
int fixturesResults = 0;
int eventsResults = 0;
int goalsCounter = 0;
int isGameLiveSum = 0;
int todayGames = 1;
int isGameLive[maxFixtures];
int firstTeamGoalsCounterArray[maxFixtures];
int secondTeamGoalsCounterArray[maxFixtures];
String eventType;
String matchGoals;
String gameDate;
String gameTime;
String fixtureID[maxFixtures];
String eventDate[maxFixtures];
String gameStatus[maxFixtures];
String timeElapsed[maxFixtures];
String extraTime[maxFixtures];
String firstTeam[maxFixtures];
String firstTeamGoals[maxFixtures];
String firstTeamPenaltyGoals[maxFixtures];
String secondTeam[maxFixtures];
String secondTeamGoals[maxFixtures];
String secondTeamPenaltyGoals[maxFixtures];
String firstTeamPlayerGoal[maxFixtures][maxGoals];
String firstTeamMinuteGoal[maxFixtures][maxGoals];
String secondTeamPlayerGoal[maxFixtures][maxGoals];
String secondTeamMinuteGoal[maxFixtures][maxGoals];


// Definimos el cliente
WiFiClientSecure client;

void setup() {
  // Iniciar serial
  Serial.begin(115200);

  // Conectar al WiFi
  Serial.print("Intentando conectarse a la red: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  // Esperar conectarse a la red:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    // Esperar un segundo para reintentar
    delay(1000);
  }
  Serial.print("Conectado a: ");
  Serial.println(ssid);

  // Configurar certificado
  client.setCACert(test_root_ca);

  // Realizar un fixture request con los partidos de la fecha indicada
  getFixtureData(fixtureReq);

  // Ver si hay partidos en la fecha indicada
  if (todayGames == 0) {
    // De no ser así imprimir la info de los siguientes cuatro fixtures
    Serial.println("No se encontraron fixtures la fecha indicada, solicitando los siguientes cuatro partidos");
    getFixtureData(nextGamesReq);
  } else {
    // De ser así obtener la información de los fixtures del día
    Serial.print("Se encontraron ");
    Serial.print(fixturesResults);
    Serial.println(" fixtures la fecha indicada");
  }
  // Imprimir en el serial la información
  printFixturesData();

  // Si hay partidos en la fecha indicada obtener los eventos
  if (todayGames > 0) {
    for (int i = 0; i < fixturesResults; i++) {
      // Construir el request de los evenfos fixture por fixture
      Serial.print("Solicitando eventos del fixture: ");
      Serial.println(fixtureID[i]);
      eventReq = String("/v3/fixtures/events") + String("?fixture=") + fixtureID[i];
      // Realizar la solicitud
      getEventsData(i, eventReq);
    }
    // Imprimir los eventos gol en el serial
    printEventsData();
  }
}

void loop() {
  // Si hay partidos en el día
  if (todayGames == 0) {
    // De no ser así obtener la información de los siguientes cuatro fixtures
    getFixtureData(nextGamesReq);
  } else {
    // De ser así obtener la información de los fixtures del día
    getFixtureData(fixtureReq);
  }

  // Imprimir la cantidad de fixtures encontrados con la información de los fixtures
  Serial.print("Se encontraron ");
  Serial.print(fixturesResults);
  Serial.println(" fixtures la fecha indicada");
  printFixturesData();

  // Imprimir los eventos de los fixtures sólo si están en vivo
  for (int i = 0; i < fixturesResults; i++) {
    if (isGameLive[i]) {
      Serial.print("Solicitando eventos del fixture: ");
      Serial.println(fixtureID[i]);
      eventReq = String("/v3/fixtures/events") + String("?fixture=") + fixtureID[i];
      getEventsData(i, eventReq);
    }
  }
  printEventsData();
  // Esperar un minuto
  delay(60000);
}


void getFixtureData(String req) {
  String result;
  //Conectar al servidor
  Serial.println("\nStarting connection to server...");
  client.setTimeout(15000);
  if (!client.connect(serverName, 443)) {
    Serial.println("Connection failed!");
    return;
  }
  Serial.println("Connected to server!");
  // Solicitud GET
  client.print(String("GET ") + req + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "X-RapidAPI-Key: " + apiKey + "\r\n" + "Connection: close\r\n\r\n");

  // Esperar a la respuesta del servidor
  while (client.connected() && !client.available()) {
    delay(10);
  }

  // Leer respuesta
  while (client.connected() || client.available()) {
    if (client.available()) {
      result = client.readStringUntil('\n');
      Serial.println(result);
    }
  }

  // Cerrar la conexión
  Serial.println("Closing connection");
  client.stop();

  // Deserializar información de la respuesta
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, result);
  Serial.println("Deserializing api data");
  if (error) {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }
  Serial.println("Deserialize succesfull");

  fixturesResults = doc["results"];
  if (fixturesResults == 0 && req == fixtureReq) {
    todayGames = 0;
  }

  int fixtureCounter = 0;

  // Leer información de la respuesta por fixture
  for (JsonObject response_item : doc["response"].as<JsonArray>()) {
    JsonObject response_item_fixture = response_item["fixture"];
    fixtureID[fixtureCounter] = String(response_item_fixture["id"]);
    eventDate[fixtureCounter] = String(response_item_fixture["date"]);

    JsonObject response_item_fixture_status = response_item_fixture["status"];
    gameStatus[fixtureCounter] = String(response_item_fixture_status["short"]);
    timeElapsed[fixtureCounter] = String(response_item_fixture_status["elapsed"]);

    JsonObject response_teams = response_item["teams"];
    firstTeam[fixtureCounter] = String(response_teams["home"]["name"]);
    secondTeam[fixtureCounter] = String(response_teams["away"]["name"]);
    firstTeamGoals[fixtureCounter] = String(response_item["goals"]["home"]);
    secondTeamGoals[fixtureCounter] = String(response_item["goals"]["away"]);

    for (JsonPair response_item_score_item : response_item["score"].as<JsonObject>()) {
      if (response_item_score_item.key().c_str() == "penalty") {
        firstTeamPenaltyGoal[fixtureCounter] = response_item_score_item.value()["home"];
        secondTeamPenaltyGoal[fixtureCounter] = response_item_score_item.value()["away"];
      }
    }



    if (gameStatus[fixtureCounter] == "1H" || gameStatus[fixtureCounter] == "2H" || gameStatus[fixtureCounter] == "ET" || gameStatus[fixtureCounter] == "P") {
      isGameLive[fixtureCounter] = 1;
    } else {
      isGameLive[fixtureCounter] = 0;
    }
    fixtureCounter += 1;
  }
}

void getEventsData(int fixtureNumber, String req) {
  String result;
  //Conectar al servidor
  Serial.println("\nStarting connection to server...");
  client.setTimeout(15000);
  if (!client.connect(serverName, 443)) {
    Serial.println("Connection failed!");
    return;
  }
  Serial.println("Connected to server!");
  // Solicitud GET
  client.print(String("GET ") + req + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "X-RapidAPI-Key: " + apiKey + "\r\n" + "Connection: close\r\n\r\n");

  // Esperar a la respuesta del servidor
  while (client.connected() && !client.available()) {
    delay(10);
  }

  // Leer respuesta
  while (client.connected() || client.available()) {
    if (client.available()) {
      result = client.readStringUntil('\n');
      Serial.println(result);
    }
  }

  // Cerrar la conexión
  Serial.println("Closing connection");
  client.stop();

  // Deserializar información de la respuesta
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, result);
  Serial.println("Deserializing api data");
  if (error) {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }
  Serial.println("Deserialize succesfull");


  int eventsCounter = 0;
  int firstTeamGoalsCounter = 0;
  int secondTeamGoalsCounter = 0;
  String extraTime = "";

  // Leer información de la respuesta por evento
  for (JsonObject response_item : doc["response"].as<JsonArray>()) {
    extraTime = "";
    // Si el evento es tipo Goal
    if (response_item["type"] == "Goal") {
      JsonObject response_item_team = response_item["team"];
      // Si es del primer equipo
      if (firstTeam[fixtureNumber] == response_item_team["name"]) {
        firstTeamPlayerGoal[fixtureNumber][firstTeamGoalsCounter] = String(response_item["player"]["name"]);
        extraTime = String(response_item["time"]["extra"]);
        // Si no fue en tiempo añadido
        if (extraTime == "null") {
          firstTeamMinuteGoal[fixtureNumber][firstTeamGoalsCounter] = String(response_item["time"]["elapsed"]) + "'";
        }
        // Si fue en tiempo añadido
        else {
          firstTeamMinuteGoal[fixtureNumber][firstTeamGoalsCounter] = String(response_item["time"]["elapsed"]) + "+" + extraTime + "'";
        }
        firstTeamGoalsCounter += 1;
      }
      // Si es del segundo equipo
      if (secondTeam[fixtureNumber] == response_item_team["name"]) {
        secondTeamPlayerGoal[fixtureNumber][secondTeamGoalsCounter] = String(response_item["player"]["name"]);
        extraTime = String(response_item["time"]["extra"]);
        // Si no fue en tiempo añadido
        if (extraTime == "null") {
          secondTeamMinuteGoal[fixtureNumber][secondTeamGoalsCounter] = String(response_item["time"]["elapsed"]) + "'";
        }
        // Si fue en tiempo añadido
        else {
          secondTeamMinuteGoal[fixtureNumber][secondTeamGoalsCounter] = String(response_item["time"]["elapsed"]) + "+" + extraTime + "'";
        }
        secondTeamGoalsCounter += 1;
      }
    }
  }
}

void printFixturesData() {
  for (int i = 0; i < fixturesResults; i++) {
    Serial.print("Id: ");
    Serial.println(fixtureID[i]);
    Serial.print("Primer equipo: ");
    Serial.println(firstTeam[i]);
    Serial.print("Segundo equipo: ");
    Serial.println(secondTeam[i]);
    Serial.print("Date: ");
    Serial.println(eventDate[i]);
    Serial.print("El partido está en vivo: ");
    Serial.println(isGameLive[i]);
    Serial.print("Game Status: ");
    Serial.println(gameStatus[i]);
    Serial.print("Elapsed: ");
    Serial.println(timeElapsed[i]);
    Serial.print("Goles primer equipo: ");
    Serial.println(firstTeamGoals[i]);
    Serial.print("Goles segundo equipo: ");
    Serial.println(secondTeamGoals[i]);
  }
}

void printEventsData() {
  for (int i = 0; i < fixturesResults; i++) {
    Serial.print("Anotadores de ");
    Serial.println(firstTeam[i]);
    for (int j = 0; j < firstTeamGoals[i].toInt(); j++) {
      Serial.print(firstTeamPlayerGoal[i][j]);
      Serial.print(" ");
      Serial.println(firstTeamMinuteGoal[i][j]);
    }
    Serial.print("Anotadores de ");
    Serial.println(secondTeam[i]);
    for (int j = 0; j < secondTeamGoals[i].toInt(); j++) {
      Serial.print(secondTeamPlayerGoal[i][j]);
      Serial.print(" ");
      Serial.println(secondTeamMinuteGoal[i][j]);
    }
  }
}

Conexiones ESP32 – Pantalla.

A continuación se muestra las tabla para las conexiones entre el ESP32 y la Pantalla LCD de 1.69″.

Codigo de programación Pantalla.

Ahora solo nos queda realizar la programación para la pantalla LCD para esto ademas de la fecha, la hora y la información de los partidos tambien ocupamos generar las banderas de los equipos que esten jugando cada partido. Para este caso mostraremos en la pantalla los resultados de los partidos de la Eurocopa Alemania 2024, es por esto que debemos de generar la bandera de cada una de las selecciones participantes, al ser esto muy laborioso optamos por el uso de una de las herramientas mas utilizadas actualmente la Inteligencia Artificial a la cual le solicitamos nos genere el codigo para cada bander. El codigo de programación quedo de la siguiente manera:

#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>

// Pines TFT
#define TFT_CS 5
#define TFT_RST 32
#define TFT_DC 33

// Colores texto
#define ORANGE_TEXT 0xDAE0
#define GREY_TEXT 0xEF7D
#define BLACK_TEXT 0x31C6
#define GREEN_TEXT 0x0680

// Colores banderas
#define WHITE 0xFFFF
#define BLACK 0x0000
#define RED 0xF800
#define GREEN 0x07E0
#define BLUE 0x001F
#define YELLOW 0xFFE0
#define ORANGE 0xFD20
#define PURPLE 0x780F
#define CYAN 0x07FF
#define LIGHT_BLUE 0x5D9E

// Enum de Países
enum countries {
  PORTUGAL,
  CZECH,
  SPAIN,
  FRANCE,
  GERMANY,
  ITALY,
  ENGLAND,
  GEORGIA,
  SWITZERLAND,
  SCOTLAND,
  HUNGARY,
  ROMANIA,
  SLOVAKIA,
  BELGIUM,
  UKRAINE,
  AUSTRIA,
  POLAND,
  NETHERLANDS,
  SERBIA,
  SLOVENIA,
  DENMARK,
  CROATIA,
  ALBANIA,
  TURKEY
};

// Declarar pantalla tft
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  // Iniciar serial
  Serial.begin(115200);

  // Inicializar pantalla TFT
  tft.init(240, 280);
  tft.setRotation(3);
  tft.fillScreen(GREY_TEXT);
  tft.setTextSize(3);
  drawtext("Iniciando", BLACK_TEXT, 60, 110);
  delay(1000);

  // Mostrar página principal
  mainPage();
}

void loop() {
}

// Función para imprimir texto en pantalla tft
void drawtext(String text, uint16_t color, int x, int y) {
  tft.setCursor(x, y);
  tft.setTextColor(color);
  tft.setTextWrap(true);
  tft.print(text);
}

// Funciones para dibujar banderas
void drawPortugalFlag(int x_center, int y_center) {
  int radius = 20; // Radio para un diámetro de 40 píxeles

  // Dibujar el fondo (verde y rojo)
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        int x_pos = x_center + x;
        int y_pos = y_center + y;
        if (x < 0) {
          tft.drawPixel(x_pos, y_pos, GREEN);
        } else {
          tft.drawPixel(x_pos, y_pos, RED);
        }
      }
    }
  }

  // Dibujar el círculo amarillo central (escudo)
  for (int y = -10; y <= 10; y++) {
    for (int x = -10; x <= 10; x++) {
      if (x * x + y * y <= 10 * 10) {
        tft.drawPixel(x_center - 5 + x, y_center + y, YELLOW);
      }
    }
  }

  // Dibujar el círculo rojo dentro del círculo amarillo
  for (int y = -7; y <= 7; y++) {
    for (int x = -7; x <= 7; x++) {
      if (x * x + y * y <= 7 * 7) {
        tft.drawPixel(x_center - 5 + x, y_center + y, RED);
      }
    }
  }

  // Dibujar el círculo blanco dentro del círculo rojo
  for (int y = -5; y <= 5; y++) {
    for (int x = -5; x <= 5; x++) {
      if (x * x + y * y <= 5 * 5) {
        tft.drawPixel(x_center - 5 + x, y_center + y, WHITE);
      }
    }
  }

  // Detalles del escudo (cruces azules)
  for (int i = -1; i <= 1; i++) {
    tft.drawPixel(x_center - 5 - 2 + i, y_center, BLUE);
    tft.drawPixel(x_center - 5 + 2 + i, y_center, BLUE);
    tft.drawPixel(x_center - 5 - 1, y_center - 1 + i, BLUE);
    tft.drawPixel(x_center - 5 + 1, y_center - 1 + i, BLUE);
    tft.drawPixel(x_center - 5, y_center - 2 + i, BLUE);
    tft.drawPixel(x_center - 5, y_center + 2 + i, BLUE);
  }

  // Castillos amarillos
  int castles[12][2] = {
    {-8, -5}, {-8, 5}, {-6, -7}, {-6, 7},
    {0, -7}, {0, 7}, {2, -5}, {2, 5},
    {4, -5}, {4, 5}, {4, -7}, {4, 7}
  };
  for (int i = 0; i < 12; i++) {
    tft.drawPixel(x_center - 5 + castles[i][0], y_center + castles[i][1], YELLOW);
  }
}

void drawCzechFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (x < 0 && abs(x) >= abs(y)) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);  // Triángulo azul
        } else if (y <= 0) {
          tft.drawPixel(x_center + x, y_center + y, WHITE);  // Parte superior blanca
        } else {
          tft.drawPixel(x_center + x, y_center + y, RED);  // Parte inferior roja
        }
      }
    }
  }
}

void drawSpainFlag(int x_center, int y_center) {
  int radius = 20;
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
        if (y >= -radius / 3 && y <= radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, YELLOW);
        }
      }
    }
  }
}

void drawFranceFlag(int x_center, int y_center) {
  int radius = 20;
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (x < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else if (x > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawGermanyFlag(int x_center, int y_center) {
  int radius = 20;
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLACK);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, YELLOW);
        } else {
          tft.drawPixel(x_center + x, y_center + y, RED);
        }
      }
    }
  }
}

void drawItalyFlag(int x_center, int y_center) {
  int radius = 20;
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (x < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, GREEN);
        } else if (x > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawEnglandFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half
  int cross_thickness = 6;  // Grosor de la cruz

  // Dibujar el círculo blanco de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, WHITE);
      }
    }
  }

  // Dibujar la línea vertical de la cruz
  for (int i = -radius; i <= radius; i++) {
    for (int j = -cross_thickness / 2; j <= cross_thickness / 2; j++) {
      if ((i * i + j * j) <= radius * radius) {
        tft.drawPixel(x_center + j, y_center + i, RED);
      }
    }
  }

  // Dibujar la línea horizontal de la cruz
  for (int i = -radius; i <= radius; i++) {
    for (int j = -cross_thickness / 2; j <= cross_thickness / 2; j++) {
      if ((i * i + j * j) <= radius * radius) {
        tft.drawPixel(x_center + i, y_center + j, RED);
      }
    }
  }
}

void drawGeorgiaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, WHITE);
      }
    }
  }

  // Dibujar la cruz central
  for (int i = -radius; i <= radius; i++) {
    if (i * i <= radius * radius) {
      // Línea vertical de la cruz central
      tft.drawPixel(x_center, y_center + i, RED);
      tft.drawPixel(x_center - 1, y_center + i, RED);
      tft.drawPixel(x_center + 1, y_center + i, RED);
      // Línea horizontal de la cruz central
      tft.drawPixel(x_center + i, y_center, RED);
      tft.drawPixel(x_center + i, y_center - 1, RED);
      tft.drawPixel(x_center + i, y_center + 1, RED);
    }
  }

  // Dibujar las cuatro cruces pequeñas
  int cross_size = 3;
  int cross_offset = radius / 2;
  for (int dy = -1; dy <= 1; dy++) {
    for (int dx = -1; dx <= 1; dx++) {
      if (abs(dx) + abs(dy) == 1) {
        for (int i = -cross_size; i <= cross_size; i++) {
          // Cruces pequeñas superior izquierda
          tft.drawPixel(x_center - cross_offset + dx * cross_size, y_center - cross_offset + dy * cross_size + i, RED);
          tft.drawPixel(x_center - cross_offset + dx * cross_size + i, y_center - cross_offset + dy * cross_size, RED);
          // Cruces pequeñas superior derecha
          tft.drawPixel(x_center + cross_offset + dx * cross_size, y_center - cross_offset + dy * cross_size + i, RED);
          tft.drawPixel(x_center + cross_offset + dx * cross_size + i, y_center - cross_offset + dy * cross_size, RED);
          // Cruces pequeñas inferior izquierda
          tft.drawPixel(x_center - cross_offset + dx * cross_size, y_center + cross_offset + dy * cross_size + i, RED);
          tft.drawPixel(x_center - cross_offset + dx * cross_size + i, y_center + cross_offset + dy * cross_size, RED);
          // Cruces pequeñas inferior derecha
          tft.drawPixel(x_center + cross_offset + dx * cross_size, y_center + cross_offset + dy * cross_size + i, RED);
          tft.drawPixel(x_center + cross_offset + dx * cross_size + i, y_center + cross_offset + dy * cross_size, RED);
        }
      }
    }
  }
}

void drawSwitzerlandFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo rojo de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
      }
    }
  }

  // Dibujar la cruz blanca
  int cross_size = 5;
  for (int i = -cross_size; i <= cross_size; i++) {
    tft.drawPixel(x_center, y_center + i, WHITE);
    tft.drawPixel(x_center + i, y_center, WHITE);
    if (abs(i) <= 2) {
      tft.drawPixel(x_center - 2, y_center + i, WHITE);
      tft.drawPixel(x_center + 2, y_center + i, WHITE);
      tft.drawPixel(x_center + i, y_center - 2, WHITE);
      tft.drawPixel(x_center + i, y_center + 2, WHITE);
    }
  }
}

void drawScotlandFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo azul de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, LIGHT_BLUE);
      }
    }
  }

  // Dibujar las dos cruces diagonales blancas
  int width = 4;  // Ancho de la cruz

  for (int i = -radius; i <= radius; i++) {
    for (int j = -width; j <= width; j++) {
      // Diagonal positiva
      int x1 = i;
      int y1 = i + j;
      if (x1 * x1 + y1 * y1 <= radius * radius) {
        tft.drawPixel(x_center + x1, y_center + y1, WHITE);
      }

      // Diagonal negativa
      int x2 = i;
      int y2 = -i + j;
      if (x2 * x2 + y2 * y2 <= radius * radius) {
        tft.drawPixel(x_center + x2, y_center + y2, WHITE);
      }
    }
  }
}

void drawHungaryFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, GREEN);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawRomaniaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (x < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else if (x > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, YELLOW);
        }
      }
    }
  }
}

void drawSlovakiaFlag(int x_center, int y_center) {
  int radius = 20; // 40 pixels diameter, radius is half

  // Dibujar el círculo de fondo de la bandera
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        }
      }
    }
  }

  // Dibujar el escudo simplificado
  int shield_width = 10;
  int shield_height = 12;
  int shield_x = x_center - radius / 2; // Posicionarlo hacia la izquierda
  int shield_y = y_center - radius / 3;  // Posicionarlo ligeramente hacia arriba

  // Dibujar el contorno del escudo con una parte inferior redondeada
  for (int y = 0; y <= shield_height; y++) {
    for (int x = 0; x <= shield_width; x++) {
      if (x == 0 || x == shield_width || y == 0 || (y == shield_height && (x > 1 && x < shield_width - 1)) ||
          (y > shield_height - 3 && (x == 1 || x == shield_width - 1))) {
        tft.drawPixel(shield_x + x, shield_y + y, WHITE);
      } else {
        tft.drawPixel(shield_x + x, shield_y + y, RED);
      }
    }
  }

  // Dibujar la cruz blanca en el escudo
  for (int y = 2; y <= 10; y++) {
    for (int x = 4; x <= 5; x++) {
      tft.drawPixel(shield_x + x, shield_y + y, WHITE);
    }
  }
  for (int y = 4; y <= 5; y++) {
    for (int x = 2; x <= 7; x++) {
      tft.drawPixel(shield_x + x, shield_y + y, WHITE);
    }
  }

  // Dibujar las montañas azules en el escudo
  for (int y = 8; y <= 10; y++) {
    for (int x = 3; x <= 6; x++) {
      tft.drawPixel(shield_x + x, shield_y + y, BLUE);
    }
  }
}

void drawBelgiumFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (x < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLACK);
        } else if (x > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, YELLOW);
        }
      }
    }
  }
}

void drawUkraineFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < 0) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else {
          tft.drawPixel(x_center + x, y_center + y, YELLOW);
        }
      }
    }
  }
}

void drawAustriaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawPolandFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < 0) {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        } else {
          tft.drawPixel(x_center + x, y_center + y, RED);
        }
      }
    }
  }
}

void drawNetherlandsFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawSerbiaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo de fondo de la bandera
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }

  // Dibujar el escudo simplificado
  int shield_radius = 6;
  int shield_center_x = x_center - radius / 3;  // Posicionarlo hacia la izquierda
  int shield_center_y = y_center - radius / 4;  // Posicionarlo ligeramente hacia arriba

  // Dibujar el contorno del escudo
  for (int y = -shield_radius; y <= shield_radius; y++) {
    for (int x = -shield_radius; x <= shield_radius; x++) {
      if (x * x + y * y <= shield_radius * shield_radius) {
        tft.drawPixel(shield_center_x + x, shield_center_y + y, RED);
      }
    }
  }

  // Dibujar la cruz blanca en el escudo
  int cross_size = 2;
  for (int y = -cross_size; y <= cross_size; y++) {
    for (int x = -cross_size; x <= cross_size; x++) {
      tft.drawPixel(shield_center_x + x, shield_center_y + y, WHITE);
    }
  }

  // Dibujar las coronas amarillas en el escudo
  int crown_radius = 3;
  for (int y = -crown_radius; y <= crown_radius; y++) {
    for (int x = -crown_radius; x <= crown_radius; x++) {
      if (x * x + y * y <= crown_radius * crown_radius) {
        tft.drawPixel(shield_center_x + x, shield_center_y - 8 + y, YELLOW);
      }
    }
  }

  // Dibujar el águila blanca simplificada en el escudo
  int eagle_width = 4;
  for (int y = -eagle_width; y <= eagle_width; y++) {
    for (int x = -eagle_width; x <= eagle_width; x++) {
      if (abs(x) == eagle_width || abs(y) == eagle_width || (abs(x) <= 2 && abs(y) <= 2)) {
        tft.drawPixel(shield_center_x + x, shield_center_y + y, WHITE);
      }
    }
  }
}

void drawSloveniaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo de fondo de la bandera
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else {
          tft.drawPixel(x_center + x, y_center + y, LIGHT_BLUE);
        }
      }
    }
  }

  // Dibujar el escudo simplificado
  int shield_radius = 6;
  int shield_center_x = x_center - radius / 3;  // Posicionarlo hacia la izquierda
  int shield_center_y = y_center - radius / 3;  // Posicionarlo ligeramente hacia arriba

  // Dibujar el contorno del escudo
  for (int y = -shield_radius; y <= shield_radius; y++) {
    for (int x = -shield_radius; x <= shield_radius; x++) {
      if (x * x + y * y <= shield_radius * shield_radius) {
        tft.drawPixel(shield_center_x + x, shield_center_y + y, BLUE);
      }
    }
  }

  // Dibujar las montañas blancas en el escudo
  int mountain_height = 3;
  for (int y = -mountain_height; y <= mountain_height; y++) {
    for (int x = -mountain_height; x <= mountain_height; x++) {
      if (y == -x || y == x || y == mountain_height) {
        tft.drawPixel(shield_center_x + x, shield_center_y + y, WHITE);
      }
    }
  }

  // Dibujar las estrellas amarillas en el escudo
  int star_points[5][2] = { { 0, -2 }, { 1, 0 }, { 2, 0 }, { 1, 1 }, { 0, 1 } };
  for (int i = 0; i < 5; i++) {
    tft.drawPixel(shield_center_x + star_points[i][0], shield_center_y - 5 + star_points[i][1], YELLOW);
    tft.drawPixel(shield_center_x + star_points[i][0] - 2, shield_center_y - 4 + star_points[i][1], YELLOW);
    tft.drawPixel(shield_center_x + star_points[i][0] + 2, shield_center_y - 4 + star_points[i][1], YELLOW);
  }
}

void drawDenmarkFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo rojo de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
      }
    }
  }

  // Dibujar la cruz blanca
  int cross_thickness = 6;  // Grosor de la cruz
  int cross_offset = 2;     // Desplazamiento de la línea horizontal de la cruz hacia arriba

  // Dibujar la línea vertical de la cruz
  for (int i = -radius; i <= radius; i++) {
    for (int j = -cross_thickness / 2; j <= cross_thickness / 2; j++) {
      if ((i * i + j * j) <= radius * radius) {
        tft.drawPixel(x_center + j - cross_offset, y_center + i, WHITE);
      }
    }
  }

  // Dibujar la línea horizontal de la cruz
  for (int i = -radius; i <= radius; i++) {
    for (int j = -cross_thickness / 2; j <= cross_thickness / 2; j++) {
      if ((i * i + j * j) <= radius * radius) {
        tft.drawPixel(x_center + i, y_center + j, WHITE);
      }
    }
  }
}

void drawCroatiaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo y la bandera dentro de él
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        if (y < -radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, RED);
        } else if (y > radius / 3) {
          tft.drawPixel(x_center + x, y_center + y, BLUE);
        } else {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }

  // Dibujar el escudo central simplificado
  int shield_radius = 5;
  for (int y = -shield_radius; y <= shield_radius; y++) {
    for (int x = -shield_radius; x <= shield_radius; x++) {
      if (x * x + y * y <= shield_radius * shield_radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
        if ((x + y) % 2 == 0) {
          tft.drawPixel(x_center + x, y_center + y, WHITE);
        }
      }
    }
  }
}

void drawAlbaniaFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo rojo de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
      }
    }
  }

  // Dibujar el águila negra simplificada (rectángulo negro en el centro)
  for (int y = -5; y <= 5; y++) {
    for (int x = -5; x <= 5; x++) {
      tft.drawPixel(x_center + x, y_center + y, BLACK);
    }
  }
}

void drawTurkeyFlag(int x_center, int y_center) {
  int radius = 20;  // 40 pixels diameter, radius is half

  // Dibujar el círculo rojo de fondo
  for (int y = -radius; y <= radius; y++) {
    for (int x = -radius; x <= radius; x++) {
      if (x * x + y * y <= radius * radius) {
        tft.drawPixel(x_center + x, y_center + y, RED);
      }
    }
  }

  // Dibujar el semicírculo blanco (luna)
  int moon_radius_outer = 8;
  int moon_radius_inner = 6;
  int moon_offset_x = -5;  // Offset para centrar la luna a la izquierda

  for (int y = -moon_radius_outer; y <= moon_radius_outer; y++) {
    for (int x = -moon_radius_outer; x <= moon_radius_outer; x++) {
      if (x * x + y * y <= moon_radius_outer * moon_radius_outer) {
        tft.drawPixel(x_center + moon_offset_x + x, y_center + y, WHITE);
      }
    }
  }

  for (int y = -moon_radius_inner; y <= moon_radius_inner; y++) {
    for (int x = -moon_radius_inner; x <= moon_radius_inner; x++) {
      if (x * x + y * y <= moon_radius_inner * moon_radius_inner) {
        tft.drawPixel(x_center + moon_offset_x + 2 + x, y_center + y, RED);
      }
    }
  }

  // Dibujar la estrella blanca inclinada
  int star_center_x = x_center + 7;
  int star_center_y = y_center - 3;
  int star_size = 3;  // Tamaño de la estrella

  // Coordenadas relativas de la estrella inclinada hacia la izquierda
  int star_points[10][2] = {
    { 0, -star_size }, { star_size / 2, -star_size / 2 }, { star_size, -star_size }, { star_size / 2, star_size / 2 }, { star_size, star_size }, { 0, star_size / 2 }, { -star_size, star_size }, { -star_size / 2, star_size / 2 }, { -star_size, -star_size }, { -star_size / 2, -star_size / 2 }
  };

  for (int i = 0; i < 10; i++) {
    tft.drawPixel(star_center_x + star_points[i][0], star_center_y + star_points[i][1], WHITE);
  }
}

// Crear array de funciones
typedef void (*DrawFlagArray)(int, int);
DrawFlagArray drawFlagArray[] = {
  drawPortugalFlag,
  drawCzechFlag,
  drawSpainFlag,
  drawFranceFlag,
  drawGermanyFlag,
  drawItalyFlag,
  drawEnglandFlag,
  drawGeorgiaFlag,
  drawSwitzerlandFlag,
  drawScotlandFlag,
  drawHungaryFlag,
  drawRomaniaFlag,
  drawSlovakiaFlag,
  drawBelgiumFlag,
  drawUkraineFlag,
  drawAustriaFlag,
  drawPolandFlag,
  drawNetherlandsFlag,
  drawSerbiaFlag,
  drawSloveniaFlag,
  drawDenmarkFlag,
  drawCroatiaFlag,
  drawAlbaniaFlag,
  drawTurkeyFlag
};

// Obtener el valor de la posición del array de funciones de la bandera deseada
countries getCountry(String country) {
  if (country == "Portugal") return PORTUGAL;
  if (country == "Czech Republic") return CZECH;
  if (country == "Spain") return SPAIN;
  if (country == "France") return FRANCE;
  if (country == "Germany") return GERMANY;
  if (country == "Italy") return ITALY;
  if (country == "England") return ENGLAND;
  if (country == "Georgia") return GEORGIA;
  if (country == "Switzerland") return SWITZERLAND;
  if (country == "Scotland") return SCOTLAND;
  if (country == "Hungary") return HUNGARY;
  if (country == "Romania") return ROMANIA;
  if (country == "Slovakia") return SLOVAKIA;
  if (country == "Belgium") return BELGIUM;
  if (country == "Ukraine") return UKRAINE;
  if (country == "Austria") return AUSTRIA;
  if (country == "Poland") return POLAND;
  if (country == "Netherlands") return NETHERLANDS;
  if (country == "Serbia") return SERBIA;
  if (country == "Slovenia") return SLOVENIA;
  if (country == "Denmark") return DENMARK;
  if (country == "Croatia") return CROATIA;
  if (country == "Albania") return ALBANIA;
  if (country == "Türkiye") return TURKEY;
  return PORTUGAL;  // Default case, handle as needed
}

// Dibujar bandera con switch case
void drawFlag(String flag, int x, int y) {
  countries country = getCountry(flag);
  switch (country) {
    case GERMANY:
      drawFlagArray[GERMANY](x, y);
      break;
    case ALBANIA:
      drawFlagArray[ALBANIA](x, y);
      break;
    case AUSTRIA:
      drawFlagArray[AUSTRIA](x, y);
      break;
    case BELGIUM:
      drawFlagArray[BELGIUM](x, y);
      break;
    case CROATIA:
      drawFlagArray[CROATIA](x, y);
      break;
    case CZECH:
      drawFlagArray[CZECH](x, y);
      break;
    case DENMARK:
      drawFlagArray[DENMARK](x, y);
      break;
    case ENGLAND:
      drawFlagArray[ENGLAND](x, y);
      break;
    case FRANCE:
      drawFlagArray[FRANCE](x, y);
      break;
    case GEORGIA:
      drawFlagArray[GEORGIA](x, y);
      break;
    case HUNGARY:
      drawFlagArray[HUNGARY](x, y);
      break;
    case ITALY:
      drawFlagArray[ITALY](x, y);
      break;
    case NETHERLANDS:
      drawFlagArray[NETHERLANDS](x, y);
      break;
    case POLAND:
      drawFlagArray[POLAND](x, y);
      break;
    case PORTUGAL:
      drawFlagArray[PORTUGAL](x, y);
      break;
    case ROMANIA:
      drawFlagArray[ROMANIA](x, y);
      break;
    case SCOTLAND:
      drawFlagArray[SCOTLAND](x, y);
      break;
    case SERBIA:
      drawFlagArray[SERBIA](x, y);
      break;
    case SLOVAKIA:
      drawFlagArray[SLOVAKIA](x, y);
      break;
    case SLOVENIA:
      drawFlagArray[SLOVENIA](x, y);
      break;
    case SPAIN:
      drawFlagArray[SPAIN](x, y);
      break;
    case SWITZERLAND:
      drawFlagArray[SWITZERLAND](x, y);
      break;
    case TURKEY:
      drawFlagArray[TURKEY](x, y);
      break;
    case UKRAINE:
      drawFlagArray[UKRAINE](x, y);
      break;
    default:
      Serial.println("País no reconocido");
      break;
  }
}

// Imprimir página principal tft
void mainPage() {
    Serial.print("Mostrando partido de ");
    Serial.println("Portugal vs Georgia");
    // EURO2024
    tft.fillScreen(BLACK_TEXT);
    tft.setTextSize(4);
    drawtext("EURO2024", GREY_TEXT, 47, 6);

    // Fecha
    tft.setTextSize(2);
    tft.fillRoundRect(70, 45, 140, 20, 4, ORANGE_TEXT);
    drawtext("2024-06-30", BLACK_TEXT, 80, 48);

    // Hora a empezar /Minutos / Finalizado
    tft.setTextSize(2);
    drawtext("13:00", ORANGE_TEXT, 110, 80);

    // Marcador
    tft.setTextSize(5);
    drawtext("  -  ", GREY_TEXT, 68, 100);

    // Banderas
    drawFlag("Portugal", 30, 116);
    drawFlag("Georgia", 245, 116);

    //Nombre de jugadores que anotaron con minuto
    tft.setTextSize(1.5);
    drawtext("Quaresma", GREY_TEXT, 10, 150);
    drawtext("2'", GREY_TEXT, 110, 150);
    drawtext("L. Dvali", GREY_TEXT, 145, 150);
    drawtext("1'", GREY_TEXT, 240, 150);
  }

  
Ejemplo en pantalla LCD.

Para comprender de mejor manera este código se recomienda ver el video explicativo que les dejaremos en esta entrada mas adelante.

Codigo Final

Para esta parte solo basta con juntar los tres códigos generados anteriormente con la diferencia que la declaración de las funciones y demás se agregaron a un archivo .h llamado “Football_Data.h” y en el código principal queda solo la aplicación. Todos los archivos utilizados en este proyecto se dejaran en un enlace de descarga mas adelante.

#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "WiFi.h"
#include "Football_Data.h"
#include "time.h"


void setup(void) {
  // Iniciar serial
  Serial.begin(115200);

  // Conectar al WiFi
  Serial.print("Intentando conectarse a la red: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  // Esperar conectarse a la red:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    // Esperar un segundo para reintentar
    delay(1000);
  }
  Serial.print("Conectado a: ");
  Serial.println(ssid);

  // Inicializar pantalla TFT
  tft.init(240, 280);
  tft.setRotation(3);
  tft.fillScreen(GREY_TEXT);
  tft.setTextSize(3);
  testdrawtext("Iniciando", BLACK_TEXT, 55, 100);
  delay(1000);

  // Init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  getLocalTime();

  // Configurar certificado
  client.setCACert(test_root_ca);


  getFixtureData(fixtureReq);
  // Ver si hay partidos en la fecha indicada
  if (todayGames == 0) {
    // De no ser así imprimir la info de los siguientes cuatro fixtures
    Serial.println("No se encontraron fixtures la fecha indicada, solicitando los siguientes cuatro partidos");
    getFixtureData(nextGamesReq);
  } else if (nextGames == 0) {
    // De ser así obtener la información de los fixtures del día
    Serial.println("No se encontraron próximos fixtures, solicitando los últimos cuatro partidos");
    getFixtureData(lastGamesReq);
  } else {
    Serial.print("Se encontraron ");
    Serial.print(fixturesResults);
    Serial.println(" fixtures la fecha indicada");
  }
  // Imprimir en el serial la información
  printFixturesData();

  // Si hay partidos en la fecha indicada obtener los eventos
  if (nextGames == 1) {
    for (int i = 0; i < fixturesResults; i++) {
      // Construir el request de los evenfos fixture por fixture
      Serial.print("Solicitando eventos del fixture: ");
      Serial.println(fixtureID[i]);
      eventsReq = String("/v3/fixtures/events") + String("?fixture=") + fixtureID[i];
      // Realizar la solicitud
      getEventsData(i, eventsReq);
    }
    // Imprimir los eventos gol en el serial
    printEventsData();
  }
  mainPage();

  maxRequestsCounter = round((60 / (fixturesResults * 8)));
}

void loop() {
  getLocalTime();
  if (requestCounter > maxRequestsCounter) {
    requestCounter = 0;
  }
  mainPage();
  requestCounter += 1;
}

void mainPage() {
  for (int i = 0; i < fixturesResults; i++) {
    String startTime = extractTime(eventDate[i]);
    if ((isGameLive[i] || (localTime.substring(0, 2) == startTime.substring(0, 2))) && (requestCounter == maxRequestsCounter)) {
      getFixtureData(fixtureReq);
      Serial.print("Fuxture en vivo: ");
      Serial.println(isGameLive[i]);
      eventsReq = String("/v3/fixtures/events") + String("?fixture=") + fixtureID[i];
      Serial.print("Solicitando el siguiente request: ");
      Serial.println(eventsReq);
      getEventsData(i, eventsReq);
      // ExtraTime
      if (((timeElapsed[i] == "45") && (maxTimeReached[i] == 0)) || ((timeElapsed[i] == "90") && (maxTimeReached[i] == 0))) {
        int extraTimeHourStart = localTime.substring(0, 2).toInt();
        int extraTimeMinuteStart = localTime.substring(3).toInt();
        extraTimeStart[i] = (extraTimeHourStart * 60) + (extraTimeMinuteStart);
        maxTimeReached[i] = 1;
      }
    } else {
      delay(8000);
    }
    //
    printFixturesData();
    printEventsData();
    Serial.print("Mostrando partido de ");
    Serial.println(firstTeam[i] + " vs " + secondTeam[i]);
    // EURO2024
    tft.fillScreen(BLACK_TEXT);
    tft.setTextSize(4);
    testdrawtext("EURO2024", GREY_TEXT, 47, 6);

    // Fecha
    tft.setTextSize(2);
    tft.fillRoundRect(70, 45, 140, 20, 4, ORANGE_TEXT);
    gameDate = extractDate(eventDate[i]);
    testdrawtext(gameDate, BLACK_TEXT, 80, 48);

    // Hora a empezar /Minutos / Finalizado
    tft.setTextSize(2);

    if (gameStatus[i] == "NS") {
      gameTime = extractTime(eventDate[i]);
      testdrawtext(gameTime, GREY_TEXT, 110, 80);
    } else if (gameStatus[i] == "FT" || gameStatus[i] == "AET" || gameStatus[i] == "PEN") {
      testdrawtext("Finalizado", ORANGE_TEXT, 85, 80);
      maxTimeReached[i] = 0;
    } else if (gameStatus[i] == "1H" || gameStatus[i] == "2H" || gameStatus[i] == "ET") {
      Serial.print("MaxTimeReached");
      Serial.println(maxTimeReached[i]);
      localTimeMinutes = (localTime.substring(0, 2).toInt() * 60) + localTime.substring(3).toInt();
      extratimeElapsed[i] = String(localTimeMinutes - extraTimeStart[i]);
      Serial.print("MaxTimeReached");
      Serial.println(localTimeMinutes);
      Serial.print("MaxTimeReached");
      Serial.println(extratimeElapsed[i]);
      if (maxTimeReached[i] == 0 || extratimeElapsed[i] == "0") {
        testdrawtext(timeElapsed[i] + "'", GREEN_TEXT, 130, 80);
      } 
      else
      {
        testdrawtext(timeElapsed[i] + "+" + extratimeElapsed[i] + "'", GREEN_TEXT, 115, 80);
      }

    } else if (gameStatus[i] == "BT") {
      testdrawtext("Descanso", GREEN_TEXT, 100, 80);
      maxTimeReached[i] = 0;
    } else if (gameStatus[i] == "P") {
      testdrawtext("Penalties", GREEN_TEXT, 90, 80);
      maxTimeReached[i] = 0;
    } else if (gameStatus[i] == "HT") {
      testdrawtext("Medio Tiempo", GREEN_TEXT, 68, 80);
      maxTimeReached[i] = 0;
    }


    // Marcador
    tft.setTextSize(5);
    if (firstTeamGoals[i] == "null" && secondTeamGoals[i] == "null") {
      testdrawtext("  -  ", GREY_TEXT, 68, 100);
    } else {
      matchGoals = firstTeamGoals[i] + " - " + secondTeamGoals[i];
      testdrawtext(matchGoals, GREY_TEXT, 68, 100);
    }

    // En caso de penalties
    if (gameStatus[i] == "PEN" || gameStatus[i] == "P") {
      matchGoals = firstTeamPenaltyGoals[i] + " - " + secondTeamPenaltyGoals[i];
      tft.setTextSize(1);
      testdrawtext("PEN", GREY_TEXT, 131, 125);
      tft.setTextSize(2);
      testdrawtext(matchGoals, GREY_TEXT, 111, 130);
    }

    // Banderas
    drawFlag(firstTeam[i], 30, 116);
    drawFlag(secondTeam[i], 245, 116);

    //Nombre Jugadores Primer equipo
    tft.setTextSize(1.5);
    for (int counter = 0; counter < firstTeamGoals[i].toInt(); counter++) {
      testdrawtext(firstTeamPlayerGoal[i][counter], GREY_TEXT, 10, 150 + (playerYGap * counter));
      testdrawtext(firstTeamMinuteGoal[i][counter], GREY_TEXT, 110, 150 + (playerYGap * counter));
    }

    for (int counter = 0; counter < secondTeamGoals[i].toInt(); counter++) {
      testdrawtext(secondTeamPlayerGoal[i][counter], GREY_TEXT, 145, 150 + (playerYGap * counter));
      testdrawtext(secondTeamMinuteGoal[i][counter], GREY_TEXT, 240, 150 + (playerYGap * counter));
    }
  }
}

Con esto terminamos nuestra aplicación para ver los resultados en vivo de los partidos de la Eurocopa 2024.

Resultado Final.

Video Explicativo.

No olviden dar like y subscribirse.

Enlace de descarga.

Enlace de descarga con todos los archivos utilizados en este proyecto.

https://mega.nz/file/OBA1URxA#xllk997bwGeilh5YaOV4ubNh6tU8NOX6_tyKHgbutNY

Contacto.

Si quieres que te ayude en algún proyecto:

ignacio.aguilera.contacto@gmail.com

Comentarios

Los campos obligatorios están marcados con *