structure7 / hvacMonitor

Monitoring supply/return air temps, run status, and on/off durations.
MIT License
10 stars 6 forks source link

Wunderground API to Blynk outdoor temp #27

Closed structure7 closed 8 years ago

structure7 commented 8 years ago

All I know is that this works. Credit bbx10node@gmail.com from here. Now just to see if it plays nice with Blynk.


#include <ESP8266WiFi.h>
#ifndef min
#define min(x,y) (((x)<(y))?(x):(y))
#endif
#ifndef max
#define max(x,y) (((x)>(y))?(x):(y))
#endif
#include <ArduinoJson.h>

static const char SSID[]     = "ssid";
static const char PASSWORD[] = "pw";

// Use your own API key by signing up for a free developer account.
// http://www.wunderground.com/weather/api/
//#define WU_API_KEY "myAPIkey"

// Specify your favorite location one of these ways.
//#define WU_LOCATION "pws:KAZTEMPE29"

// US ZIP code
//#define WU_LOCATION "90210"

// Country and city
#define WU_LOCATION "Australia/Sydney"

// 5 minutes between update checks. The free developer account has a limit
// on the  number of calls so don't go wild.
#define DELAY_NORMAL    (5*60*1000)
// 20 minute delay between updates after an error
#define DELAY_ERROR     (20*60*1000)

/***********************************************************
 *
 * Sample requests and responses
 *
http://api.wunderground.com/api/c596b7af83bae426/conditions/q/CA/San_Francisco.json

{
  "response": {
    ...don't care
    }
  },
  "current_observation": {
    "image": {
    ...don't care
    },
    "display_location": {
    ...don't care
    },
    "observation_location": {
    ...don't care
    },
    "estimated": {},
    "station_id": "KCASANFR58",
    "observation_time": "Last Updated on June 27, 5:27 PM PDT",
    "observation_time_rfc822": "Wed, 27 Jun 2012 17:27:13 -0700",
    "observation_epoch": "1340843233",
    "local_time_rfc822": "Wed, 27 Jun 2012 17:27:14 -0700",
    "local_epoch": "1340843234",
    "local_tz_short": "PDT",
    "local_tz_long": "America/Los_Angeles",
    "local_tz_offset": "-0700",
    "weather": "Partly Cloudy",
    "temperature_string": "66.3 F (19.1 C)",
    "temp_f": 66.3,
    "temp_c": 19.1,
    "relative_humidity": "65%",
    "wind_string": "From the NNW at 22.0 MPH Gusting to 28.0 MPH",
    "wind_dir": "NNW",
    "wind_degrees": 346,
    "wind_mph": 22.0,
    "wind_gust_mph": "28.0",
    "wind_kph": 35.4,
    "wind_gust_kph": "45.1",
    "pressure_mb": "1013",
    "pressure_in": "29.93",
    "pressure_trend": "+",
    "dewpoint_string": "54 F (12 C)",
    "dewpoint_f": 54,
    "dewpoint_c": 12,
    "heat_index_string": "NA",
    "heat_index_f": "NA",
    "heat_index_c": "NA",
    "windchill_string": "NA",
    "windchill_f": "NA",
    "windchill_c": "NA",
    "feelslike_string": "66.3 F (19.1 C)",
    "feelslike_f": "66.3",
    "feelslike_c": "19.1",
    "visibility_mi": "10.0",
    "visibility_km": "16.1",
    "solarradiation": "",
    "UV": "5",
    "precip_1hr_string": "0.00 in ( 0 mm)",
    "precip_1hr_in": "0.00",
    "precip_1hr_metric": " 0",
    "precip_today_string": "0.00 in (0 mm)",
    "precip_today_in": "0.00",
    "precip_today_metric": "0",
    "icon": "partlycloudy",
    "icon_url": "http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
    "forecast_url": "http://www.wunderground.com/US/CA/San_Francisco.html",
    "history_url": "http://www.wunderground.com/history/airport/KCASANFR58/2012/6/27/DailyHistory.html",
    "ob_url": "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.773285,-122.417725"
    }
}

TODO alerts
http://api.wunderground.com/api/c596b7af83bae426/alerts/q/IA/Des_Moines.json

{
  "response": {
  "version": "0.1",
  "termsofService": "http://www.wunderground.com/weather/api/d/terms.html",
  "features": {
  "alerts": 1
  }
  },
  "alerts": [{
  "type": "HEA",
  "description": "Heat Advisory",
  "date": "11:14 am CDT on July 3, 2012",
  "date_epoch": "1341332040",
  "expires": "7:00 AM CDT on July 07, 2012",
  "expires_epoch": "1341662400",
  "message": "\u000A...Heat advisory remains in effect until 7 am CDT Saturday...\u000A\u000A* temperature...heat indices of 100 to 105 are expected each \u000A afternoon...as Max temperatures climb into the mid to upper \u000A 90s...combined with dewpoints in the mid 60s to around 70. \u000A Heat indices will remain in the 75 to 80 degree range at \u000A night. \u000A\u000A* Impacts...the hot and humid weather will lead to an increased \u000A risk of heat related stress and illnesses. \u000A\u000APrecautionary/preparedness actions...\u000A\u000AA heat advisory means that a period of hot temperatures is\u000Aexpected. The combination of hot temperatures and high humidity\u000Awill combine to create a situation in which heat illnesses are\u000Apossible. Drink plenty of fluids...stay in an air-conditioned\u000Aroom...stay out of the sun...and check up on relatives...pets...\u000Aneighbors...and livestock.\u000A\u000ATake extra precautions if you work or spend time outside. Know\u000Athe signs and symptoms of heat exhaustion and heat stroke. Anyone\u000Aovercome by heat should be moved to a cool and shaded location.\u000AHeat stroke is an emergency...call 9 1 1.\u000A\u000A\u000A\u000AMjb\u000A\u000A\u000A",
  "phenomena": "HT",
  "significance": "Y",
  "ZONES": [
  {
  "state":"UT",
  "ZONE":"001"
  }
  ],
  "StormBased": {
  "vertices":[
  {
  "lat":"38.87",
  "lon":"-87.13"
  }
  ,
  {
  "lat":"38.89",
  "lon":"-87.13"
  }
  ,
  {
  "lat":"38.91",
  "lon":"-87.11"
  }
  ,
  {
  "lat":"38.98",
  "lon":"-86.93"
  }
  ,
  {
  "lat":"38.87",
  "lon":"-86.69"
  }
  ,
  {
  "lat":"38.75",
  "lon":"-86.3"
  }
  ,
  {
  "lat":"38.84",
  "lon":"-87.16"
  }
  ],
  "Vertex_count":7,
  "stormInfo": {
  "time_epoch": 1363464360,
  "Motion_deg": 243,
  "Motion_spd": 18,
  "position_lat":38.90,
  "position_lon":-86.96
  }
  }
  }]
}
*************************************************************/

#define WUNDERGROUND "api.wunderground.com"

// HTTP request
const char WUNDERGROUND_REQ[] =
    "GET /api/a76b5f288962f088/conditions/q/pws:KAZTEMPE29.json HTTP/1.1\r\n"
    "User-Agent: ESP8266/0.1\r\n"
    "Accept: */*\r\n"
    "Host: " WUNDERGROUND "\r\n"
    "Connection: close\r\n"
    "\r\n";

void setup()
{
  Serial.begin(115200);

  // We start by connecting to a WiFi network
  Serial.println();
  Serial.println();
  Serial.print(F("Connecting to "));
  Serial.println(SSID);

  WiFi.begin(SSID, PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(F("."));
  }

  Serial.println();
  Serial.println(F("WiFi connected"));
  Serial.println(F("IP address: "));
  Serial.println(WiFi.localIP());
}

static char respBuf[4096];

bool showWeather(char *json);

void loop()
{
  // TODO check for disconnect from AP

  // Open socket to WU server port 80
  Serial.print(F("Connecting to "));
  Serial.println(WUNDERGROUND);

  // Use WiFiClient class to create TCP connections
  WiFiClient httpclient;
  const int httpPort = 80;
  if (!httpclient.connect(WUNDERGROUND, httpPort)) {
    Serial.println(F("connection failed"));
    delay(DELAY_ERROR);
    return;
  }

  // This will send the http request to the server
  Serial.print(WUNDERGROUND_REQ);
  httpclient.print(WUNDERGROUND_REQ);
  httpclient.flush();

  // Collect http response headers and content from Weather Underground
  // HTTP headers are discarded.
  // The content is formatted in JSON and is left in respBuf.
  int respLen = 0;
  bool skip_headers = true;
  while (httpclient.connected() || httpclient.available()) {
    if (skip_headers) {
      String aLine = httpclient.readStringUntil('\n');
      //Serial.println(aLine);
      // Blank line denotes end of headers
      if (aLine.length() <= 1) {
        skip_headers = false;
      }
    }
    else {
      int bytesIn;
      bytesIn = httpclient.read((uint8_t *)&respBuf[respLen], sizeof(respBuf) - respLen);
      Serial.print(F("bytesIn ")); Serial.println(bytesIn);
      if (bytesIn > 0) {
        respLen += bytesIn;
        if (respLen > sizeof(respBuf)) respLen = sizeof(respBuf);
      }
      else if (bytesIn < 0) {
        Serial.print(F("read error "));
        Serial.println(bytesIn);
      }
    }
    delay(1);
  }
  httpclient.stop();

  if (respLen >= sizeof(respBuf)) {
    Serial.print(F("respBuf overflow "));
    Serial.println(respLen);
    delay(DELAY_ERROR);
    return;
  }
  // Terminate the C string
  respBuf[respLen++] = '\0';
  Serial.print(F("respLen "));
  Serial.println(respLen);
  //Serial.println(respBuf);

  if (showWeather(respBuf)) {
    delay(DELAY_NORMAL);
  }
  else {
    delay(DELAY_ERROR);
  }
}

bool showWeather(char *json)
{
  StaticJsonBuffer<3*1024> jsonBuffer;

  // Skip characters until first '{' found
  // Ignore chunked length, if present
  char *jsonstart = strchr(json, '{');
  //Serial.print(F("jsonstart ")); Serial.println(jsonstart);
  if (jsonstart == NULL) {
    Serial.println(F("JSON data missing"));
    return false;
  }
  json = jsonstart;

  // Parse JSON
  JsonObject& root = jsonBuffer.parseObject(json);
  if (!root.success()) {
    Serial.println(F("jsonBuffer.parseObject() failed"));
    return false;
  }

  // Extract weather info from parsed JSON
  JsonObject& current = root["current_observation"];
  const float temp_f = current["temp_f"];
  Serial.print(temp_f, 1); Serial.print(F(" °F "));
  /*
  const float temp_c = current["temp_c"];
  Serial.print(temp_c, 1); Serial.print(F(" C, "));
  const char *humi = current[F("relative_humidity")];
  Serial.print(humi);   Serial.println(F(" RH"));
  const char *weather = current["weather"];
  Serial.println(weather);
  const char *pressure_mb = current["pressure_mb"];
  Serial.println(pressure_mb);
  const char *observation_time = current["observation_time_rfc822"];
  Serial.println(observation_time);
  */
  //Date/time string looks like this
  //Wed, 27 Jun 2012 17:27:14 -0700
  //012345678901234567890
  //          1         2
  // LCD has 14 characters per line so show date on one line and time on the
  // next. And shorten the date so it fits.
/*
  char date[14+1];
  const char *time;
  //Wed, 27 Jun 12
  memcpy(date, observation_time, 12);
  memcpy(&date[12], &observation_time[14], 2);
  date[14] = '\0';
  //17:27:14 -0700
  time = &observation_time[17];
  */

  return true;
}
structure7 commented 8 years ago

Brought it in to the code. Works!