Starting Electronics needs your help! Please make a donation to help cover our hosting and other costs. Click the donate button to send a donation of any amount.

Improved Arduino Cricket Score Ticker using SD Card

Created on: 15 June 2015

This is a much improved version of the Arduino cricket score ticker to display live cricket scores from the Internet.

This version of the cricket score ticker first captures the live cricket score XML file and saves it to SD card, then parses the saved XML file to extract the required score and game information.

The advantage of saving the XML file to SD card is that the file can be randomly accessed for the required information rather than trying to parse the incoming data on-the-fly as the previous version of the ticker tried to do (link above to previous version).

Similar projects that require an XML file to be parsed by an Arduino can be developed using the same XML parsing functions used in this project.

The Arduino sketch for the SD card ticker can be found below with an explanation of how the sketch and XML parsing functions work.

Cricket Score Ticker Hardware and Software

The sketch is too big to fit into the memory of an Arduino Uno, so an Arduino MEGA or similar Arduino with more memory than the Uno is required. To get some perspective on Arduino sketch sizes, see this blog.startingelectronics.com/a-comparison-of-arduino-sketch-sizes-in-memory/ (no longer available) blog post on Arduino sketches and memory used on an Arduino Uno.

Below is a list of the hardware and software used for developing this project. Similar compatible hardware should also work.

  • Arduino MEGA 2560
  • Arduino Ethernet Shield
  • 2GB micro SD card
  • Arduino IDE Version 1.6.4
  • Ethernet Patch Cable to Connect Ethernet Shield to Internet Router
  • USB Cable for Powering and Programming the Arduino

Books that may interest you:

C Programming with Arduino Book Ultimate Arduino MEGA 2560 Hardware Manual Ultimage Arduino Uno Hardware Manual

Cricket Score Ticker Arduino Sketch Code

Copy the sketch below and paste it to the Arduino IDE. Load the sketch to an Arduino MEGA with Ethernet shield, SD card and Internet connection.

Open the Serial Monitor window of the Arduino IDE to see the available cricket matches – make sure that the Arduino Serial Monitor window baud rate is set to 115200. Enter the number of the match to view.

The cricket match information being viewed will be updated every 15 seconds. Sending M to the Arduino from the Serial Monitor window will display the menu of available matches again.

/* Gets the cricket score from synd.cricbuzz.com/j2me/1.0/livematches.xml
 * Saves the XML score file to SD card
 * Parses the XML file on the SD card for the desired data
 * All results are displayed in the Arduino IDE Serial Monitor window
 *
 * References and further information at:
 * http://startingelectronics.org/software/arduino/live-cricket-score-SD/
 *
 * Author: W.A. Smith      Date: 15 June 2015
 */

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

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

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

boolean currentLineIsBlank = true;  // used to find end of incoming HTTP header
File xmlFile;                       // handle of XML file stored on SD card
char *file_name = "crick.xml";      // name of file the received XML is saved to on SD card
int match_id = -1;                  // match to view, negative number means no match selected

void setup() {
  // disable Ethernet chip
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  
  // cricket match results are printed to the serial port
  // for display in the Arduino IDE Serial Monitor window,
  // so initialize the serial port
  Serial.begin(115200);

  if (!SD.begin(4)) {
    // SD card initialization failed
    return;
  }

  // delete old XML file if it exists
  if (SD.exists(file_name)) {
    SD.remove(file_name);
  }

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    // 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() {
  // request the XML file at the set interval
  httpRequest();
  
  // if there are incoming bytes available
  // from the server, strip the HTTP header
  // and save the incoming XML file
  if (client.available()) {
    char c = client.read();
    if (c == '\n' && currentLineIsBlank) {
      // file to save the XML data to
      xmlFile = SD.open(file_name, FILE_WRITE);
      // end of received HTTP header, now save incoming bytes to XML file
      while (client.connected()) {
        // stay in this loop until all XML file bytes have been received
        if (client.available()) {
          // get the XML file bytes after the HTTP header
          c = client.read();
          // replace all single quotes in the XML file with double quotes
          if (c == '\'') {
            c = '"';
          }
          if (xmlFile) {
            // save the received byte to the XML file
            xmlFile.print(c);
          }
        }
      }
      // The XML file has been received and saved to SD card.
      // Display a menu of the matches from the SD card and
      // allow the user to choose which match details to
      // display.
      if (xmlFile) {
        // finished writing to the file, so close the file
        xmlFile.close();
        // now open the file for reading
        xmlFile = SD.open(file_name, FILE_READ);

        if (xmlFile) {
          if (match_id < 0) {
            // a match has not been selected - bring up a menu for the user to select a match
            int num_matches;          // number of matches in the XML file
            int match_selected = 0;   // match selected by user, numbered 1 to num_matches
            char value[50] = {0};     // value read from XML file name / value pair
            
            // get the number of matches in the XML file
            num_matches = xmlGetNumTags("<match", "</mchdata>");
            Serial.print(num_matches);
            Serial.println(F(" matches available, enter match number to view:"));

            // print the match menu
            for (int i = 0; i < num_matches; i++) {
              // start at the beginning of the file
              xmlFile.seek(0);
              // go to each <match> child tag inside parent <mchdata> tag
              if (xmlGoToChildTag("<match ", i + 1, "</mchdata>")) {
                Serial.print(i + 1);  // match number in order found
                Serial.print(". ");
                // print the match series
                if (xmlGetTagNameValue("srs", value, 50)) {
                  Serial.print(value);
                }
                // print the match description
                if (xmlGetTagNameValue("mchDesc", value, 50)) {
                  Serial.print(" - ");
                  Serial.println(value);
                }
              }
            }
            
            // get match number from user
            char rx_byte[2] = {0};
            
            // user must enter match number from Serial Monitor window
            while (!rx_byte[0]) {
              if (Serial.available() > 0) {
                rx_byte[0] = Serial.read();
                match_selected = atoi(rx_byte);
                // is the user entered match number valid?
                if ((match_selected < 1) || (match_selected > num_matches)) {
                  // invalid match number
                  Serial.println(F("Invalid, please enter match number."));
                  rx_byte[0] = 0;
                }
                else {
                  // a match was selected from the menu, get the match ID
                  // start at the beginning of the file
                  xmlFile.seek(0);
                  if (xmlGoToChildTag("<match", match_selected, "</mchdata>")) {
                    if (xmlGetTagNameValue("id", value, 50)) {
                      // The match ID number was found, save it as an integer.
                      // The match will be accessed by match ID number to display the match
                      // details in case the match changes position in the XML file.
                      match_id = atoi(value);
                    }
                  }
                }
              }
            }
          }
          else {
            // A valid match was selected from the menu.
  
            // print the match parameters of the selected match
            PrintMatchData(match_id);

          }
          xmlFile.close();
        }
        // finished with the file, so delete it
        SD.remove(file_name);
      }
      else {
        Serial.println(F("Error opening crick.xml"));
      }
      c = 0;
    }
    // detect the end of the incoming HTTP header
    if (c == '\n') {
      // starting a new line
      currentLineIsBlank = true;
    }
    else if (c != '\r') {
      // got a character on the current line
      currentLineIsBlank = false;
    }
  }
  // check for input from Serial Monitor window
  if (Serial.available() > 0) {
    char rx_opt = Serial.read();
    // if user presses the M key, display the menu again
    if (rx_opt == 'm' || rx_opt == 'M') {
      // user requests match menu
      match_id = -1;
      Serial.println(F("\r\nNew menu will be printed after next data request."));
    }
  }
}

// an example function that shows how to print the desired match parameters
// customise this function to display the desired match parameters
void PrintMatchData(int match_id)
{
  char value[50] = {0};
  
  // start at the beginning of the file
  xmlFile.seek(0);
  
  // go to the match of the specified match ID in the XML file
  if (xmlGoToTagID("<match", match_id)) {
    // get attributes from the <match> tag
    if (xmlGetTagNameValue("mchDesc", value, 50)) {
      Serial.print(F("Teams: "));
      Serial.println(value);
    }
    if (xmlGetTagNameValue("srs", value, 50)) {
      Serial.print(F("Series: "));
      Serial.println(value);
    }
    if (xmlGetTagNameValue("type", value, 50)) {
      Serial.print(F("Match Type: "));
      Serial.println(value);
    }
    if (xmlGetTagNameValue("mnum", value, 50)) {
      Serial.print(F("Match Number: "));
      Serial.println(value);
    }
    if (xmlGetTagNameValue("vcountry", value, 50)) {
      Serial.print(F("Country: "));
      Serial.println(value);
    }
    if (xmlGetTagNameValue("vcity", value, 50)) {
      Serial.print(F("City: "));
      Serial.println(value);
    }
    // get attributes from the <state> tag, child of the <match> tag
    if (xmlGoToChildTag("<state", 1, "</match>")) {
      if (xmlGetTagNameValue("status", value, 50)) {
        Serial.print(F("Match Status: "));
        Serial.println(value);
      }
      if (xmlGetTagNameValue("mchState", value, 50)) {
        Serial.print(F("Match State: "));
        Serial.println(value);
      }
    }
  }
  
  // only print the innings detail if the match is in progress
  // value must contain the match state
  if (strcmp(value, "inprogress") == 0) {
    // start at the beginning of the file
    xmlFile.seek(0);
    // get innings detail
    if (xmlGoToTagID("<match", match_id)) {
      if (xmlGoToChildTag("<mscr", 1, "</match>")) {
        if (xmlGoToChildTag("<inngsdetail", 1, "</mscr>")) {
          if (xmlGetTagNameValue("noofovers", value, 50)) {
            Serial.print(F("Number of overs: "));
            Serial.println(value);
          }
          if (xmlGetTagNameValue("crr", value, 50)) {
            Serial.print(F("Current run rate: "));
            Serial.println(value);
          }
        }
      }
    }
    xmlFile.seek(0);
    // get batting team details
    if (xmlGoToTagID("<match", match_id)) {
      if (xmlGoToChildTag("<mscr", 1, "</match>")) {
        if (xmlGoToChildTag("<btTm", 1, "</mscr>")) {
          if (xmlGetTagNameValue("sName", value, 50)) {
            Serial.print(F("Batting team: "));
            Serial.println(value);
          }
          if (xmlGoToChildTag("<Inngs", 1, "</btTm>")) {
            if (xmlGetTagNameValue("r", value, 50)) {
              Serial.print(F("Runs: "));
              Serial.println(value);
            }
            if (xmlGetTagNameValue("ovrs", value, 50)) {
              Serial.print(F("Overs: "));
              Serial.println(value);
            }
            if (xmlGetTagNameValue("wkts", value, 50)) {
              Serial.print(F("Wickets: "));
              Serial.println(value);
            }
          }
        }
      }
    }
    
    xmlFile.seek(0);
    // get bowling team details
    if (xmlGoToTagID("<match", match_id)) {
      if (xmlGoToChildTag("<mscr", 1, "</match>")) {
        if (xmlGoToChildTag("blgTm", 1, "</mscr>")) {
          if (xmlGetTagNameValue("sName", value, 50)) {
            Serial.print(F("Bowling team: "));
            Serial.println(value);
          }
        }
      }
    }
  }
}

// Count the number of specified tags in the XML file
// tag:     tag to look for
// end_tag: stop looking when this tag is found
// returns: number of tags found specified by tag
int xmlGetNumTags(char *tag, char *end_tag)
{
  int num_tags = 0;
  
  // start at the beginning of the file
  xmlFile.seek(0);
  
  while (xmlFile.findUntil(tag, end_tag)) {
    num_tags++;
  }
  
  return num_tags;
}

// Go to the specified child tag in the XML file
// tag:     tag to find
// tag_num: occurence number, e.g. 2 for second occurrence of tag
// end_tag: closing tag that stops search, usually should be parent tag
bool xmlGoToChildTag(char *tag, int tag_num, char *end_tag)
{
  bool tag_found = false;
  int  count = 0;
  
  while (xmlFile.findUntil(tag, end_tag)) {
    count++;
    if (count == tag_num) {
      tag_found = true;
      break;
    }
  }
  
  return tag_found;
}

// Get the value of the specified name / value pair of the current tag
// The internal file pointer must be in the tag to search by using xmlGoToTagID() or xmlGoToChildTag()
// attribute_name:  input  - name of the desired name value pair
// attribute_value: output - value of the name / value pair read from file
// val_len:         input  - length of attribute_value array
// returns:         true if requested attribute name is found
// resets the internal file pointer to the position when the function was called
bool xmlGetTagNameValue(char *attribute_name, char *attribute_value, int val_len)
{
  bool name_found = false;
  char name_str[25] = {0};
  int  pointer_pos = 0;
  int  bytes_read = 0;
  
  pointer_pos = xmlFile.position();
  strcpy(name_str, attribute_name);
  strcat(name_str, "=\"");  // put =" at end of attribute name
  
  if (xmlFile.findUntil(name_str, ">")) {
    bytes_read = xmlFile.readBytesUntil('\"', attribute_value, (val_len -1 ));
    attribute_value[bytes_read] = 0;  // terminate the string
    name_found = true;
  }
  
  xmlFile.seek(pointer_pos);
  
  return name_found;
}

// move internal file pointer to tag with the specified id so that tag attributes can be read
bool xmlGoToTagID(char *tag, int id)
{
  bool tag_id_found = false;
  char str_buf[50] = {0};
  int bytes_read;
  int id_read = 0;
  int pointer_pos = 0;
  
  while (xmlFile.find(tag)) {
    pointer_pos = xmlFile.position();
    // found the tag, now find if the id name exists
    if (xmlFile.findUntil("id=\"", ">")) {
      // id name found, now get the id value
      bytes_read = xmlFile.readBytesUntil('\"', str_buf, 49);
      if (bytes_read) {
        // terminate the string
        str_buf[bytes_read] = 0;
        id_read = atoi(str_buf);
        if (id_read == id) {
          // the correct id has been found, now rewind the internal pointer to the beginning of the tag
          xmlFile.seek(pointer_pos);
          tag_id_found = true;
          break;
        }
      }
    }
  }
  
  return tag_id_found;
}

// this method makes a HTTP connection to the server:
void httpRequest() {
  static unsigned long lastConnectionTime = 0;                             // last time 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 specified time has elapsed since last connection,
  // then connect again and send request
  if (millis() - lastConnectionTime > postingInterval) {
  
    // close any connection before sending a new request
    client.stop();
    
    if (client.connect(server, 80)) {
      Serial.println("\r\nconnected\r\n");
      // 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(F("connection failed"));
    }
  }
}

This video shows the cricket score ticker sketch running:

Can't see the video? View on YouTube →

How the Score Ticker Code Works

The setup() Function

The setup() function does the following initialization when the sketch starts running.

Disable Ethernet Chip

The setup() function first disables the W5100 Ethernet chip because the SPI bus is shared by the Ethernet chip and SD card. This ensures that data intended for the SD card does not get sent to the Ethernet chip.

Serial Port

The serial port baud rate is set to 115200. Match information is sent over the serial port and viewed in the Arduino IDE Serial Monitor window, so the serial port must first be initialized.

SD Card

The SD card is used to save the XML file, so must be initialized. If the file on the SD card used for the incoming XML data exists from previously running the sketch it is deleted. The file is deleted because if it exists, it will not be overwritten, but rather new XML data will be appended to it.

Ethernet Connection

Finally the Ethernet chip is initialized so that it can be used to request the XML file over the Internet.

The Main loop() Function

Requesting the XML File

The main loop connects to the cricket score website and requests the XML file using the httpRequest() function.

The httpRequest() function only sends a request for the XML file every 15 seconds, set by REFRESH_PERIOD_SEC at the top of the sketch.

Saving the XML File

In the main loop, the code looks for the incoming blank line that occurs after the HTTP header has been received. The bytes that come through after the HTTP header are the bytes that make up the XML file and are saved to the SD card.

A file is opened to save the XML bytes to and then all of the incoming bytes are saved inside the loop:

while (client.connected()) {

Printing the Match Menu

After the XML file bytes have been saved, the file is closed for writing to and then opened for reading.

If the a menu item has not been selected, or the user enters an invalid menu item number, the menu will be displayed. The default menu number is set to invalid by setting its value to -1.

Inside the following if statement the file is parsed to find the number of matches that it contains:

if (match_id < 0) {

A for loop is used to print the match menu.

Getting the Match Number from the User

After the match menu is printed, the code sits in the following loop waiting for the user to enter a valid match number in the serial monitor window:

while (!rx_byte[0]) {

When the user enters a valid match number, the match ID for the selected match is saved to the match_id variable.

Match numbers are assigned to matches in the order that they are found in the XML file. Each <match> tag in the file contains a match ID. The match ID is saved to the variable in case matches are added or removed from the XML file by the host site. By searching the file by match ID, the match can be moved to any position in the file and the correct match will be found.

The code that later prints out the match information and scores only accesses the match by match ID and not the order number that the match originally appeared in.

Printing the Selected Match Data

Once a valid match has been selected and the match ID saved, the PrintMatchData() function is used to extract the desired match data from the XML file and send it to the Serial Monitor window.

PrintMatchData() demonstrates how to get some of the selected match data and can be customized to print the desired match data.

Displaying the Match Menu Again

With a valid match selected, the code will also check to see if M is sent to the Arduino from the serial monitor window which will bring up the match menu again.

XML File Format

The sketch requests an XML file that contains the live cricket score data from the CricBuzz website's http://synd.cricbuzz.com/j2me/1.0/livematches.xml (no longer working) live XML feed.

Sample XML File

The following XML file was captured from CricBuzz's live feed.

<mchdata NMchs="11">
	<links fUrlBase="http://synd.cricbuzz.com/j2me/1.0/flags/team_" />


	<match id="3" type="TEST" srs="Australia tour of West Indies, 2015" mchDesc="WI vs AUS" mnum="2nd Test"  vcity="Kingston, Jamaica" vcountry="West Indies" grnd="Sabina Park" inngCnt="4" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_WI_AUS/WI_AUS_JUN11_JUN15/" >
 <state mchState="complete" status="Aus won by 277 runs" TW="WI" decisn="Fielding" addnStatus="" splStatus="">
</state>
<Tm id="10" Name="WI" sName="WI"  flag='1'/>
<Tm id="4" Name="Aus" sName="AUS"  flag='1'/>


<Tme Dt="Jun 11 2015" stTme="15:00" enddt ="Jun 15 2015" ></Tme>		

<mscr>
	<inngsdetail noofovers="0" rrr="0" crr="2.71" cprtshp=""/>
<btTm id="10" sName="WI">
		<Inngs desc="2nd Inns" r="114"  Decl="0" FollowOn="0" ovrs="42" wkts="10"/>
		<Inngs desc="1st Inns" r="220"  Decl="0" FollowOn="0" ovrs="59.5" wkts="10"/>
</btTm>
<blgTm id="4" sName="AUS">
		<Inngs desc="2nd Inns" r="212"  Decl="1" FollowOn="0" ovrs="65" wkts="2"/>
		<Inngs desc="1st Inns" r="399"  Decl="0" FollowOn="0" ovrs="126.5" wkts="10"/>
</blgTm>

</mscr>
</match>



	<match id="1" type="ODI" srs="New Zealand tour of England, 2015" mchDesc="ENG vs NZ" mnum="3rd ODI"  vcity="Southampton" vcountry="England" grnd="The Rose Bowl" inngCnt="2" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_ENG_NZ/ENG_NZ_JUN14/" >
 <state mchState="complete" status="NZ won by 3 wkts" TW="Eng" decisn="Batting" addnStatus="" splStatus="">
</state>
<Tm id="9" Name="Eng" sName="ENG"  flag='1'/>
<Tm id="13" Name="NZ" sName="NZ"  flag='1'/>


<Tme Dt="Jun 14 2015" stTme="09:30" enddt ="Jun 14 2015" ></Tme>		

<mscr>
	<inngsdetail noofovers="50" rrr="0" crr="6.24" cprtshp="6(5)"/>
<btTm id="13" sName="NZ">
		<Inngs desc="Inns" r="306"  Decl="0" FollowOn="0" ovrs="49" wkts="7"/>
</btTm>
<blgTm id="9" sName="ENG">
		<Inngs desc="Inns" r="302"  Decl="0" FollowOn="0" ovrs="45.2" wkts="10"/>
</blgTm>

</mscr>
</match>



	<match id="2" type="TEST" srs="India tour of Bangladesh, 2015" mchDesc="BAN vs IND" mnum="Only Test"  vcity="Fatullah" vcountry="Bangladesh" grnd="Khan Shaheb Osman Ali Stadium" inngCnt="3" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_BAN_IND/BAN_IND_JUN10_JUN14/" >
 <state mchState="complete" status="Match drawn" TW="Ind" decisn="Batting" addnStatus="" splStatus="">
</state>
<Tm id="6" Name="Ban" sName="BAN"  flag='1'/>
<Tm id="2" Name="Ind" sName="IND"  flag='1'/>


<Tme Dt="Jun 10 2015" stTme="04:00" enddt ="Jun 14 2015" ></Tme>		

<mscr>
	<inngsdetail noofovers="0" rrr="0" crr="1.53" cprtshp="23(90)"/>
<btTm id="6" sName="BAN">
		<Inngs desc="2nd Inns" r="23"  Decl="0" FollowOn="1" ovrs="15" wkts="0"/>
		<Inngs desc="1st Inns" r="256"  Decl="0" FollowOn="0" ovrs="65.5" wkts="10"/>
</btTm>
<blgTm id="2" sName="IND">
		<Inngs desc="1st Inns" r="462"  Decl="1" FollowOn="0" ovrs="103.3" wkts="6"/>
</blgTm>

</mscr>
</match>



<match id='14586' type="TEST" srs="Australia tour of West Indies, 2015" mchDesc="WI vs AUS" mnum="2nd Test"  inngCnt="4" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_WI_AUS/WI_AUS_JUN11_JUN15/" >

	<state mchState="Result" status="Aus won by 277 runs">	</state>
		<manofthematch NoOfPlayers="1">
			<mom Name="Steven Smith"/>
		</manofthematch>
		<ManOftheSeries  NoOfPlayers="1">
			<mos Name="Josh Hazlewood"/>
		</ManOftheSeries >
	<Tm id="10" Name="WI" sName="WI"  flag='1'/>
	<Tm id="4" Name="Aus" sName="AUS"  flag='1'/>
	
	<Tme Dt="Jun 11 2015" stTme="15:00" enddt ="Jun 15 2015"></Tme>

</match>


<match id='13747' type="ODI" srs="New Zealand tour of England, 2015" mchDesc="ENG vs NZ" mnum="3rd ODI"  inngCnt="2" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_ENG_NZ/ENG_NZ_JUN14/" >

	<state mchState="Result" status="NZ won by 3 wkts">	</state>
		<manofthematch NoOfPlayers="1">
			<mom Name="Kane Williamson"/>
		</manofthematch>
	<Tm id="9" Name="Eng" sName="ENG"  flag='1'/>
	<Tm id="13" Name="NZ" sName="NZ"  flag='1'/>
	
	<Tme Dt="Jun 14 2015" stTme="09:30" enddt ="Jun 14 2015"></Tme>

</match>
</mchdata>


The Arduino sketch uses the XML parsing functions (explained below) to move to specific tags inside the XML file and then get the value from a name / value pair within a tag.

An XML parsing function will first move the internal file pointer to the match tag of the desired ID. The XML below extracted from the above file shows the first match tag which has an ID of 3.

<match id="3"

The above XML file contains 5 matches, each with a unique match ID. The top level structure of the file is shown below where the XML file has been formatted for easier reading and the child tags of each match tag have been removed.

<mchdata NMchs="11">
    <links fUrlBase="http://synd.cricbuzz.com/j2me/1.0/flags/team_" />

    <match id="3" type="TEST" srs="Australia tour of West Indies, 2015" mchDesc="WI vs AUS" mnum="2nd Test"  vcity="Kingston, Jamaica" vcountry="West Indies" grnd="Sabina Park" inngCnt="4" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_WI_AUS/WI_AUS_JUN11_JUN15/" >
    </match>

    <match id="1" type="ODI" srs="New Zealand tour of England, 2015" mchDesc="ENG vs NZ" mnum="3rd ODI"  vcity="Southampton" vcountry="England" grnd="The Rose Bowl" inngCnt="2" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_ENG_NZ/ENG_NZ_JUN14/" >
    </match>

    <match id="2" type="TEST" srs="India tour of Bangladesh, 2015" mchDesc="BAN vs IND" mnum="Only Test"  vcity="Fatullah" vcountry="Bangladesh" grnd="Khan Shaheb Osman Ali Stadium" inngCnt="3" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_BAN_IND/BAN_IND_JUN10_JUN14/" >
    </match>

    <match id='14586' type="TEST" srs="Australia tour of West Indies, 2015" mchDesc="WI vs AUS" mnum="2nd Test"  inngCnt="4" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_WI_AUS/WI_AUS_JUN11_JUN15/" >
    </match>

    <match id='13747' type="ODI" srs="New Zealand tour of England, 2015" mchDesc="ENG vs NZ" mnum="3rd ODI"  inngCnt="2" datapath="http://synd.cricbuzz.com/j2me/1.0/match/2015/2015_ENG_NZ/ENG_NZ_JUN14/" >
    </match>
</mchdata>

We can see that the mchdata tag contains five match child tags. Each child tag contains attributes in the form of name / value pairs, for example type="TEST" is a name value pair where type is the name and TEST is the value.

To parse the file, first a function is called to move to the match tag with the desired ID. Once at the match tag, match data can be extracted from the tag's attributes.

Each match tag contains further child tags, which themselves may contain further child tags. The principle of operation is to find out if a child tag exists by moving to it. If the child tag exists and contains a child tag with the desired data, the internal file pointer is then moved to this child tag if it is found.

The same XML parsing function is called each time to move one child tag level deeper until the desired tag is found.

XML Parsing Functions

The above explanations give an overview of how the top level application works and a look at the structure of the XML file. XML parsing functions do the actual work of extracting or parsing the XML file data.

All the XML parsing functions access the XML file using the global file handle xmlFile and expect it to be a valid file handle.

xmlGoToTagID() and xmlGoToChildTag() are used to position the internal file pointer at the start of the desired tag which contains the cricket match data in name / value pairs. xmlGetTagNameValue() is then used to get the value of the name / value pair from the tag that the internal file pointer is at.

xmlGetNumTags()

xmlGetNumTags() is used to find the number of occurrences of a specific tag and is used to find the the number of matches in the XML file by finding the number of match child tags inside the mchdata parent tag.

Usage in the sketch:

// get the number of matches in the XML file
num_matches = xmlGetNumTags("<match", "</mchdata>");

xmlGoToTagID()

This function moves the internal file pointer to the specified tag with the specified ID and is used to go to the match tag with the specified match ID.

Usage in the sketch:

// go to the match of the specified match ID in the XML file
if (xmlGoToTagID("<match", match_id)) {

xmlGoToChildTag()

Moves the internal file pointer to the beginning of the specified child tag inside the specified parent tag.

This function must be called to move through child tags in the order that they occur. First to find a parent tag, then the child tag of the parent tag. If the child tag contains a further child tag, then the function must be called again to move to the child tag one level deeper.

Example usage from the sketch:

if (xmlGoToChildTag("<state", 1, "</match>")) {

This example goes to the first occurrence of the state child tag inside the match parent tag.

xmlGetTagNameValue()

Gets the value of a name / value pair from the XML file. It expects the internal file pointer to be at the start of the tag to search for the name / value pair.

Move to the desired tag using either xmlGoToTagID() or xmlGoToChildTag() before calling xmlGetTagNameValue() so that the internal file pointer is at the tag to search.

This function moves the internal file pointer back to the beginning of the specified child tag name before it returns so that it can be called multiple times on a single tag even if the order of the name / value pairs is changed in the tag. In this way it can extract as much data as needed from multiple name / value pairs within a tag irrespective of the order in which the name / value pairs occur.

Usage example from the sketch:

// print the match series
if (xmlGetTagNameValue("srs", value, 50)) {
  Serial.print(value);
}

This example searches for the srs name in the match tag to get it's value. The internal file pointer was moved to the match tag using xmlGoToChildTag() before calling this function.

References

This project used the following resources: