DIWIFI Weather with ROBOTC

During DIWIFI week 2 presented by Dexter Industries, one of the projects was a ROBOTC program that would use the DIWIFI to read the current weather, provided by Google.

The program worked fine, but I made some modifications to improve it.

The way the original program would read the incoming data limited speed to about 9600 Baud. I like to be able to run communications faster than that, so I made some modifications to allow up to 57600 Baud.

The data provided by Google includes more than just the current conditions, temp in C and F, and wind conditions. I added several things to allow the NXT to parse and display the humidity, and the data for the next forecasted day. The debug stream outputs all this, as well as the other three forecasted days.

I also improved several other things to speed up the process of getting the data. Now it takes about 1 1/2 seconds to connect, request, receive, parse, and display the weather.

The program appears to work similarly, but several of the internal functions are totally redesigned to allow the extra functionality.

DIWIFI-Weather.c

// Read Google Weather
//
// DIWFI Weather uses the Dexter Industries Wifi Sensor to get the weather
// from Google.com
//
// Run this program with the RobotC Debugging Stream On to view any errors.
// Run AFTER connecting to a Wifi network!
// For more information visit http://www.dexterindustries.com/
//
// This program uses HTTP GET examples to retrieve the weather and
// display the current conditions on the NXT.
//
// See more about the Dexter Industries Wifi Sensor for the Lego Mindstorms NXT here:  http://www.dexterindustries.com/wifi.html
//
//  With modifications by Matthew Richardson.
//  matthewrichardson37<at>gmail.com
//  https://mattallen37.wordpress.com/
//
//////////////////////////////////////////////////////////////////////////////////////////////////////

#include "drivers/common.h"
#include "DIWIFI-Weather.h"

void weather(string location){

  closeAllConns();                  // Housekeeping: Close any open connections.
  clear_read_buffer();              // Housekeeping: Clear out the buffer.
  int CID = 0;
  CID = start_TCP_client();         // We start the HTTP client here.  Now we're acting as a client, not a server.
  wait1Msec(10);
  GetWeather(CID, location);        // This kicks off the meat of the project.  We're starting to send a tweet through the connection ID.
  read_weather_data();              // Load the weather data into an array so we can deal with it.

  writeDebugStreamLine("");         // New line in the debug stream.
  writeDebugStreamLine("");         // New line in the debug stream.

  print_weather();
}

task main()
{
  clear_read_buffer();

  while(true){                      // Repeat forever
    weather("Kabul");               // Connect, request, receive, parse, and display the current and forcasted weather
//    wait10Msec(100);                // Optionally slow down.
  }
}

DIWIFI-Weather.h

////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Header file for reading the weather using the DIWIFI
//
//  With modifications by Matthew Richardson
//  matthewrichardson37<at>gmail.com
//  https://mattallen37.wordpress.com/
//
////////////////////////////////////////////////////////////////////////////////////////////////////////

string IP_num = "173.194.73.106";       // Google IP Number
ubyte byteStart[] = {27, 'S', '0'};     // Begin a Transmission
ubyte byteEnd[] = {27, 'E'};            // End a transmission
ubyte BytesRead[1];                     //
char weather_data[2000];                // Weather Data goes into this array.
int index = 0;                          // This will be the position index in weather_data

////////////////////////////////////////////////////////////////////////////////////////////////////////
//      Clear Read Buffer
//      Run this to clear out the reading buffer.
//      Simply sends a carriage return, then clears the buffer out.
////////////////////////////////////////////////////////////////////////////////////////////////////////

void clear_read_buffer()
{
    ubyte nData[] = {13};
    nxtWriteRawHS(nData[0], 1);   // Send the carriage return
    wait1Msec(50);
    while(nxtGetAvailHSBytes()){
      nxtReadRawHS(BytesRead[0], 1);    // Read the response.  Probably an error.
    }
}

// This function writes a string to the DebugStream and Port4
// We wrote this out because it is easier to recognize and see
// strings; easier than working with arrays of characters.
void writeStr(string sData)
{
  ubyte byteData = 0;
  for(int i = 0; i < strlen(sData); i++)
  {
    byteData = strIndex(sData, i);
    nxtWriteRawHS(byteData, 1);
    writeDebugStream("%c", byteData);     // Critical for debugging.  Make sure you're getting some time in there
                                          // to read what you wrote.
    while(nxtHS_Status == HS_SENDING) wait1Msec(1);
    wait1Msec(1);                         // With
  }
}

//
// This function just writes a line break to DebugStream and Port4.
// It is necessary to have this in RobotC because strings are limited to 17 characters
// and sometimes you will need to send commands longer than 17 characters.
void writeLineBreak()
{
  writeDebugStream("%c", 13);
  ubyte newline = 0x0D;
  nxtWriteRawHS(newline, 1);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Send Carriage Return, New Line
// This is used in the HTTP request
////////////////////////////////////////////////////////////////////////////////////////////////////////
void crln()
{
  writeDebugStream("%c", 13);
  writeDebugStream("%c", 10);              // Display this on the debug for our information.
  ubyte newline[] = {0x0D, 0x0A};          // Carriage return and then line feed.
  nxtWriteRawHS(newline, 2);
  wait1Msec(5);                            // ABSOLUTELY CRITICAL.  We added this because
                                           // we found that in line-feeds, the first bytes of the next transmission get
                                           // dropped.  VERY IMPORTANT TO HAVE THIS DELAY IN HERE.
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Receive Bytes
// Reads whatever is in the buffer and prints to debug
////////////////////////////////////////////////////////////////////////////////////////////////////////
/*void Receive(bool wait=false)
{
  if (wait)
    while (nxtGetAvailHSBytes() == 0) wait1Msec(1);

  while (nxtGetAvailHSBytes() > 0) {
    nxtReadRawHS(BytesRead[0], 1);
    writeDebugStream("%c", BytesRead[0]);
    wait1Msec(1);
  }
}*/

void Receive(bool wait=false)
{
  if (wait)
    while (nxtGetAvailHSBytes() == 0) wait1Msec(1);

  time1[T1] = 0;
  int LastTick = 0;
  while (LastTick + 10 > time1[T1]){                 // Keep looping until it hasn't received any data for at least 10ms
    if(nxtGetAvailHSBytes() > 0) {                   // If there is data to receive
	    nxtReadRawHS(BytesRead[0], 1);                 // Grab a byte
	    writeDebugStream("%c", BytesRead[0]);          // Send it to the debug stream
      LastTick = time1[T1];                          // Reset the timer offset
    }
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Housekeeping: Close down the Connections
////////////////////////////////////////////////////////////////////////////////////////////////////////
void closeAllConns() {
  writeDebugStreamLine("closeAllCons");
  clear_read_buffer();
  writeStr("at+ncloseall");
  writeLineBreak();
//  wait10Msec(10);
  Receive(true);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Connect to google via TCP.
// Open up a client connection with the Google Server.
////////////////////////////////////////////////////////////////////////////////////////////////////////
int start_TCP_client() {
  int CID = 0;
  bool connected = false;   // This will tell us if we're connected or not.
  string port = ",80";

  while(!connected){        // Do this over and over again until we get a connection.
	  writeStr("AT+NCTCP=");
	  writeStr(IP_num);
	  writeStr(port);
	  writeLineBreak();

	  // Don't replace the following code with a Receive function.
	  // This code picks out the CID number.
	  while (nxtGetAvailHSBytes() == 0) wait1Msec(5);
	  while (nxtGetAvailHSBytes() > 0) {
	    nxtReadRawHS(BytesRead[0], 1);
	    writeDebugStream("%c", BytesRead[0]);
	    if(BytesRead[0] < 58 && BytesRead[0] > 47){  // So if it's a number . . .
	      CID = BytesRead[0]-48;  // Works for connections 0 through 9.
	      connected = true;
	    }
	    wait1Msec(2);
	  }
	}
  return CID;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Send information to Google to get weather.
////////////////////////////////////////////////////////////////////////////////////////////////////////
void GetWeather(int CID, string location) {
  nxtWriteRawHS(byteStart, 3);
  writeDebugStream("%c", byteStart, 2);
  writeDebugStreamLine("%c", (CID+48));

  wait1Msec(10);

  writeStr("GET /ig/api?");
  writeStr("weather=");
  writeStr(location);
  writeStr(" HTTP/1.1");
  crln();

  writeStr("User-Agent: ");
  writeStr("DIWifi");
  crln();

  writeStr("Host: ");
  writeStr("www.google.com");
  crln();
  crln();

  wait1Msec(10);
  nxtWriteRawHS(byteEnd, 2);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Receive the stream of data sent to the NXT at the request of GetWeather
///////////////////////////////////////////////////////////////////////////////////////////////////
void read_weather_data(){
  while(true){                                                       // Repeat forever, or until break
	  while (nxtGetAvailHSBytes() == 0) wait1Msec(1);                  // Wait until data has been received
	  time1[T1] = 0;                                                   // Reset timer 1
	  int LastTick = 0;                                                // Set the timer offset to 0
	  while (LastTick + 10 > time1[T1]){                               // Repeat while it's been less than 10ms since the last byte was received
	    if(nxtGetAvailHSBytes() > 0) {                                 // If data has been received
		    nxtReadRawHS(BytesRead[0], 1);                               // Read a byte
		    writeDebugStream("%c", BytesRead[0]);                        // Write it to the debug stream
		    weather_data[index] = BytesRead[0];                          // Add the byte to the weather_data
		    index++;                                                     // Increment the weather_data index
	      LastTick = time1[T1];                                        // Reset the timer offset
	    }
	  }
    if(weather_data[index-1] == 69 && weather_data[index-2] == 27) break;  // This looks for the <ESC> E Sequence that signals the end of a transmission.
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Search for a string in the weather_data array
///////////////////////////////////////////////////////////////////////////////////////////////////
int StringFindWeather(string F_String, int Begin_Here = 0){          // Input a string to look for in the weather data array, optionally a place to start, and return the position
  byte F_Size = strlen(F_String);                                    // Get the length of the string to look for
  byte F_Array[20];                                                  // An array to hold the string of chars to look for
  memcpy(F_Array, F_String, F_Size);                                 // Copy the string into the array
  for (int i = Begin_Here; i < sizeof(weather_data); i++){           // repeat for the number of times the weather_data array is long
    if (weather_data[i]==F_Array[0]){                                // If the current char is the same as the first char in the string to look for
      for (int ii = 0; ii < F_Size; ii++){                           // Repeat for the number of times the find string is long
        if (weather_data[i+ii]!=F_Array[ii]) goto LookForFirstChar;  // If the chars don't match up, go back to looking for the first char again
      }
      return i;                                                      // It found the matching string, so return the start char position
    }
    LookForFirstChar:                                                // Label to jump to to look again for the first char of the string to search for
  }
  return -1;                                                         // It didn't find the string, so return -1
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Find data, send it to the debug stream, and optionally display it on the NXT LCD
///////////////////////////////////////////////////////////////////////////////////////////////////
int find_and_print_XML(string findme, int DispLine, string Header = "", int Begin_Here = 0){
  int Start_Location = StringFindWeather(findme, Begin_Here);                      // Find the starting position of the findme string
  if(Start_Location == -1) return -1;                                              // If it didn't exist, return -1
  int End_Location = StringFindWeather("/>", Start_Location);                      // Find the next "/>", starting at the Start_Location
  if(End_Location == -1) return -1;                                                // If it didn't exist, return -1
  int findme_Length = strlen(findme);                                              // Determine the length of the string it's looking for

  int Info_Length = End_Location - Start_Location - findme_Length - 2;             // Determine the length of the information string

  byte Info[20];                                               // Create an array to temporarily hold the information string
  memset(Info, 0, 20);                                         // Set all elements to 0
  for(int i = 0; i < Info_Length; i++){                        // Repeat once for each character in the information string
    Info[i] = weather_data[Start_Location+findme_Length+1+i];  // Load the information array with data from the wather_data array
  }

  string strCond;                                              // Create a string to hold the information
  StringFromChars(strCond, Info);                              // Copy the information array into the information string

  if(StringFind(strCond, "%")!=-1){                            // If the character "%" exists (will only be at the end of the humidity infomation)
    strCond += "%";                                            // Append another one. The diplay and debug stream functions use the "%" symbol as a formatter, so the first one isn't displayed.
  }

  writeDebugStream(Header);                                    // Write the header string to the debug stream
  writeDebugStreamLine(strCond);                               // Write the information string to the debug stream, and create another line

  if(DispLine>-1){                                             // If it is supposed to display to the NXT LCD
	  string combined = Header+strCond;                          // Combine the header with the information string
	  nxtDisplayString(DispLine, combined);                      // Display the combined strings
  }
  return 1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// Print the weather.
////////////////////////////////////////////////////////////////////////////////////////////////////////
void print_weather(){

  writeDebugStreamLine("");                               // New line in the debug stream.

  find_and_print_XML("condition data=" ,0 , ""        );  // General Conditions
  find_and_print_XML("temp_f data="    ,1 , "Temp F: ");  // Temp in F
  find_and_print_XML("temp_c data="    ,-1, "Temp C: ");  // Temp in C
  find_and_print_XML("_condition data=",2 , ""        );  // WIND!  "wind_condition data="
  find_and_print_XML("humidity data="  ,3 , ""        );  // Humidity %

  writeDebugStreamLine("");                               // New line in the debug stream.

  int parser_pointer = StringFindWeather("cast_conditions");                       // Go to the first day of forcast
  find_and_print_XML("_of_week data="  , 4, "Forecast for ", parser_pointer);      // The day of the week, for the first forcasted day
  find_and_print_XML("condition data=" , 5, ""             , parser_pointer);      // General Conditions for the first forcasted day. "day_of_week data="
  find_and_print_XML("low data="       , 6, "Low  F: "     , parser_pointer);      // Forcasted low for the first forcased day
  find_and_print_XML("high data="      , 7, "High F: "     , parser_pointer);      // Forcasted high for the first forcased day
  parser_pointer = StringFindWeather("</forecast_cond"     , parser_pointer+1);    // End of the first day of forcast

  writeDebugStreamLine("");                                                        // New line in the debug stream.

  parser_pointer = StringFindWeather("cast_conditions"     , parser_pointer+10);   // Go to the beginning of the second day of forcast
  find_and_print_XML("_of_week data="  ,-1, "Forecast for ", parser_pointer);      // The day of the week, for the second forcasted day. "day_of_week data="
  find_and_print_XML("condition data=" ,-1, ""             , parser_pointer);      // General Conditions for the second day
  find_and_print_XML("low data="       ,-1, "Low  F: "     , parser_pointer);      // Low for the second day
  find_and_print_XML("high data="      ,-1, "High F: "     , parser_pointer);      // High for the second day
  parser_pointer = StringFindWeather("</forecast_cond"     , parser_pointer+1);    // End of the second day of forcast

  writeDebugStreamLine("");                                                        // New line in the debug stream.

  parser_pointer = StringFindWeather("cast_conditions"     , parser_pointer+10);   // Go to the beginning of the third day of forcast
  find_and_print_XML("_of_week data="  ,-1, "Forecast for ", parser_pointer);      // The day of the week, for the third forcasted day. "day_of_week data="
  find_and_print_XML("condition data=" ,-1, ""             , parser_pointer);      // General Conditions for the third day
  find_and_print_XML("low data="       ,-1, "Low  F: "     , parser_pointer);      // Low for the third day
  find_and_print_XML("high data="      ,-1, "High F: "     , parser_pointer);      // High for the third day
  parser_pointer = StringFindWeather("</forecast_cond"     , parser_pointer+1);    // End of the third day of forcast

  writeDebugStreamLine("");                                                        // New line in the debug stream.

  parser_pointer = StringFindWeather("cast_conditions"     , parser_pointer+10);   // Go to the beginning of the fourth day of forcast
  find_and_print_XML("_of_week data="  ,-1, "Forecast for ", parser_pointer);      // The day of the week, for the fourth forcasted day. "day_of_week data="
  find_and_print_XML("condition data=" ,-1, ""             , parser_pointer);      // General Conditions for the fourth day
  find_and_print_XML("low data="       ,-1, "Low  F: "     , parser_pointer);      // Low for the fourth day
  find_and_print_XML("high data="      ,-1, "High F: "     , parser_pointer);      // High for the fourth day
  parser_pointer = StringFindWeather("</forecast_cond"     , parser_pointer+1);    // End of the fourth day of forcast

  writeDebugStreamLine("");                                                        // New line in the debug stream.

  index = 0;                                            // Reset the count so we can do this over and over again.
  memset(weather_data, 0, 2000);                        // Reset the weather data array to zeros.  Good housekeeping.
}
Advertisements
This entry was posted in Dexter Industries, Drivers, NXT, ROBOTC and tagged , , , , . Bookmark the permalink.

One Response to DIWIFI Weather with ROBOTC

  1. Pingback: DIWIFI Weather: Improved | Dexter Industries Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s