I have some code that uses webhooks to retrieve the current weather conditions, sunrise and sunset times, and the 4-day forecast as shown in the screenshot below:
I have heard that using String’s is a bad idea when it comes to the stability of the Microcontroller.
Can you guys look at the code I use to parse the weather response from the webhook and let me know if you think using this process is a bad Idea of if it’s acceptable in this case? Is there a better approach to doing the same thing?
Here is the code:
// called once on startup
const char* cityLocation = "Fishers"; //City for my Photon
const char* stateLocation = "IN"; // State for my Photon
char publishString[128];
void setup() {
// For simplicity, we'll format our weather data as text, and pipe it to serial.
// but you could just as easily display it in a webpage or pass the data to another system.
Serial.begin(115200);
Particle.subscribe(System.deviceID(), deviceWebhookHandler, MY_DEVICES); // every webhook preceeded by your deviceID gets handled by deviceWebhookHandler()!!
// Lets give ourselves 10 seconds before we actually start the program.
// That will just give us a chance to open the serial monitor before the program sends the request
for(int i=0;i<5;i++) {
Serial.println("waiting " + String(5-i) + " seconds before we publish");
delay(1000);
}
}
// called forever really fast
void loop() {
// Let's request the weather, but no more than once every 60 seconds.
Serial.println("");
Serial.println("-------------------");
Serial.println("Requesting Weather!");
Serial.println("");
sprintf(publishString, "{\"my-city\": \"%s\", \"my-state\": \"%s\" }", cityLocation, stateLocation);
// publish the event that will trigger our Webhook
Particle.publish("GetCurrentWeatherConditions", publishString, PRIVATE);
delay(4000);
Particle.publish("GetSunRise_SunSetTimes", publishString, PRIVATE);
delay(4000);
Particle.publish("Get3DayWeatherForecast", publishString, PRIVATE);
// and wait at least 60 seconds before doing it again
delay(60000 * 5 );
}
///////////////////////////////////////////////////////////////////////////
void deviceWebhookHandler(const char* event, const char* data)
{
if(strstr(event, "GetCurrentWeatherConditions"))
{
GetCurrentWeatherConditions(event, data);
}
else if(strstr(event, "Get3DayWeatherForecast"))
{
Get3DayWeatherForecast(event, data);
}
else if(strstr(event, "GetSunRise_SunSetTimes"))
{
GetHoursOfDayLight(event, data);
}
else
{
Serial.println("recieved some unknown webhook");
}
}
///////////////////////////////////////////////////////////////////////////
// This function will get called when weather data comes in
void GetCurrentWeatherConditions(const char *name, const char *data) {
// Important note! -- Right now the response comes in 512 byte chunks.
// This code assumes we're getting the response in large chunks, and this
// assumption breaks down if a line happens to be split across response chunks.
String str = String(data);
String locationStr = tryExtractString(str, "<Location>", "</Location>");
String timeStr = tryExtractString(str, "<Time>", "</Time>");
String weatherdiscriptionStr = tryExtractString(str, "<WeatherDescription>", "</WeatherDescription>");
String tempStr = tryExtractString(str, "<Temp>", "</Temp>");
String humidityStr = tryExtractString(str, "<Humidity>", "</Humidity>");
String windStr = tryExtractString(str, "<Wind>", "</Wind>");
String heatindexStr = tryExtractString(str, "<HeatIndex>", "</HeatIndex>");
String windchillStr = tryExtractString(str, "<WindChill>", "</WindChill>");
String feelslikeStr = tryExtractString(str, "<FeelsLike>", "</FeelsLike>");
String visibilityStr = tryExtractString(str, "<Visibility>", "</Visibility>");
String sunradiationStr = tryExtractString(str, "<SunRadiation>", "</SunRadiation>");
String uvindexStr = tryExtractString(str, "<UvIndex>", "</UvIndex>");
String totalrainStr = tryExtractString(str, "<TotalRainInches>", "</TotalRainInches>");
String iconStr = tryExtractString(str, "<Icon>", "</Icon>");
if (locationStr != NULL) {
Serial.println("Weather for: " + locationStr);
}
if (timeStr != NULL) {
Serial.println("Timestamp: " + timeStr);
}
if (weatherdiscriptionStr != NULL) {
Serial.println("Weather Conditon: " + weatherdiscriptionStr);
}
if (tempStr != NULL) {
Serial.println("The Temp is: " + tempStr + String(" F"));
}
if (feelslikeStr != NULL) {
Serial.println("The Real Feel Temp is: " + feelslikeStr + String(" F"));
}
if (humidityStr != NULL) {
Serial.println("The Humidity is: " + humidityStr );
}
if (windStr != NULL) {
Serial.println("Wind Conditions: " + windStr);
}
if (heatindexStr != NULL) {
Serial.println("The Heat Index is: " + heatindexStr + String(" F"));
}
if (windchillStr != NULL) {
Serial.println("The Wind Chill Index is: " + windchillStr + String(" F"));
}
if (visibilityStr != NULL) {
Serial.println("The Visibility is: " + visibilityStr + String(" Miles"));
}
if (sunradiationStr != NULL) {
Serial.println("The Solar Radiation is: " + sunradiationStr + String(" W/m2"));
}
if (uvindexStr != NULL) {
Serial.println("The UV Index is: " + uvindexStr );
}
if (totalrainStr != NULL) {
Serial.println("Total Daily Rain Fall: " + totalrainStr + String(" Inches"));
}
if (iconStr != NULL) {
Serial.println("Icon for current weather: " + iconStr);
//Serial.println("");
}
}
//////////////////////////////////////////////////////////////////////////////
void Get3DayWeatherForecast(const char* event, const char* data)
{
static char weatherData[1548] = "";
if(strstr(event, "_Get3DayWeatherForecast/0"))
{
strcpy(weatherData, ""); // empty the string
strcpy(weatherData, data);
//Serial.println(weatherData);
}
else if(strstr(event, "_Get3DayWeatherForecast/1"))
{
strcat(weatherData, data); //concatenate the first return with the second
//Serial.println(weatherData);
String WeatherIcon0 = strtok(weatherData, "~");
String Period0 = strtok(NULL, "~");
String Period0Forcast = strtok(NULL, "~");
int POP0 = atoi(strtok(NULL, "~"));
String WeatherIcon1 = strtok(NULL, "~");
String Period1 = strtok(NULL, "~");
String Period1Forcast = strtok(NULL, "~");
int POP1 = atoi(strtok(NULL, "~"));
String WeatherIcon2 = strtok(NULL, "~");
String Period2 = strtok(NULL, "~");
String Period2Forcast = strtok(NULL, "~");
int POP2 = atoi(strtok(NULL, "~"));
String WeatherIcon3 = strtok(NULL, "~");
String Period3 = strtok(NULL, "~");
String Period3Forcast = strtok(NULL, "~");
int POP3 = atoi(strtok(NULL, "~"));
String WeatherIcon4 = strtok(NULL, "~");
String Period4 = strtok(NULL, "~");
String Period4Forcast = strtok(NULL, "~");
int POP4 = atoi(strtok(NULL, "~"));
String WeatherIcon5 = strtok(NULL, "~");
String Period5 = strtok(NULL, "~");
String Period5Forcast = strtok(NULL, "~");
int POP5 = atoi(strtok(NULL, "~"));
String WeatherIcon6 = strtok(NULL, "~");
String Period6 = strtok(NULL, "~");
String Period6Forcast = strtok(NULL, "~");
int POP6 = atoi(strtok(NULL, "~"));
String WeatherIcon7 = strtok(NULL, "~");
String Period7 = strtok(NULL, "~");
String Period7Forcast = strtok(NULL, "~");
int POP7 = atoi(strtok(NULL, "~"));
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon0);
Serial.print("Period: ");
Serial.println(Period0);
Serial.print("Forcast: ");
Serial.println(Period0Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP0);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon1);
Serial.print("Period: ");
Serial.println(Period1);
Serial.print("Forcast: ");
Serial.println(Period1Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP1);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon2);
Serial.print("Period: ");
Serial.println(Period2);
Serial.print("Forcast: ");
Serial.println(Period2Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP2);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon3);
Serial.print("Period: ");
Serial.println(Period3);
Serial.print("Forcast: ");
Serial.println(Period3Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP3);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon4);
Serial.print("Period: ");
Serial.println(Period4);
Serial.print("Forcast: ");
Serial.println(Period4Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP4);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon5);
Serial.print("Period: ");
Serial.println(Period5);
Serial.print("Forcast: ");
Serial.println(Period5Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP5);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon6);
Serial.print("Period: ");
Serial.println(Period6);
Serial.print("Forcast: ");
Serial.println(Period6Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP6);
Serial.println("");
//Serial.print("Weather Icon: ");
//Serial.println(WeatherIcon7);
Serial.print("Period: ");
Serial.println(Period7);
Serial.print("Forcast: ");
Serial.println(Period7Forcast);
//Serial.print("Probability of Precipitation: ");
//Serial.println(POP7);
Serial.println("-------------------");
}
else if (strstr(event, "_Get3DayWeatherForecast/2"))
{
strcat(weatherData, data); //concatenate the first return with the second
//Serial.print("Complete String:\t");
//Serial.println(weatherData);
}
}
///////////////////////////////////////////////////////////////////////
// This function will get called when weather data comes in
void GetHoursOfDayLight(const char *name, const char *data) {
// Important note! -- Right now the response comes in 512 byte chunks.
// This code assumes we're getting the response in large chunks, and this
// assumption breaks down if a line happens to be split across response chunks.
String str = String(data);
String sunrisehourStr = tryExtractString(str, "<SunriseHour>", "</SunriseHour>");
String sunriseminuteStr = tryExtractString(str, "<SunriseMin>", "</SunriseMin>");
String sunsethourStr = tryExtractString(str, "SunsetHour>", "</SunsetHour>");
String sunsetminuteStr = tryExtractString(str, "<SunsetMinute>", "</SunsetMinute>");
uint hourofsunrise = sunrisehourStr.toInt();
uint minofsunrise = sunriseminuteStr.toInt();
uint hourofsunset = sunsethourStr.toInt();
uint sunsetmin = sunsetminuteStr.toInt();
int adjsunsethour = (hourofsunset-12); //Eliminate the negative sign in the hour number.
int morningsunhours = 12 - hourofsunrise;
//int afternoonsunhours = adjsunsethour;
int sunhoursconvertedtomins = (morningsunhours + adjsunsethour) * 60;
int totaldaysunmins = (60 - minofsunrise + sunsetmin);
float totaldaylighthours = (sunhoursconvertedtomins + totaldaysunmins) / 60.0;
if (sunrisehourStr != NULL) {
Serial.println("Sunrise at: " + sunrisehourStr + String(":") + sunriseminuteStr + String(" AM") + " / Sunset at: " + adjsunsethour + String(":") + sunsetminuteStr + String(" PM"));
//Serial.println("Hours of Daylight = " + hoursofdaylight + String(" Hours") minsofdaylight + String(" Mins"));
Serial.print("Total Hours of Sunlight Today = ");
Serial.println(totaldaylighthours);
}
Serial.println("-------------------");
}
/////////////////////////////////////////////////////////////////////////
// Returns any text found between a start and end string inside 'str'
// example: startfooend -> returns foo
String tryExtractString(String str, const char* start, const char* end) {
if (str == NULL) {
return "";
}
int idx = str.indexOf(start);
if (idx < 0) {
return "";
}
int endIdx = str.indexOf(end);
if (endIdx < 0) {
return "";
}
return str.substring(idx + strlen(start), endIdx);
}