diff --git a/src/config.cpp b/src/config.cpp index d3921a4..2876edc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -83,9 +83,10 @@ bool validateChecksum(uint32_t checksumEEPROM, Config &tmp) { } void config_defaults() { - config.mode = GAME_OF_LIFE_GRAYSCALE; + config.mode = COUNT_DOWN_SLEEP; config.speed = 1.0; config.brightness = 16; + config.date = {0}; Serial.printf("Config DEFAULTS loaded.\n"); } diff --git a/src/config.h b/src/config.h index be1ff0d..a0cf06b 100644 --- a/src/config.h +++ b/src/config.h @@ -7,6 +7,7 @@ struct Config { ModeId mode; double speed; uint8_t brightness; + tm date; }; extern Config config; diff --git a/src/display/Display.cpp b/src/display/Display.cpp index 9d059d6..c374b69 100644 --- a/src/display/Display.cpp +++ b/src/display/Display.cpp @@ -106,6 +106,34 @@ bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT] = { X, _, _, _, _, X, }, + { + X, X, X, + _, X, _, + _, X, _, + _, X, _, + _, X, _, + }, + { + _, X, _, + X, _, X, + X, X, X, + X, _, X, + X, _, X, + }, + { + _, X, X, + X, _, _, + X, X, X, + X, _, X, + _, X, _, + }, + { + X, X, X, + X, _, _, + X, X, X, + X, _, _, + X, X, X, + }, // this must always be the last symbol (fallback) { X, X, X, diff --git a/src/display/Display.h b/src/display/Display.h index 0fd9a34..07607a5 100644 --- a/src/display/Display.h +++ b/src/display/Display.h @@ -5,9 +5,16 @@ #include "Adafruit_NeoPixel.h" #include "Vector.h" -#define SYMBOL_COUNT 16 +#define SYMBOL_COUNT 20 + #define SYMBOL_DASH 13 #define SYMBOL_PERCENT 14 + +#define SYMBOL_T 15 +#define SYMBOL_A 16 +#define SYMBOL_G 17 +#define SYMBOL_E 18 + #define DISPLAY_CHAR_WIDTH 3 #define DISPLAY_CHAR_HEIGHT 5 diff --git a/src/mode.cpp b/src/mode.cpp index 8bcbba2..b82ea1e 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -99,10 +99,13 @@ void loadNewMode() { mode = new SpaceInvaders(display); break; case COUNT_DOWN: - mode = new CountDown(display, false); + mode = new CountDown(display, false, false); break; case COUNT_DOWN_BARS: - mode = new CountDown(display, true); + mode = new CountDown(display, true, false); + break; + case COUNT_DOWN_SLEEP: + mode = new CountDown(display, false, true); break; case STARFIELD: mode = new Starfield(display); diff --git a/src/mode/CountDown/CountDown.h b/src/mode/CountDown/CountDown.h index 329b234..bbba1b4 100644 --- a/src/mode/CountDown/CountDown.h +++ b/src/mode/CountDown/CountDown.h @@ -14,14 +14,22 @@ private: 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) : - Mode(display), bars(bars) { + CountDown(Display &display, bool bars, bool plus1DayForSleepingCount) : + Mode(display), bars(bars), plus1DayForSleepingCount(plus1DayForSleepingCount) { for (auto &firework: fireworks) { firework.init(display); } @@ -40,25 +48,45 @@ protected: void step(microseconds_t microseconds) override { if (!realtimeOK) { setMode(NO_TIME); - } else if (now.tm_mon != 1 || now.tm_mday != 1 || now.tm_hour != 0) { - days = getDayCountForYear(now.tm_year) - now.tm_yday - 1; - setMode(COUNTDOWN); - if (days == 0) { - loopLastDay(); - } else { - loopMultipleDays(); - } - } else { + 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);; + int levelTmp = (int) round(32 * realtimeMilliseconds / 1000.0); if (level != levelTmp) { level = levelTmp; markDirty(); @@ -71,17 +99,21 @@ protected: } } + 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 (now.tm_mon == 1 && now.tm_mday == 1 && now.tm_hour == 0) { + } else if (dateReached()) { for (auto &firework: fireworks) { firework.draw(display); } drawYear(display, now.tm_year); } else { - drawCountdown(display, now); + drawCountdown(display); } } @@ -102,25 +134,37 @@ private: } } - void drawCountdown(Display &display, const tm &now) { - uint8_t hours = (24 - now.tm_hour - (now.tm_min > 0 || now.tm_sec > 0 ? 1 : 0)); - uint8_t minutes = (60 - now.tm_min - (now.tm_sec > 0 ? 1 : 0)) % 60; - uint8_t seconds = (60 - now.tm_sec) % 60; - if (days <= 0 && bars) { - drawCountdownBars(display, hours, minutes, seconds); + void drawCountdown(Display &display) { + if (plus1DayForSleepingCount) { + drawSleepingCount(display); + } else if (days <= 0 && bars) { + drawCountdownBars(display); } else { - drawCountdownNumbers(display, hours, minutes, seconds); + drawCountdownNumbers(display); } } - void drawCountdownBars(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) { - drawBar(display, 0, 0, 24, 1, 0, 24, hours, RED, MAGENTA, 0); - drawBar(display, 0, 2, 30, 2, 0, 60, minutes, BLUE, VIOLET, 5); - drawBar(display, 0, 5, 30, 2, 0, 60, seconds, GREEN, YELLOW, 5); + 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 drawBar(Display &display, uint8_t _x, uint8_t _y, uint8_t _w, uint8_t _h, uint8_t min, uint8_t max, uint8_t value, const Color &color, const Color &tickColor, uint8_t ticks) { - auto totalOnCount = (uint8_t) round(((double) value - min) / (max - min) * _w * _h); + 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++) { @@ -134,20 +178,12 @@ private: c = tickColor; } } - display.set(_x + x, _y + y, c); + display.set(x, _y + y, c); } } } - static Color factor(Color color, double factor) { - return { - (uint8_t) round(color.r * factor), - (uint8_t) round(color.g * factor), - (uint8_t) round(color.b * factor), - }; - } - - void drawCountdownNumbers(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) const { + void drawCountdownNumbers(Display &display) const { uint8_t x = 0; if (days > 0) { drawDay(display, days, &x); @@ -179,40 +215,36 @@ private: 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); + 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); + *x += display.print(*x, 1, days / 100, WHITE, true) + 1; } else { - *x += 3; + *x += DISPLAY_CHAR_WIDTH + 1; } - (*x)++; if (days >= 10) { - *x += display.print(*x, 1, days / 10 % 10, WHITE, true); + *x += display.print(*x, 1, days / 10 % 10, WHITE, true) + 1; } else { - *x += 3; + *x += DISPLAY_CHAR_WIDTH + 1; } - (*x)++; *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); + *x += display.print(*x, 1, hours / 10, WHITE, true) + 1; } else { - *x += 3; + *x += DISPLAY_CHAR_WIDTH + 1; } - (*x)++; *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); - (*x)++; + *x += display.print(*x, 1, value / 10, WHITE, true) + 1; *x += display.print(*x, 1, value % 10, WHITE, true); } @@ -234,23 +266,12 @@ private: } } - static int getDayCountForYear(int year) { - bool leapYear = year % 4 == 0 && (year % 400 == 0 || year % 100 != 0); - if (leapYear) { - return 366; - } - return 365; - } - static void drawYear(Display &display, int year) { uint8_t x = 8; - x += display.print(x, 1, year / 1000 % 10, WHITE, true); - x++; - x += display.print(x, 1, year / 100 % 10, WHITE, true); - x++; - x += display.print(x, 1, year / 10 % 10, WHITE, true); - x++; - x += display.print(x, 1, year / 1 % 10, WHITE, true); + 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; + display.print(x, 1, year / 1 % 10, WHITE, true); } }; diff --git a/src/mode/Mode.h b/src/mode/Mode.h index e59b1a9..9536657 100644 --- a/src/mode/Mode.h +++ b/src/mode/Mode.h @@ -21,6 +21,7 @@ enum ModeId { SPACE_INVADERS, COUNT_DOWN, COUNT_DOWN_BARS, + COUNT_DOWN_SLEEP, STARFIELD, MATRIX, POWER, @@ -63,7 +64,7 @@ protected: milliseconds_t realtimeMilliseconds = 0; - time_t epoch = 0; + time_t nowEpochSeconds = 0; virtual void tick(uint8_t index, microseconds_t microseconds) {}; @@ -127,9 +128,9 @@ private: tmp += ((FAKE_DAYS * 24 + FAKE_HOURS) * 60 + FAKE_MINUTES) * 60 + FAKE_SECONDS; realtimeOK = tmp > 1600000000; if (realtimeOK) { - realtimeChanged = epoch != tmp; + realtimeChanged = nowEpochSeconds != tmp; if (realtimeChanged) { - epoch = tmp; + nowEpochSeconds = tmp; localtime_r(&tmp, &now); now.tm_year += 1900; now.tm_mon += 1; diff --git a/src/server.cpp b/src/server.cpp index 7c26226..37f5591 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -13,7 +13,7 @@ body { font-size: 8vw; margin: 0; } -button{ +button.player{ width: 33vmin; height: 33vmin; font-size: 9vw; @@ -61,6 +61,8 @@ void web_fps_on(); void web_fps_off(); +void web_config_date(); + void server_setup() { server.on("/", web_index); server.on("/player", web_player); @@ -73,6 +75,7 @@ void server_setup() { server.on("/slower", web_slower); server.on("/fps/on", web_fps_on); server.on("/fps/off", web_fps_off); + server.on("/config/date", web_config_date); server.begin(); } @@ -103,10 +106,11 @@ void web_index() { server.sendContent(R"(SPACE_INVADERS
)"); server.sendContent(R"(COUNT_DOWN
)"); server.sendContent(R"(COUNT_DOWN_BARS
)"); - server.sendContent(R"(STARFIELD
)"); - server.sendContent(R"(MATRIX
)"); - server.sendContent(R"(POWER
)"); - server.sendContent(R"(ENERGY
)"); + server.sendContent(R"(COUNT_DOWN_SLEEP
)"); + server.sendContent(R"(STARFIELD
)"); + server.sendContent(R"(MATRIX
)"); + server.sendContent(R"(POWER
)"); + server.sendContent(R"(ENERGY
)"); server.sendContent(R"(

)"); server.sendContent(R"(

)"); @@ -114,6 +118,14 @@ void web_index() { server.sendContent(R"(Geschwindigkeit: + / -
)"); server.sendContent(R"(FPS: EIN / AUS
)"); server.sendContent(R"(

)"); + + server.sendContent(R"(

)"); + server.sendContent(R"()"); + server.sendContent(R"()"); + server.sendContent(R"()"); + server.sendContent(R"()"); + server.sendContent(R"(

)"); + server.client().flush(); } @@ -141,7 +153,7 @@ void web_player() { server.sendContent(R"(←)"); server.sendContent(R"()"); - snprintf(buffer, sizeof buffer, R"(
)", index); + snprintf(buffer, sizeof buffer, R"(
)", index); server.sendContent(buffer); server.sendContent(R"()"); @@ -152,17 +164,17 @@ void web_player() { server.sendContent(R"()"); server.sendContent(R"()"); - snprintf(buffer, sizeof buffer, R"(
)", index); + snprintf(buffer, sizeof buffer, R"(
)", index); server.sendContent(buffer); server.sendContent(R"()"); server.sendContent(R"()"); - snprintf(buffer, sizeof buffer, R"(
)", index); + snprintf(buffer, sizeof buffer, R"(
)", index); server.sendContent(buffer); server.sendContent(R"()"); server.sendContent(R"()"); - snprintf(buffer, sizeof buffer, R"(
)", index); + snprintf(buffer, sizeof buffer, R"(
)", index); server.sendContent(buffer); server.sendContent(R"()"); @@ -173,7 +185,7 @@ void web_player() { server.sendContent(R"( )"); server.sendContent(R"()"); - snprintf(buffer, sizeof buffer, R"(
)", index); + snprintf(buffer, sizeof buffer, R"(
)", index); server.sendContent(buffer); server.sendContent(R"()"); @@ -272,3 +284,19 @@ void web_fps_off() { display.fpsShow = false; server.send(200); } + +void web_config_date() { + double year = strtod(server.arg("year").c_str(), nullptr); + double month = strtod(server.arg("month").c_str(), nullptr); + double day = strtod(server.arg("day").c_str(), nullptr); + if (!isnan(year)) { + config.date.tm_year = (int) year; + config.date.tm_mon = (int) month; + config.date.tm_mday = (int) day; + config.date.tm_hour = 0; + config.date.tm_min = 0; + config.date.tm_sec = 0; + server.send(200); + } + server.send(400); +}