├── ESP32_WU_ePaper42_3day_Forecast_01.ino ├── ESP8266_WU_ePaper42_3day_Forecast.ino ├── ESP8266_WU_ePaper42_3day_Forecast_04.ino ├── Licence.txt ├── README.md └── Wiring_Diagram.jpg /ESP32_WU_ePaper42_3day_Forecast_01.ino: -------------------------------------------------------------------------------- 1 | /*######################## Weather Display ############################# 2 | * Receives and displays the weather forecast from the Weather Underground and then displays using a 3 | * JSON decoder wx data to display on an SPI bus e-Paper 4.2" (Waveshare) 400x300 display. 4 | * Weather data received via WiFi connection to Weather Underground Servers and using their 'Forecast' and 'Astronomny' API and the resultant data 5 | * is decoded using the excellent Benoit Blanchon's (c) 2014-2017 JSON library. 6 | * This source code is protected under the terms of the MIT License and is copyright (c) 2017 by David Bird and permission is hereby granted, free of charge, to 7 | * any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software 8 | * without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, but not to sub-license and/or 9 | * to sell copies of the Software or to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | * 15 | * In update mode, power consumption = 74mA for 5-secs 16 | * In sleep mode, power consumption = 0.010mA for ~15-mins 17 | * 18 | * Therefore mAH consumption = 0.01mA x 4 x 15 + 74mA * 4 * 15*60/3600 = 0.421mAH so a 1500mAH battery would last for ~148-days 19 | * 20 | * See more at http://dsbird.org.uk */ 21 | 22 | #include 23 | #include // https://github.com/bblanchon/ArduinoJson 24 | #include 25 | #include "time.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | //################# LIBRARIES ########################## 34 | String version = "1.0"; // Version of this program 35 | //################ VARIABLES ########################### 36 | 37 | // Define each of the *icons for display 38 | const unsigned char sunny[] PROGMEM = { // 56x48 39 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 40 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 41 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xe1, 0xff, 0x9f, 0xff, 0xff, 0xf0, 0x7f, 0xf3, 0xff, 0x0f, 0xff, 42 | 0xff, 0xf8, 0x3f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xfe, 0x1f, 0x81, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xbc, 0x00, 0x1c, 0x7f, 0xff, 43 | 0xff, 0xff, 0xf0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xc3, 0xff, 0xff, 44 | 0xff, 0xff, 0x81, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xfc, 0x03, 0x87, 0xff, 0xf0, 0xe0, 0x3f, 0xf8, 0x01, 0x87, 0xff, 0xf0, 0xc0, 0x1f, 45 | 0xf8, 0x01, 0x87, 0xff, 0xf0, 0xc0, 0x1f, 0xfc, 0x03, 0x87, 0xff, 0xf0, 0xe0, 0x3f, 0xff, 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xc1, 0xff, 0xff, 46 | 0xff, 0xff, 0xc1, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x06, 0xff, 0xff, 47 | 0xff, 0xff, 0x3e, 0x00, 0x3c, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0x81, 0xfc, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xfe, 0x07, 0xff, 48 | 0xff, 0xf8, 0x7f, 0xf3, 0xff, 0x0f, 0xff, 0xff, 0xfc, 0xff, 0xe1, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 49 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 50 | 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 51 | 52 | const unsigned char mostlysunny[] PROGMEM = { // 56x48 53 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 54 | 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 55 | 0xff, 0xff, 0xfe, 0xff, 0x87, 0xfe, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x87, 0xf8, 0x7f, 0xff, 0xff, 0xf8, 0x3f, 0xcf, 0xf0, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xe0, 0xff, 56 | 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x03, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 57 | 0xff, 0xff, 0xc0, 0xf8, 0x78, 0x3f, 0xff, 0xff, 0xff, 0x80, 0x00, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x01, 0xfe, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x01, 0xff, 0x0f, 0xff, 58 | 0xff, 0xfc, 0x00, 0x01, 0xff, 0x87, 0xff, 0xff, 0xf0, 0x7f, 0xe1, 0xff, 0xe7, 0xff, 0xff, 0xf0, 0xff, 0xf1, 0xff, 0xe7, 0xff, 0xff, 0xe1, 0xff, 0xf0, 0x7f, 0xe4, 0x07, 59 | 0xff, 0xc3, 0xff, 0xf8, 0x1f, 0xc0, 0x03, 0xff, 0xc7, 0xff, 0xfc, 0x0f, 0x88, 0x03, 0xff, 0xc7, 0xff, 0xfe, 0x07, 0x8c, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xe3, 0x1f, 0xff, 60 | 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 61 | 0xc7, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x67, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x67, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x63, 0xff, 62 | 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x61, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x60, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0x7f, 0xcf, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0x7f, 63 | 0xcf, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 64 | 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 65 | 66 | const unsigned char rain[] PROGMEM = { // 56x48 67 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 68 | 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7e, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0x80, 0xff, 0xff, 69 | 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x07, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xfc, 0x01, 0xff, 70 | 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0x00, 0x1f, 0xfc, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x0f, 71 | 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 72 | 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 73 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0x07, 0x03, 0xff, 0x87, 74 | 0xc3, 0xff, 0xff, 0x06, 0x07, 0xff, 0x07, 0xe1, 0xff, 0xfe, 0x0e, 0x07, 0xff, 0x07, 0xe0, 0xff, 0xfe, 0x1e, 0x0f, 0xfc, 0x0f, 0xe0, 0x3f, 0xfe, 0x1e, 0x0f, 0xf0, 0x1f, 75 | 0xe0, 0x00, 0x3c, 0x1c, 0x1c, 0x00, 0x1f, 0xfc, 0x00, 0x78, 0x3c, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x78, 0x78, 0x38, 0x03, 0xff, 0xff, 0xc0, 0x78, 0x78, 0x78, 0x0f, 0xff, 76 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xff, 77 | 0xff, 0xff, 0x81, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x83, 0xff, 0xff, 0xff, 78 | 0xff, 0xff, 0x07, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 79 | 80 | const unsigned char tstorms[] PROGMEM = { // 56x48 81 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 82 | 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0x80, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x83, 0xff, 0xf0, 0x3f, 0xff, 83 | 0xff, 0xff, 0x07, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfe, 0x00, 0x7f, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0x00, 0x3f, 84 | 0xff, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x0f, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x07, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc7, 85 | 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 86 | 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 87 | 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc7, 0xff, 0xf0, 0xf8, 0xf8, 0x3f, 0x87, 0xc1, 0xff, 0xf0, 0xf8, 0xf0, 0x3f, 0x07, 0xe0, 0xff, 0xc0, 0xe1, 0xe0, 0x7c, 0x0f, 88 | 0xe0, 0x3f, 0x81, 0xe1, 0xc0, 0xf8, 0x0f, 0xf0, 0x07, 0x83, 0xc1, 0xc0, 0xf8, 0x3f, 0xfc, 0x07, 0x83, 0x83, 0x81, 0xf0, 0xff, 0xfe, 0x0f, 0x87, 0x83, 0x83, 0xf1, 0xff, 89 | 0xff, 0xcf, 0x0f, 0x07, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0x0e, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0x1e, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x3e, 0x1e, 0x00, 0xff, 0xff, 90 | 0xff, 0xf8, 0x7c, 0x3f, 0xe1, 0xff, 0xff, 0xff, 0xf0, 0x7c, 0x7f, 0xe1, 0xff, 0xff, 0xff, 0xf0, 0x78, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xfe, 0xf0, 0xff, 0xc3, 0xff, 0xff, 91 | 0xff, 0xff, 0xe0, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0x9f, 0xff, 0xff, 92 | 0xff, 0xff, 0x87, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 93 | 94 | const unsigned char cloudy[] PROGMEM = { // 56x48 95 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 96 | 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3e, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0x80, 0xff, 0xff, 97 | 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x03, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xfc, 0x01, 0xff, 98 | 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xfc, 0x3f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0x00, 0x1f, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x0f, 99 | 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 100 | 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 101 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 102 | 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 103 | 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0xff, 104 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 105 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 106 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 107 | 108 | const unsigned char snow[] PROGMEM = { // 56x48 109 | 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xe0, 0x0f, 0xff, 110 | 0xff, 0xff, 0xfc, 0x07, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xe8, 0x1f, 0xfe, 0x07, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x83, 0xff, 111 | 0xff, 0xf0, 0x00, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 112 | 0xff, 0xc3, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe1, 113 | 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 114 | 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 115 | 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 116 | 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xb9, 0xef, 117 | 0xff, 0xc7, 0xff, 0xff, 0xff, 0x09, 0x8f, 0xfc, 0x46, 0x7f, 0xff, 0xff, 0xc0, 0x1f, 0xfc, 0x04, 0x7f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0x00, 0x7f, 0xf3, 0xfc, 0x00, 0x07, 118 | 0xff, 0x80, 0xff, 0xf1, 0xfc, 0x00, 0x07, 0xf0, 0x00, 0x1f, 0x71, 0x1f, 0xe0, 0x3f, 0xf0, 0x00, 0x1f, 0x10, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0xff, 0x80, 0xff, 0x08, 0x8f, 119 | 0xfe, 0x00, 0x7f, 0xc0, 0xff, 0x98, 0xcf, 0xfc, 0x06, 0x7c, 0x00, 0x07, 0xf8, 0xff, 0xfc, 0xc7, 0x7c, 0x00, 0x07, 0xf9, 0xff, 0xff, 0xc7, 0xff, 0x80, 0xff, 0xff, 0xff, 120 | 0xff, 0xef, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x71, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff}; 121 | 122 | const unsigned char sleet[] PROGMEM = { // 56x48 123 | 0xff, 0xff, 0xff, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xe0, 0x0f, 0xff, 124 | 0xff, 0xff, 0xfc, 0x07, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xfe, 0x07, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x83, 0xff, 125 | 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xc3, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 126 | 0xff, 0x83, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc1, 127 | 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 128 | 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 129 | 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 130 | 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 131 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xfc, 0x07, 0xff, 0xcf, 0xff, 0x80, 0x3f, 132 | 0xf8, 0x07, 0xff, 0x8f, 0xff, 0x80, 0x3f, 0xf0, 0x07, 0xf8, 0x8c, 0xff, 0x80, 0x7f, 0xf0, 0x07, 0xf8, 0x88, 0xff, 0x80, 0x7f, 0xf0, 0x0f, 0xff, 0x01, 0xff, 0xe1, 0xff, 133 | 0xf0, 0x0f, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 134 | 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff}; 135 | 136 | const unsigned char fog[] PROGMEM = { // 56x48 137 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 138 | 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0xff, 0xff, 139 | 0xff, 0xff, 0xf0, 0x7f, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0x8f, 0xff, 140 | 0xff, 0xff, 0x8f, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xc7, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xe7, 0xff, 141 | 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xe7, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0x3f, 0xff, 0xff, 0xe0, 0x1f, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 142 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 143 | 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 144 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 145 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 146 | 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 147 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 149 | 150 | const unsigned char nodata[] PROGMEM = { // 56x48 151 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 152 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 153 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 154 | 0xff, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0c, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 155 | 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 156 | 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 157 | 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 158 | 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 159 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 160 | 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 161 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 162 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 163 | 164 | const unsigned char thermo[] PROGMEM = { // 64x24 165 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 166 | 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xfb, 0xdf, 0xff, 0xff, 0xf3, 0xff, 0xdf, 0xff, 0xfb, 0x5f, 0xff, 0xff, 0xe9, 0xff, 0x0f, 0xff, 167 | 0xfb, 0x5f, 0xfc, 0x7f, 0xed, 0xbf, 0x0f, 0xff, 0xfa, 0x5e, 0x18, 0x3f, 0xed, 0x7e, 0x07, 0xff, 0xfb, 0x5e, 0xd9, 0x9f, 0xed, 0x7e, 0x07, 0xff, 168 | 0xfb, 0x5e, 0xd3, 0xdf, 0xe3, 0x7c, 0x03, 0xff, 0xfa, 0x5e, 0x13, 0xff, 0xf2, 0xfc, 0x03, 0xdf, 0xfb, 0x5f, 0xf7, 0xff, 0xfe, 0xfc, 0x13, 0xdf, 169 | 0xfb, 0x5f, 0xf7, 0xff, 0xfd, 0x9c, 0x07, 0xdf, 0xfa, 0x5f, 0xf7, 0xff, 0xfd, 0x4e, 0x07, 0x8f, 0xfb, 0x5f, 0xf3, 0xdf, 0xfb, 0x6f, 0x0f, 0x8f, 170 | 0xfb, 0x5f, 0xf1, 0x9f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x5f, 0xf8, 0x3f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x1f, 0xfc, 0x7f, 0xf7, 0x1f, 0xff, 0x03, 171 | 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x13, 172 | 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f }; 173 | 174 | const unsigned char probrain[] PROGMEM = { // 32x24 175 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xc7, 0xe0, 0x7f, 176 | 0xff, 0xdf, 0xf8, 0x3f, 0xff, 0x1f, 0xff, 0x9f, 0xfc, 0x3f, 0xff, 0xcf, 0xe0, 0x7f, 0xdf, 0xc7, 0xc0, 0xff, 0x9f, 0xc3, 0x9f, 0xff, 0x1f, 0xf9, 177 | 0x3f, 0xff, 0x1f, 0xfc, 0x3f, 0xfe, 0x1f, 0xfc, 0x3f, 0xfb, 0x1f, 0xfc, 0x9f, 0xf3, 0x3b, 0xf9, 0xc0, 0x63, 0xf3, 0x03, 0xe0, 0x63, 0xe3, 0x87, 178 | 0xff, 0xc3, 0xe3, 0xff, 0xff, 0xe7, 0xc3, 0xff, 0xff, 0xe7, 0xe3, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 179 | 180 | // pins_arduino.h, e.g. WEMOS Lolin 181 | //static const uint8_t CS = 13; // ORANGE 182 | //static const uint8_t MOSI = 23; // BLUE 183 | //static const uint8_t SCK = 18; // YELLOW 184 | //static const uint8_t MISO = ; 185 | //static const uint8_t DC = 12; // GREEN 186 | //static const uint8_t RST = 14; // WHITE 187 | //static const uint8_t BUSY = 22; // VIOLET 188 | 189 | // GxIO_SPI(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1, int8_t bl = -1); 190 | GxIO_Class io(SPI, 13, 12, 14); 191 | // GxGDEP015OC1(GxIO& io, uint8_t rst = D4, uint8_t busy = D2); 192 | GxEPD_Class display(io, 14, 22); 193 | 194 | //------ NETWORK VARIABLES--------- 195 | // Use your own API key by signing up for a free developer account at http://www.wunderground.com/weather/api/ 196 | String apikey = "----------------"; // See: http://www.wunderground.com/weather/api/d/docs (change here with your KEY) 197 | String City = "Lyneham"; // Your home city 198 | String Country = "UK"; // Your country 199 | String Conditions = "conditions"; // See: http://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1 200 | char wxserver[] = "api.wunderground.com"; // Address for WeatherUnderGround 201 | unsigned long lastConnectionTime = 0; // Last time you connected to the server, in milliseconds 202 | const unsigned long postingInterval = 15L*60L*1000L; // Delay between updates, in milliseconds, WU allows 500 requests per-day maximum, set to every 15-mins or 144/day 203 | String Units = "X"; // M for Metric, X for Mixed and I for Imperial 204 | String time_str, currCondString; // strings to hold time and received weather data; 205 | 206 | //################ PROGRAM VARIABLES and OBJECTS ################ 207 | // Conditions 208 | String WDay0, Day0, Icon0, High0, Low0, Conditions0, Pop0, Averagehumidity0, 209 | WDay1, Day1, Icon1, High1, Low1, Conditions1, Pop1, Averagehumidity1, 210 | WDay2, Day2, Icon2, High2, Low2, Conditions2, Pop2, Averagehumidity2, 211 | WDay3, Day3, Icon3, High3, Low3, Conditions3, Pop3, Averagehumidity3; 212 | 213 | // Astronomy 214 | String DphaseofMoon, Sunrise, Sunset, Moonrise, Moonset, Moonlight; 215 | 216 | const char* ssid = "yourSSID"; 217 | const char* password = "yourPASSWORD"; 218 | const char* host = "api.wunderground.com"; 219 | 220 | WiFiClient client; // wifi client object 221 | 222 | void setup() { 223 | Serial.begin(115200); 224 | display.init(); 225 | display.fillScreen(GxEPD_WHITE); 226 | display.setTextColor(GxEPD_BLACK); 227 | display.setFont(&FreeSans9pt7b); 228 | display.setRotation(2); 229 | display.setCursor(0, 12); 230 | display.println(F("David Bird (C) 2017\n\rStarting...\n\rReading and Decoding Wx Data...")); 231 | display.update(); 232 | delay(500); 233 | StartWiFi(ssid,password); 234 | SetupTime(); 235 | lastConnectionTime = millis(); 236 | obtain_forecast("forecast"); 237 | obtain_forecast("astronomy"); 238 | DisplayForecast(); 239 | ESP.deepSleep(15*60*1000000); // Sleep for 15 minutes 240 | } 241 | 242 | void loop() { // Retain for diagnosticswhen sleep line 239 is disabled 243 | if (millis() - lastConnectionTime > postingInterval) { 244 | obtain_forecast("forecast"); 245 | obtain_forecast("astronomy"); 246 | lastConnectionTime = millis(); 247 | DisplayForecast(); 248 | } 249 | } 250 | 251 | void DisplayForecast(){ // Display is 400x300 resolution 252 | display.fillScreen(GxEPD_WHITE); 253 | DisplayWXicon(0,0, Icon0); DisplayWXicon(75,0, "thermo"); DisplayWXicon(155,0, "probrain"); 254 | DisplayText(200,15,WDay0 + " " + Day0); 255 | DisplayText(195,30,Conditions0); 256 | DisplayText(60,40,High0 + "/" + Low0); 257 | DisplayText(115,40,Averagehumidity0 + "%"); 258 | DisplayText(158,40,Pop0 + "%"); 259 | DisplayText(60,55,"--------------------------"); 260 | DisplayWXicon(0,60, Icon1); DisplayWXicon(75,60, "thermo"); DisplayWXicon(155,60, "probrain"); 261 | DisplayText(200,75,WDay1 + " " + Day1); 262 | DisplayText(195,90,Conditions1); 263 | DisplayText(60,100,High1 + "/" + Low1); 264 | DisplayText(115,100,Averagehumidity1 + "%"); 265 | DisplayText(158,100,Pop1 + "%"); 266 | DisplayText(60,115,"-------------------------"); 267 | DisplayWXicon(0,120, Icon2); DisplayWXicon(75,120, "thermo"); DisplayWXicon(155,120, "probrain"); 268 | DisplayText(200,135,WDay2 + " " + Day2); 269 | DisplayText(195,150,Conditions2); 270 | DisplayText(60,160,High2 + "/" + Low2); 271 | DisplayText(115,160,Averagehumidity2 + "%"); 272 | DisplayText(158,160,Pop2 + "%"); 273 | DisplayText(60,175,"--------------------------"); 274 | DisplayWXicon(0,180, Icon3); DisplayWXicon(75,180, "thermo"); DisplayWXicon(155,180, "probrain"); 275 | DisplayText(200,195,WDay3 + " " + Day3); 276 | DisplayText(195,210,Conditions3); 277 | DisplayText(60,220,High3 + "/" + Low3); 278 | DisplayText(115,220,Averagehumidity3 + "%"); 279 | DisplayText(158,220,Pop3 + "%"); 280 | DisplayText(60,235,"--------------------------"); 281 | 282 | DisplayText(000,245,"Sunrise/Set"); 283 | DisplayText(000,262,Sunrise+"/"+Sunset); 284 | DisplayText(110,245,"Moonrise/Set"); 285 | DisplayText(110,262,Moonrise+"/"+Moonset); 286 | DisplayText(225,245,"Moonphase"); 287 | DisplayText(225,262,DphaseofMoon+" "+Moonlight+"%"); 288 | UpdateTime(); 289 | DisplayText(85,292,time_str); 290 | display.update(); 291 | } 292 | 293 | void DisplayText(int x, int y, String text){ 294 | display.setCursor(x,y); 295 | display.println(text); 296 | } 297 | 298 | void DisplayWXicon(int x, int y, String IconName){ 299 | int len = 56*48; char myChar[56*48]={};; 300 | for (int k = 0; k < len; k++) { 301 | myChar[k] = IconName[k]; 302 | } 303 | Serial.println(IconName); 304 | if (IconName == "rain" || IconName == "nt_rain" || 305 | IconName == "chancerain" || IconName == "nt_chancerain") 306 | display.drawBitmap(x,y, rain, 56,48, GxEPD_BLACK); 307 | else if (IconName == "snow" || IconName == "nt_snow" || 308 | IconName == "flurries" || IconName == "nt_flurries" || 309 | IconName == "chancesnow" || IconName == "nt_chancesnow" || 310 | IconName == "chanceflurries" || IconName == "nt_chanceflurries") 311 | display.drawBitmap(x,y, snow, 56,48, GxEPD_BLACK); 312 | else if (IconName == "sleet" || IconName == "nt_sleet" || 313 | IconName == "chancesleet" || IconName == "nt_chancesleet") 314 | display.drawBitmap(x,y, sleet, 56,48, GxEPD_BLACK); 315 | else if (IconName == "sunny" || IconName == "nt_sunny" || 316 | IconName == "clear" || IconName == "nt_clear") 317 | display.drawBitmap(x,y, sunny, 56,48, GxEPD_BLACK); 318 | else if (IconName == "partlysunny" || IconName == "nt_partlysunny" || 319 | IconName == "mostlysunny" || IconName == "nt_mostlysunny") 320 | display.drawBitmap(x,y, mostlysunny, 56,48, GxEPD_BLACK); 321 | else if (IconName == "cloudy" || IconName == "nt_cloudy" || 322 | IconName == "mostlycloudy" || IconName == "nt_mostlycloudy" || 323 | IconName == "partlycloudy" || IconName == "nt_partlycloudy") 324 | display.drawBitmap(x,y, cloudy, 56,48, GxEPD_BLACK); 325 | else if (IconName == "tstorms" || IconName == "nt_tstorms" || 326 | IconName == "chancetstorms" || IconName == "nt_chancetstorms") 327 | display.drawBitmap(x,y, tstorms, 56,48, GxEPD_BLACK); 328 | else if (IconName == "fog" || IconName == "nt_fog" || 329 | IconName == "hazy" || IconName == "nt_hazy") 330 | display.drawBitmap(x,y, fog, 56,48, GxEPD_BLACK); 331 | else if (IconName == "thermo") 332 | display.drawBitmap(x,y, thermo,64,24, GxEPD_BLACK); 333 | else if (IconName == "probrain") 334 | display.drawBitmap(x,y, probrain,32,24, GxEPD_BLACK); 335 | else display.drawBitmap(x,y, nodata, 56,48, GxEPD_BLACK); 336 | } 337 | 338 | bool obtain_forecast (String forecast_type) { 339 | client.stop(); // Clear any current connections 340 | Serial.println("Connecting to "+String(host)); // start a new connection 341 | const int httpPort = 80; 342 | if (!client.connect(host, httpPort)) { 343 | Serial.println("Connection failed"); 344 | return false; 345 | } 346 | // Weather Underground API address http://api.wunderground.com/api/YOUR_API_KEY/conditions/q/YOUR_STATE/YOUR_CITY.json 347 | String url = "http://api.wunderground.com/api/"+apikey+"/"+forecast_type+"/q/"+Country+"/"+City+".json"; 348 | Serial.println("Requesting URL: "+String(url)); 349 | client.print(String("GET ") + url + " HTTP/1.1\r\n" + 350 | "Host: " + host + "\r\n" + 351 | "Connection: close\r\n\r\n"); 352 | unsigned long timeout = millis(); 353 | while (client.available() == 0) { 354 | if (millis() - timeout > 5000) { 355 | Serial.println(">>> Client Connection Timeout...Stopping"); 356 | client.stop(); 357 | return false; 358 | } 359 | } 360 | Serial.print("Receiving API weather data"); 361 | while(client.available()) { 362 | currCondString = client.readStringUntil('\r'); 363 | Serial.print("."); 364 | } 365 | Serial.println("\r\nClosing connection"); 366 | //Serial.println(*currCondString); 367 | if (forecast_type == "forecast"){ 368 | if (showWeather_forecast(&currCondString)); else Serial.println("Failed to get Weather Data"); 369 | } 370 | if (forecast_type == "astronomy"){ 371 | if (showWeather_astronomy(&currCondString)); else Serial.println("Failed to get Astronomy Data"); 372 | } 373 | return true; 374 | } 375 | 376 | bool showWeather_astronomy(String* currCondString) { 377 | Serial.println("Creating object..."); 378 | DynamicJsonBuffer jsonBuffer(1*1024); 379 | // Create root object and parse the json file returned from the api. The API returns errors and these need to be checked to ensure successful decoding 380 | JsonObject& root = jsonBuffer.parseObject(*(currCondString)); 381 | if (!root.success()) { 382 | Serial.println(F("jsonBuffer.parseObject() failed")); 383 | } 384 | // Extract weather info from parsed JSON 385 | JsonObject& current = root["moon_phase"]; 386 | String percentIlluminated = current["percentIlluminated"]; 387 | String phaseofMoon = current["phaseofMoon"]; 388 | int SRhour = current["sunrise"]["hour"]; 389 | int SRminute = current["sunrise"]["minute"]; 390 | int SShour = current["sunset"]["hour"]; 391 | int SSminute = current["sunset"]["minute"]; 392 | int MRhour = current["moonrise"]["hour"]; 393 | int MRminute = current["moonrise"]["minute"]; 394 | int MShour = current["moonset"]["hour"]; 395 | int MSminute = current["moonset"]["minute"]; 396 | Sunrise = (SRhour<10?"0":"")+String(SRhour)+":"+(SRminute<10?"0":"")+String(SRminute); 397 | Sunset = (SShour<10?"0":"")+String(SShour)+":"+(SSminute<10?"0":"")+String(SSminute); 398 | Moonrise = (MRhour<10?"0":"")+String(MRhour)+":"+(MRminute<10?"0":"")+String(MRminute); 399 | Moonset = (MShour<10?"0":"")+String(MShour)+":"+(MSminute<10?"0":"")+String(MSminute); 400 | Moonlight = percentIlluminated; 401 | DphaseofMoon = phaseofMoon; 402 | return true; 403 | } 404 | 405 | bool showWeather_forecast(String* currCondString) { 406 | Serial.println("Creating object..."); 407 | DynamicJsonBuffer jsonBuffer(8*1024); 408 | // Create root object and parse the json file returned from the api. The API returns errors and these need to be checked to ensure successful decoding 409 | JsonObject& root = jsonBuffer.parseObject(*(currCondString)); 410 | if (!root.success()) { 411 | Serial.println(F("jsonBuffer.parseObject() failed")); 412 | } 413 | JsonObject& forecast = root["forecast"]["simpleforecast"]; 414 | String wday0 = forecast["forecastday"][0]["date"]["weekday_short"]; WDay0 = wday0; 415 | int day0 = forecast["forecastday"][0]["date"]["day"]; day0<10?(Day0="0"+String(day0)):(Day0=String(day0)); 416 | String mon0 = forecast["forecastday"][0]["date"]["monthname_short"]; 417 | String year0 = forecast["forecastday"][0]["date"]["year"]; Day0 += "-" + mon0 + "-" + year0.substring(2); 418 | String icon0 = forecast["forecastday"][0]["icon"]; Icon0 = icon0; 419 | String high0 = forecast["forecastday"][0]["high"]["celsius"]; High0 = high0; 420 | String low0 = forecast["forecastday"][0]["low"]["celsius"]; Low0 = low0; 421 | String conditions0 = forecast["forecastday"][0]["conditions"]; Conditions0 = conditions0; 422 | String pop0 = forecast["forecastday"][0]["pop"]; Pop0 = pop0; 423 | String averagehumidity0 = forecast["forecastday"][0]["avehumidity"]; Averagehumidity0 = averagehumidity0; 424 | 425 | String wday1 = forecast["forecastday"][1]["date"]["weekday_short"]; WDay1 = wday1; 426 | int day1 = forecast["forecastday"][1]["date"]["day"]; day1<10?(Day1="0"+String(day1)):(Day1=String(day1)); 427 | String mon1 = forecast["forecastday"][1]["date"]["monthname_short"]; 428 | String year1 = forecast["forecastday"][1]["date"]["year"]; Day1 += "-" + mon1 + "-" + year1.substring(2); 429 | String icon1 = forecast["forecastday"][1]["icon"]; Icon1 = icon1; 430 | String high1 = forecast["forecastday"][1]["high"]["celsius"]; High1 = high1; 431 | String low1 = forecast["forecastday"][1]["low"]["celsius"]; Low1 = low1; 432 | String conditions1 = forecast["forecastday"][1]["conditions"]; Conditions1 = conditions1; 433 | String pop1 = forecast["forecastday"][1]["pop"]; Pop1 = pop1; 434 | String averagehumidity1 = forecast["forecastday"][1]["avehumidity"]; Averagehumidity1 = averagehumidity1; 435 | 436 | String wday2 = forecast["forecastday"][2]["date"]["weekday_short"]; WDay2 = wday2; 437 | int day2 = forecast["forecastday"][2]["date"]["day"]; day2<10?(Day2="0"+String(day2)):(Day2=String(day2)); 438 | String mon2 = forecast["forecastday"][2]["date"]["monthname_short"]; 439 | String year2 = forecast["forecastday"][2]["date"]["year"]; Day2 += "-" + mon2 + "-" + year2.substring(2); 440 | String icon2 = forecast["forecastday"][2]["icon"]; Icon2 = icon2; 441 | String high2 = forecast["forecastday"][2]["high"]["celsius"]; High2 = high2; 442 | String low2 = forecast["forecastday"][2]["low"]["celsius"]; Low2 = low2; 443 | String conditions2 = forecast["forecastday"][2]["conditions"]; Conditions2 = conditions2; 444 | String pop2 = forecast["forecastday"][2]["pop"]; Pop2 = pop2; 445 | String averagehumidity2 = forecast["forecastday"][2]["avehumidity"]; Averagehumidity2 = averagehumidity2; 446 | 447 | String wday3 = forecast["forecastday"][3]["date"]["weekday_short"]; WDay3 = wday3; 448 | int day3 = forecast["forecastday"][3]["date"]["day"]; day3<10?(Day3="0"+String(day3)):(Day3=String(day3)); 449 | String mon3 = forecast["forecastday"][3]["date"]["monthname_short"]; 450 | String year3 = forecast["forecastday"][3]["date"]["year"]; Day3 += "-" + mon3 + "-" + year3.substring(2); 451 | String icon3 = forecast["forecastday"][3]["icon"]; Icon3 = icon3; 452 | String high3 = forecast["forecastday"][3]["high"]["celsius"]; High3 = high3; 453 | String low3 = forecast["forecastday"][3]["low"]["celsius"]; Low3 = low3; 454 | String conditions3 = forecast["forecastday"][3]["conditions"]; Conditions3 = conditions3; 455 | String pop3 = forecast["forecastday"][3]["pop"]; Pop3 = pop3; 456 | String averagehumidity3 = forecast["forecastday"][3]["avehumidity"]; Averagehumidity3 = averagehumidity3; 457 | 458 | return true; 459 | } 460 | 461 | int StartWiFi(const char* ssid, const char* password){ 462 | int connAttempts = 0; 463 | Serial.print(F("\r\nConnecting to: ")); Serial.println(String(ssid)); 464 | WiFi.begin(ssid, password); 465 | while (WiFi.status() != WL_CONNECTED ) { 466 | delay(500); Serial.print("."); 467 | if(connAttempts > 20) return -5; 468 | connAttempts++; 469 | } 470 | Serial.print(F("WiFi connected at: ")); 471 | Serial.println(WiFi.localIP()); 472 | return 1; 473 | } 474 | 475 | void clear_screen() { 476 | display.fillScreen(GxEPD_WHITE); 477 | display.update(); 478 | } 479 | 480 | void SetupTime(){ 481 | configTime(0 * 3600, 3600, "0.uk.pool.ntp.org", "time.nist.gov"); 482 | UpdateTime(); 483 | } 484 | 485 | void UpdateTime(){ 486 | struct tm timeinfo; 487 | while (!getLocalTime(&timeinfo)){ 488 | Serial.println("Failed to obtain time"); 489 | } 490 | //See http://www.cplusplus.com/reference/ctime/strftime/ 491 | Serial.println(&timeinfo, "%a %b %d %Y %H:%M:%S"); // Displays: Saturday, June 24 2017 14:05:49 492 | time_str = asctime(&timeinfo); // Displays: Sat Jun 24 14:05:49 2017 493 | time_str = time_str.substring(0,24); // Displays: Sat Jun 24 14:05 494 | } 495 | 496 | -------------------------------------------------------------------------------- /ESP8266_WU_ePaper42_3day_Forecast.ino: -------------------------------------------------------------------------------- 1 | /*######################## Weather Display ############################# 2 | * Receives and displays the weather forecast from the Weather Underground and then displays using a 3 | * JSON decoder wx data to show weather data on a 4.2" 400x300 pixel SPI bus e-paper display. 4 | * Weather data received via WiFi connection to Weather Underground Servers and using their 'Forecast' and 'Astronomy' API and data 5 | * is decoded using the excellent Benoit Blanchon's (c) 2014-2017 JSON library. 6 | * This source code is protected under the terms of the MIT License and is copyright (c) 2017 by David Bird and permission is hereby granted, free of charge, to 7 | * any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software 8 | * without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, but not to sub-license and/or 9 | * to sell copies of the Software or to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | * 15 | * See more at http://dsbird.org.uk */ 16 | 17 | #include 18 | #include // https://github.com/bblanchon/ArduinoJson 19 | #include 20 | #include "time.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | //################# LIBRARIES ########################## 29 | String version = "1.0"; // Version of this program 30 | //################ VARIABLES ########################### 31 | 32 | // Define each of the *icons for display 33 | const unsigned char sunny_icon[] = { // 56x48 34 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 35 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 36 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xe1, 0xff, 0x9f, 0xff, 0xff, 0xf0, 0x7f, 0xf3, 0xff, 0x0f, 0xff, 37 | 0xff, 0xf8, 0x3f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xfe, 0x1f, 0x81, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xbc, 0x00, 0x1c, 0x7f, 0xff, 38 | 0xff, 0xff, 0xf0, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xc3, 0xff, 0xff, 39 | 0xff, 0xff, 0x81, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xfc, 0x03, 0x87, 0xff, 0xf0, 0xe0, 0x3f, 0xf8, 0x01, 0x87, 0xff, 0xf0, 0xc0, 0x1f, 40 | 0xf8, 0x01, 0x87, 0xff, 0xf0, 0xc0, 0x1f, 0xfc, 0x03, 0x87, 0xff, 0xf0, 0xe0, 0x3f, 0xff, 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xc1, 0xff, 0xff, 41 | 0xff, 0xff, 0xc1, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x06, 0xff, 0xff, 42 | 0xff, 0xff, 0x3e, 0x00, 0x3c, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0x81, 0xfc, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xfe, 0x07, 0xff, 43 | 0xff, 0xf8, 0x7f, 0xf3, 0xff, 0x0f, 0xff, 0xff, 0xfc, 0xff, 0xe1, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 44 | 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 45 | 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 46 | 47 | const unsigned char mostlysunny_icon[] = { // 56x48 48 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 49 | 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 50 | 0xff, 0xff, 0xfe, 0xff, 0x87, 0xfe, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0x87, 0xf8, 0x7f, 0xff, 0xff, 0xf8, 0x3f, 0xcf, 0xf0, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xe0, 0xff, 51 | 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x03, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 52 | 0xff, 0xff, 0xc0, 0xf8, 0x78, 0x3f, 0xff, 0xff, 0xff, 0x80, 0x00, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x01, 0xfe, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x01, 0xff, 0x0f, 0xff, 53 | 0xff, 0xfc, 0x00, 0x01, 0xff, 0x87, 0xff, 0xff, 0xf0, 0x7f, 0xe1, 0xff, 0xe7, 0xff, 0xff, 0xf0, 0xff, 0xf1, 0xff, 0xe7, 0xff, 0xff, 0xe1, 0xff, 0xf0, 0x7f, 0xe4, 0x07, 54 | 0xff, 0xc3, 0xff, 0xf8, 0x1f, 0xc0, 0x03, 0xff, 0xc7, 0xff, 0xfc, 0x0f, 0x88, 0x03, 0xff, 0xc7, 0xff, 0xfe, 0x07, 0x8c, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xe3, 0x1f, 0xff, 55 | 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 56 | 0xc7, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x67, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x67, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x63, 0xff, 57 | 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x61, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0x60, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0x7f, 0xcf, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0x7f, 58 | 0xcf, 0xff, 0xff, 0xff, 0xf1, 0xfc, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 59 | 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 60 | 61 | const unsigned char rain_icon[] = { // 56x48 62 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 63 | 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7e, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0x80, 0xff, 0xff, 64 | 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x07, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xfc, 0x01, 0xff, 65 | 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0x00, 0x1f, 0xfc, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x0f, 66 | 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 67 | 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 68 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0x07, 0x03, 0xff, 0x87, 69 | 0xc3, 0xff, 0xff, 0x06, 0x07, 0xff, 0x07, 0xe1, 0xff, 0xfe, 0x0e, 0x07, 0xff, 0x07, 0xe0, 0xff, 0xfe, 0x1e, 0x0f, 0xfc, 0x0f, 0xe0, 0x3f, 0xfe, 0x1e, 0x0f, 0xf0, 0x1f, 70 | 0xe0, 0x00, 0x3c, 0x1c, 0x1c, 0x00, 0x1f, 0xfc, 0x00, 0x78, 0x3c, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x78, 0x78, 0x38, 0x03, 0xff, 0xff, 0xc0, 0x78, 0x78, 0x78, 0x0f, 0xff, 71 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xff, 72 | 0xff, 0xff, 0x81, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x83, 0xff, 0xff, 0xff, 73 | 0xff, 0xff, 0x07, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 74 | 75 | const unsigned char tstorms_icon[] = { // 56x48 76 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 77 | 0xff, 0xff, 0xe0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0x80, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x83, 0xff, 0xf0, 0x3f, 0xff, 78 | 0xff, 0xff, 0x07, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfe, 0x00, 0x7f, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0x00, 0x3f, 79 | 0xff, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x0f, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x07, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc7, 80 | 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 81 | 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 82 | 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc7, 0xff, 0xf0, 0xf8, 0xf8, 0x3f, 0x87, 0xc1, 0xff, 0xf0, 0xf8, 0xf0, 0x3f, 0x07, 0xe0, 0xff, 0xc0, 0xe1, 0xe0, 0x7c, 0x0f, 83 | 0xe0, 0x3f, 0x81, 0xe1, 0xc0, 0xf8, 0x0f, 0xf0, 0x07, 0x83, 0xc1, 0xc0, 0xf8, 0x3f, 0xfc, 0x07, 0x83, 0x83, 0x81, 0xf0, 0xff, 0xfe, 0x0f, 0x87, 0x83, 0x83, 0xf1, 0xff, 84 | 0xff, 0xcf, 0x0f, 0x07, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0x0e, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x1f, 0x1e, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x3e, 0x1e, 0x00, 0xff, 0xff, 85 | 0xff, 0xf8, 0x7c, 0x3f, 0xe1, 0xff, 0xff, 0xff, 0xf0, 0x7c, 0x7f, 0xe1, 0xff, 0xff, 0xff, 0xf0, 0x78, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xfe, 0xf0, 0xff, 0xc3, 0xff, 0xff, 86 | 0xff, 0xff, 0xe0, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0x9f, 0xff, 0xff, 87 | 0xff, 0xff, 0x87, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 88 | 89 | const unsigned char cloudy_icon[] = { // 56x48 90 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 91 | 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3e, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0x80, 0xff, 0xff, 92 | 0xff, 0xff, 0x81, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0x03, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xfc, 0x01, 0xff, 93 | 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xfc, 0x3f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0x00, 0x1f, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x0f, 94 | 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 95 | 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 96 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 97 | 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 98 | 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0xff, 99 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 100 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 101 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 102 | 103 | const unsigned char snow_icon[] = { // 56x48 104 | 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xe0, 0x0f, 0xff, 105 | 0xff, 0xff, 0xfc, 0x07, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xe8, 0x1f, 0xfe, 0x07, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x83, 0xff, 106 | 0xff, 0xf0, 0x00, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 107 | 0xff, 0xc3, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe1, 108 | 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 109 | 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 110 | 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 111 | 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xb9, 0xef, 112 | 0xff, 0xc7, 0xff, 0xff, 0xff, 0x09, 0x8f, 0xfc, 0x46, 0x7f, 0xff, 0xff, 0xc0, 0x1f, 0xfc, 0x04, 0x7f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0x00, 0x7f, 0xf3, 0xfc, 0x00, 0x07, 113 | 0xff, 0x80, 0xff, 0xf1, 0xfc, 0x00, 0x07, 0xf0, 0x00, 0x1f, 0x71, 0x1f, 0xe0, 0x3f, 0xf0, 0x00, 0x1f, 0x10, 0x1f, 0xc0, 0x1f, 0xff, 0x00, 0xff, 0x80, 0xff, 0x08, 0x8f, 114 | 0xfe, 0x00, 0x7f, 0xc0, 0xff, 0x98, 0xcf, 0xfc, 0x06, 0x7c, 0x00, 0x07, 0xf8, 0xff, 0xfc, 0xc7, 0x7c, 0x00, 0x07, 0xf9, 0xff, 0xff, 0xc7, 0xff, 0x80, 0xff, 0xff, 0xff, 115 | 0xff, 0xef, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x71, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff}; 116 | 117 | const unsigned char sleet_icon[] = { // 56x48 118 | 0xff, 0xff, 0xff, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xe0, 0x0f, 0xff, 119 | 0xff, 0xff, 0xfc, 0x07, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xfe, 0x07, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x83, 0xff, 120 | 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xc3, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x0f, 121 | 0xff, 0x83, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc1, 122 | 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 123 | 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 124 | 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0x81, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 125 | 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 126 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xfc, 0x07, 0xff, 0xcf, 0xff, 0x80, 0x3f, 127 | 0xf8, 0x07, 0xff, 0x8f, 0xff, 0x80, 0x3f, 0xf0, 0x07, 0xf8, 0x8c, 0xff, 0x80, 0x7f, 0xf0, 0x07, 0xf8, 0x88, 0xff, 0x80, 0x7f, 0xf0, 0x0f, 0xff, 0x01, 0xff, 0xe1, 0xff, 128 | 0xf0, 0x0f, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 129 | 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff}; 130 | 131 | const unsigned char fog_icon[] = { // 56x48 132 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 133 | 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0xff, 0xff, 134 | 0xff, 0xff, 0xf0, 0x7f, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0x8f, 0xff, 135 | 0xff, 0xff, 0x8f, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xc7, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xe7, 0xff, 136 | 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xe7, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0x3f, 0xff, 0xff, 0xe0, 0x1f, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 137 | 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 138 | 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 139 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 140 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 142 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 144 | 145 | const unsigned char nodata_icon[] = { // 56x48 146 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 147 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 148 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 149 | 0xff, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0c, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 150 | 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 151 | 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 152 | 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 153 | 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 154 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 155 | 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 156 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 157 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 158 | 159 | const unsigned char thermo_icon[] = { // 64x24 160 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 161 | 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xfb, 0xdf, 0xff, 0xff, 0xf3, 0xff, 0xdf, 0xff, 0xfb, 0x5f, 0xff, 0xff, 0xe9, 0xff, 0x0f, 0xff, 162 | 0xfb, 0x5f, 0xfc, 0x7f, 0xed, 0xbf, 0x0f, 0xff, 0xfa, 0x5e, 0x18, 0x3f, 0xed, 0x7e, 0x07, 0xff, 0xfb, 0x5e, 0xd9, 0x9f, 0xed, 0x7e, 0x07, 0xff, 163 | 0xfb, 0x5e, 0xd3, 0xdf, 0xe3, 0x7c, 0x03, 0xff, 0xfa, 0x5e, 0x13, 0xff, 0xf2, 0xfc, 0x03, 0xdf, 0xfb, 0x5f, 0xf7, 0xff, 0xfe, 0xfc, 0x13, 0xdf, 164 | 0xfb, 0x5f, 0xf7, 0xff, 0xfd, 0x9c, 0x07, 0xdf, 0xfa, 0x5f, 0xf7, 0xff, 0xfd, 0x4e, 0x07, 0x8f, 0xfb, 0x5f, 0xf3, 0xdf, 0xfb, 0x6f, 0x0f, 0x8f, 165 | 0xfb, 0x5f, 0xf1, 0x9f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x5f, 0xf8, 0x3f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x1f, 0xfc, 0x7f, 0xf7, 0x1f, 0xff, 0x03, 166 | 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x13, 167 | 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f }; 168 | 169 | const unsigned char probrain_icon[] = { // 32x24 170 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xc7, 0xe0, 0x7f, 171 | 0xff, 0xdf, 0xf8, 0x3f, 0xff, 0x1f, 0xff, 0x9f, 0xfc, 0x3f, 0xff, 0xcf, 0xe0, 0x7f, 0xdf, 0xc7, 0xc0, 0xff, 0x9f, 0xc3, 0x9f, 0xff, 0x1f, 0xf9, 172 | 0x3f, 0xff, 0x1f, 0xfc, 0x3f, 0xfe, 0x1f, 0xfc, 0x3f, 0xfb, 0x1f, 0xfc, 0x9f, 0xf3, 0x3b, 0xf9, 0xc0, 0x63, 0xf3, 0x03, 0xe0, 0x63, 0xe3, 0x87, 173 | 0xff, 0xc3, 0xe3, 0xff, 0xff, 0xe7, 0xc3, 0xff, 0xff, 0xe7, 0xe3, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 174 | 175 | // pins_arduino.h, e.g. WEMOS D1 Mini 176 | //static const uint8_t SS = D8; 177 | //static const uint8_t MOSI = D7; 178 | //static const uint8_t MISO = ; 179 | //static const uint8_t SCK = D5; 180 | // GxIO_SPI(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1, int8_t bl = -1); 181 | GxIO_Class io(SPI, D8, D3, D4); 182 | // GxGDEP015OC1(GxIO& io, uint8_t rst = D4, uint8_t busy = D2); 183 | GxEPD_Class display(io, D4, D6); 184 | 185 | //------ NETWORK VARIABLES--------- 186 | // Use your own API key by signing up for a free developer account at http://www.wunderground.com/weather/api/ 187 | String API_key = "2--------------7"; // See: http://www.wunderground.com/weather/api/d/docs (change here with your KEY) 188 | String City = "Melksham"; // Your home city 189 | String Country = "UK"; // Your country 190 | String Conditions = "conditions"; // See: http://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1 191 | char wxserver[] = "api.wunderground.com"; // Address for WeatherUnderGround 192 | unsigned long lastConnectionTime = 0; // Last time you connected to the server, in milliseconds 193 | const unsigned long postingInterval = 10L*60L*1000L; // Delay between updates, in milliseconds, WU allows 500 requests per-day maximum, set to every 10-mins or 144/day 194 | String Units = "X"; // M for Metric, X for Mixed and I for Imperial 195 | 196 | //################ PROGRAM VARIABLES and OBJECTS ################ 197 | // Conditions 198 | String WDay0, Day0, Icon0, High0, Low0, Conditions0, Pop0, Averagehumidity0, 199 | WDay1, Day1, Icon1, High1, Low1, Conditions1, Pop1, Averagehumidity1, 200 | WDay2, Day2, Icon2, High2, Low2, Conditions2, Pop2, Averagehumidity2, 201 | WDay3, Day3, Icon3, High3, Low3, Conditions3, Pop3, Averagehumidity3; 202 | 203 | 204 | // Astronomy 205 | String DphaseofMoon, Sunrise, Sunset, Moonrise, Moonset, Moonlight; 206 | 207 | const char* ssid = "pourSSID"; 208 | const char* password = "yourPASSWORD"; 209 | const char* host = "api.wunderground.com"; 210 | 211 | String currCondString; // string to hold received API weather data 212 | 213 | WiFiClient client; // wifi client object 214 | 215 | void setup() { 216 | Serial.begin(115200); 217 | display.init(); 218 | display.fillScreen(GxEPD_WHITE); 219 | display.setTextColor(GxEPD_BLACK); 220 | display.setFont(&FreeSans9pt7b); 221 | display.setCursor(0, 12); 222 | display.setRotation(2); 223 | display.println("David Bird (C) 2017\n\rStarting...\n\rReading and Decoding Wx Data..."); 224 | display.update(); 225 | delay(500); 226 | StartWiFi(ssid,password); 227 | SetupTime(); 228 | time_t now = time(nullptr); 229 | Serial.println(ctime(&now)); 230 | lastConnectionTime = millis(); 231 | obtain_forecast("forecast"); 232 | obtain_forecast("astronomy"); 233 | DisplayForecast(); 234 | ESP.deepSleep(10*60*1000000, WAKE_RF_DEFAULT); // Sleep for 10 minutes 235 | } 236 | 237 | void loop() { 238 | if (millis() - lastConnectionTime > postingInterval) { 239 | obtain_forecast("forecast"); 240 | obtain_forecast("astronomy"); 241 | lastConnectionTime = millis(); 242 | DisplayForecast(); 243 | } 244 | } 245 | void DisplayForecast(){ // Display is 400x300 resolution 246 | display.fillScreen(GxEPD_WHITE); 247 | display.setCursor(0,12); 248 | DisplayWXicon(0,0, Icon0); DisplayWXicon(75,0, "thermo"); DisplayWXicon(155,0, "probrain"); 249 | display.setCursor(200,15); display.println(WDay0 + " " + Day0); 250 | display.setCursor(195,30); display.println(Conditions0); 251 | display.setCursor(60,40); display.println(High0 + "/" + Low0); 252 | display.setCursor(115,40); display.println(Averagehumidity0 + "%"); 253 | display.setCursor(158,40); display.println(Pop0 + "%"); 254 | display.setCursor(60,55); display.println("--------------------------"); 255 | DisplayWXicon(0,60, Icon1); DisplayWXicon(75,60, "thermo"); DisplayWXicon(155,60, "probrain"); 256 | display.setCursor(200,75); display.println(WDay1 + " " + Day1); 257 | display.setCursor(195,90); display.println(Conditions1); 258 | display.setCursor(60,100); display.println(High1 + "/" + Low1); 259 | display.setCursor(115,100); display.println(Averagehumidity1 + "%"); 260 | display.setCursor(158,100); display.println(Pop1 + "%"); 261 | display.setCursor(60,115); display.println("-------------------------"); 262 | DisplayWXicon(0,120, Icon2); DisplayWXicon(75,120, "thermo"); DisplayWXicon(155,120, "probrain"); 263 | display.setCursor(200,135); display.println(WDay2 + " " + Day2); 264 | display.setCursor(195,150); display.println(Conditions2); 265 | display.setCursor(60,160); display.println(High2 + "/" + Low2); 266 | display.setCursor(115,160); display.println(Averagehumidity2 + "%"); 267 | display.setCursor(158,160); display.println(Pop2 + "%"); 268 | display.setCursor(60,175); display.println("--------------------------"); 269 | DisplayWXicon(0,180, Icon3); DisplayWXicon(75,180, "thermo"); DisplayWXicon(155,180, "probrain"); 270 | display.setCursor(200,195); display.println(WDay3 + " " + Day3); 271 | display.setCursor(195,210); display.println(Conditions3); 272 | display.setCursor(60,220); display.println(High3 + "/" + Low3); 273 | display.setCursor(115,220); display.println(Averagehumidity3 + "%"); 274 | display.setCursor(158,220); display.println(Pop3 + "%"); 275 | display.setCursor(60,235); display.println("--------------------------"); 276 | 277 | display.setCursor(000,245); display.println("Sunrise/Set"); 278 | display.setCursor(000,262); display.println(Sunrise+"/"+Sunset); 279 | display.setCursor(110,245); display.println("Moonrise/Set"); 280 | display.setCursor(110,262); display.println(Moonrise+"/"+Moonset); 281 | display.setCursor(225,245); display.println("Moonphase"); 282 | display.setCursor(225,262); display.println(DphaseofMoon+" "+Moonlight+"%"); 283 | time_t now = time(nullptr); display.setCursor(85,292); display.println(ctime(&now)); 284 | 285 | display.update(); 286 | } 287 | 288 | void DisplayWXicon(int x, int y, String IconName){ 289 | Serial.println(IconName); 290 | if (IconName == "rain" || IconName == "nt_rain" || 291 | IconName == "chancerain" || IconName == "nt_chancerain") 292 | display.drawBitmap(x,y, rain_icon, 56,48, GxEPD_BLACK); 293 | else if (IconName == "snow" || IconName == "nt_snow" || 294 | IconName == "flurries" || IconName == "nt_flurries" || 295 | IconName == "chancesnow" || IconName == "nt_chancesnow" || 296 | IconName == "chanceflurries" || IconName == "nt_chanceflurries") 297 | display.drawBitmap(x,y, snow_icon, 56,48, GxEPD_BLACK); 298 | else if (IconName == "sleet" || IconName == "nt_sleet" || 299 | IconName == "chancesleet" || IconName == "nt_chancesleet") 300 | display.drawBitmap(x,y, sleet_icon, 56,48, GxEPD_BLACK); 301 | else if (IconName == "sunny" || IconName == "nt_sunny" || 302 | IconName == "clear" || IconName == "nt_clear") 303 | display.drawBitmap(x,y, sunny_icon, 56,48, GxEPD_BLACK); 304 | else if (IconName == "partlysunny" || IconName == "nt_partlysunny" || 305 | IconName == "mostlysunny" || IconName == "nt_mostlysunny") 306 | display.drawBitmap(x,y, mostlysunny_icon, 56,48, GxEPD_BLACK); 307 | else if (IconName == "cloudy" || IconName == "nt_cloudy" || 308 | IconName == "mostlycloudy" || IconName == "nt_mostlycloudy" || 309 | IconName == "partlycloudy" || IconName == "nt_partlycloudy") 310 | display.drawBitmap(x,y, cloudy_icon, 56,48, GxEPD_BLACK); 311 | else if (IconName == "tstorms" || IconName == "nt_tstorms" || 312 | IconName == "chancetstorms" || IconName == "nt_chancetstorms") 313 | display.drawBitmap(x,y, tstorms_icon, 56,48, GxEPD_BLACK); 314 | else if (IconName == "fog" || IconName == "nt_fog" || 315 | IconName == "hazy" || IconName == "nt_hazy") 316 | display.drawBitmap(x,y, fog_icon, 56,48, GxEPD_BLACK); 317 | else if (IconName == "thermo") 318 | display.drawBitmap(x,y, thermo_icon,64,24, GxEPD_BLACK); 319 | else if (IconName == "probrain") 320 | display.drawBitmap(x,y, probrain_icon,32,24, GxEPD_BLACK); 321 | else display.drawBitmap(x,y,nodata_icon, 56,48, GxEPD_BLACK); 322 | } 323 | 324 | void obtain_forecast (String forecast_type) { 325 | static char RxBuf[8704]; 326 | String request; 327 | request = "GET /api/" + API_key + "/"+ forecast_type + "/q/" + Country + "/" + City + ".json HTTP/1.1\r\n"; 328 | request += F("User-Agent: Weather Webserver v"); 329 | request += version; 330 | request += F("\r\n"); 331 | request += F("Accept: */*\r\n"); 332 | request += "Host: " + String(wxserver) + "\r\n"; 333 | request += F("Connection: close\r\n"); 334 | request += F("\r\n"); 335 | Serial.println(request); 336 | Serial.print(F("Connecting to ")); Serial.println(wxserver); 337 | WiFiClient httpclient; 338 | if (!httpclient.connect(wxserver, 80)) { 339 | Serial.println(F("connection failed")); 340 | httpclient.flush(); 341 | httpclient.stop(); 342 | return; 343 | } 344 | Serial.print(request); 345 | httpclient.print(request); //send the http request to the server 346 | httpclient.flush(); 347 | // Collect http response headers and content from Weather Underground, discarding HTTP headers, the content is JSON formatted and will be returned in RxBuf. 348 | uint16_t respLen = 0; 349 | bool skip_headers = true; 350 | String rx_line; 351 | while (httpclient.connected() || httpclient.available()) { 352 | if (skip_headers) { 353 | rx_line = httpclient.readStringUntil('\n'); //Serial.println(rx_line); 354 | if (rx_line.length() <= 1) { // a blank line denotes end of headers 355 | skip_headers = false; 356 | } 357 | } 358 | else { 359 | int bytesIn; 360 | bytesIn = httpclient.read((uint8_t *)&RxBuf[respLen], sizeof(RxBuf) - respLen); 361 | //Serial.print(F("bytesIn ")); Serial.println(bytesIn); 362 | if (bytesIn > 0) { 363 | respLen += bytesIn; 364 | if (respLen > sizeof(RxBuf)) respLen = sizeof(RxBuf); 365 | } 366 | else if (bytesIn < 0) { 367 | Serial.print(F("read error ")); 368 | Serial.println(bytesIn); 369 | } 370 | } 371 | delay(1); 372 | } 373 | httpclient.stop(); 374 | 375 | if (respLen >= sizeof(RxBuf)) { 376 | Serial.print(F("RxBuf overflow ")); 377 | Serial.println(respLen); 378 | delay(1000); 379 | return; 380 | } 381 | RxBuf[respLen++] = '\0'; // Terminate the C string 382 | Serial.print(F("respLen ")); Serial.println(respLen); Serial.println(RxBuf); 383 | if (forecast_type == "forecast"){ 384 | showWeather_forecast(RxBuf); 385 | } 386 | if (forecast_type == "astronomy"){ 387 | showWeather_astronomy(RxBuf); 388 | } 389 | } 390 | 391 | bool showWeather_astronomy(char *json) { 392 | StaticJsonBuffer<1*1024> jsonBuffer; 393 | char *jsonstart = strchr(json, '{'); // Skip characters until first '{' found 394 | //Serial.print(F("jsonstart ")); Serial.println(jsonstart); 395 | if (jsonstart == NULL) { 396 | Serial.println(F("JSON data missing")); 397 | return false; 398 | } 399 | json = jsonstart; 400 | // Parse JSON 401 | JsonObject& root = jsonBuffer.parseObject(json); 402 | if (!root.success()) { 403 | Serial.println(F("jsonBuffer.parseObject() failed")); 404 | return false; 405 | } 406 | // Extract weather info from parsed JSON 407 | JsonObject& current = root["moon_phase"]; 408 | String percentIlluminated = current["percentIlluminated"]; 409 | String phaseofMoon = current["phaseofMoon"]; 410 | int SRhour = current["sunrise"]["hour"]; 411 | int SRminute = current["sunrise"]["minute"]; 412 | int SShour = current["sunset"]["hour"]; 413 | int SSminute = current["sunset"]["minute"]; 414 | int MRhour = current["moonrise"]["hour"]; 415 | int MRminute = current["moonrise"]["minute"]; 416 | int MShour = current["moonset"]["hour"]; 417 | int MSminute = current["moonset"]["minute"]; 418 | Sunrise = (SRhour<10?"0":"")+String(SRhour)+":"+(SRminute<10?"0":"")+String(SRminute); 419 | Sunset = (SShour<10?"0":"")+String(SShour)+":"+(SSminute<10?"0":"")+String(SSminute); 420 | Moonrise = (MRhour<10?"0":"")+String(MRhour)+":"+(MRminute<10?"0":"")+String(MRminute); 421 | Moonset = (MShour<10?"0":"")+String(MShour)+":"+(MSminute<10?"0":"")+String(MSminute); 422 | Moonlight = percentIlluminated; 423 | DphaseofMoon = phaseofMoon; 424 | return true; 425 | } 426 | 427 | bool showWeather_forecast(char *json) { 428 | DynamicJsonBuffer jsonBuffer(8704); 429 | char *jsonstart = strchr(json, '{'); 430 | Serial.print(F("jsonstart ")); Serial.println(jsonstart); 431 | if (jsonstart == NULL) { 432 | Serial.println(F("JSON data missing")); 433 | return false; 434 | } 435 | json = jsonstart; 436 | 437 | // Parse JSON 438 | JsonObject& root = jsonBuffer.parseObject(json); 439 | if (!root.success()) { 440 | Serial.println(F("jsonBuffer.parseObject() failed")); 441 | return false; 442 | } 443 | 444 | JsonObject& forecast = root["forecast"]["simpleforecast"]; 445 | String wday0 = forecast["forecastday"][0]["date"]["weekday_short"]; WDay0 = wday0; 446 | int day0 = forecast["forecastday"][0]["date"]["day"]; day0<10?(Day0="0"+String(day0)):(Day0=String(day0)); 447 | String mon0 = forecast["forecastday"][0]["date"]["monthname_short"]; 448 | String year0 = forecast["forecastday"][0]["date"]["year"]; Day0 += "-" + mon0 + "-" + year0.substring(2); 449 | String icon0 = forecast["forecastday"][0]["icon"]; Icon0 = icon0; 450 | String high0 = forecast["forecastday"][0]["high"]["celsius"]; High0 = high0; 451 | String low0 = forecast["forecastday"][0]["low"]["celsius"]; Low0 = low0; 452 | String conditions0 = forecast["forecastday"][0]["conditions"]; Conditions0 = conditions0; 453 | String pop0 = forecast["forecastday"][0]["pop"]; Pop0 = pop0; 454 | String averagehumidity0 = forecast["forecastday"][0]["avehumidity"]; Averagehumidity0 = averagehumidity0; 455 | 456 | String wday1 = forecast["forecastday"][1]["date"]["weekday_short"]; WDay1 = wday1; 457 | int day1 = forecast["forecastday"][1]["date"]["day"]; day1<10?(Day1="0"+String(day1)):(Day1=String(day1)); 458 | String mon1 = forecast["forecastday"][1]["date"]["monthname_short"]; 459 | String year1 = forecast["forecastday"][1]["date"]["year"]; Day1 += "-" + mon1 + "-" + year1.substring(2); 460 | String icon1 = forecast["forecastday"][1]["icon"]; Icon1 = icon1; 461 | String high1 = forecast["forecastday"][1]["high"]["celsius"]; High1 = high1; 462 | String low1 = forecast["forecastday"][1]["low"]["celsius"]; Low1 = low1; 463 | String conditions1 = forecast["forecastday"][1]["conditions"]; Conditions1 = conditions1; 464 | String pop1 = forecast["forecastday"][1]["pop"]; Pop1 = pop1; 465 | String averagehumidity1 = forecast["forecastday"][1]["avehumidity"]; Averagehumidity1 = averagehumidity1; 466 | 467 | String wday2 = forecast["forecastday"][2]["date"]["weekday_short"]; WDay2 = wday2; 468 | int day2 = forecast["forecastday"][2]["date"]["day"]; day2<10?(Day2="0"+String(day2)):(Day2=String(day2)); 469 | String mon2 = forecast["forecastday"][2]["date"]["monthname_short"]; 470 | String year2 = forecast["forecastday"][2]["date"]["year"]; Day2 += "-" + mon2 + "-" + year2.substring(2); 471 | String icon2 = forecast["forecastday"][2]["icon"]; Icon2 = icon2; 472 | String high2 = forecast["forecastday"][2]["high"]["celsius"]; High2 = high2; 473 | String low2 = forecast["forecastday"][2]["low"]["celsius"]; Low2 = low2; 474 | String conditions2 = forecast["forecastday"][2]["conditions"]; Conditions2 = conditions2; 475 | String pop2 = forecast["forecastday"][2]["pop"]; Pop2 = pop2; 476 | String averagehumidity2 = forecast["forecastday"][2]["avehumidity"]; Averagehumidity2 = averagehumidity2; 477 | 478 | String wday3 = forecast["forecastday"][3]["date"]["weekday_short"]; WDay3 = wday3; 479 | int day3 = forecast["forecastday"][3]["date"]["day"]; day3<10?(Day3="0"+String(day3)):(Day3=String(day3)); 480 | String mon3 = forecast["forecastday"][3]["date"]["monthname_short"]; 481 | String year3 = forecast["forecastday"][3]["date"]["year"]; Day3 += "-" + mon3 + "-" + year3.substring(2); 482 | String icon3 = forecast["forecastday"][3]["icon"]; Icon3 = icon3; 483 | String high3 = forecast["forecastday"][3]["high"]["celsius"]; High3 = high3; 484 | String low3 = forecast["forecastday"][3]["low"]["celsius"]; Low3 = low3; 485 | String conditions3 = forecast["forecastday"][3]["conditions"]; Conditions3 = conditions3; 486 | String pop3 = forecast["forecastday"][3]["pop"]; Pop3 = pop3; 487 | String averagehumidity3 = forecast["forecastday"][3]["avehumidity"]; Averagehumidity3 = averagehumidity3; 488 | 489 | return true; 490 | } 491 | 492 | int StartWiFi(const char* ssid, const char* password){ 493 | int connAttempts = 0; 494 | Serial.println("\r\nConnecting to: "+String(ssid)); 495 | WiFi.begin(ssid, password); 496 | while (WiFi.status() != WL_CONNECTED ) { 497 | delay(500); Serial.print("."); 498 | if(connAttempts > 20) return -5; 499 | connAttempts++; 500 | } 501 | Serial.println("WiFi connected\r\nIP address: "); 502 | Serial.println(WiFi.localIP()); 503 | return 1; 504 | } 505 | 506 | void clear_screen() { 507 | display.fillScreen(GxEPD_WHITE); 508 | display.update(); 509 | } 510 | 511 | void SetupTime(){ 512 | configTime(1 * 3600, 0, "pool.ntp.org", "time.nist.gov"); 513 | Serial.println("\nWaiting for time"); 514 | while (!time(nullptr)) { 515 | delay(500); 516 | } 517 | } 518 | 519 | 520 | 521 | -------------------------------------------------------------------------------- /ESP8266_WU_ePaper42_3day_Forecast_04.ino: -------------------------------------------------------------------------------- 1 | /*######################## Weather Display ############################# 2 | * Receives and displays the weather forecast from the Weather Underground and then displays using a 3 | * JSON decoder wx data to display on a web page using a webserver. 4 | * Weather data received via WiFi connection to Weather Underground Servers and using their 'Forecast' API and data 5 | * is decoded using Copyright Benoit Blanchon's (c) 2014-2017 excellent JSON library. 6 | * This source code is protected under the terms of the MIT License and is copyright (c) 2017 by David Bird and permission is hereby granted, free of charge, to 7 | * any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software 8 | * without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, but not to sub-license and/or 9 | * to sell copies of the Software or to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | * 15 | * See more at http://dsbird.org.uk */ 16 | 17 | #include 18 | #include // https://github.com/bblanchon/ArduinoJson 19 | #include 20 | #include "time.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | //################# LIBRARIES ########################## 30 | String version = "1.0"; // Version of this program 31 | //################ VARIABLES ########################### 32 | 33 | const unsigned char thermo_icon[] = { // 64x24 34 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 35 | 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xfb, 0xdf, 0xff, 0xff, 0xf3, 0xff, 0xdf, 0xff, 0xfb, 0x5f, 0xff, 0xff, 0xe9, 0xff, 0x0f, 0xff, 36 | 0xfb, 0x5f, 0xfc, 0x7f, 0xed, 0xbf, 0x0f, 0xff, 0xfa, 0x5e, 0x18, 0x3f, 0xed, 0x7e, 0x07, 0xff, 0xfb, 0x5e, 0xd9, 0x9f, 0xed, 0x7e, 0x07, 0xff, 37 | 0xfb, 0x5e, 0xd3, 0xdf, 0xe3, 0x7c, 0x03, 0xff, 0xfa, 0x5e, 0x13, 0xff, 0xf2, 0xfc, 0x03, 0xdf, 0xfb, 0x5f, 0xf7, 0xff, 0xfe, 0xfc, 0x13, 0xdf, 38 | 0xfb, 0x5f, 0xf7, 0xff, 0xfd, 0x9c, 0x07, 0xdf, 0xfa, 0x5f, 0xf7, 0xff, 0xfd, 0x4e, 0x07, 0x8f, 0xfb, 0x5f, 0xf3, 0xdf, 0xfb, 0x6f, 0x0f, 0x8f, 39 | 0xfb, 0x5f, 0xf1, 0x9f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x5f, 0xf8, 0x3f, 0xfb, 0x6f, 0xff, 0x07, 0xfa, 0x1f, 0xfc, 0x7f, 0xf7, 0x1f, 0xff, 0x03, 40 | 0xf0, 0x0f, 0xff, 0xff, 0xff, 0x9f, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x13, 41 | 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f }; 42 | 43 | const unsigned char probrain_icon[] = { // 32x24 44 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xc7, 0xe0, 0x7f, 45 | 0xff, 0xdf, 0xf8, 0x3f, 0xff, 0x1f, 0xff, 0x9f, 0xfc, 0x3f, 0xff, 0xcf, 0xe0, 0x7f, 0xdf, 0xc7, 0xc0, 0xff, 0x9f, 0xc3, 0x9f, 0xff, 0x1f, 0xf9, 46 | 0x3f, 0xff, 0x1f, 0xfc, 0x3f, 0xfe, 0x1f, 0xfc, 0x3f, 0xfb, 0x1f, 0xfc, 0x9f, 0xf3, 0x3b, 0xf9, 0xc0, 0x63, 0xf3, 0x03, 0xe0, 0x63, 0xe3, 0x87, 47 | 0xff, 0xc3, 0xe3, 0xff, 0xff, 0xe7, 0xc3, 0xff, 0xff, 0xe7, 0xe3, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 48 | 49 | // pins_arduino.h, e.g. WEMOS D1 Mini 50 | //static const uint8_t SS = D8; 51 | //static const uint8_t MOSI = D7; 52 | //static const uint8_t MISO = ; 53 | //static const uint8_t SCK = D5; 54 | // GxIO_SPI(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1, int8_t bl = -1); 55 | GxIO_Class io(SPI, D8, D3, D4); 56 | // GxGDEP015OC1(GxIO& io, uint8_t rst = D4, uint8_t busy = D2); 57 | GxEPD_Class display(io, D4, D6); 58 | 59 | //------ NETWORK VARIABLES--------- 60 | // Use your own API key by signing up for a free developer account at http://www.wunderground.com/weather/api/ 61 | String API_key = "2--------------7"; // See: http://www.wunderground.com/weather/api/d/docs (change here with your KEY) 62 | String City = "Melksham"; // Your home city 63 | String Country = "UK"; // Your country 64 | String Conditions = "conditions"; // See: http://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1 65 | char wxserver[] = "api.wunderground.com"; // Address for WeatherUnderGround 66 | unsigned long lastConnectionTime = 0; // Last time you connected to the server, in milliseconds 67 | const unsigned long postingInterval = 15L*60L*1000L; // Delay between updates, in milliseconds, WU allows 500 requests per-day maximum, set to every 10-mins or 144/day 68 | String Units = "X"; // M for Metric, X for Mixed and I for Imperial 69 | 70 | //################ PROGRAM VARIABLES and OBJECTS ################ 71 | // Conditions 72 | String WDay0, Day0, Icon0, High0, Low0, Conditions0, Pop0, Averagehumidity0, 73 | WDay1, Day1, Icon1, High1, Low1, Conditions1, Pop1, Averagehumidity1, 74 | WDay2, Day2, Icon2, High2, Low2, Conditions2, Pop2, Averagehumidity2, 75 | WDay3, Day3, Icon3, High3, Low3, Conditions3, Pop3, Averagehumidity3; 76 | 77 | 78 | // Astronomy 79 | String DphaseofMoon, Sunrise, Sunset, Moonrise, Moonset, Moonlight; 80 | 81 | const char* ssid = "your_ssid"; 82 | const char* password = "your_password"; 83 | const char* host = "api.wunderground.com"; 84 | 85 | String currCondString; // string to hold received API weather data 86 | 87 | WiFiClient client; // wifi client object 88 | #include "imglib/gridicons_align_right.h" 89 | 90 | void setup() { 91 | Serial.begin(115200); 92 | display.init(); 93 | display.fillScreen(GxEPD_WHITE); 94 | display.setTextColor(GxEPD_BLACK); 95 | display.setFont(&FreeSans9pt7b); 96 | display.setCursor(0, 12); 97 | display.setRotation(0); 98 | display.println(F("David Bird (C) 2017\n\rStarting...\n\rReading and Decoding Wx Data...")); 99 | display.update(); 100 | delay(500); 101 | StartWiFi(ssid,password); 102 | SetupTime(); 103 | time_t now = time(nullptr); 104 | Serial.println(ctime(&now)); 105 | lastConnectionTime = millis(); 106 | obtain_forecast("forecast"); 107 | obtain_forecast("astronomy"); 108 | DisplayForecast(); 109 | //ESP.deepSleep(10*60*1000000, WAKE_RF_DEFAULT); // Sleep for 10 minutes 110 | } 111 | 112 | void loop() { 113 | if (millis() - lastConnectionTime > postingInterval) { 114 | obtain_forecast("forecast"); 115 | obtain_forecast("astronomy"); 116 | lastConnectionTime = millis(); 117 | DisplayForecast(); 118 | } 119 | } 120 | void DisplayForecast(){ // Display is 400x300 resolution 121 | display.fillScreen(GxEPD_WHITE); 122 | display.setCursor(0,12); 123 | DisplayWXicon(0,0, Icon0); DisplayWXicon(75,0, "thermo"); DisplayWXicon(155,0, "probrain"); 124 | display.setCursor(200,15); display.println(WDay0 + " " + Day0); 125 | display.setCursor(195,30); display.println(Conditions0); 126 | display.setCursor(60,40); display.println(High0 + "/" + Low0); 127 | display.setCursor(115,40); display.println(Averagehumidity0 + "%"); 128 | display.setCursor(158,40); display.println(Pop0 + "%"); 129 | display.setCursor(60,55); display.println(F("--------------------------")); 130 | DisplayWXicon(0,60, Icon1); DisplayWXicon(75,60, "thermo"); DisplayWXicon(155,60, "probrain"); 131 | display.setCursor(200,75); display.println(WDay1 + " " + Day1); 132 | display.setCursor(195,90); display.println(Conditions1); 133 | display.setCursor(60,100); display.println(High1 + "/" + Low1); 134 | display.setCursor(115,100); display.println(Averagehumidity1 + "%"); 135 | display.setCursor(158,100); display.println(Pop1 + "%"); 136 | display.setCursor(60,115); display.println(F("-------------------------")); 137 | DisplayWXicon(0,120, Icon2); DisplayWXicon(75,120, "thermo"); DisplayWXicon(155,120, "probrain"); 138 | display.setCursor(200,135); display.println(WDay2 + " " + Day2); 139 | display.setCursor(195,150); display.println(Conditions2); 140 | display.setCursor(60,160); display.println(High2 + "/" + Low2); 141 | display.setCursor(115,160); display.println(Averagehumidity2 + "%"); 142 | display.setCursor(158,160); display.println(Pop2 + "%"); 143 | display.setCursor(60,175); display.println(F("--------------------------")); 144 | DisplayWXicon(0,180, Icon3); DisplayWXicon(75,180, "thermo"); DisplayWXicon(155,180, "probrain"); 145 | display.setCursor(200,195); display.println(WDay3 + " " + Day3); 146 | display.setCursor(195,210); display.println(Conditions3); 147 | display.setCursor(60,220); display.println(High3 + "/" + Low3); 148 | display.setCursor(115,220); display.println(Averagehumidity3 + "%"); 149 | display.setCursor(158,220); display.println(Pop3 + "%"); 150 | display.setCursor(60,235); display.println(F("--------------------------")); 151 | 152 | display.setCursor(000,245); display.println("Sunrise/Set"); 153 | display.setCursor(000,262); display.println(Sunrise+"/"+Sunset); 154 | display.setCursor(110,245); display.println("Moonrise/Set"); 155 | display.setCursor(110,262); display.println(Moonrise+"/"+Moonset); 156 | display.setCursor(225,245); display.println("Moonphase"); 157 | display.setCursor(225,262); display.println(DphaseofMoon+" "+Moonlight+"%"); 158 | time_t now = time(nullptr); display.setCursor(85,292); display.println(ctime(&now)); 159 | 160 | display.update(); 161 | } 162 | 163 | /* Avialble symbols 164 | MostlyCloudy(x,y,scale) 165 | MostlySunny(x,y,scale) 166 | Rain(x,y,scale) 167 | Cloudy(x,y,scale) 168 | Sunny(x,y,scale) 169 | ExpectRain(x,y,scale) 170 | Tstorms(x,y,scale) 171 | Snow(x,y,scale) 172 | Fog(x,y,scale) 173 | Nodata(x,y,scale) 174 | */ 175 | 176 | void DisplayWXicon(int x, int y, String IconName){ 177 | int scale = 10; // Adjust size as necessary 178 | Serial.println(IconName); 179 | if (IconName == "rain" || IconName == "nt_rain") Rain(x,y, scale); 180 | else if (IconName == "chancerain" || IconName == "nt_chancerain") ExpectRain(x,y,scale); 181 | else if (IconName == "snow" || IconName == "nt_snow" || 182 | IconName == "flurries" || IconName == "nt_flurries" || 183 | IconName == "chancesnow" || IconName == "nt_chancesnow" || 184 | IconName == "chanceflurries" || IconName == "nt_chanceflurries") Snow(x,y,scale); 185 | else if (IconName == "sleet" || IconName == "nt_sleet" || 186 | IconName == "chancesleet" || IconName == "nt_chancesleet") Snow(x,y,scale); 187 | else if (IconName == "sunny" || IconName == "nt_sunny" || 188 | IconName == "clear" || IconName == "nt_clear") Sunny(x,y,scale); 189 | else if (IconName == "partlysunny" || IconName == "nt_partlysunny" || 190 | IconName == "mostlysunny" || IconName == "nt_mostlysunny") MostlySunny(x,y,scale); 191 | else if (IconName == "cloudy" || IconName == "nt_cloudy" || 192 | IconName == "mostlycloudy" || IconName == "nt_mostlycloudy" || 193 | IconName == "partlycloudy" || IconName == "nt_partlycloudy") Cloudy(x,y,scale); 194 | else if (IconName == "tstorms" || IconName == "nt_tstorms" || 195 | IconName == "chancetstorms" || IconName == "nt_chancetstorms") Tstorms(x,y,scale); 196 | else if (IconName == "fog" || IconName == "nt_fog" || 197 | IconName == "hazy" || IconName == "nt_hazy") Fog(x,y,scale); 198 | else if (IconName == "thermo") 199 | display.drawBitmap(x,y, thermo_icon,64,24, GxEPD_BLACK); 200 | else if (IconName == "probrain") 201 | display.drawBitmap(x,y, probrain_icon,32,24, GxEPD_BLACK); 202 | else Nodata(x,y,scale); 203 | } 204 | 205 | void obtain_forecast (String forecast_type) { 206 | static char RxBuf[8704]; 207 | String request; 208 | request = "GET /api/" + API_key + "/"+ forecast_type + "/q/" + Country + "/" + City + ".json HTTP/1.1\r\n"; 209 | request += F("User-Agent: Weather Webserver v"); 210 | request += version; 211 | request += F("\r\n"); 212 | request += F("Accept: */*\r\n"); 213 | request += "Host: " + String(wxserver) + "\r\n"; 214 | request += F("Connection: close\r\n"); 215 | request += F("\r\n"); 216 | Serial.println(request); 217 | Serial.print(F("Connecting to ")); Serial.println(wxserver); 218 | WiFiClient httpclient; 219 | if (!httpclient.connect(wxserver, 80)) { 220 | Serial.println(F("connection failed")); 221 | httpclient.flush(); 222 | httpclient.stop(); 223 | return; 224 | } 225 | Serial.print(request); 226 | httpclient.print(request); //send the http request to the server 227 | httpclient.flush(); 228 | // Collect http response headers and content from Weather Underground, discarding HTTP headers, the content is JSON formatted and will be returned in RxBuf. 229 | uint16_t respLen = 0; 230 | bool skip_headers = true; 231 | String rx_line; 232 | while (httpclient.connected() || httpclient.available()) { 233 | if (skip_headers) { 234 | rx_line = httpclient.readStringUntil('\n'); //Serial.println(rx_line); 235 | if (rx_line.length() <= 1) { // a blank line denotes end of headers 236 | skip_headers = false; 237 | } 238 | } 239 | else { 240 | int bytesIn; 241 | bytesIn = httpclient.read((uint8_t *)&RxBuf[respLen], sizeof(RxBuf) - respLen); 242 | //Serial.print(F("bytesIn ")); Serial.println(bytesIn); 243 | if (bytesIn > 0) { 244 | respLen += bytesIn; 245 | if (respLen > sizeof(RxBuf)) respLen = sizeof(RxBuf); 246 | } 247 | else if (bytesIn < 0) { 248 | Serial.print(F("read error ")); 249 | Serial.println(bytesIn); 250 | } 251 | } 252 | delay(1); 253 | } 254 | httpclient.stop(); 255 | 256 | if (respLen >= sizeof(RxBuf)) { 257 | Serial.print(F("RxBuf overflow ")); 258 | Serial.println(respLen); 259 | delay(1000); 260 | return; 261 | } 262 | RxBuf[respLen++] = '\0'; // Terminate the C string 263 | Serial.print(F("respLen ")); Serial.println(respLen); Serial.println(RxBuf); 264 | if (forecast_type == "forecast"){ 265 | showWeather_forecast(RxBuf); 266 | } 267 | if (forecast_type == "astronomy"){ 268 | showWeather_astronomy(RxBuf); 269 | } 270 | } 271 | 272 | bool showWeather_astronomy(char *json) { 273 | StaticJsonBuffer<1*1024> jsonBuffer; 274 | char *jsonstart = strchr(json, '{'); // Skip characters until first '{' found 275 | //Serial.print(F("jsonstart ")); Serial.println(jsonstart); 276 | if (jsonstart == NULL) { 277 | Serial.println(F("JSON data missing")); 278 | return false; 279 | } 280 | json = jsonstart; 281 | // Parse JSON 282 | JsonObject& root = jsonBuffer.parseObject(json); 283 | if (!root.success()) { 284 | Serial.println(F("jsonBuffer.parseObject() failed")); 285 | return false; 286 | } 287 | // Extract weather info from parsed JSON 288 | JsonObject& current = root["moon_phase"]; 289 | String percentIlluminated = current["percentIlluminated"]; 290 | String phaseofMoon = current["phaseofMoon"]; 291 | int SRhour = current["sunrise"]["hour"]; 292 | int SRminute = current["sunrise"]["minute"]; 293 | int SShour = current["sunset"]["hour"]; 294 | int SSminute = current["sunset"]["minute"]; 295 | int MRhour = current["moonrise"]["hour"]; 296 | int MRminute = current["moonrise"]["minute"]; 297 | int MShour = current["moonset"]["hour"]; 298 | int MSminute = current["moonset"]["minute"]; 299 | Sunrise = (SRhour<10?"0":"")+String(SRhour)+":"+(SRminute<10?"0":"")+String(SRminute); 300 | Sunset = (SShour<10?"0":"")+String(SShour)+":"+(SSminute<10?"0":"")+String(SSminute); 301 | Moonrise = (MRhour<10?"0":"")+String(MRhour)+":"+(MRminute<10?"0":"")+String(MRminute); 302 | Moonset = (MShour<10?"0":"")+String(MShour)+":"+(MSminute<10?"0":"")+String(MSminute); 303 | Moonlight = percentIlluminated; 304 | DphaseofMoon = phaseofMoon; 305 | return true; 306 | } 307 | 308 | bool showWeather_forecast(char *json) { 309 | DynamicJsonBuffer jsonBuffer(8704); 310 | char *jsonstart = strchr(json, '{'); 311 | Serial.print(F("jsonstart ")); Serial.println(jsonstart); 312 | if (jsonstart == NULL) { 313 | Serial.println(F("JSON data missing")); 314 | return false; 315 | } 316 | json = jsonstart; 317 | 318 | // Parse JSON 319 | JsonObject& root = jsonBuffer.parseObject(json); 320 | if (!root.success()) { 321 | Serial.println(F("jsonBuffer.parseObject() failed")); 322 | return false; 323 | } 324 | 325 | JsonObject& forecast = root["forecast"]["simpleforecast"]; 326 | String wday0 = forecast["forecastday"][0]["date"]["weekday_short"]; WDay0 = wday0; 327 | int day0 = forecast["forecastday"][0]["date"]["day"]; day0<10?(Day0="0"+String(day0)):(Day0=String(day0)); 328 | String mon0 = forecast["forecastday"][0]["date"]["monthname_short"]; 329 | String year0 = forecast["forecastday"][0]["date"]["year"]; Day0 += "-" + mon0 + "-" + year0.substring(2); 330 | String icon0 = forecast["forecastday"][0]["icon"]; Icon0 = icon0; 331 | String high0 = forecast["forecastday"][0]["high"]["celsius"]; High0 = high0; 332 | String low0 = forecast["forecastday"][0]["low"]["celsius"]; Low0 = low0; 333 | String conditions0 = forecast["forecastday"][0]["conditions"]; Conditions0 = conditions0; 334 | String pop0 = forecast["forecastday"][0]["pop"]; Pop0 = pop0; 335 | String averagehumidity0 = forecast["forecastday"][0]["avehumidity"]; Averagehumidity0 = averagehumidity0; 336 | 337 | String wday1 = forecast["forecastday"][1]["date"]["weekday_short"]; WDay1 = wday1; 338 | int day1 = forecast["forecastday"][1]["date"]["day"]; day1<10?(Day1="0"+String(day1)):(Day1=String(day1)); 339 | String mon1 = forecast["forecastday"][1]["date"]["monthname_short"]; 340 | String year1 = forecast["forecastday"][1]["date"]["year"]; Day1 += "-" + mon1 + "-" + year1.substring(2); 341 | String icon1 = forecast["forecastday"][1]["icon"]; Icon1 = icon1; 342 | String high1 = forecast["forecastday"][1]["high"]["celsius"]; High1 = high1; 343 | String low1 = forecast["forecastday"][1]["low"]["celsius"]; Low1 = low1; 344 | String conditions1 = forecast["forecastday"][1]["conditions"]; Conditions1 = conditions1; 345 | String pop1 = forecast["forecastday"][1]["pop"]; Pop1 = pop1; 346 | String averagehumidity1 = forecast["forecastday"][1]["avehumidity"]; Averagehumidity1 = averagehumidity1; 347 | 348 | String wday2 = forecast["forecastday"][2]["date"]["weekday_short"]; WDay2 = wday2; 349 | int day2 = forecast["forecastday"][2]["date"]["day"]; day2<10?(Day2="0"+String(day2)):(Day2=String(day2)); 350 | String mon2 = forecast["forecastday"][2]["date"]["monthname_short"]; 351 | String year2 = forecast["forecastday"][2]["date"]["year"]; Day2 += "-" + mon2 + "-" + year2.substring(2); 352 | String icon2 = forecast["forecastday"][2]["icon"]; Icon2 = icon2; 353 | String high2 = forecast["forecastday"][2]["high"]["celsius"]; High2 = high2; 354 | String low2 = forecast["forecastday"][2]["low"]["celsius"]; Low2 = low2; 355 | String conditions2 = forecast["forecastday"][2]["conditions"]; Conditions2 = conditions2; 356 | String pop2 = forecast["forecastday"][2]["pop"]; Pop2 = pop2; 357 | String averagehumidity2 = forecast["forecastday"][2]["avehumidity"]; Averagehumidity2 = averagehumidity2; 358 | 359 | String wday3 = forecast["forecastday"][3]["date"]["weekday_short"]; WDay3 = wday3; 360 | int day3 = forecast["forecastday"][3]["date"]["day"]; day3<10?(Day3="0"+String(day3)):(Day3=String(day3)); 361 | String mon3 = forecast["forecastday"][3]["date"]["monthname_short"]; 362 | String year3 = forecast["forecastday"][3]["date"]["year"]; Day3 += "-" + mon3 + "-" + year3.substring(2); 363 | String icon3 = forecast["forecastday"][3]["icon"]; Icon3 = icon3; 364 | String high3 = forecast["forecastday"][3]["high"]["celsius"]; High3 = high3; 365 | String low3 = forecast["forecastday"][3]["low"]["celsius"]; Low3 = low3; 366 | String conditions3 = forecast["forecastday"][3]["conditions"]; Conditions3 = conditions3; 367 | String pop3 = forecast["forecastday"][3]["pop"]; Pop3 = pop3; 368 | String averagehumidity3 = forecast["forecastday"][3]["avehumidity"]; Averagehumidity3 = averagehumidity3; 369 | 370 | return true; 371 | } 372 | 373 | int StartWiFi(const char* ssid, const char* password){ 374 | int connAttempts = 0; 375 | Serial.println("\r\nConnecting to: "+String(ssid)); 376 | WiFi.begin(ssid, password); 377 | while (WiFi.status() != WL_CONNECTED ) { 378 | delay(500); Serial.print("."); 379 | if(connAttempts > 20) return -5; 380 | connAttempts++; 381 | } 382 | Serial.println("WiFi connected\r\nIP address: "); 383 | Serial.println(WiFi.localIP()); 384 | return 1; 385 | } 386 | 387 | void clear_screen() { 388 | display.fillScreen(GxEPD_WHITE); 389 | display.update(); 390 | } 391 | 392 | void SetupTime(){ 393 | configTime(1 * 3600, 0, "pool.ntp.org", "time.nist.gov"); 394 | Serial.println("\nWaiting for time"); 395 | while (!time(nullptr)) { 396 | delay(500); 397 | } 398 | } 399 | 400 | 401 | //########################################################################### 402 | // Symbols are drawn on a relative 10x15 grid and 1 scale unit = 1 drawing unit 403 | void addcloud(int x, int y, int scale) { 404 | int linesize = 3; 405 | //Draw cloud outer 406 | display.fillCircle(x-scale*3, y, scale, GxEPD_BLACK); // Left most circle 407 | display.fillCircle(x+scale*3, y, scale, GxEPD_BLACK); // Right most circle 408 | display.fillCircle(x-scale, y-scale, scale*1.4, GxEPD_BLACK); // left middle upper circle 409 | display.fillCircle(x+scale*1.5, y-scale*1.3, scale*1.75, GxEPD_BLACK); // Right middle upper circle 410 | display.fillRect(x-scale*3, y-scale, scale*6, scale*2+1,GxEPD_BLACK); // Upper and lower lines 411 | //Clear cloud inner 412 | display.fillCircle(x-scale*3, y, scale-linesize, GxEPD_WHITE); // Clear left most circle 413 | display.fillCircle(x+scale*3, y, scale-linesize, GxEPD_WHITE); // Clear right most circle 414 | display.fillCircle(x-scale, y-scale, scale*1.4-linesize, GxEPD_WHITE); // left middle upper circle 415 | display.fillCircle(x+scale*1.5, y-scale*1.3, scale*1.75-linesize, GxEPD_WHITE); // Right middle upper circle 416 | display.fillRect(x-scale*3, y-scale+linesize, scale*6, scale*2-linesize*2+1, GxEPD_WHITE); // Upper and lower lines 417 | } 418 | 419 | void addrain(int x, int y, int scale){ 420 | y = y + scale/2; 421 | for (int i = 0; i < 6; i++){ 422 | display.drawLine(x-scale*4+scale*i*1.3+0, y+scale*1.9, x-scale*3.5+scale*i*1.3+0, y+scale,GxEPD_BLACK); 423 | display.drawLine(x-scale*4+scale*i*1.3+1, y+scale*1.9, x-scale*3.5+scale*i*1.3+1, y+scale,GxEPD_BLACK); 424 | display.drawLine(x-scale*4+scale*i*1.3+2, y+scale*1.9, x-scale*3.5+scale*i*1.3+2, y+scale,GxEPD_BLACK); 425 | } 426 | } 427 | 428 | void addsnow(int x, int y, int scale){ 429 | int dxo, dyo, dxi, dyi; 430 | for (int flakes = 0; flakes < 5;flakes++){ 431 | for (int i = 0; i <360; i = i + 45) { 432 | dxo = 0.5*scale * cos((i-90)*3.14/180); dxi = dxo*0.1; 433 | dyo = 0.5*scale * sin((i-90)*3.14/180); dyi = dyo*0.1; 434 | display.drawLine(dxo+x+0+flakes*1.5*scale-scale*3,dyo+y+scale*2,dxi+x+0+flakes*1.5*scale-scale*3,dyi+y+scale*2,GxEPD_BLACK); 435 | } 436 | } 437 | } 438 | 439 | void addtstorm(int x, int y, int scale){ 440 | y = y + scale/2; 441 | for (int i = 0; i < 5; i++){ 442 | display.drawLine(x-scale*4+scale*i*1.5+0, y+scale*1.5, x-scale*3.5+scale*i*1.5+0, y+scale,GxEPD_BLACK); 443 | display.drawLine(x-scale*4+scale*i*1.5+1, y+scale*1.5, x-scale*3.5+scale*i*1.5+1, y+scale,GxEPD_BLACK); 444 | display.drawLine(x-scale*4+scale*i*1.5+2, y+scale*1.5, x-scale*3.5+scale*i*1.5+2, y+scale,GxEPD_BLACK); 445 | display.drawLine(x-scale*4+scale*i*1.5, y+scale*1.5+0, x-scale*3+scale*i*1.5+0, y+scale*1.5+0,GxEPD_BLACK); 446 | display.drawLine(x-scale*4+scale*i*1.5, y+scale*1.5+1, x-scale*3+scale*i*1.5+0, y+scale*1.5+1,GxEPD_BLACK); 447 | display.drawLine(x-scale*4+scale*i*1.5, y+scale*1.5+2, x-scale*3+scale*i*1.5+0, y+scale*1.5+2,GxEPD_BLACK); 448 | display.drawLine(x-scale*3.5+scale*i*1.4+0, y+scale*2.5, x-scale*3+scale*i*1.5+0, y+scale*1.5,GxEPD_BLACK); 449 | display.drawLine(x-scale*3.5+scale*i*1.4+1, y+scale*2.5, x-scale*3+scale*i*1.5+1, y+scale*1.5,GxEPD_BLACK); 450 | display.drawLine(x-scale*3.5+scale*i*1.4+2, y+scale*2.5, x-scale*3+scale*i*1.5+2, y+scale*1.5,GxEPD_BLACK); 451 | } 452 | } 453 | 454 | void addsun(int x, int y, int scale) { 455 | int linesize = 3; 456 | int dxo, dyo, dxi, dyi; 457 | display.fillCircle(x, y, scale,GxEPD_BLACK); 458 | display.fillCircle(x, y, scale-linesize,GxEPD_WHITE); 459 | for (float i = 0; i <360; i = i + 45) { 460 | dxo = 2.2*scale * cos((i-90)*3.14/180); dxi = dxo * 0.6; 461 | dyo = 2.2*scale * sin((i-90)*3.14/180); dyi = dyo * 0.6; 462 | if (i == 0 || i == 180) { 463 | display.drawLine(dxo+x-1,dyo+y,dxi+x-1,dyi+y,GxEPD_BLACK); 464 | display.drawLine(dxo+x+0,dyo+y,dxi+x+0,dyi+y,GxEPD_BLACK); 465 | display.drawLine(dxo+x+1,dyo+y,dxi+x+1,dyi+y,GxEPD_BLACK); 466 | } 467 | if (i == 90 || i == 270) { 468 | display.drawLine(dxo+x,dyo+y-1,dxi+x,dyi+y-1,GxEPD_BLACK); 469 | display.drawLine(dxo+x,dyo+y+0,dxi+x,dyi+y+0,GxEPD_BLACK); 470 | display.drawLine(dxo+x,dyo+y+1,dxi+x,dyi+y+1,GxEPD_BLACK); 471 | } 472 | if (i == 45 || i == 135 || i == 225 || i == 315) { 473 | display.drawLine(dxo+x-1,dyo+y,dxi+x-1,dyi+y,GxEPD_BLACK); 474 | display.drawLine(dxo+x+0,dyo+y,dxi+x+0,dyi+y,GxEPD_BLACK); 475 | display.drawLine(dxo+x+1,dyo+y,dxi+x+1,dyi+y,GxEPD_BLACK); 476 | } 477 | } 478 | } 479 | 480 | void addfog(int x, int y, int scale){ 481 | int linesize = 4; 482 | for (int i = 0; i < 5; i++){ 483 | display.fillRect(x-scale*3, y+scale*1.5, scale*6, linesize, GxEPD_BLACK); 484 | display.fillRect(x-scale*3, y+scale*2, scale*6, linesize, GxEPD_BLACK); 485 | display.fillRect(x-scale*3, y+scale*2.5, scale*6, linesize, GxEPD_BLACK); 486 | } 487 | } 488 | 489 | void MostlyCloudy(int x, int y, int scale){ 490 | addsun(x-scale*1.8,y-scale*1.8,scale); 491 | addcloud(x,y,scale); 492 | } 493 | 494 | void MostlySunny(int x, int y, int scale){ 495 | addcloud(x,y,scale); 496 | addsun(x-scale*1.8,y-scale*1.8,scale); 497 | } 498 | 499 | void Rain(int x, int y, int scale){ 500 | addcloud(x,y,scale); 501 | addrain(x,y,scale); 502 | } 503 | 504 | void Cloudy(int x, int y, int scale){ 505 | addcloud(x,y,scale); 506 | } 507 | 508 | void Sunny(int x, int y, int scale){ 509 | scale = scale * 1.5; 510 | addsun(x,y,scale); 511 | } 512 | 513 | void ExpectRain(int x, int y, int scale){ 514 | addsun(x-scale*1.8,y-scale*1.8,scale); 515 | addcloud(x,y,scale); 516 | addrain(x,y,scale); 517 | } 518 | 519 | void Tstorms(int x, int y, int scale){ 520 | addcloud(x,y,scale); 521 | addtstorm(x,y,scale); 522 | } 523 | 524 | void Snow(int x, int y, int scale){ 525 | addcloud(x,y,scale); 526 | addsnow(x,y,scale); 527 | } 528 | 529 | void Fog(int x, int y, int scale){ 530 | addcloud(x,y,scale); 531 | addfog(x,y,scale); 532 | } 533 | 534 | void Nodata(int x, int y, int scale){ 535 | if (scale == 10) display.setTextSize(3); else display.setTextSize(1); 536 | display.setCursor(x,y); 537 | display.println("?"); 538 | display.setTextSize(1); 539 | } 540 | 541 | //########################################################################### 542 | 543 | 544 | -------------------------------------------------------------------------------- /Licence.txt: -------------------------------------------------------------------------------- 1 | This software, the ideas and concepts is Copyright (c) David Bird 2014 and beyond. 2 | 3 | All rights to this software are reserved. 4 | 5 | It is prohibited to redistribute or reproduce of any part or all of the software contents in any form other than the following: 6 | 7 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 8 | 9 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 10 | 11 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 12 | 13 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 14 | 15 | 5. You MUST include all of this copyright and permission notice ('as annotated') and this shall be included in all copies or substantial portions of the software and where the software use is visible to an end-user. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. 18 | 19 | FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | 21 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-8266-ePaper-Weather-Display-Examples 2 | 3 | This is self-contained sourcecode, so no external graphic files are required. 4 | 5 | To implement the code, choose either the ESP8266 or ESP32 version and connect your display to your processor as for an SPI device, load the code into your IDE, enter your Weather Underground API key and WiFi credentials (SSID, Password) compile and upload. 6 | 7 | SPI pins are found in the source code. Note some ESP32 modules will not allow pins like 5, 19, 21, 22 to be re-defined as the compiler directives override the display driver settings of the required pins. Therefore, I recommend using the GPIO pins I have used. 8 | 9 | Enjoy! 10 | -------------------------------------------------------------------------------- /Wiring_Diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP32-8266-ePaper-Weather-Display-Examples/9b416fba1b4f7a47db7bf3ddfd8b6aa324125059/Wiring_Diagram.jpg --------------------------------------------------------------------------------