Wiznet makers

gavinchang

Published December 02, 2022 ©

50 UCC

25 WCC

56 VAR

0 Contests

3 Followers

0 Following

Original Link

FIFA World Cup 2022 Schedule Monitor(Raspberry Pi Pico & WizFi360 &Football-data)

Use Raspberry Pi RP2040 & WizFi360 to display FIFA 2022 Schedule through football-data API on the screen(GC9A01)

COMPONENTS Hardware components

Raspberry Pi - RP2040

x 1


WIZnet - WizFi360

x 1

Software Apps and online services

Arduino - Arduino IDE

x 1


PROJECT DESCRIPTION

I want to display the FIFA World Cup Qatar 2022 Schedule on a screen, such as What game is on today, home team, away team, game time and other information.

In this project, I use the football-data API to get FIFA World Cup 2022 match Schedule information.

 

 

 

The website of football-data API is “https://www.football-data.org/”.

 

Football-data.org provides football data and statistics (live scores, fixtures, tables, squads, lineups/subs, etc.) in a machine-readable way.

I won't announce how awesome football-data is, you're welcome to find out by yourself (or not). Access to the top competitions is and will be free forever.

I use wizfi360 as http client to send football-data API request and get football-data parameters. WizFi360 is a WiFi module, which can connect to WiFi through commands and perform TCP or TCP (SSL) connections. I have used it many times and it is very convenient.

RP2040 acts as an MCU, after get get football match info from wizfi360, it performs data processing and displays the content on the screen.

This project is divided into four steps:

Step 1: Create New Account on the football-data website and get API TOKEN;

Step2: Install library files and board support in the Arduino IDE;

Step 3: Get parameters from football-data API through WizFi360;

Step 4: Displays the football-data match info on the screen(GC9A01);

The following are step by step instructions.

 

 

 

Step 1: Create New Account on the football-data website and get API TOKEN;

After creat account in this website, you can see your API TOKEN on the "My accounts" page. Please record it, because this TOKEN will be required for future page visits.

 

I am using a free service with the following limitations, but a free account is sufficient for my needs.

 

Everyone could get Documentation at https://www.football-data.org/documentation/quickstart

 

 

 

Step2: Install library files and board support in the Arduino IDE;

Add "WIZnet WizFi360-EVB-PICO" support to Arduino IDE

Open up the Arduino IDE and go to File->Preferences.

In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:

https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json

 

Search "WizFi360" and Install Board support by "Board Manager"

 

"Tools->Board:"***"-> Raspberry Pi RP2040 Boards(2.6.1) " Select “WIZnet WizFi360-EVB-PICO”.

 

Add “GFX Library for rduino”, this library support the round screen GC9A01.

 

Because we need to display the team's icon, we need to load a PNG library“PNGdec” to decode the image.

 

 

 

Step 3: Get parameters from football-data API through WizFi360;

#include "WizFi360.h"

// Wi-Fi info //
char ssid[] = "WIZNET_test";       // your network SSID (name)//
char pass[] = "********";          // your network password//
int status = WL_IDLE_STATUS;       // the Wifi radio's status//
// Initialize the Ethernet client object
WiFiClient client;
 

Initialize serial port for WizFi360 module and change the baudrate to 2000000bps(MAX baudrate for wizfi360).

The first initialization is 115200, and then setting the baud rate (2000000) is added to the initialization part of the WiZfi360 library, and the second time is changed to 2000000bps.

// initialize serial for WizFi360 module
  Serial2.setFIFOSize(4096);
  Serial2.begin(2000000);
  WiFi.init(&Serial2);

Check the wizfi360 Link status of wifi in the “void setup()”

// check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    while (true);// don't continue
  }  
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    status = WiFi.begin(ssid, pass);// Connect to WPA/WPA2 network
  }

Create a SSL TCP Socket on port 443 of the “ api.football-data.org” and send request to API.

The format and examples of api requests are as follows:

https://api.football-data.org/v4/matches?date=TODAY

This is to query the current game information

HEADER need to contain TOKEN,

X-Auth-Token:[token]

case link_football_server:
       {
        // if you get a connection, report back via serial
        if (client.connectSSL(football_server,443)) {
          Serial.println("Connected to server");
          // Make a HTTP request
          client.println(String("GET /v4/matches?date=TODAY HTTP/1.1"));
          client.println(String("Host:") + String(football_server));
          client.println(String("X-Auth-Token:") + String(football_token));
          client.println("Connection: close");
          client.println();
          data_now = 0;
        }
        currentState = get_football_data;
       }
      break;

After that, wizFi360 can get the football match info JSON returned by the API.

Example of API response

{📷
    "filters": {📷
        "dateFrom": "2022-12-01", 
        "dateTo": "2022-12-02", 
        "permission": "TIER_ONE"
    }, 
    "resultSet": {📷
        "count": 4, 
        "competitions": "WC", 
        "first": "2022-12-01", 
        "last": "2022-12-01", 
        "played": 0
    }, 
    "matches": [📷
        {📷}, 
        {📷}, 
        {📷}, 
        {📷}
    ]
}

It contains the information of 4 games, and the format of each game is as follows:

"matches": [📷
        {📷
            "area": {📷}, 
            "competition": {📷}, 
            "season": {📷}, 
            "id": 391915, 
            "utcDate": "2022-12-01T15:00:00Z", 
            "status": "TIMED", 
            "matchday": 3, 
            "stage": "GROUP_STAGE", 
            "group": "GROUP_F", 
            "lastUpdated": "2022-11-30T15:33:46Z", 
            "homeTeam": {📷
                "id": 799, 
                "name": "Croatia", 
                "shortName": "Croatia", 
                "tla": "CRO", 
                "crest": "https://crests.football-data.org/799.svg"
            }, 
            "awayTeam": {📷
                "id": 805, 
                "name": "Belgium", 
                "shortName": "Belgium", 
                "tla": "BEL", 
                "crest": "https://crests.football-data.org/805.svg"
            }, 
            "score": {📷}, 
            "odds": {📷}, 
            "referees": [📷]
        },

The main information we need is:

"utcDate": "2022-12-01T15:00:00Z",

"homeTeam"-"name": "Croatia",

"awayTeam"-"name": "Belgium",

This is the receiving process of JSON:

case get_football_data:
       {
          while (client.available()) {
            json_String += (char)client.read();
            data_now =1;            
          }
          if(data_now)
          {
            //Serial.println(json_String);            
            dataStart = json_String.indexOf("en-US") + strlen("en-US")+4;
            dataEnd = json_String.indexOf("\r\n", dataStart); 
            dataStr = json_String.substring(dataStart, dataEnd);
            football_api_len = HexStr2Int(dataStr);
            
            json_String = json_String.substring(dataEnd+2, json_String.length());

            uint16_t football_api_cnt;
            football_api_cnt = football_api_len - json_String.length();
            
            while(football_api_cnt>0)
            {
              while(client.available()){
                json_String += (char)client.read();
                football_api_cnt--;
              }
                if(football_api_cnt != 0)
                {
                  dataEnd = json_String.lastIndexOf("\r\n")- strlen("\r\n"); 
                  dataStart = json_String.indexOf("\r\n", dataEnd-10)+ strlen("\r\n"); 
                  dataStr = json_String.substring(dataStart, dataEnd);
                  if(HexStr2Int(dataStr) == 0)
                  {
                    football_api_cnt = 0;
                  }
                }
            }            
            //Serial.println(json_String);
            dataEnd = json_String.indexOf("utcDate");

            
            currentState = display_wait_timeout;
            tft->fillRect(0,65,240,100,WHITE);
            client.stop();
        }
      }
      break;

This is the process of obtaining the game time. It should be noted that the time is international standard time and needs to be converted to local time:

dataStart = json_String.indexOf("utcDate",dataEnd) + strlen("utcDate")+3;
              dataEnd = json_String.indexOf("\",", dataStart);
              football_match_day[i] = json_String.substring(dataStart+8, dataStart+10);
              match_time_hour = json_String.substring(dataStart+11, dataStart+13);
              match_time_minute = json_String.substring(dataStart+14, dataStart+16);
              if((match_time_hour>="16"))
              {
                if(football_match_day[i].toInt()+1 < 10)
                {
                  football_match_day[i] = (String("2022-12-0") + (String)(football_match_day[i].toInt()+1));
                }
                else
                {
                  football_match_day[i] = (String("2022-12-") + (String)(football_match_day[i].toInt()+1));
                }
                
                football_match_time[i] = ("0"+String(match_time_hour.toInt()-16) + ":"  + (String)match_time_minute);
              }
              else
              {
                if(football_match_day[i].toInt() < 10)
                {
                  football_match_day[i] = (String("2022-12-0") + (String)(football_match_day[i].toInt()));
                }
                else
                {
                  football_match_day[i] = (String("2022-12-") + (String)(football_match_day[i].toInt()));
                }
                if(match_time_hour.toInt()+8 > 10 )
                {
                  football_match_time[i] = (String(match_time_hour.toInt()+8) + ":" + (String)match_time_minute);  
                }
                else
                {
                  football_match_time[i] = ("0"+String(match_time_hour.toInt()+8) + ":" + (String)match_time_minute);  
                }
              }
              Serial.print("football_match_time");
              Serial.println(i);
              Serial.println(football_match_day[i]);
              Serial.println(football_match_time[i]);

This is the information acquisition process of the home team and the visiting team for the four games:

for(int i =0; i<4;i++)
            {

              dataStart = json_String.indexOf("name", dataEnd)+ strlen("name")+3;
              dataEnd = json_String.indexOf("\",", dataStart);
              football_match_homeTeam[i]= json_String.substring(dataStart, dataEnd);
              Serial.print("football_match_homeTeam");
              Serial.println(i);
              Serial.println(football_match_homeTeam[i]);
              dataStart = json_String.indexOf("name", dataEnd)+ strlen("name")+3;
              dataEnd = json_String.indexOf("\",", dataStart);
              football_match_awayTeam[i]= json_String.substring(dataStart, dataEnd);
              Serial.print("football_match_awayTeam");
              Serial.println(i);
              Serial.println(football_match_awayTeam[i]);   
            }

 

 

 

Step 4: Displays the football-data match info on the screen(GC9A01);

#include <Arduino_GFX_Library.h>
Arduino_GFX *tft = create_default_Arduino_GFX();

define of pin which is used by GC9A01 in the "libraries\GFX_Library_for_Arduino\src\Arduino_GFX_Library.h"

 
#elif defined(ARDUINO_RASPBERRY_PI_PICO)||defined(ARDUINO_WIZNET_WIZFI360_EVB_PICO)||defined(ARDUINO_WIZNET_5100S_EVB_PICO)
#define DF_GFX_SCK 26
#define DF_GFX_MOSI 27
#define DF_GFX_MISO GFX_NOT_DEFINED
#define DF_GFX_CS 25
#define DF_GFX_DC 23
#define DF_GFX_RST 28
#define DF_GFX_BL 22

Initialize the screen and open the backlight of the screen in the “void setup()”

tft->begin();
  tft->fillScreen(WHITE);
  pinMode(22, OUTPUT); 
  digitalWrite(22, HIGH);

During the process of connecting to WiFi, the connection status is displayed.

void display_wifi_status()
{
  if( status != WL_CONNECTED)
  {
    tft->fillCircle(120,230,3,DARKGREY);
    tft->fillArc(120,230, 5, 7, 225, 315, DARKGREY); 
    tft->fillArc(120,230, 9, 11, 225, 315, DARKGREY); 
    tft->fillArc(120,230, 13, 15, 225, 315, DARKGREY); 
  }
  else
  {
    tft->fillCircle(120,230,3,GREEN);
    tft->fillArc(120,230, 5, 7, 225, 315, GREEN); 
    tft->fillArc(120,230, 9, 11, 225, 315, GREEN); 
    tft->fillArc(120,230, 13, 15, 225, 315, GREEN); 
  }
}

show as:

 

In order to display the interface beautifully, we write the framework of the interface during "Setup()".

void display_dashboard()
{
    image_x = 93;
    image_y = 10;    
    int rc = png.openFLASH((uint8_t *)WorldCupIcon, sizeof(WorldCupIcon), PNGDraw);
    if (rc == PNG_SUCCESS) {
      char szTemp[256];
      sprintf(szTemp, "image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
      Serial.print(szTemp);
      rc = png.decode(NULL, 0); // no private structure and skip CRC checking
      png.close();
    } // png opened successfully
    else
    {
      Serial.println("ERROR");
    }
    tft->setTextColor(LIGHTGREY);
    tft->setTextSize(4);
    tft->setCursor(76, 72);
    tft->print("FIFA");
    tft->setTextSize(2);
    tft->setCursor(69, 111);
    tft->print("World Cup");
    tft->setCursor(65, 136);
    tft->print("Qatar 2022");
    tft->setTextSize(3);
    tft->setCursor(52, 162);
    tft->print("Schedule");
    
    tft->setTextColor(DARKGREY);
    tft->setTextSize(4);
    tft->setCursor(74, 70);
    tft->print("FIFA");
    tft->setTextSize(2);
    tft->setCursor(68, 110);
    tft->print("World Cup");
    tft->setCursor(64, 135);
    tft->print("Qatar 2022");
    tft->setTextSize(3);
    tft->setCursor(50, 160);
    tft->print("Schedule");
    tft->drawRoundRect(40,157,160,30,20,DARKGREY);
}
 

After get the parameters in STEP3, through the following processing, update the screen display as football match info.

void display_match_info(uint8_t num)
{    
    image_x = 21;
    image_y = 55;
    display_country_icon(football_match_homeTeam[num]);
    tft->fillArc(60,95, 40, 45, 0, 360, LIGHTGREY); 
    tft->fillArc(60,95, 42, 43, 0, 360, DARKGREY); 
    image_x = 141;
    image_y = 55;
    display_country_icon(football_match_awayTeam[num]);  
    tft->fillArc(180,95, 40, 45, 0, 360, LIGHTGREY);
    tft->fillArc(180,95, 42, 43, 0, 360, DARKGREY); 

    tft->setTextColor(LIGHTGREY);
    tft->setTextSize(2);
    tft->setCursor(111, 111);
    tft->print("VS");
    tft->setTextColor(DARKGREY);
    tft->setCursor(109, 109);
    tft->print("VS");
    
    tft->fillRect(0,139,240,70,WHITE);
    tft->setTextColor(DARKGREY);
    tft->setTextSize(2);
    tft->setCursor(55-(football_match_homeTeam[num].length())*5, 145);
    tft->print(football_match_homeTeam[num]);
    tft->setTextColor(DARKGREY);
    tft->setCursor(175-(football_match_awayTeam[num].length())*5, 145);  
    tft->print(football_match_awayTeam[num]);
   
    tft->setTextColor(DARKGREY);
    tft->setTextSize(2);
    tft->setCursor(62, 175);  
    tft->print(football_match_day[num]); 
    tft->setTextSize(2);
    tft->setCursor(91, 195);
    tft->print(football_match_time[num]); 
}

The flag of each country is stored in Flash,

 

The process of displaying the national flag:

void display_country_icon(String Country)
{
  char szTemp[256];
  int rc;
  if(Country =="Argentina")
  {
    rc = png.openFLASH((uint8_t *)Argentina, sizeof(Argentina), PNGDraw);
  }
  else if(Country =="Australia")
  {
    rc = png.openFLASH((uint8_t *)Australia, sizeof(Australia), PNGDraw);
  }
  else if(Country =="Belgium")
  {
    rc = png.openFLASH((uint8_t *)Belgium, sizeof(Belgium), PNGDraw);
  }
  else if(Country =="Brazil")
  {
    rc = png.openFLASH((uint8_t *)Brazil, sizeof(Brazil), PNGDraw);
  }
  else if(Country =="Cameroon")
  {
    rc = png.openFLASH((uint8_t *)Cameroon, sizeof(Cameroon), PNGDraw);
  }
  else if(Country =="Canada")
  {
    rc = png.openFLASH((uint8_t *)Canada, sizeof(Canada), PNGDraw);
  }
  else if(Country =="Costa Rica")
  {
    rc = png.openFLASH((uint8_t *)Costa_rica, sizeof(Costa_rica), PNGDraw);
  }
  else if(Country =="Croatia")
  {
    rc = png.openFLASH((uint8_t *)Croatia, sizeof(Croatia), PNGDraw);
  }
  else if(Country =="Denmark")
  {
    rc = png.openFLASH((uint8_t *)Denmark, sizeof(Denmark), PNGDraw);
  }
  else if(Country =="Ecuador")
  {
    rc = png.openFLASH((uint8_t *)Ecuador, sizeof(Ecuador), PNGDraw);
  }
  else if(Country =="England")
  {
    rc = png.openFLASH((uint8_t *)England, sizeof(England), PNGDraw);
  }
  else if(Country =="France")
  {
    rc = png.openFLASH((uint8_t *)France, sizeof(France), PNGDraw);
  }
  else if(Country =="Germany")
  {
    rc = png.openFLASH((uint8_t *)Germany, sizeof(Germany), PNGDraw);
  }
  else if(Country =="Ghana")
  {
    rc = png.openFLASH((uint8_t *)Ghana, sizeof(Ghana), PNGDraw);
  }
  else if(Country =="Iran")
  {
    rc = png.openFLASH((uint8_t *)Iran, sizeof(Iran), PNGDraw);
  }
  else if(Country =="Japan")
  {
    rc = png.openFLASH((uint8_t *)Japan, sizeof(Japan), PNGDraw);
  }
  else if(Country =="Mexico")
  {
    rc = png.openFLASH((uint8_t *)Mexico, sizeof(Mexico), PNGDraw);
  }
  else if(Country =="Morocco")
  {
    rc = png.openFLASH((uint8_t *)Morocco, sizeof(Morocco), PNGDraw);
  }
  else if(Country =="Netherlands")
  {
    rc = png.openFLASH((uint8_t *)Netherlands, sizeof(Netherlands), PNGDraw);
  }
  else if(Country =="Poland")
  {
    rc = png.openFLASH((uint8_t *)Poland, sizeof(Poland), PNGDraw);
  }
  else if(Country =="Portugal")
  {
    rc = png.openFLASH((uint8_t *)Portugal, sizeof(Portugal), PNGDraw);
  }
  else if(Country =="Qatar")
  {
    rc = png.openFLASH((uint8_t *)Qatar, sizeof(Qatar), PNGDraw);
  }
  else if(Country =="Saudi Arabia")
  {
    rc = png.openFLASH((uint8_t *)Saudi_arabia, sizeof(Saudi_arabia), PNGDraw);
  }
  else if(Country =="Senegal")
  {
    rc = png.openFLASH((uint8_t *)Senegal, sizeof(Senegal), PNGDraw);
  }
  else if(Country =="Serbia")
  {
    rc = png.openFLASH((uint8_t *)Serbia, sizeof(Serbia), PNGDraw);
  }
  else if(Country =="South Korea")
  {
    rc = png.openFLASH((uint8_t *)South_korea, sizeof(South_korea), PNGDraw);
  }
  else if(Country =="Spain")
  {
    rc = png.openFLASH((uint8_t *)Spain, sizeof(Spain), PNGDraw);
  }
  else if(Country =="Switzerland")
  {
    rc = png.openFLASH((uint8_t *)Switzerland, sizeof(Switzerland), PNGDraw);
  }
  else if(Country =="Tunisia")
  {
    rc = png.openFLASH((uint8_t *)Tunisia, sizeof(Tunisia), PNGDraw);
  }
  else if(Country =="United States")
  {
    rc = png.openFLASH((uint8_t *)United_states, sizeof(United_states), PNGDraw);
  }
  else if(Country =="Uruguay")
  {
    rc = png.openFLASH((uint8_t *)Uruguay, sizeof(Uruguay), PNGDraw);
  }
  else if(Country =="Wales")
  {
    rc = png.openFLASH((uint8_t *)Wales, sizeof(Wales), PNGDraw);
  }
    if (rc == PNG_SUCCESS) {      
    sprintf(szTemp, "image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
    Serial.print(szTemp);
    rc = png.decode(NULL, 0); // no private structure and skip CRC checking
    png.close();
    } // png opened successfully
    else
    {
      Serial.println("ERROR");
    }
}

The final display effect is as follows:

case display_wait_timeout:
       {
          Serial.print("team_num");
          Serial.println(team_num);
          display_match_info(team_num);
          team_num++;
          if(team_num == 4)
          {
            team_num = 0;
          }
          delay(6000);
       }
      break;
 

Below is a video demo of the project.

https://youtube.com/shorts/1PkWHyLVnj0

 

Documents
  • FIFA_World_Cup_Schedule_Moniter_Code

  • FIFA World Cup Qatar 2022 Schedule Moniter Schmatic

Comments Write