modifiable date for countdown + COUNT_DOWN_SLEEP

This commit is contained in:
Patrick Haßel 2024-12-31 12:01:32 +01:00
parent f3d4c68e94
commit 399723d77a
8 changed files with 171 additions and 81 deletions

View File

@ -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");
}

View File

@ -7,6 +7,7 @@ struct Config {
ModeId mode;
double speed;
uint8_t brightness;
tm date;
};
extern Config config;

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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"(<a onclick="get('/mode?mode=8');">SPACE_INVADERS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=9');">COUNT_DOWN</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=11');">STARFIELD</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=12');">MATRIX</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=13');">POWER</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=14');">ENERGY</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=11');">COUNT_DOWN_SLEEP</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=12');">STARFIELD</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=13');">MATRIX</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=14');">POWER</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=15');">ENERGY</a><br>)");
server.sendContent(R"(</p>)");
server.sendContent(R"(<p>)");
@ -114,6 +118,14 @@ void web_index() {
server.sendContent(R"(Geschwindigkeit: <a onclick="get('/faster');">+</a> / <a onclick="get('/slower');">-</a><br>)");
server.sendContent(R"(FPS: <a onclick="get('/fps/on');">EIN</a> / <a onclick="get('/fps/off');">AUS</a><br>)");
server.sendContent(R"(</p>)");
server.sendContent(R"(<p>)");
server.sendContent(R"(<input type="number" min="1900" max="3000" step="1" name="year" id="year">)");
server.sendContent(R"(<input type="number" min="1" max="12" step="1" name="month" id="month">)");
server.sendContent(R"(<input type="number" min="1" max="31" step="1" name="day" id="day">)");
server.sendContent(R"(<button onclick="get('/config/date?year=' + document.getElementById('year').value + '&month=' + document.getElementById('month').value + '&day=' + document.getElementById('day').value);">Datum setzen</button>)");
server.sendContent(R"(</p>)");
server.client().flush();
}
@ -141,7 +153,7 @@ void web_player() {
server.sendContent(R"(<td><a href='/'>&larr;</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=0&y=-1');">&uarr;</button><br>)", index);
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=-1');">&uarr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
@ -152,17 +164,17 @@ void web_player() {
server.sendContent(R"(<tr>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=-1&y=0');">&larr;</button><br>)", index);
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=-1&y=0');">&larr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/fire?index=%d');">X</button><br>)", index);
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/fire?index=%d');">X</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=+1&y=0');">&rarr;</button><br>)", index);
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=+1&y=0');">&rarr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
@ -173,7 +185,7 @@ void web_player() {
server.sendContent(R"(<td>&nbsp;</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=0&y=+1');">&darr;</button><br>)", index);
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=+1');">&darr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
@ -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);
}