From 0f29a82542edf019765ded73702175b5659d794f Mon Sep 17 00:00:00 2001 From: Thomas Ballmann Date: Thu, 27 Feb 2020 22:08:12 +0100 Subject: [PATCH] initial commit calendar face #16 --- include/datetime.h | 16 ++- src/datetime.cpp | 103 +++++-------------- src/faceCalendar.cpp | 229 +++++++++++++++++++++++++++++-------------- src/imagePNG.cpp | 43 +------- 4 files changed, 189 insertions(+), 202 deletions(-) diff --git a/include/datetime.h b/include/datetime.h index d33e545..381a0d1 100644 --- a/include/datetime.h +++ b/include/datetime.h @@ -2,23 +2,19 @@ #define DATETIME_H #include +#include // TODO -struct time_struct +// @see http://www.cplusplus.com/reference/ctime/tm/ +// @see http://www.cplusplus.com/reference/ctime/strftime/ +struct datetime_struct : tm { - char wday[4]; - char month[4]; uint8_t month_num; - uint8_t mday; - uint8_t mil_hour; - uint8_t hour; - uint8_t min; - uint8_t sec; uint8_t day_offset; // 1st day of the month offset, Monday = 0 - int year; + uint8_t days_in_month; }; -extern RTC_DATA_ATTR struct time_struct now; +extern RTC_DATA_ATTR struct datetime_struct now; void setupDateTime(); diff --git a/src/datetime.cpp b/src/datetime.cpp index aeeec85..9516a86 100644 --- a/src/datetime.cpp +++ b/src/datetime.cpp @@ -3,100 +3,49 @@ #include #include "datetime.h" -RTC_DATA_ATTR struct time_struct now; // keep track of time -//String time_zone_base = "UTC"; +RTC_DATA_ATTR struct datetime_struct now; + +// TODO offset const char *ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 3600; const int daylightOffset_sec = 3600; -void printLocalTime(); -int8_t get_date_dtls(String time_zone); - - -// TODO refactore complete file - -void setupDateTime() -{ - configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); - printLocalTime(); - get_date_dtls(""); -} -void printLocalTime() +int getNumberOfDays(int month, int year) { - struct tm timeinfo; - if (!getLocalTime(&timeinfo)) + // leap year condition, if month is 2 + if (month == 2) { - Serial.println("Failed to obtain time"); - return; + if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) + return 29; + else + return 28; } - Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); + // months which has 31 days + else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) + return 31; + else + return 30; } -// Time APIs -int8_t get_date_dtls(String time_zone) +void setupDateTime() { - //String time_zone_string = time_zone_base + time_zone; - //setenv("TZ", time_zone_string.c_str(), 1); - //setenv("TZ", "CET-1CEST,M3.5.0/02,M10.5.0/03", 1); - - struct tm timeinfo; - if (!getLocalTime(&timeinfo)) + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + if (!getLocalTime(&now)) { Serial.println("Failed to obtain time"); - return -1; + return; } - time_t epoch = mktime(&timeinfo); + Serial.println(&now, "%A, %B %d %Y %H:%M:%S"); - sscanf(ctime(&epoch), "%s %s %hhd %hhd:%hhd:%hhd %d", now.wday, now.month, &now.mday, &now.mil_hour, &now.min, &now.sec, &now.year); + // + now.days_in_month = getNumberOfDays(now.tm_mon + 1, now.tm_year); + now.month_num = now.tm_mon + 1; - now.month_num = timeinfo.tm_mon + 1; // gives offset of first day of the month with respect to Monday - //https://www.tondering.dk/claus/cal/chrweek.php#calcdow - // 1=Monday to 7=Sunday + // https://www.tondering.dk/claus/cal/chrweek.php#calcdow uint8_t a = (14 - now.month_num) / 12; - uint16_t y = now.year - a; + uint16_t y = (now.tm_year + 1900) - a; uint16_t m = now.month_num + (12 * a) - 2; - // change +7 at the end to whatever first day of week you want. - // But change the header '"Mon Tue Wed Thu Fri Sat Sun"' as well above. - // currently +7 => Monday - // +1 => Sunday now.day_offset = (((1 + y + (y / 4) - (y / 100) + (y / 400) + ((31 * m) / 12)) % 7) + 7) % 7; - - // convert to 12 hour - if (now.mil_hour > 12) - { - now.hour = now.mil_hour - 12; - } - else - { - now.hour = now.mil_hour; - } - - Serial.printf("Time is %d %d:%d:%d on %s on the %d/%d/%d . It is the month of %s. day_offset: %d\n", now.mil_hour, now.hour, now.min, now.sec, now.wday, now.mday, now.month_num, now.year, now.month, now.day_offset); - return 0; -} -/* -int8_t set_time() -{ - - struct tm t; - t.tm_year = 2020 - 1900; - t.tm_mon = 1 - 1; // Month, 1 - jan to 12 - dec - t.tm_mday = 27; // Day of the month - t.tm_hour = 9; - t.tm_min = 57; - t.tm_sec = 0; - t.tm_isdst = -1; // Is DST on? 1 = yes, 0 = no, -1 = unknown - - time_t epoch; - epoch = mktime(&t); - - struct timeval now; - now.tv_sec = epoch; - now.tv_usec = 0; - - struct timezone tz = {-330, 0}; - return settimeofday(&now, &tz); -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/faceCalendar.cpp b/src/faceCalendar.cpp index 84a1048..99d4437 100644 --- a/src/faceCalendar.cpp +++ b/src/faceCalendar.cpp @@ -1,10 +1,22 @@ #include "faceCalendar.h" #include "display.h" #include "datetime.h" +#include "SPIFFS.h" +#include "pngle.h" + +#include // weekday - month year +#include // current day void display_calender(); -void display_lines(); +void display_picture(); void display_time(); +void on_draw2(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]); + +using std::uint8_t; +static constexpr int MAX_WIDTH = 640 - 250; +static constexpr int MAX_HEIGHT = 384; +static int16_t curRowDelta[MAX_WIDTH + 1]; +static int16_t nextRowDelta[MAX_WIDTH + 1]; void setupFaceCalendar() { @@ -12,118 +24,186 @@ void setupFaceCalendar() setupDateTime(); - display.setRotation(0); display.setFullWindow(); display.firstPage(); display.fillScreen(GxEPD_WHITE); // draw... - display_lines(); + //display_time(); + display_picture(); display_calender(); - display_time(); Serial.println("displayFlush"); display.nextPage(); - // https://raw.githubusercontent.com/rgujju/paperdink/master/Images/full.jpg // https://github.com/rgujju/paperdink/blob/master/Software/paperd.ink/GUI.cpp + // https://images.unsplash.com/photo-1580886349729-1bd109928600?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjExMDM0OH0&w=640&h=200&fm=png&fit=crop + // https://images.unsplash.com/photo-1580886349729-1bd109928600?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjExMDM0OH0&w=390&h=384&fm=png&fit=crop&colorquant=2 + // final filter: &w=390&h=384&fm=png&fit=crop&duotone=000000,FFFFFF // display_weather + } void loopFaceCalendar() { } -void display_lines() +void display_calender() +{ + // init + int16_t sideWidth = 250; + int16_t tbx, tby; + uint16_t tbw, tbh, x; + char label[64]; + display.setTextColor(GxEPD_WHITE); + display.setTextSize(1); + display.setRotation(0); + + // left side + display.fillRect(0, 0, sideWidth, display.height(), GxEPD_BLACK); + + // weekday + strftime(label, 64, "%A", &now); + display.setFont(&FreeMono12pt7b); + display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); + x = ((sideWidth - tbw) / 2) - tbx; + display.setCursor(x, 30); + display.println(label); + + // today + display.setFont(&FreeSansBold24pt7b); + display.setTextSize(2); + display.getTextBounds("29", 0, 0, &tbx, &tby, &tbw, &tbh); + x = ((sideWidth - tbw) / 2) - tbx; + display.setCursor(x, 120); + display.println(now.tm_mday); + + // month yearh + strftime(label, 64, "%B %Y", &now); + display.setTextSize(1); + display.setFont(&FreeMono12pt7b); + display.getTextBounds(label, 0, 0, &tbx, &tby, &tbw, &tbh); + x = ((sideWidth - tbw) / 2) - tbx; + display.setCursor(x, 150); + display.println(label); + + // weekday headline + display.setFont(&FreeMonoBold9pt7b); + display.setCursor(20, 192); + display.println("Mo Tu We Th Fr Sa Su"); + + display.setCursor(20, 220); + + // skip week days from previous month + for (uint8_t d = 1; d < now.day_offset; d++) + { + display.print(" "); + } + + for (uint8_t d = 1; d <= now.days_in_month; d++) + { + display.printf("%2d ", d); + + if ((d + now.day_offset - 1) % 7 == 0) + { + // new week + display.println(""); + display.setCursor(20, display.getCursorY()); + } + } + + // TODO current weather + display.drawLine(15, 320, sideWidth - 15, 320, GxEPD_WHITE); +} + +void display_picture() { - // black area bottom - display.fillRect(0, (display.height() / 3) * 2, display.width(), (display.height() / 3), GxEPD_BLACK); + SPIFFS.begin(); + displayOpen(); + + pngle_t *pngle = pngle_new(); + pngle_set_draw_callback(pngle, on_draw2); - // vertical lines - display.fillRect((display.width() / 4), 0, 5, (display.height() / 3) * 2, GxEPD_BLACK); - display.fillRect((display.width() / 4), (display.height() / 3) * 2, 5, (display.height() / 3), GxEPD_WHITE); + //File file = SPIFFS.open("/blackPNG.png", "r"); + File file = SPIFFS.open("/calendarPhoto.png", "r"); + if (!file) + { + Serial.println(" file not found"); + } + + // TODO check why a small buffer is not working correct + char buff[1280] = {0}; + while (int c = file.readBytes(buff, sizeof(buff))) + { + pngle_feed(pngle, buff, c); + } + file.close(); + + Serial.print(" width: "); + Serial.print(pngle_get_width(pngle)); + Serial.print(" height: "); + Serial.println(pngle_get_height(pngle)); + + Serial.println(" read png done"); + pngle_destroy(pngle); } /** - * @todo make it better :) + * Floyd-Steinberg-Algorithmus */ -void display_calender() +void on_draw2(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]) { - int16_t x1, y1; - uint16_t w, h; - display.setFont(&FreeMonoBold9pt7b); - display.setTextSize(1); - display.setTextColor(GxEPD_BLACK); - int16_t calender_base_y = 40; - int16_t calender_base_x = 120; - display.setCursor(calender_base_x, calender_base_y); - display.println("Mon Tue Wed Thu Fri Sat Sun"); - display.getTextBounds("Mon Tue Wed Thu Fri Sat Sun", calender_base_x, calender_base_y, &x1, &y1, &w, &h); - uint8_t num_offset, print_valid = 0; - uint8_t day = 1; - for (uint8_t j = 0; j <= 5; j++) + uint8_t r = rgba[0]; // 0 - 255 + uint8_t g = rgba[1]; // 0 - 255 + uint8_t b = rgba[2]; // 0 - 255 + //uint8_t a = rgba[3]; // 0: fully transparent, 255: fully opaque + + int16_t gray = round(r * 0.3 + g * 0.59 + b * 0.11); + int16_t blackOrWhite; + + // Add errors to color if there are + gray += curRowDelta[x]; + + if (gray <= 127) { - for (uint8_t i = 1; i <= 7 && day <= 31; i++) - { - // you can hack around with this value to align your text properly based on what font, font size etc you are using - num_offset = 21; // 21 is what works for me for the first 2 columns - if (i >= 3 && i <= 7) - { - num_offset = 17; // then i need to reduce to 17 - } - if (j == 0 && i == now.day_offset) - { - // start from the offset in the month, ie which day does 1st of the month lie on - print_valid = 1; - } - if (print_valid) - { - display.setCursor(calender_base_x + (i * (w / 7)) - num_offset, calender_base_y + ((j + 1) * h) + ((j + 1) * 7)); - if (day == now.mday) - { - char str[3]; - sprintf(str, "%d", day); - int16_t x2, y2; - uint16_t w2, h2; - display.getTextBounds(str, calender_base_x + (i * (w / 7)) - num_offset, calender_base_y + ((j + 1) * h) + ((j + 1) * 7), &x2, &y2, &w2, &h2); - display.fillRect(x2 - 4, y2 - 4, w2 + 8, h2 + 8, GxEPD_BLACK); - display.setTextColor(GxEPD_WHITE); - } - else - { - display.setTextColor(GxEPD_BLACK); - } - // once the offset is reached, start incrementing - display.println(day); - day += 1; - } - } + blackOrWhite = 0; + } + else + { + blackOrWhite = 255; } - // display day - display.setTextColor(GxEPD_WHITE); - display.setFont(&FreeMonoBold9pt7b); // LARGE_FONT - display.setTextSize(1); - display.setCursor(33, 250); - display.println(now.mday); + int16_t oldPixel = gray; + int16_t newPixel = blackOrWhite; - // display month - display.setTextColor(GxEPD_WHITE); - display.setFont(&FreeMonoBold9pt7b); // MED_FONT - display.setTextSize(2); - display.setCursor(30, 290); - display.println(now.month); + int err = oldPixel - newPixel; + + if (x > 0) + nextRowDelta[x - 1] += err * 3 / 16; + nextRowDelta[x] += err * 5 / 16; + nextRowDelta[x + 1] += err * 1 / 16; + curRowDelta[x + 1] += err * 7 / 16; + + if (x == 0 && y > 0) + { + // new line + memcpy(curRowDelta, nextRowDelta, sizeof(curRowDelta)); + memset(nextRowDelta, 0, sizeof(nextRowDelta)); + } + + displayWritePixel(x + 250, y, blackOrWhite); } void display_time() { + /* int16_t x1, y1; uint16_t w, h; //display time - display.setFont(&FreeMonoBold9pt7b); // LARGE_FONT + display.setFont(&FreeMonoBold9pt7b); // LARGE_FONT display.setTextSize(1); display.setTextColor(GxEPD_BLACK); int16_t time_base_y = 60; @@ -152,4 +232,5 @@ void display_time() { display.println(now.min); } + */ } diff --git a/src/imagePNG.cpp b/src/imagePNG.cpp index 42b3d08..24021a6 100644 --- a/src/imagePNG.cpp +++ b/src/imagePNG.cpp @@ -1,7 +1,7 @@ #include #include "imagePNG.h" #include "pngle.h" -#include "SPIFFS.h" +//#include "SPIFFS.h" #include "display.h" pngle_t *pngle; @@ -17,7 +17,6 @@ void setupImagePNG() void pngOpenFramebuffer() { - //Serial.println("pngOpenFramebuffer"); displayOpen(); pngle = pngle_new(); @@ -35,48 +34,10 @@ void pngWriteFramebuffer(int offset, uint8_t bitmap[], int c) void pngFlushFramebuffer() { - //Serial.println("pngFlushFramebuffer"); pngle_destroy(pngle); displayFlush(); -/* - SPIFFS.begin(); - displayOpen(); - - pngle_t *pngle = pngle_new(); - pngle_set_draw_callback(pngle, on_draw); - - File file = SPIFFS.open("/blackPNG.png", "r"); - if (!file) - { - Serial.println(" file not found"); - } - - // get filesize - long size = file.size(); - - // read contents of the file into the vector - char *buffer = (char *)malloc((unsigned long)size); - if (!buffer) - { - file.close(); - Serial.println(" allow failedd"); - } - file.readBytes(buffer, (size_t)size); - file.close(); - - Serial.println(" read png"); - int ret = pngle_feed(pngle, buffer, size); - - //Serial.println(" return: " + ret); - //Serial.println(" width: " + pngle_get_width(pngle)); - //Serial.println(" height: " + pngle_get_height(pngle)); - - Serial.println(" read png done"); - pngle_destroy(pngle); - - displayFlush(); - */ } + /* void setupImagePNG__demo() {