/* * Copyright Paul Warren 2019 * * Released under the terms of the GPLv3 license. */ #include #include #include #include #include #include #include "epd.h" #include "credential.h" // Weather bits // Australian BOM bits, Belconnen is mine. // see http://www.bom.gov.au/catalogue/data-feeds.shtml //const char* aac = "NSW_PT254"; //const char* bom_url = "ftp://ftp.bom.gov.au/anon/gen/fwo/IDN11060.xml"; // openweathermap, api_key from credential.h // const String owm_url = "http://api.openweathermap.org/data/2.5/weather?q=Belconnen,AU&units=metric&APPID="; // DarkSky API key from credential.h // substitute decimal latitude and longitude in ds_ll const String ds_url = "http://pwarren.id.au/darksky/darksky.json"; bool ds_done = false; // Timezone bits // Australia Eastern Time Zone (Sydney, Melbourne) TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours Timezone ausET(aEDT, aEST); TimeChangeRule *tcr; // Network clients WiFiUDP ntpUDP; HTTPClient http; NTPClient timeClient(ntpUDP, "raspberrypi.lan", 0, 60000); //use internal ntp server, update every 10 minutes //NTPClient timeClient(ntpUDP, "au.pool.ntp.org", 0, 60000); //use internal ntp server, update every 10 minutes // defined in "credentials.h" const char* ssid = ssid_name; // The SSID (name) of your Wi-Fi network const char* password = ssid_pass; // The password of the Wi-Fi network const int led = LED_BUILTIN; // Font width int dwidth = 185; // yes this seems backwards but it works... void ledOn(void) { digitalWrite(led, LOW);} void ledOff(void) { digitalWrite(led, HIGH);} void wait(void) { delay(1500);} void setup() { pinMode(led, OUTPUT); ledOff(); wifi_set_sleep_type(LIGHT_SLEEP_T); epd_init(); epd_wakeup(); epd_set_memory(MEM_NAND); epd_set_color(BLACK, WHITE); epd_set_en_font(ASCII64); epd_clear(); Serial.begin(115200); // Start serial communication Serial.setTimeout(2000); while (!Serial) { } Serial.println("\r\n\r\nSTARTING\r\n"); wifi(); printDateTime(0); timeClient.begin(); ntpUpdate(); epd_clear(); weatherUpdate(); printDateTime(1); epd_update(); ledOff(); } void wifi() { WiFi.persistent(false); WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); // Access WiFi Serial.print("Connecting to "); Serial.print(ssid); Serial.print(" ..."); while (WiFi.status() != WL_CONNECTED) { // Wait for WiFi to connect delay(250); Serial.print("."); } Serial.println('\n'); Serial.println("WiFi connection established"); Serial.print("Device's IP address is "); Serial.println(WiFi.localIP()); // Show device's IP address } void ntpUpdate() { if (timeClient.update()) { setTime(timeClient.getEpochTime()); } } void disp_digit(int digit, int xpos){ // digit: digit to display // xpos: X digit position, indexed at 1 int dpos = 0; int ypos = 0; dpos = ((xpos - 1) * dwidth); if (xpos > 2) { dpos += 60; // account for colon } switch (digit) { case 0: epd_disp_bitmap("0.JPG", dpos, ypos); break; case 1: epd_disp_bitmap("1.JPG", dpos, ypos); break; case 2: epd_disp_bitmap("2.JPG", dpos, ypos); break; case 3: epd_disp_bitmap("3.JPG", dpos, ypos); break; case 4: epd_disp_bitmap("4.JPG", dpos, ypos); break; case 5: epd_disp_bitmap("5.JPG", dpos, ypos); break; case 6: epd_disp_bitmap("6.JPG", dpos, ypos); break; case 7: epd_disp_bitmap("7.JPG", dpos, ypos); break; case 8: epd_disp_bitmap("8.JPG", dpos, ypos); break; case 9: epd_disp_bitmap("9.JPG", dpos, ypos); break; } delay(500); } void disp_time(int hours, int minutes) { // first digit of hours if (hours > 19) { disp_digit(2, 1); hours -= 20; } else if (hours > 9) { disp_digit(1, 1); hours -= 10; } else { disp_digit(0, 1); } // second hours digit disp_digit(hours, 2); epd_disp_bitmap("COLO.JPG", 2 * dwidth, 0); delay(500); // first minutes digit if (minutes > 49) { disp_digit(5, 3); minutes -= 50; } else if (minutes > 39) { disp_digit(4, 3); minutes -= 40; } else if (minutes > 29) { disp_digit(3, 3); minutes -= 30; } else if (minutes > 19) { disp_digit(2, 3); minutes -= 20; } else if (minutes > 9) { disp_digit(1, 3); minutes -= 10; } else { disp_digit(0, 3); } // second minutes digit disp_digit(minutes, 4); } void printDateTime(int update_epd) { char buf[64]; time_t t; t = ausET.toLocal(now(), &tcr); char m[4]; // temporary storage for month string (DateStrings.cpp uses shared buffer) strcpy(m, monthShortStr(month(t))); sprintf(buf, "%.2d:%.2d:%.2d", hour(t), minute(t), second(t)); Serial.print(buf); Serial.print(" "); if (update_epd > 0) { disp_time(hour(t), minute(t)); wait(); } sprintf(buf, "%s %.2d %s %d %s", dayShortStr(weekday(t)), day(t), m, year(t), &tcr -> abbrev); Serial.println(buf); if (update_epd > 0) { epd_set_en_font(ASCII64); epd_disp_string(buf, 150, 225); delay(500); Serial.println("Screen Updated"); } } void weatherUpdate(void) { const size_t capacity = 4*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + 150; DynamicJsonBuffer jsonBuffer(capacity); time_t t; char buf[96] = {}; const char* nstr = "Now"; const char* tstr = "Today"; const char* tmstr = "Tomorrow"; http.begin(ds_url); int httpCode = http.GET(); if (httpCode > 0) { JsonObject& root = jsonBuffer.parseObject(http.getString()); if (!root.success()) { Serial.print(F("parseObject() failed: ")); return; } float current_temp = root["current"]["temp"]; const char* current_icon = root["current"]["icon"]; float today_max = root["today"]["max"]; const char* today_icon = root["today"]["icon"]; float tomorrow_max = root["tomorrow"]["max"]; const char* tomorrow_icon = root["tomorrow"]["icon"]; float day_after_max = root["day_after"]["max"]; const char* day_after_icon = root["day_after"]["icon"]; epd_set_en_font(ASCII64); //Today t = ausET.toLocal(now()); epd_disp_string(dayStr(weekday(t)), 0, 536); delay(500); (String(today_max, 1)).toCharArray(buf,64); epd_disp_string(buf, 0, 472); delay(500); //Tomorrow t = ausET.toLocal(now() + 86400, &tcr); epd_disp_string(dayStr(weekday(t)), 267, 536); delay(500); (String(tomorrow_max, 1)).toCharArray(buf,64); epd_disp_string(buf, 267, 472); delay(500); //Day after Tomorrow t = ausET.toLocal(now() + 86400 + 86400); epd_disp_string(dayStr(weekday(t)), 533, 536); delay(500); (String(day_after_max, 1)).toCharArray(buf,64); epd_disp_string(buf, 533, 472); delay(500); } else { Serial.print("Error on HTTP Request: "); Serial.println(httpCode); } http.end(); } void loop() { int sleep_seconds = 0; Serial.println("Back from sleep!"); if (WiFi.status() != WL_CONNECTED) { wifi(); } ntpUpdate(); if (second(now()) >= 55) { epd_wakeup(); while (second(now()) != 57) { delay(500); } epd_clear(); weatherUpdate(); printDateTime(1); epd_update(); delay(5000); epd_enter_stopmode(); } sleep_seconds = 55 - second(now()); Serial.print("Sleeping for: "); Serial.println(sleep_seconds); delay(sleep_seconds * 1000); }