├── .gitignore ├── LICENSE ├── README.md ├── esp-pcd-weather.ino ├── esp-pcd-weather.jpg └── photos ├── 2015-10-04 14.23.16.jpg ├── 2015-10-04 14.24.23.jpg ├── 2015-10-04 14.28.53.jpg └── 2015-10-04 14.42.40.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 by bbx10node@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-pcd-weather 2 | ESP8266 Arduino connected to PCD8544/5110 LCD display showing weather from Weather Underground 3 | 4 | ![Weather on LCD screen](./esp-pcd-weather.jpg) 5 | 6 | ## Hardware Parts ## 7 | 8 | * [Adafruit Huzzah ESP8266](https://www.adafruit.com/products/2471) 9 | * [Adafruit PCD8544/5110 display](https://www.adafruit.com/product/338) 10 | * [Adafruit USB to TTL serial cable](https://www.adafruit.com/products/954) 11 | 12 | ## Connections ## 13 | 14 | USB TTL |Huzzah ESP8266|PCD8544/Nokia 5110 |Description 15 | -----------|-----------|-----------|------------------------------------------------------------- 16 | |GND |GND |Ground 17 | |3V |VCC |3.3V from Huzzah to display 18 | |14 |CLK |Output from ESP SPI clock 19 | |13 |DIN |Output from ESP SPI MOSI to display data input 20 | |12 |D/C |Output from display data/command to ESP 21 | |#5 |CS |Output from ESP to chip select/enable display 22 | |#4 |RST |Output from ESP to reset display 23 | |15 |LED |3.3V to turn backlight on, GND off 24 | GND (blk) |GND | |Ground 25 | 5V (red) |V+ | |5V power from PC or charger 26 | TX (green)|RX | |Serial data from IDE to ESP 27 | RX (white)|TX | |Serial data to ESP from IDE 28 | 29 | ## Dependencies ## 30 | 31 | * [Fork of Adafruit PCD8544 library with changes for ESP8266] 32 | (https://github.com/bbx10/Adafruit-PCD8544-Nokia-5110-LCD-library/tree/esp8266). 33 | Use the esp8266 branch! 34 | * Adafruit GFX library. Use the Arduino IDE Library Manager to get the latest version 35 | * Arduino JSON library. Use the Arduino IDE Library Manager to get the latest version 36 | * ESP8266 board package. Use the stable version. https://github.com/esp8266/arduino#stable-version- 37 | 38 | ## Recently tested versions ## 39 | 40 | The libraries and board package should be installed using the IDE library and 41 | board managers. 42 | 43 | * IDE 1.6.8 44 | * Adafruit GFX library 1.1.5 45 | * ArduinoJSON library 5.1.1 46 | * ESP8266 board package 2.1.0 47 | * Fork of Adafruit PCD8544 library 1.0.0 with ESP8266 changes. Download the ZIP file from the 48 | following link then use the IDE `Sketch | Include Library | Add .ZIP Library` option to install it. 49 | 50 | https://github.com/bbx10/Adafruit-PCD8544-Nokia-5110-LCD-library/tree/esp8266 51 | -------------------------------------------------------------------------------- /esp-pcd-weather.ino: -------------------------------------------------------------------------------- 1 | /**************************** 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 by bbx10node@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ******************************/ 24 | 25 | #include 26 | #ifndef min 27 | #define min(x,y) (((x)<(y))?(x):(y)) 28 | #endif 29 | #ifndef max 30 | #define max(x,y) (((x)>(y))?(x):(y)) 31 | #endif 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | static const char SSID[] = "***********"; 38 | static const char PASSWORD[] = "************"; 39 | 40 | // Use your own API key by signing up for a free developer account. 41 | // http://www.wunderground.com/weather/api/ 42 | #define WU_API_KEY "****************" 43 | 44 | // Specify your favorite location one of these ways. 45 | //#define WU_LOCATION "CA/San_Francisco" 46 | 47 | // US ZIP code 48 | //#define WU_LOCATION "90210" 49 | 50 | // Country and city 51 | #define WU_LOCATION "Australia/Sydney" 52 | 53 | /****************************************************************** 54 | ESP8266 with PCD8544 display 55 | 56 | == Parts == 57 | 58 | * Adafruit Huzzah ESP8266 https://www.adafruit.com/products/2471 59 | 60 | * Adafruit PCD8544/5110 display https://www.adafruit.com/product/338 61 | 62 | * Adafruit USB to TTL serial cable https://www.adafruit.com/products/954 63 | 64 | == Connection == 65 | 66 | USB TTL Huzzah Nokia 5110 Description 67 | ESP8266 PCD8544 68 | 69 | GND GND Ground 70 | 3V VCC 3.3V from Huzzah to display 71 | 14 CLK Output from ESP SPI clock 72 | 13 DIN Output from ESP SPI MOSI to display data input 73 | 12 D/C Output from display data/command to ESP 74 | #5 CS Output from ESP to chip select/enable display 75 | #4 RST Output from ESP to reset display 76 | 15 LED 3.3V to turn backlight on, GND off 77 | 78 | GND (blk) GND Ground 79 | 5V (red) V+ 5V power from PC or charger 80 | TX (green) RX Serial data from IDE to ESP 81 | RX (white) TX Serial data to ESP from IDE 82 | ******************************************************************/ 83 | 84 | // Hardware SPI (faster, but must use certain hardware pins): 85 | // SCK is LCD serial clock (SCLK) - this is pin 14 on Huzzah ESP8266 86 | // MOSI is LCD DIN - this is pin 13 on an Huzzah ESP8266 87 | // pin 12 - Data/Command select (D/C) on an Huzzah ESP8266 88 | // pin 5 - LCD chip select (CS) 89 | // pin 4 - LCD reset (RST) 90 | Adafruit_PCD8544 display = Adafruit_PCD8544(12, 5, 4); 91 | 92 | // 5 minutes between update checks. The free developer account has a limit 93 | // on the number of calls so don't go wild. 94 | #define DELAY_NORMAL (5*60*1000) 95 | // 20 minute delay between updates after an error 96 | #define DELAY_ERROR (20*60*1000) 97 | 98 | /*********************************************************** 99 | * 100 | * Sample requests and responses 101 | * 102 | http://api.wunderground.com/api/c596b7af83bae426/conditions/q/CA/San_Francisco.json 103 | 104 | { 105 | "response": { 106 | ...don't care 107 | } 108 | }, 109 | "current_observation": { 110 | "image": { 111 | ...don't care 112 | }, 113 | "display_location": { 114 | ...don't care 115 | }, 116 | "observation_location": { 117 | ...don't care 118 | }, 119 | "estimated": {}, 120 | "station_id": "KCASANFR58", 121 | "observation_time": "Last Updated on June 27, 5:27 PM PDT", 122 | "observation_time_rfc822": "Wed, 27 Jun 2012 17:27:13 -0700", 123 | "observation_epoch": "1340843233", 124 | "local_time_rfc822": "Wed, 27 Jun 2012 17:27:14 -0700", 125 | "local_epoch": "1340843234", 126 | "local_tz_short": "PDT", 127 | "local_tz_long": "America/Los_Angeles", 128 | "local_tz_offset": "-0700", 129 | "weather": "Partly Cloudy", 130 | "temperature_string": "66.3 F (19.1 C)", 131 | "temp_f": 66.3, 132 | "temp_c": 19.1, 133 | "relative_humidity": "65%", 134 | "wind_string": "From the NNW at 22.0 MPH Gusting to 28.0 MPH", 135 | "wind_dir": "NNW", 136 | "wind_degrees": 346, 137 | "wind_mph": 22.0, 138 | "wind_gust_mph": "28.0", 139 | "wind_kph": 35.4, 140 | "wind_gust_kph": "45.1", 141 | "pressure_mb": "1013", 142 | "pressure_in": "29.93", 143 | "pressure_trend": "+", 144 | "dewpoint_string": "54 F (12 C)", 145 | "dewpoint_f": 54, 146 | "dewpoint_c": 12, 147 | "heat_index_string": "NA", 148 | "heat_index_f": "NA", 149 | "heat_index_c": "NA", 150 | "windchill_string": "NA", 151 | "windchill_f": "NA", 152 | "windchill_c": "NA", 153 | "feelslike_string": "66.3 F (19.1 C)", 154 | "feelslike_f": "66.3", 155 | "feelslike_c": "19.1", 156 | "visibility_mi": "10.0", 157 | "visibility_km": "16.1", 158 | "solarradiation": "", 159 | "UV": "5", 160 | "precip_1hr_string": "0.00 in ( 0 mm)", 161 | "precip_1hr_in": "0.00", 162 | "precip_1hr_metric": " 0", 163 | "precip_today_string": "0.00 in (0 mm)", 164 | "precip_today_in": "0.00", 165 | "precip_today_metric": "0", 166 | "icon": "partlycloudy", 167 | "icon_url": "http://icons-ak.wxug.com/i/c/k/partlycloudy.gif", 168 | "forecast_url": "http://www.wunderground.com/US/CA/San_Francisco.html", 169 | "history_url": "http://www.wunderground.com/history/airport/KCASANFR58/2012/6/27/DailyHistory.html", 170 | "ob_url": "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.773285,-122.417725" 171 | } 172 | } 173 | 174 | TODO alerts 175 | http://api.wunderground.com/api/c596b7af83bae426/alerts/q/IA/Des_Moines.json 176 | 177 | { 178 | "response": { 179 | "version": "0.1", 180 | "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", 181 | "features": { 182 | "alerts": 1 183 | } 184 | }, 185 | "alerts": [{ 186 | "type": "HEA", 187 | "description": "Heat Advisory", 188 | "date": "11:14 am CDT on July 3, 2012", 189 | "date_epoch": "1341332040", 190 | "expires": "7:00 AM CDT on July 07, 2012", 191 | "expires_epoch": "1341662400", 192 | "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", 193 | "phenomena": "HT", 194 | "significance": "Y", 195 | "ZONES": [ 196 | { 197 | "state":"UT", 198 | "ZONE":"001" 199 | } 200 | ], 201 | "StormBased": { 202 | "vertices":[ 203 | { 204 | "lat":"38.87", 205 | "lon":"-87.13" 206 | } 207 | , 208 | { 209 | "lat":"38.89", 210 | "lon":"-87.13" 211 | } 212 | , 213 | { 214 | "lat":"38.91", 215 | "lon":"-87.11" 216 | } 217 | , 218 | { 219 | "lat":"38.98", 220 | "lon":"-86.93" 221 | } 222 | , 223 | { 224 | "lat":"38.87", 225 | "lon":"-86.69" 226 | } 227 | , 228 | { 229 | "lat":"38.75", 230 | "lon":"-86.3" 231 | } 232 | , 233 | { 234 | "lat":"38.84", 235 | "lon":"-87.16" 236 | } 237 | ], 238 | "Vertex_count":7, 239 | "stormInfo": { 240 | "time_epoch": 1363464360, 241 | "Motion_deg": 243, 242 | "Motion_spd": 18, 243 | "position_lat":38.90, 244 | "position_lon":-86.96 245 | } 246 | } 247 | }] 248 | } 249 | *************************************************************/ 250 | 251 | #define WUNDERGROUND "api.wunderground.com" 252 | 253 | // HTTP request 254 | const char WUNDERGROUND_REQ[] = 255 | "GET /api/" WU_API_KEY "/conditions/q/" WU_LOCATION ".json HTTP/1.1\r\n" 256 | "User-Agent: ESP8266/0.1\r\n" 257 | "Accept: */*\r\n" 258 | "Host: " WUNDERGROUND "\r\n" 259 | "Connection: close\r\n" 260 | "\r\n"; 261 | 262 | void setup() 263 | { 264 | Serial.begin(115200); 265 | 266 | // Turn LCD backlight on 267 | pinMode(15, OUTPUT); 268 | digitalWrite(15, HIGH); 269 | 270 | // TODO Reconnect to AP, if disconnected 271 | 272 | // Initialize LCD 273 | display.begin(); 274 | display.setContrast(50); 275 | display.setTextSize(1); 276 | display.setTextWrap(false); 277 | display.setTextColor(BLACK); 278 | 279 | // Give WU credit. 280 | display.clearDisplay(); 281 | display.println(F(" Powered by")); 282 | display.println(F(" Weather")); 283 | display.println(F(" Underground")); 284 | display.display(); 285 | 286 | // We start by connecting to a WiFi network 287 | Serial.println(); 288 | Serial.println(); 289 | Serial.print(F("Connecting to ")); 290 | Serial.println(SSID); 291 | 292 | WiFi.begin(SSID, PASSWORD); 293 | 294 | while (WiFi.status() != WL_CONNECTED) { 295 | delay(500); 296 | Serial.print(F(".")); 297 | } 298 | 299 | Serial.println(); 300 | Serial.println(F("WiFi connected")); 301 | Serial.println(F("IP address: ")); 302 | Serial.println(WiFi.localIP()); 303 | } 304 | 305 | static char respBuf[4096]; 306 | 307 | bool showWeather(char *json); 308 | 309 | void loop() 310 | { 311 | // TODO check for disconnect from AP 312 | 313 | // Open socket to WU server port 80 314 | Serial.print(F("Connecting to ")); 315 | Serial.println(WUNDERGROUND); 316 | 317 | // Use WiFiClient class to create TCP connections 318 | WiFiClient httpclient; 319 | const int httpPort = 80; 320 | if (!httpclient.connect(WUNDERGROUND, httpPort)) { 321 | Serial.println(F("connection failed")); 322 | delay(DELAY_ERROR); 323 | return; 324 | } 325 | 326 | // This will send the http request to the server 327 | Serial.print(WUNDERGROUND_REQ); 328 | httpclient.print(WUNDERGROUND_REQ); 329 | httpclient.flush(); 330 | 331 | // Collect http response headers and content from Weather Underground 332 | // HTTP headers are discarded. 333 | // The content is formatted in JSON and is left in respBuf. 334 | int respLen = 0; 335 | bool skip_headers = true; 336 | while (httpclient.connected() || httpclient.available()) { 337 | if (skip_headers) { 338 | String aLine = httpclient.readStringUntil('\n'); 339 | //Serial.println(aLine); 340 | // Blank line denotes end of headers 341 | if (aLine.length() <= 1) { 342 | skip_headers = false; 343 | } 344 | } 345 | else { 346 | int bytesIn; 347 | bytesIn = httpclient.read((uint8_t *)&respBuf[respLen], sizeof(respBuf) - respLen); 348 | Serial.print(F("bytesIn ")); Serial.println(bytesIn); 349 | if (bytesIn > 0) { 350 | respLen += bytesIn; 351 | if (respLen > sizeof(respBuf)) respLen = sizeof(respBuf); 352 | } 353 | else if (bytesIn < 0) { 354 | Serial.print(F("read error ")); 355 | Serial.println(bytesIn); 356 | } 357 | } 358 | delay(1); 359 | } 360 | httpclient.stop(); 361 | 362 | if (respLen >= sizeof(respBuf)) { 363 | Serial.print(F("respBuf overflow ")); 364 | Serial.println(respLen); 365 | delay(DELAY_ERROR); 366 | return; 367 | } 368 | // Terminate the C string 369 | respBuf[respLen++] = '\0'; 370 | Serial.print(F("respLen ")); 371 | Serial.println(respLen); 372 | //Serial.println(respBuf); 373 | 374 | if (showWeather(respBuf)) { 375 | delay(DELAY_NORMAL); 376 | } 377 | else { 378 | delay(DELAY_ERROR); 379 | } 380 | } 381 | 382 | bool showWeather(char *json) 383 | { 384 | StaticJsonBuffer<3*1024> jsonBuffer; 385 | 386 | // Skip characters until first '{' found 387 | // Ignore chunked length, if present 388 | char *jsonstart = strchr(json, '{'); 389 | //Serial.print(F("jsonstart ")); Serial.println(jsonstart); 390 | if (jsonstart == NULL) { 391 | Serial.println(F("JSON data missing")); 392 | return false; 393 | } 394 | json = jsonstart; 395 | 396 | // Parse JSON 397 | JsonObject& root = jsonBuffer.parseObject(json); 398 | if (!root.success()) { 399 | Serial.println(F("jsonBuffer.parseObject() failed")); 400 | return false; 401 | } 402 | 403 | // Extract weather info from parsed JSON 404 | JsonObject& current = root["current_observation"]; 405 | const float temp_f = current["temp_f"]; 406 | Serial.print(temp_f, 1); Serial.print(F(" F, ")); 407 | const float temp_c = current["temp_c"]; 408 | Serial.print(temp_c, 1); Serial.print(F(" C, ")); 409 | const char *humi = current[F("relative_humidity")]; 410 | Serial.print(humi); Serial.println(F(" RH")); 411 | const char *weather = current["weather"]; 412 | Serial.println(weather); 413 | const char *pressure_mb = current["pressure_mb"]; 414 | Serial.println(pressure_mb); 415 | const char *observation_time = current["observation_time_rfc822"]; 416 | Serial.println(observation_time); 417 | //Date/time string looks like this 418 | //Wed, 27 Jun 2012 17:27:14 -0700 419 | //012345678901234567890 420 | // 1 2 421 | // LCD has 14 characters per line so show date on one line and time on the 422 | // next. And shorten the date so it fits. 423 | char date[14+1]; 424 | const char *time; 425 | //Wed, 27 Jun 12 426 | memcpy(date, observation_time, 12); 427 | memcpy(&date[12], &observation_time[14], 2); 428 | date[14] = '\0'; 429 | //17:27:14 -0700 430 | time = &observation_time[17]; 431 | 432 | display.clearDisplay(); 433 | display.println(date); 434 | display.println(time); 435 | display.print(temp_f, 1); display.print (F(" F ")); 436 | display.print(temp_c, 1); display.println(F(" C")); 437 | display.print(humi); display.print(F(" RH ")); 438 | display.print(pressure_mb); display.println(F("mb")); 439 | display.setTextWrap(true); 440 | display.print(weather); 441 | display.display(); 442 | display.setTextWrap(false); 443 | return true; 444 | } 445 | -------------------------------------------------------------------------------- /esp-pcd-weather.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbx10/esp-pcd-weather/d39b518cf93429f000006b7091b0099de41611dd/esp-pcd-weather.jpg -------------------------------------------------------------------------------- /photos/2015-10-04 14.23.16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbx10/esp-pcd-weather/d39b518cf93429f000006b7091b0099de41611dd/photos/2015-10-04 14.23.16.jpg -------------------------------------------------------------------------------- /photos/2015-10-04 14.24.23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbx10/esp-pcd-weather/d39b518cf93429f000006b7091b0099de41611dd/photos/2015-10-04 14.24.23.jpg -------------------------------------------------------------------------------- /photos/2015-10-04 14.28.53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbx10/esp-pcd-weather/d39b518cf93429f000006b7091b0099de41611dd/photos/2015-10-04 14.28.53.jpg -------------------------------------------------------------------------------- /photos/2015-10-04 14.42.40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbx10/esp-pcd-weather/d39b518cf93429f000006b7091b0099de41611dd/photos/2015-10-04 14.42.40.jpg --------------------------------------------------------------------------------