Starting Electronics Needs Your Help!
It is that time of the year when we need to pay for web hosting and buy new components and equipment for new tutorials. You can help by making a donation. Contribute to this website by clicking the Donate button. The total will be updated once daily. (You may need to clear your browser cache to see the updates.)

Target Amount: $2000
Amount Raised: $495

Donations Received

Top Donor: D.C. $30

X

Getting Live Cricket Scores with Arduino

Created on: 2 June 2015

In this project, an Arduino and Ethernet shield are used to get live cricket scores from the Internet. The same technique could be used to get sport scores for other sports.

The Arduino sketch configures the Arduino as an Ethernet client that periodically requests the scores from the cricbuzz.com website. The scores are received as an XML file which the Arduino parses to extract the score information.

The extracted scores and game information are displayed in the Arduino IDE serial monitor window.

This project can be used as a basis for getting started with similar projects for other sports scores and results. The code can also be adapted to support other displays such as an LCD.

The video below shows the Arduino fetching the sports scores and displaying them in the Serial Monitor window:

Can't see the video? View on YouTube →

Update

See the improved cricket score ticker that uses an SD card to first save the entire XML file before parsing it.

Cricket Score Arduino Sketch

The code below can be copied and pasted to the Arduino IDE and then loaded to the Arduino.

Arduino Hardware

An Arduino and Ethernet shield or Arduino with built-in Ethernet is needed. The SD card is not used.

Running the Sketch and Serial Port Settings

After loading the sketch to the Arduino, open the Arduino IDE Serial Monitor window to view the scores. Make sure that the baud rate is set to 115200 as shown in the video above.

Sketch Limitations and Explanation

Be sure to read the text below the sketch to understand the limitations of the sketch and for an explanation on how the code works.

Generate the Code

Use the form below to configure the sketch to suit your network and desired settings.

Change these settings to suit your network and Arduino board. After making changes, click the Generate Code button to apply the changes to the Arduino sketch below. Click the Select Code button to select the code for copying.

MAC:

: : : : :
MAC address found on sticker on bottom of Ethernet shield.

IP:

. . .
IP address that the Arduino will be at.
Must be a unique IP address on the local network.

Refresh:


Cricket score refresh rate in seconds.

Leonardo
Extra code for Arduino Leonardo Serial Port Initialization.
Needed for Arduino Leonardo only – check box if using Leonardo.


After making changes in any of the fields above, click this button to update the code below.


Select the code below before copying.

/* Gets the cricket score from synd.cricbuzz.com/j2me/1.0/livematches.xml
 * Displays the score in the Serial Monitor window
 *
 * References: Arduino Examples from IDE
 *             - File --> Examples --> Ethernet --> WebClient
 *             - File --> Examples --> Ethernet --> WebClientRepeating
 *
 * More information at:
 * http://startingelectronics.org/software/arduino/live-cricket-score/
 * 
 * Author: W.A. Smith    Date: 2 June 2015
 */
#include <SPI.h>
#include <Ethernet.h>

// score refresh period in seconds
#define REFRESH_PERIOD_SEC  15L

// size of parse buffer in bytes
#define BUF_PARSE_SZ  50

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char server[] = "synd.cricbuzz.com";
IPAddress ip(192, 168, 0, 50);
EthernetClient client;

void setup() {
  // disable the SD card by switching pin 4 high
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  
  Serial.begin(115200);
  
  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
}

void loop() {
  static char pars_buffer[BUF_PARSE_SZ] = {0};
  static int pars_buf_idx = 0;
  
  httpRequest();
  
  // if there are incoming bytes available
  // from the server, read them and print them:
  if (client.available()) {
    char c = client.read();
    pars_buffer[pars_buf_idx] = c;
    pars_buf_idx++;
    if (ParseXMLBuffer(pars_buffer) == true) {
      // reset the buffer;
      pars_buf_idx = 0;
      pars_buffer[0] = 0;
    }
    // last char in parse buffer is always null terminator 0
    if (pars_buf_idx >= (BUF_PARSE_SZ - 1)) {
      LeftShiftBuffer(pars_buffer, (BUF_PARSE_SZ - 1));
      pars_buf_idx = BUF_PARSE_SZ - 2;
    }
  }
}

// this method makes a HTTP connection to the server:
void httpRequest() {
  static unsigned long lastConnectionTime = 0;                             // last time you connected to the server, in milliseconds
  static const unsigned long postingInterval = REFRESH_PERIOD_SEC * 1000L; // delay between updates, in milliseconds
  // the "L" is needed to use long type numbers
  
  // if ten seconds have passed since your last connection,
  // then connect again and send data:
  if (millis() - lastConnectionTime > postingInterval) {
  
    // close any connection before send a new request.
    // This will free the socket on the WiFi shield
    client.stop();
    
    if (client.connect(server, 80)) {
      Serial.println("\r\nconnected");
      // Make a HTTP request:
      client.println("GET /j2me/1.0/livematches.xml HTTP/1.1");
      client.println("Host: synd.cricbuzz.com");
      client.println("Connection: close");
      client.println();
      
      // note the time that the connection was made:
      lastConnectionTime = millis();
    }
    else {
      // didn't get a connection to the server:
      Serial.println("connection failed");
    }
  }
}

boolean ParseXMLBuffer(char *str)
{
  int reset_buffer = false;
  static int state = 0;
  static bool result = false; // match has a result
  
  switch (state) {
    case 0:
      // start of match information <match>
      if (strncmp(str, "<match", 6) == 0) {
        state++;
        Serial.println("\r\n");
      }
    break;
    
    case 1:
      if (PrintXMLNameValue("Match Type: ", "type", str)) {
        Serial.println();
        state++;
      }
    break;
    
    case 2:
      if (PrintXMLNameValue("Match Series: ", "srs", str)) {
        Serial.println();
        state++;
      }
    break;
    
    case 3:
      if (PrintXMLNameValue("Match Description: ", "mchDesc", str)) {
        Serial.println();
        state++;
      }
    break;
    
    case 4:
      if (PrintXMLNameValue("Match Number: ", "mnum", str)) {
        Serial.println();
        state++;
      }
    break;
    
    case 5:
      // get match status
      if (strncmp(str, "mchState=\"", 10) == 0) {
        if (strncmp(&str[10], "Result", 6) == 0) {
          result = true;
        }
        else {
          result = false;
        }
        state++;
      }
    break;
    
    case 6:
      if (PrintXMLNameValue("Match Status: ", "status", str)) {
        Serial.println();
        state++;
      }
    break;
    
    case 7:
      if (result == false) {  // match does not have a result, so get the details
        if (GetInningsDetail(str) == true) {
          state++;
        }
      }
      else {
        state++;
      }
    break;
    
    case 8:
      // end of match information </match>
      if (strncmp(str, "</match>", 8) == 0) {
        Serial.println("\r\n");
        state = 0;
        result = false;
      }
    break;
    
    default:
      state = 0;
      result = false;
      reset_buffer = true;
    break;
  }
  return reset_buffer;
}

bool GetInningsDetail(char *str_xml)
{
  static int innings_state = 0;
  bool done = false;
  
  switch (innings_state) {
    case 0:
      if (PrintXMLNameValue("Number of overs: ", "noofovers", str_xml)) {
        Serial.println();
        innings_state++;
      }
    break;
    
    case 1:
      if (PrintXMLNameValue("Required run rate: ", "rrr", str_xml)) {
        Serial.println();
        innings_state++;
      }
    break;
    
    case 2:
      if (PrintXMLNameValue("Current run rate: ", "crr", str_xml)) {
        Serial.println();
        innings_state++;
      }
    break;
    
    case 3:
      if (PrintXMLNameValue("Team: ", "sName", str_xml)) {
        Serial.print(" ");
        innings_state++;
      }
      else if (strncmp(str_xml, "</mscr>", 7) == 0) {
        // end of score data
        innings_state = 7;
      }
    break;
    
    case 4:
      if (PrintXMLNameValue("-- Runs: ", "r", str_xml)) {
        Serial.print(" ");
        innings_state++;
      }
      else if (strncmp(str_xml, "</mscr>", 7) == 0) {
        Serial.println(" -- ");
        // end of score data
        innings_state = 7;
      }
    break;
    
    case 5:
      if (PrintXMLNameValue("-- Overs: ", "ovrs", str_xml)) {
        Serial.print(" ");
        innings_state++;
      }
    break;
    
    case 6:
      if (PrintXMLNameValue("-- Wickets: ", "wkts", str_xml)) {
        Serial.println("");
        innings_state = 3;  // check other teams score
      }
    break;
    
    case 7:
      done = true;
      innings_state = 0;
    break;
    
    default:
      done = true;
      innings_state = 0;
    break;
  }
  
  return done;
}

// print the value from the XML name str_xml_name found in str_xml
// label        - title of parameter(value) displayed on screen
// str_xml_name - name of the XML name/value pair
// str_xml      - XML string to search for the value of the name/value pair
bool PrintXMLNameValue(char *label, char *str_xml_name, char *str_xml)
{
  char temp_str[35] = {0};
  int str_length = 0;
  bool found_name = false;
  
  strcpy(temp_str, str_xml_name);
  // string to search for is in the format name=", so add =" to the end of name
  strcat(temp_str, "=\"");
  str_length = strlen(temp_str);
  if (strncmp(str_xml, temp_str, str_length) == 0) {
    Serial.print(label);
    PrintUntilCloseQuote(&str_xml[str_length]);
    found_name = true;
  }
  
  return found_name;
}

// print characters from string str until a quote " is found or end of string
void PrintUntilCloseQuote(char *str)
{
  int i = 0;
  
  do {
    Serial.print(str[i]);
    i++;
  } while ((str[i] != '\"') && (str[i] != 0));
}

// shift contents of buffer buf[] one byte to the left
void LeftShiftBuffer(char *buf, int buf_sz)
{
  for (int i = 0; i < buf_sz; i++) {
    buf[i] = buf[i + 1];
  }
}

Amazon.com

Amazon.co.uk





Code Notes and Limitations

Firstly note that this is a first attempt at parsing the XML cricket score file and uses a weak method of parsing the file. This is partly due to the memory limitations of the smaller Arduinos.

The buffering and parsing methods in this code are not efficient and can be improved – hopefully in a future project that will improve on this current project.

Because of the weak parsing techniques used in the code, it is possible that the code will break if anything unexpected is encountered in the XML file. This includes changing the order in which name/value pairs appear in the XML.

This project is really a quick sketch to get the cricket score displayed and to serve as a starting point for future better projects.

How the Sketch Works

Getting the XML into a Buffer

After connecting to the XML feed, the Arduino puts bytes received from the feed into the array / buffer pars_buffer[] one byte at a time. When the buffer is full, the entire buffer is left-shifted and each new byte is added to the end of the buffer after shifting.

The buffer is a "window" of the incoming data that contains a portion of the incoming data limited by the size of the buffer. This buffer is parsed "on the fly" by the Arduino without saving the entire incoming XML file to memory.

Parsing the Buffer

After a new byte is placed in the buffer, the buffer is checked to see if it contains the desired XML tag or name of a name/value pair. This is done by the ParseXMLBuffer() function.

The ParseXMLBuffer() function looks for for various tags and names in sequence in a switch statement. For example, it first looks for the opening <match tag, ignoring all other text until this tag is found.

After finding the opening match tag, it will then look for the type name and get and print the value of the match type using the PrintXMLNameValue() function e.g. if the name value pair is type="TEST" then the value TEST (for test match) is extracted and displayed.

Here we can see one of the weaknesses of the code in that it reads name/value pairs in the expected order. If any name/value pair order is changed the code will break.

Conditional Parsing

Some matches will be finished and will have a result. If the match result is available, then the data for the match will not contain information such as the number of overs and the current run rate. The result variable in the ParseXMLBuffer() function is used to flag matches that have a result so that the code does not try to extract data that does not exist for a finished match.

Only if the match does not have a result, will the function GetInningsDetail() be called that gets further live information about the match that is currently being played.

References and Other Information

Thanks to John for asking a question on the Starting Electronics blog about how to display the live cricket score which got this project started.

The client connection and resource request part of this project is based on two examples from the Arduino IDE built-in examples. These are found in the IDE under:

  • File → Examples → Ethernet → WebClient
  • File → Examples → Ethernet → WebClientRepeating

The pages for these two examples can be found on the Arduino website at:

The live cricket XML feed is read from http://synd.cricbuzz.com/j2me/1.0/livematches.xml