/* * Copyright Paul Warren 2019 * * Released under the terms of the GPLv3 license. * *D1Mini: Colour : UART Module * D5: White : DOUT * D6: Green : DIN * D7: Yellow : Wake Up * D8: Blue : Reset * * */ #include #include #include #include #include #include #include "epd.h" #include "credential.h" // BOM, see bom_rrd2.py const String ds_url = "http://pwarren.id.au/darksky/bom.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; // when to wake up the display and get going int wakeup_seconds = 50; // Network clients WiFiClient wfclient; WiFiUDP ntpUDP; HTTPClient http; NTPClient timeClient(ntpUDP, "pi.lan", 0, 60000); //use internal ntp server, update every 10 minutes, taking in to account the 9 seconds it takes to update the screen for the current time //NTPClient timeClient(ntpUDP, "au.pool.ntp.org", 0, 60000); //use external 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);} // globals for the temperature :( const char* current_outside; const char* current_bedroom; const char* current_lounge; 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(); timeClient.begin(); ntpUpdate(); /* weatherUpdate(); printDateTime(1); epd_update(); wait(); */ 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; } // wait(); } 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()+60, &tcr); char m[4]; // temporary storage for month string (DateStrings.cpp uses shared buffer) strncpy(m, monthShortStr(month(t)), 4); sprintf(buf, "%.2d:%.2d:%.2d", hour(t), minute(t), second(t)); Serial.print(buf); Serial.print(" "); if (update_epd > 0) { Serial.println("Adding Time"); disp_time(hour(t), minute(t)); } sprintf(buf, "%s %.2d %s %d", dayShortStr(weekday(t)), day(t), m, year(t)); Serial.println(buf); if (update_epd > 0) { Serial.println("Adding Date"); epd_set_en_font(ASCII64); epd_disp_string(buf, 50, 225); } } void weatherUpdate(void) { char buf[64]; const size_t capacity = 4*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(4) + 150; DynamicJsonDocument jsonBuffer(capacity); http.begin(wfclient, ds_url); int httpCode = http.GET(); if (httpCode > 0) { auto error = deserializeJson(jsonBuffer, http.getString()); if (error) { Serial.print("Failed to parse json"); return; } http.end(); const char* today_day = jsonBuffer["today"]["day"]; // "Wednesday" const char* today_icon = jsonBuffer["today"]["icon"]; // "sunny.jpg" int today_max = jsonBuffer["today"]["max"]; // "27" const char* tomorrow_day = jsonBuffer["tomorrow"]["day"]; // "Thursday" const char* tomorrow_icon = jsonBuffer["tomorrow"]["icon"]; // "sunny.jpg" int tomorrow_max = jsonBuffer["tomorrow"]["max"]; // "31" const char* day_after_day = jsonBuffer["day_after"]["day"]; // "Friday" const char* day_after_icon = jsonBuffer["day_after"]["icon"]; // "pcloud.jpg" int day_after_max = jsonBuffer["day_after"]["max"]; // "33" int current_outside = jsonBuffer["current"]["outside"]; int current_bedroom = jsonBuffer["current"]["bedroom"]; int current_lounge = jsonBuffer["current"]["lounge"]; Serial.println("Updating EPD with weather"); epd_set_en_font(ASCII64); //Today epd_disp_bitmap(today_icon, 0, 290); epd_disp_string(today_day, 0, 536); sprintf(buf, "%d", today_max); epd_disp_string(buf, 75, 460); //Tomorrow epd_disp_bitmap(tomorrow_icon, 267, 290); epd_disp_string(tomorrow_day, 260, 536); sprintf(buf, "%d", tomorrow_max); epd_disp_string(buf, 352, 460); //Day After epd_disp_bitmap(day_after_icon, 533, 290); epd_disp_string(day_after_day, 533, 536); sprintf(buf, "%d", day_after_max); epd_disp_string(buf, 615, 460); //Serial.println("Adding Temps"); sprintf(buf, "E: %d I: %d", current_outside, current_lounge); //Serial.println(buf); epd_disp_string(buf, 500, 225); } else { Serial.print("Error on HTTP Request: "); Serial.println(httpCode); } } void loop() { int sleep_seconds = 0; Serial.println("Back from sleep!"); if (WiFi.status() != WL_CONNECTED) { wifi(); } ntpUpdate(); if (second(now()) >= wakeup_seconds) { epd_wakeup(); epd_clear(); weatherUpdate(); Serial.print("Weather details sent: "); Serial.println(second(now())); printDateTime(1); Serial.println("Waiting on minute to change"); while (second(now()) != 0) { delay(500); } } Serial.println("EPD Update"); epd_update(); delay(4000); Serial.println("EPD Stop"); epd_enter_stopmode(); sleep_seconds = wakeup_seconds - second(now()); if (sleep_seconds < 0) { sleep_seconds = 10; } Serial.print("Sleeping for: "); Serial.println(sleep_seconds); delay(sleep_seconds * 1000); }