├── README.md ├── examples └── openWeatherMap │ └── openWeatherMap.ino ├── OpenWeatherMap.h └── OpenWeatherMap.cpp /README.md: -------------------------------------------------------------------------------- 1 | # openweathermap.org-ESP8266 2 | ESP8266 parsing library for openweathermap.org site 3 | 4 | UPDATE HISTORY. 5 | 11/02/2019: 6 | fix issue "current weather conditions appears empty". 7 | 05/15/2019: 8 | minor bug fix in void OWMrequest::doUpdate() method 9 | -------------------------------------------------------------------------------- /examples/openWeatherMap/openWeatherMap.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const char *ow_key = "Your Open Weather Map key here"; 10 | const char *nodename = "esp8266-weather"; 11 | const char *wifi_ssid = "ssid"; 12 | const char *wifi_passwd = "password"; 13 | 14 | 15 | //============================================================================================================== 16 | 17 | typedef enum wifi_s { 18 | W_AP = 0, W_READY, W_TRY 19 | } WifiStat; 20 | 21 | OWMconditions owCC; 22 | OWMfiveForecast owF5; 23 | OWMsixteenForecast owF16; 24 | WifiStat WF_status; 25 | 26 | void connectWiFiInit(void) { 27 | WiFi.hostname(nodename); 28 | String ssid = wifi_ssid; 29 | String passwd = wifi_passwd; 30 | WiFi.begin(ssid.c_str(), passwd.c_str()); 31 | } 32 | 33 | String dateTime(String timestamp) { 34 | time_t ts = timestamp.toInt(); 35 | char buff[30]; 36 | sprintf(buff, "%2d:%02d %02d-%02d-%4d", hour(ts), minute(ts), day(ts), month(ts), year(ts)); 37 | return String(buff); 38 | } 39 | 40 | void setup() { 41 | Serial.begin(115200); 42 | Serial.println("\n\n\n\n"); 43 | 44 | connectWiFiInit(); 45 | WF_status = W_TRY; 46 | } 47 | 48 | void currentConditions(void) { 49 | OWM_conditions *ow_cond = new OWM_conditions; 50 | owCC.updateConditions(ow_cond, ow_key, "ru", "Moscow", "metric"); 51 | Serial.print("Latitude & Longtitude: "); 52 | Serial.print("<" + ow_cond->longtitude + " " + ow_cond->latitude + "> @" + dateTime(ow_cond->dt) + ": "); 53 | Serial.println("icon: " + ow_cond->icon + ", " + " temp.: " + ow_cond->temp + ", press.: " + ow_cond->pressure); 54 | delete ow_cond; 55 | } 56 | 57 | void fiveDayFcast(void) { 58 | OWM_fiveForecast *ow_fcast5 = new OWM_fiveForecast[40]; 59 | byte entries = owF5.updateForecast(ow_fcast5, 40, ow_key, "ru", "Moscow", "metric"); 60 | Serial.print("Entries: "); Serial.println(entries+1); 61 | for (byte i = 0; i <= entries; ++i) { 62 | Serial.print(dateTime(ow_fcast5[i].dt) + ": icon: "); 63 | Serial.print(ow_fcast5[i].icon + ", temp.: [" + ow_fcast5[i].t_min + ", " + ow_fcast5[i].t_max + "], press.: " + ow_fcast5[i].pressure); 64 | Serial.println(", descr.: " + ow_fcast5[i].description + ":: " + ow_fcast5[i].cond + " " + ow_fcast5[i].cond_value); 65 | } 66 | delete[] ow_fcast5; 67 | } 68 | 69 | void sixteedDayFcast(void) { 70 | OWM_sixteenLocation *location = new OWM_sixteenLocation; 71 | OWM_sixteenForecast *ow_fcast16 = new OWM_sixteenForecast[7]; 72 | byte entries = owF16.updateForecast(location, ow_fcast16, 7, ow_key, "ru", "Moscow", "metric"); 73 | Serial.print("Entries: "); Serial.println(entries+1); 74 | Serial.print(location->city_name + ", " + location->country +"(" + location->city_id); 75 | Serial.println(") <" + location->longtitude + ", " + location->latitude + "> :"); 76 | for (byte i = 0; i <= entries; ++i) { 77 | Serial.print(dateTime(ow_fcast16[i].dt) + ": icon: "); 78 | Serial.print(ow_fcast16[i].icon + ", temp.: "); 79 | Serial.print("{" + ow_fcast16[i].t_min + ", " + ow_fcast16[i].t_max + "}, temp. change: "); 80 | Serial.print(ow_fcast16[i].t_night + " -> " + ow_fcast16[i].t_morning + " -> " + ow_fcast16[i].t_day + " -> " + ow_fcast16[i].t_evening); 81 | Serial.println("; pressure: " + ow_fcast16[i].pressure + ", descr.:" + ow_fcast16[i].description); 82 | } 83 | delete location; 84 | delete[] ow_fcast16; 85 | } 86 | 87 | void loop() { 88 | if (WF_status == W_TRY) { 89 | if (WiFi.status() == WL_CONNECTED) { 90 | MDNS.begin(nodename); 91 | WF_status = W_READY; 92 | Serial.println("Current Conditions: "); 93 | currentConditions(); 94 | Serial.println("Five days forecast: "); 95 | fiveDayFcast(); 96 | Serial.println("16 days forecast: "); 97 | sixteedDayFcast(); 98 | } 99 | } 100 | delay(1000); 101 | } 102 | 103 | -------------------------------------------------------------------------------- /OpenWeatherMap.h: -------------------------------------------------------------------------------- 1 | #ifndef OpenWeather_h 2 | #define OpenWeather_h 3 | 4 | #include 5 | #include 6 | 7 | //------------------------------------------ Universal weather request parcer for opewWeatherMap site --------- 8 | #define OWM_max_layers 4 9 | class OWMrequest: public JsonListener { 10 | public: 11 | void init(void); 12 | OWMrequest() : JsonListener() { } 13 | virtual void key(String key) { currentKey = String(key); } 14 | virtual void endObject(); 15 | virtual void startObject(); 16 | virtual void whitespace(char c) { } 17 | virtual void startDocument() { } 18 | virtual void endArray() { } 19 | virtual void endDocument() { } 20 | virtual void startArray() { } 21 | protected: 22 | void doUpdate(String url, uint8_t maxForecasts = 0); 23 | String currentKey; 24 | String currentParent; 25 | String p_key[OWM_max_layers]; 26 | }; 27 | 28 | //------------------------------------------ Current weather conditions from openweatrhermap.org -------------- 29 | typedef struct sOWM_conditions { 30 | String longtitude; 31 | String latitude; 32 | String id; 33 | String main; 34 | String description; 35 | String icon; 36 | String temp; 37 | String pressure; 38 | String humidity; 39 | String t_min; 40 | String t_max; 41 | String visibility; 42 | String w_speed; 43 | String w_deg; 44 | String cond; // conditions: cloud, rain, snow 45 | String cond_value; 46 | String dt; 47 | String sunrise; 48 | String sunset; 49 | } OWM_conditions; 50 | 51 | class OWMconditions : public OWMrequest { 52 | public: 53 | OWMconditions() { currentParent = ""; } 54 | void init(void) { currentParent = ""; } 55 | void updateConditions(OWM_conditions *conditions, String apiKey, 56 | String country, String city, String units = "", String language = ""); 57 | virtual void value(String value); 58 | 59 | private: 60 | OWM_conditions *conditions; 61 | }; 62 | 63 | 64 | //------------------------------------------ Five day forecast from openweatrhermap.org ----------------------- 65 | typedef struct sOWM_fiveForecast { 66 | String dt; 67 | String temp; 68 | String t_min; 69 | String t_max; 70 | String pressure; 71 | String sea_pressure; 72 | String humidity; 73 | String id; 74 | String main; 75 | String description; 76 | String icon; 77 | String w_speed; 78 | String w_deg; 79 | String cond; // conditions: cloud, rain, snow 80 | String cond_value; 81 | } OWM_fiveForecast; 82 | 83 | class OWMfiveForecast : public OWMrequest { 84 | public: 85 | OWMfiveForecast() { } 86 | uint8_t updateForecast(OWM_fiveForecast *forecasts, uint8_t maxForecasts, String apiKey, 87 | String country, String city, String units = "", String language = ""); 88 | virtual void value(String value); 89 | 90 | private: 91 | uint8_t index; 92 | uint32_t timestamp; 93 | uint8_t max_forecasts; 94 | OWM_fiveForecast *forecasts; 95 | }; 96 | 97 | //------------------------------------------ Sexteen days weather forecast from openweathermap.org ----------- 98 | typedef struct sOWM_sixteenLocation { 99 | String city_id; 100 | String city_name; 101 | String longtitude; 102 | String latitude; 103 | String country; 104 | String population; 105 | String cod; 106 | String message; 107 | } OWM_sixteenLocation; 108 | 109 | typedef struct sOWM_sixteenForecast { 110 | String dt; 111 | String t_min; 112 | String t_max; 113 | String t_night; 114 | String t_morning; 115 | String t_day; 116 | String t_evening; 117 | String pressure; 118 | String humidity; 119 | String id; 120 | String main; 121 | String description; 122 | String icon; 123 | String w_speed; 124 | String w_deg; 125 | String clouds; 126 | } OWM_sixteenForecast; 127 | 128 | class OWMsixteenForecast : public OWMrequest { 129 | public: 130 | OWMsixteenForecast() { } 131 | uint8_t updateForecast(OWM_sixteenLocation *location, OWM_sixteenForecast *forecasts, uint8_t maxForecasts, String apiKey, 132 | String country, String city, String units = "", String language = ""); 133 | virtual void value(String value); 134 | 135 | private: 136 | uint8_t index; 137 | uint32_t timestamp; 138 | uint8_t max_forecasts; 139 | OWM_sixteenLocation *location; 140 | OWM_sixteenForecast *forecasts; 141 | }; 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /OpenWeatherMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "OpenWeatherMap.h" 5 | 6 | //------------------------------------------ Universal weather request parcer for opewWeatherMap site --------- 7 | void OWMrequest::doUpdate(String url, byte maxForecasts) { 8 | JsonStreamingParser parser; 9 | parser.setListener(this); 10 | 11 | HTTPClient http; 12 | 13 | http.begin(url); 14 | bool isBody = false; 15 | char c; 16 | int httpCode = http.GET(); 17 | if(httpCode > 0) { 18 | WiFiClient * client = http.getStreamPtr(); 19 | while(client->connected()) { 20 | while(true) { 21 | if (client->available() > 0) { 22 | c = client->read(); 23 | if (c == '{' || c == '[') { 24 | isBody = true; 25 | } 26 | if (isBody) { 27 | parser.parse(c); 28 | } 29 | } else { 30 | uint32_t now_ms = millis(); 31 | while (millis() - now_ms < 200) { 32 | yield(); 33 | if (client->available() > 0) 34 | break; 35 | } 36 | if (client->available() <= 0) 37 | break; 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | void OWMrequest::init(void) { 45 | for (byte i = 0; i < OWM_max_layers; ++i) 46 | p_key[i] = ""; 47 | currentParent = currentKey = ""; 48 | } 49 | 50 | void OWMrequest::startObject() { 51 | for (byte i = OWM_max_layers-1; i > 0; --i) { 52 | p_key[i] = p_key[i-1]; 53 | } 54 | p_key[0] = currentParent; 55 | currentParent = currentKey; 56 | } 57 | 58 | void OWMrequest::endObject() { 59 | currentParent = p_key[0]; 60 | for (byte i = 0; i < OWM_max_layers-1; ++i) { 61 | p_key[i] = p_key[i+1]; 62 | } 63 | currentKey = ""; 64 | } 65 | 66 | /*------------------------------------------ Current weather conditions from openweatrhermap.org -------------- 67 | /* Example: 68 | /* {"coord":{"lon":-0.13,"lat":51.51}, 69 | "weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}], 70 | "base":"stations", 71 | "main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15}, 72 | "visibility":10000, 73 | "wind":{"speed":4.1,"deg":80}, 74 | "clouds":{"all":90}, 75 | "dt":1485789600, 76 | "sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875}, 77 | "id":2643743,"name":"London","cod":200} 78 | */ 79 | void OWMconditions::updateConditions(OWM_conditions *conditions, String apiKey, String country, String city, String units, String language) { 80 | this->conditions = conditions; 81 | OWMrequest::init(); 82 | 83 | String url = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + country; 84 | if (language != "") url += "&lang=" + language; 85 | if (units != "") url += "&units=" + units; 86 | url += + "&appid=" + apiKey; 87 | doUpdate(url); 88 | this->conditions = nullptr; 89 | } 90 | 91 | void OWMconditions::value(String value) { 92 | if (currentParent == "coord") { 93 | if (currentKey == "lon") { 94 | conditions->longtitude = value; 95 | } else if (currentKey == "lat") { 96 | conditions->latitude = value; 97 | } 98 | } else if (currentParent == "weather") { 99 | if (currentKey == "id") { 100 | conditions->id = value; 101 | } else if (currentKey == "main") { 102 | conditions->main = value; 103 | } else if (currentKey == "description") { 104 | conditions->description = value; 105 | } else if (currentKey == "icon") { 106 | conditions->icon = value; 107 | } 108 | } else if (currentParent == "main") { 109 | if (currentKey == "temp") { 110 | conditions->temp = value; 111 | } else if (currentKey == "pressure") { 112 | conditions->pressure = value; 113 | } else if (currentKey == "humidity") { 114 | conditions->humidity = value; 115 | } else if (currentKey == "temp_min") { 116 | conditions->t_min = value; 117 | } else if (currentKey == "temp_max") { 118 | conditions->t_max = value; 119 | } 120 | } else if (currentParent == "wind") { 121 | if (currentKey == "speed") { 122 | conditions->w_speed = value; 123 | } else if (currentKey == "deg") { 124 | conditions->w_deg = value; 125 | } 126 | } else if (currentParent == "clouds") { 127 | if (currentKey == "all") { 128 | conditions->cond = currentParent; 129 | conditions->cond_value = value; 130 | } 131 | } else if (currentParent == "rain") { 132 | if (currentKey == "3h") { 133 | conditions->cond = currentParent; 134 | conditions->cond_value = value; 135 | } 136 | } else if (currentParent == "snow") { 137 | if (currentKey == "3h") { 138 | conditions->cond = currentParent; 139 | conditions->cond_value = value; 140 | } 141 | } else if (currentParent == "sys") { 142 | if (currentKey == "sunrise") { 143 | conditions->sunrise = value; 144 | } else if (currentKey == "sunset") { 145 | conditions->sunset = value; 146 | } 147 | } else { 148 | if (currentKey == "visibility") { 149 | conditions->visibility = value; 150 | } else if (currentKey == "dt") { 151 | conditions->dt = value; 152 | } 153 | } 154 | } 155 | 156 | //------------------------------------------ Five day forecast from openweatrhermap.org ----------------------- 157 | byte OWMfiveForecast::updateForecast(OWM_fiveForecast *forecasts, byte maxForecasts, String apiKey, String country, String city, String units, String language) { 158 | this->forecasts = forecasts; 159 | this->max_forecasts = maxForecasts; 160 | OWMrequest::init(); 161 | index = 0; 162 | timestamp = 0; 163 | 164 | String url = "http://api.openweathermap.org/data/2.5/forecast?q=" + city + "," + country; 165 | if (language != "") url += "&lang=" + language; 166 | if (units != "") url += "&units=" + units; 167 | url += + "&appid=" + apiKey; 168 | doUpdate(url, maxForecasts); 169 | 170 | this->forecasts = nullptr; 171 | return index; 172 | } 173 | 174 | void OWMfiveForecast::value(String value) { 175 | if (currentKey == "dt") { 176 | if (timestamp == 0) { 177 | index = 0; 178 | forecasts[index].dt = value; 179 | timestamp = value.toInt(); 180 | } else { 181 | uint32_t t = value.toInt(); 182 | if (t > timestamp) { // new forecast time 183 | if (index < max_forecasts - 1) { 184 | ++index; 185 | timestamp = t; 186 | forecasts[index].dt = value; 187 | } 188 | } 189 | } 190 | } else 191 | if (currentKey == "temp") { 192 | forecasts[index].temp = value; 193 | } else 194 | if (currentKey == "temp_min") { 195 | forecasts[index].t_min = value; 196 | } else 197 | if (currentKey == "temp_max") { 198 | forecasts[index].t_max = value; 199 | } else 200 | if (currentKey == "pressure") { 201 | forecasts[index].pressure = value; 202 | } else 203 | if (currentKey == "sea_level") { 204 | forecasts[index].sea_pressure = value; 205 | } else 206 | if (currentKey == "humidity") { 207 | forecasts[index].humidity = value; 208 | } else 209 | if (currentParent == "weather" && currentKey == "description") { 210 | forecasts[index].description = value; 211 | } else 212 | if (currentParent == "weather" && currentKey == "icon") { 213 | forecasts[index].icon = value; 214 | } else 215 | if (currentParent == "weather" && currentKey == "id") { 216 | forecasts[index].id = value; 217 | } else 218 | if (currentParent == "wind" && currentKey == "speed") { 219 | forecasts[index].w_speed = value; 220 | } else 221 | if (currentParent == "wind" && currentKey == "deg") { 222 | forecasts[index].w_deg = value; 223 | } else 224 | if (currentParent == "clouds" && currentKey == "all") { 225 | forecasts[index].cond = currentParent; 226 | forecasts[index].cond_value = value; 227 | } else 228 | if (currentParent == "rain" && currentKey == "3h") { 229 | forecasts[index].cond = currentParent; 230 | forecasts[index].cond_value = value; 231 | } else 232 | if (currentParent == "snow" && currentKey == "3h") { 233 | forecasts[index].cond = currentParent; 234 | forecasts[index].cond_value = value; 235 | } 236 | } 237 | 238 | //------------------------------------------ Sexteen days weather forecast from openweathermap.org ----------- 239 | byte OWMsixteenForecast::updateForecast(OWM_sixteenLocation *location, OWM_sixteenForecast *forecasts, 240 | byte maxForecasts, String apiKey, String country, String city, String units, String language) { 241 | this->location = location; 242 | this->forecasts = forecasts; 243 | this->max_forecasts = maxForecasts; 244 | OWMrequest::init(); 245 | index = 0; 246 | timestamp = 0; 247 | 248 | String url = "http://api.openweathermap.org/data/2.5/forecast/daily?q=" + city + "," + country; 249 | if (language != "") url += "&lang=" + language; 250 | if (units != "") url += "&units=" + units; 251 | url += + "&appid=" + apiKey; 252 | doUpdate(url, maxForecasts); 253 | this->forecasts = nullptr; 254 | this->location = nullptr; 255 | return index; 256 | } 257 | 258 | void OWMsixteenForecast::value(String value) { 259 | // First fill up the location info 260 | if (location) { 261 | if (currentParent == "city" && currentKey == "id") { 262 | location->city_id = value; 263 | } else 264 | if (currentParent == "city" && currentKey == "name") { 265 | location->city_name = value; 266 | } else 267 | if (currentParent == "coord" && currentKey == "lon") { 268 | location->longtitude = value; 269 | } else 270 | if (currentParent == "coord" && currentKey == "lat") { 271 | location->latitude = value; 272 | } else 273 | if (currentParent == "city" && currentKey == "country") { 274 | location->country = value; 275 | } else 276 | if (currentParent == "city" && currentKey == "population") { 277 | location->population = value; 278 | } else 279 | if (currentKey == "cod") { 280 | location->cod = value; 281 | } else 282 | if (currentKey == "message") { 283 | location->message = value; 284 | } 285 | } 286 | 287 | // Second, fill up the dayly forecast 288 | if (currentKey == "dt") { 289 | if (timestamp == 0) { 290 | index = 0; 291 | forecasts[index].dt = value; 292 | timestamp = value.toInt(); 293 | } else { 294 | uint32_t t = value.toInt(); 295 | if (t > timestamp) { // new forecast time 296 | if (index < max_forecasts - 1) { 297 | ++index; 298 | timestamp = t; 299 | forecasts[index].dt = value; 300 | } 301 | } 302 | } 303 | } else 304 | if (currentParent == "temp" && currentKey == "min") { 305 | forecasts[index].t_min = value; 306 | } else 307 | if (currentParent == "temp" && currentKey == "max") { 308 | forecasts[index].t_max = value; 309 | } else 310 | if (currentParent == "temp" && currentKey == "night") { 311 | forecasts[index].t_night = value; 312 | } else 313 | if (currentParent == "temp" && currentKey == "morn") { 314 | forecasts[index].t_morning = value; 315 | } else 316 | if (currentParent == "temp" && currentKey == "day") { 317 | forecasts[index].t_day = value; 318 | } else 319 | if (currentParent == "temp" && currentKey == "eve") { 320 | forecasts[index].t_evening = value; 321 | } else 322 | 323 | if (currentKey == "pressure") { 324 | forecasts[index].pressure = value; 325 | } else 326 | if (currentKey == "humidity") { 327 | forecasts[index].humidity = value; 328 | } else 329 | if (currentParent == "weather" && currentKey == "description") { 330 | forecasts[index].description = value; 331 | } else 332 | if (currentParent == "weather" && currentKey == "icon") { 333 | forecasts[index].icon = value; 334 | } else 335 | if (currentParent == "weather" && currentKey == "id") { 336 | forecasts[index].id = value; 337 | } else 338 | if (currentParent == "weather" && currentKey == "description") { 339 | forecasts[index].description = value; 340 | } else 341 | 342 | if (currentKey == "speed") { 343 | forecasts[index].w_speed = value; 344 | } else 345 | if (currentKey == "deg") { 346 | forecasts[index].w_deg = value; 347 | } else 348 | if (currentKey == "clouds") { 349 | forecasts[index].clouds = value; 350 | } 351 | } 352 | 353 | --------------------------------------------------------------------------------