FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)

使用Raspberry Pi RP2040和WizFi360通过足球数据API在屏幕上显示FIFA 2022赛程表(GC9A01)

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第1张图片

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

项目介绍

我想在屏幕上显示2022年卡塔尔世界杯赛程,比如今天有什么比赛、主队、客队、比赛时间等信息。

在这个项目中,我使用 Football-data API 来获取 FIFA 2022 世界杯比赛日程信息。

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第2张图片

足球数据API的网站是 “football-data.org - ur src for machine readable football data”.

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第3张图片

Football-data.org 以机器可读的方式提供足球数据和统计数据(实时比分、赛程、积分表、球队、阵容/替补等)。

我不会宣布足球数据有多棒,欢迎您自己去发现(或不去发现)。 参加顶级比赛现在和将来都是免费的。

我使用wizfi360作为http客户端发送足球数据API请求并获取足球数据参数。 WizFi360是一款WiFi模块,可以通过命令连接WiFi,并进行TCP或TCP(SSL)连接。 我用过很多次了,非常方便。

RP2040作为MCU,从wizfi360获取足球比赛信息后,进行数据处理并将内容显示在屏幕上。

该项目分为四个步骤:

第一步:在足球数据网站创建新账户并获取API TOKEN;

Step2:在Arduino IDE中安装库文件和板卡支持;

步骤3:通过WizFi360从football-data API获取参数;

第四步:在屏幕上显示足球数据比赛信息(GC9A01);

第一步:在足球数据网站创建新账户并获取API TOKEN;

在本网站创建账户后,您可以在“我的账户”页面看到您的API TOKEN。 请记录下来,因为以后的页面访问都需要这个TOKEN。

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第4张图片

我使用的免费服务有以下限制,但免费帐户足以满足我的需求。

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第5张图片

每个人都可以在以下位置获取文档 football-data.org - API Quickstart

Step2:在Arduino IDE中安装库文件和板卡支持;

为 Arduino IDE 添加“WIZnet WizFi360-EVB-PICO”支持

打开 Arduino IDE 并转到“文件”->“首选项”。

在弹出的对话框中,在“其他 Boards Manager URL”字段中输入以下 URL:

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

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第6张图片

搜索“WizFi360”并通过“板管理器”安装板支持

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第7张图片

“工具->开发板:“***”-> Raspberry Pi RP2040 开发板(2.6.1) " 选择“WIZnet WizFi360-EVB-PICO”。

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第8张图片

添加“GFX Library for rduino”,该库支持圆屏GC9A01。

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第9张图片

因为我们需要显示球队的图标,所以我们需要加载一个PNG库“PNGdec”来解码图像。

步骤3:通过WizFi360从football-data API获取参数;

#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;

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第10张图片

初始化WizFi360模块的串口,并将波特率更改为2000000bps(wizfi360的最大波特率)。

第一次初始化为115200,然后在WiZfi360库的初始化部分添加设置波特率(2000000),第二次改为2000000bps。

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

在“void setup()”中检查wizfi360 wifi的链接状态

// 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
  }

在“api.football-data.org”的端口 443 上创建 SSL TCP 套接字并向 API 发送请求。

api请求的格式及示例如下:

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

这是查询当前游戏信息

HEADER需要包含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;

之后wizFi360就可以获取API返回的足球比赛信息JSON。

API 响应示例

{
    "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": [
        {}, 
        {}, 
        {}, 
        {}
    ]
}

包含4场比赛的信息,每场比赛的格式如下:

"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": []
        },

我们需要的主要信息是:

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

"homeTeam"-"name": "克罗地亚",

"awayTeam"-"name": "比利时",

这是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;

这就是获取游戏时间的过程。 需要注意的是,时间为国际标准时间,需要转换为当地时间:

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]);

这是四场比赛主队和客队的信息获取过程:

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]);   
            }

第四步:在屏幕上显示足球数据比赛信息(GC9A01);

#include 
Arduino_GFX *tft = create_default_Arduino_GFX();

GC9A01使用的引脚定义 "libraries\GFX_Library_for_Arduino\src\Arduino_GFX_Library.h"

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第11张图片

#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

在“void setup()”中初始化屏幕并打开屏幕背光

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

连接WiFi过程中,会显示连接状态。

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); 
  }
}

显示为:

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第12张图片

为了美观地显示界面,我们在“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);
}

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第13张图片

在STEP3中得到参数后,通过以下处理,将屏幕显示更新为足球比赛信息。

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]); 
}

各个国家的国旗存储在Flash中,

FIFA 世界杯 2022 赛程监控器(Raspberry Pi Pico & WizFi360 &足球数据)_第14张图片

展示国旗的过程:

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");
    }
}

最终显示效果如下:

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;

你可能感兴趣的:(arduino,wizfi360,wiznet)