#ifndef MODE_COUNT_DOWN_H #define MODE_COUNT_DOWN_H #define MAX_FIREWORKS 6 #include "mode/Mode.h" #include "CountDownFirework.h" class CountDown : public Mode { private: Firework fireworks[MAX_FIREWORKS]; uint16_t days = 0; uint16_t hours = 0; uint16_t minutes = 0; uint16_t seconds = 0; uint8_t level = 0; bool bars; bool plus1DayForSleepingCount; public: CountDown(Display& display, bool bars, bool plus1DayForSleepingCount) : Mode(display), bars(bars), plus1DayForSleepingCount(plus1DayForSleepingCount) { for (auto& firework: fireworks) { firework.init(display); } } const char *getName() override { if (bars) { return "CountDown (Bars)"; } else { return "CountDown (Numbers)"; } } protected: void step(microseconds_t microseconds) override { if (!realtimeOK) { setMode(NO_TIME); return; } if (dateReached()) { setMode(FIREWORK); for (auto& firework: fireworks) { firework.step(microseconds); } markDirty(); return; } // GRRRRRRR... config.date.tm_year -= 1900; config.date.tm_mon -= 1; const time_t dateEpochSeconds = mktime(&config.date); config.date.tm_year += 1900; config.date.tm_mon += 1; // --- const double diffSeconds = difftime(dateEpochSeconds, nowEpochSeconds); days = (int) floor(diffSeconds / (24 * 60 * 60)); hours = (int) floor(diffSeconds / (60 * 60)) % 24; minutes = (int) floor(diffSeconds / 60) % 60; seconds = (int) diffSeconds % 60; // Serial.printf("now=%4d.%02d.%02d (%ld), conf=%4d.%02d.%02d (%ld), diff=%f, %dd %2d:%02d:%02d\n", // now.tm_year, now.tm_mon, now.tm_mday, nowEpochSeconds, // config.date.tm_year, config.date.tm_mon, config.date.tm_mday, dateEpochSeconds, // diffSeconds, // days, hours, minutes, seconds); setMode(COUNTDOWN); if (days == 0) { loopLastDay(); } else { loopMultipleDays(); } } void loopLastDay() { int levelTmp = (int) round(32 * realtimeMilliseconds / 1000.0); if (level != levelTmp) { level = levelTmp; markDirty(); } } void loopMultipleDays() { if (realtimeChanged) { markDirty(); } } bool dateReached() { return now.tm_year == config.date.tm_year && now.tm_mon == config.date.tm_mon && now.tm_mday == config.date.tm_mday; } void draw(Display& display) override { display.clear(); if (!realtimeOK) { drawNoTime(display); } else if (dateReached()) { for (auto& firework: fireworks) { firework.draw(display); } drawYear(display, now.tm_year); } else { drawCountdown(display); } } private: enum State { NO_TIME, COUNTDOWN, FIREWORK, }; State _state = NO_TIME; void setMode(State state) { if (_state != state) { _state = state; markDirty(); } } void drawCountdown(Display& display) { if (plus1DayForSleepingCount) { drawSleepingCount(display); } else if (days <= 0 && bars) { drawCountdownBars(display); } else { drawCountdownNumbers(display); } } void drawSleepingCount(Display& display) const { int sleepCount = days + 1; int y = 1; uint8_t x = display.print2(3 * (DISPLAY_CHAR_WIDTH + 1), y, sleepCount, WHITE); x += 2; x += display.print(x, y, SYMBOL_T, WHITE, true) + 1; x += display.print(x, y, SYMBOL_A, WHITE, true) + 1; x += display.print(x, y, SYMBOL_G, WHITE, true) + 1; if (sleepCount != 1) { display.print(x, y, SYMBOL_E, WHITE, true); } } void drawCountdownBars(Display& display) const { drawBar(display, 0, 24, 1, 24, hours, RED, MAGENTA, 0); drawBar(display, 2, 30, 2, 60, minutes, BLUE, VIOLET, 5); drawBar(display, 5, 30, 2, 60, seconds, GREEN, YELLOW, 5); } static void drawBar(Display& display, uint8_t _y, uint8_t _w, uint8_t _h, uint8_t max, uint8_t value, const Color& color, const Color& tickColor, uint8_t ticks) { auto totalOnCount = (uint8_t) round((double) value / max * _w * _h); uint8_t doneOnCount = 0; for (uint8_t y = 0; y < _h; y++) { for (uint8_t x = 0; x < _w; x++) { if (doneOnCount >= totalOnCount) { return; } doneOnCount++; Color c = color; if (ticks != 0) { if (doneOnCount % ticks == 0) { c = tickColor; } } display.set(x, _y + y, c); } } } void drawCountdownNumbers(Display& display) const { uint8_t x = 0; if (days > 0) { drawDay(display, days, &x); x += display.print(x, 1, 10, WHITE, true); } else { x += 2; } drawHour(display, days, hours, &x); x += display.print(x, 1, 10, WHITE, true); draw2Digit(display, minutes, &x); if (days <= 0) { x += display.print(x, 1, 10, WHITE, true); draw2Digit(display, seconds, &x); drawSubSecondsBar(display); } else { drawSecondsBar(display, seconds); } } static void drawNoTime(Display& display) { uint8_t x = 2; x += display.print(x, 1, SYMBOL_DASH, WHITE, true); x++; x += display.print(x, 1, SYMBOL_DASH, WHITE, true); x += display.print(x, 1, 10, WHITE, true); x += display.print(x, 1, SYMBOL_DASH, WHITE, true); x++; x += display.print(x, 1, SYMBOL_DASH, WHITE, true); x += display.print(x, 1, 10, WHITE, true); x += display.print(x, 1, SYMBOL_DASH, WHITE, true); x++; display.print(x, 1, SYMBOL_DASH, WHITE, true); } static void drawDay(Display& display, int days, uint8_t *x) { if (days >= 100) { *x += display.print(*x, 1, days / 100, WHITE, true) + 1; } else { *x += DISPLAY_CHAR_WIDTH + 1; } if (days >= 10) { *x += display.print(*x, 1, days / 10 % 10, WHITE, true) + 1; } else { *x += DISPLAY_CHAR_WIDTH + 1; } *x += display.print(*x, 1, days % 10, WHITE, true); } static void drawHour(Display& display, int days, int hours, uint8_t *x) { if (days > 0 || hours >= 10) { *x += display.print(*x, 1, hours / 10, WHITE, true) + 1; } else { *x += DISPLAY_CHAR_WIDTH + 1; } *x += display.print(*x, 1, hours % 10, WHITE, true); } static void draw2Digit(Display& display, int value, uint8_t *x) { *x += display.print(*x, 1, value / 10, WHITE, true) + 1; *x += display.print(*x, 1, value % 10, WHITE, true); } static void drawSecondsBar(Display& display, int seconds) { for (int pos = 0; pos < 30; pos++) { if (pos <= seconds - 30) { display.set(pos + 1, 7, GREEN); } else if (pos <= seconds) { display.set(pos + 1, 7, RED); } } } void drawSubSecondsBar(Display& display) const { for (int pos = 0; pos < 32; pos++) { if (pos < 32 - level) { display.set(pos, 7, GREEN); } } } // ReSharper disable CppDFAUnusedValue void drawYear(Display& display, int year) const { if (plus1DayForSleepingCount) { uint8_t x = 1; x += display.printM(x, 1, WHITE); x += 1; x += display.print(x, 1,SYMBOL_A, WHITE, true); x += 1; x += display.printM(x, 1, WHITE); x += 1; x += display.print(x, 1,SYMBOL_A, WHITE, true); x += 4; x += display.print(x, 1, 3, WHITE, true); x += 1; x += display.print(x, 1, 2, WHITE, true); display.printHeart(15, 4); } else { uint8_t x = 8; x += display.print(x, 1, year / 1000 % 10, WHITE, true) + 1; x += display.print(x, 1, year / 100 % 10, WHITE, true) + 1; x += display.print(x, 1, year / 10 % 10, WHITE, true) + 1; x += display.print(x, 1, year / 1 % 10, WHITE, true); } } // ReSharper restore CppDFAUnusedValue }; #endif