details
Description:
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.
Hardware:
- Arduino Ethernet Shield
- 2GB micro SD card
- Ethernet Patch Cable to Connect Ethernet Shield to Internet Router
- USB Cable for Powering and Programming the Arduino
#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")); } } }
Demo:
Tags:201802,Arduino mega 2560,Arduino shield w5100,2GB SD Card,USB cable ,Ethernet patch cable.
COMMENTS