diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..2508865 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,83 @@ +#include +#include "config.h" + +uint32_t calculateChecksum(Config *ptr); + +void config_defaults(); + +bool validateChecksum(uint32_t checksumEEPROM, Config &tmp); + +Config config; + +bool dirty = false; + +unsigned long lastDirtyMillis = 0; + +void config_setup() { + if (!config_load()) { + config_defaults(); + } +} + +void config_set_dirty() { + dirty = true; + lastDirtyMillis = millis(); +} + +void config_loop() { + if (!dirty) { + return; + } + if (millis() - lastDirtyMillis <= 30000) { + return; + } + dirty = false; + + uint32_t checksum = calculateChecksum(&config); + + EEPROM.begin(512); + EEPROM.writeBytes(0, &config, sizeof(Config)); + EEPROM.writeUInt(sizeof(Config), checksum); + EEPROM.end(); + + Serial.printf("Config saved to EEPROM.\n"); +} + +bool config_load() { + Config tmp{}; + + EEPROM.begin(512); + EEPROM.readBytes(0, &tmp, sizeof(Config)); + uint32_t checksum = EEPROM.readUInt(sizeof(Config)); + EEPROM.end(); + + bool success = validateChecksum(checksum, tmp); + if (success) { + memcpy(&config, &tmp, sizeof(Config)); + Serial.printf("Config loaded from EEPROM.\n"); + } else { + Serial.printf("Failed to load config from EEPROM.\n"); + } + + return success; +} + +bool validateChecksum(uint32_t checksumEEPROM, Config &tmp) { + uint32_t calculated = calculateChecksum(&tmp); + return checksumEEPROM == calculated; +} + +void config_defaults() { + config.mode = GAME_OF_LIFE_GRAYSCALE; + config.speed = 1.0; + config.brightness = 16; + Serial.printf("Config DEFAULTS loaded.\n"); +} + +uint32_t calculateChecksum(Config *ptr) { + uint32_t checksum = 0; + for (auto *b = (uint8_t *) ptr; b < (uint8_t *) ptr + sizeof(Config); b++) { + checksum += *b; + } + return checksum; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..3745601 --- /dev/null +++ b/src/config.h @@ -0,0 +1,22 @@ +#ifndef MEDIATABLE_CONFIG_H +#define MEDIATABLE_CONFIG_H + +#include "mode/Mode.h" + +struct Config { + ModeId mode; + double speed; + uint8_t brightness; +}; + +extern Config config; + +void config_setup(); + +void config_loop(); + +bool config_load(); + +void config_set_dirty(); + +#endif diff --git a/src/display/Display.h b/src/display/Display.h index b801642..d4d974f 100644 --- a/src/display/Display.h +++ b/src/display/Display.h @@ -40,9 +40,9 @@ private: public: Display(uint8_t width, uint8_t height) : - width(width), height(height), - pixelCount(width * height), - leds(pixelCount, GPIO_NUM_13) { + width(width), height(height), + pixelCount(width * height), + leds(pixelCount, GPIO_NUM_13) { // nothing } @@ -121,7 +121,7 @@ public: } void setBrightness(int brightness) { - leds.setBrightness(min(255, max(0, brightness))); + leds.setBrightness(max(0, min(brightness, 255))); } int getBrightness() { diff --git a/src/main.cpp b/src/main.cpp index c74f8d2..3aa8adf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "display/Display.h" #include #include +#include #include "mode/GameOfLife/GameOfLife.h" #include "mode/Pong/Pong.h" @@ -13,34 +14,18 @@ #include "mode/SpaceInvaders/SpaceInvaders.h" #include "mode/NewYear/NewYear.h" #include "mode/Border/Border.h" - -enum ModeId { - NONE, - BORDER, - CLOCK, - GAME_OF_LIFE_BLACK_WHITE, - GAME_OF_LIFE_GRAYSCALE, - GAME_OF_LIFE_COLOR_FADE, - GAME_OF_LIFE_RANDOM_COLOR, - PONG, - SPACE_INVADERS, - NEW_YEAR, -}; +#include "config.h" WebServer server(80); Display display(32, 8); -ModeId newModeId = NEW_YEAR; - ModeId currentModeId = NONE; microseconds_t lastMicros = 0; ModeBase *mode = nullptr; -double speed = 1.0; - bool connected = false; void mode_check(); @@ -51,9 +36,9 @@ void unloadOldMode(); void loadNewMode(); -void setBrightness(int value); +void setBrightness(int brightness); -void setSpeed(double value); +void setSpeed(double speed); void serial_read(); @@ -63,6 +48,8 @@ void ntp_setup(); uint32_t ip2int(const IPAddress &ip); +void setMode(ModeId value); + void web_index() { server.setContentLength(CONTENT_LENGTH_UNKNOWN); server.send(200, "text/html", ""); @@ -77,6 +64,7 @@ void web_index() { server.sendContent("SPACE_INVADERS
"); server.sendContent("NEW_YEAR
"); server.sendContent("Helligkeit: + / -
"); + server.sendContent("Geschwindigkeit: + / -
"); server.client().flush(); } @@ -90,11 +78,19 @@ void web_setMode() { server.send(400, "text/plain", "'mode' not a number"); return; } - newModeId = (ModeId) value; + setMode((ModeId) value); server.sendHeader("location", "/"); server.send(301, "text/plain", "ok"); } +void setMode(ModeId value) { + if (config.mode == value) { + return; + } + config.mode = value; + config_set_dirty(); +} + void web_brighter() { setBrightness(display.getBrightness() + 10); server.sendHeader("location", "/"); @@ -107,6 +103,18 @@ void web_darker() { server.send(301, "text/plain", "ok"); } +void web_faster() { + setSpeed(config.speed * 1.1); + server.sendHeader("location", "/"); + server.send(301, "text/plain", "ok"); +} + +void web_slower() { + setSpeed(config.speed / 1.1); + server.sendHeader("location", "/"); + server.send(301, "text/plain", "ok"); +} + void setup() { delay(500); Serial.begin(115200); @@ -142,8 +150,11 @@ void setup() { ArduinoOTA.begin(); yield(); + config_setup(); + display.setup(); yield(); + display.setBrightness(config.brightness); server.on("", web_index); server.on("/", web_index); @@ -153,15 +164,20 @@ void setup() { server.on("/brighter/", web_brighter); server.on("/darker", web_darker); server.on("/darker/", web_darker); + server.on("/faster", web_faster); + server.on("/faster/", web_faster); + server.on("/slower", web_slower); + server.on("/slower/", web_slower); server.begin(); } void loop() { yield(); ArduinoOTA.handle(); - server.handleClient(); - wifi_loop(); serial_read(); + server.handleClient(); + config_loop(); + wifi_loop(); mode_check(); display.loop(); mode_step(); @@ -225,7 +241,7 @@ void serial_read() { case '7': case '8': case '9': - newModeId = (ModeId) (input - '0'); + setMode((ModeId) (input - '0')); break; case '+': setBrightness(display.getBrightness() + 10); @@ -234,10 +250,10 @@ void serial_read() { setBrightness(max(1, display.getBrightness() - 10)); break; case ',': - setSpeed(speed / 1.1); + setSpeed(config.speed / 1.1); break; case '.': - setSpeed(speed * 1.1); + setSpeed(config.speed * 1.1); break; default: Serial.printf("Unknown command: %c\n", input); @@ -246,18 +262,29 @@ void serial_read() { } } -void setBrightness(int value) { - display.setBrightness(value); - Serial.printf("Setting brightness to %5.1f%%\n", value / 2.55); +void setBrightness(int brightness) { + brightness = max(0, min(brightness, 255)); + if (display.getBrightness() == brightness) { + return; + } + config.brightness = brightness; + display.setBrightness(brightness); + config_set_dirty(); + Serial.printf("Setting brightness to %5.1f%%\n", brightness / 2.55); } -void setSpeed(double value) { - speed = value; - Serial.printf("Setting speed to %6.2fx\n", value); +void setSpeed(double speed) { + speed = min(max(0.01, speed), 10000.0); + if (config.speed == speed) { + return; + } + config.speed = speed; + config_set_dirty(); + Serial.printf("Setting speed to %6.2fx\n", config.speed); } void mode_check() { - if (currentModeId != newModeId) { + if (currentModeId != config.mode) { unloadOldMode(); loadNewMode(); } @@ -272,11 +299,9 @@ void unloadOldMode() { } void loadNewMode() { - currentModeId = newModeId; + currentModeId = config.mode; lastMicros = 0; switch (currentModeId) { - case NONE: - break; case BORDER: mode = new Border(&display); break; @@ -304,6 +329,9 @@ void loadNewMode() { case NEW_YEAR: mode = new NewYear(&display); break; + default: + Serial.print("No mode loaded.\n"); + break; } Serial.printf("Mode: %s\n", mode == nullptr ? "None" : mode->getName()); } @@ -313,7 +341,7 @@ void mode_step() { return; } auto currentMicros = (int64_t) micros(); - microseconds_t dt = (microseconds_t) min(1000000.0, max(1.0, (double) (currentMicros - lastMicros) * speed)); + microseconds_t dt = (microseconds_t) min(1000000.0, max(1.0, (double) (currentMicros - lastMicros) * config.speed)); lastMicros = currentMicros; mode->step(dt); } diff --git a/src/mode/Mode.h b/src/mode/Mode.h index bdc7269..315037e 100644 --- a/src/mode/Mode.h +++ b/src/mode/Mode.h @@ -8,6 +8,19 @@ #include "Timer.h" #include "ModeBase.h" +enum ModeId { + NONE, + BORDER, + CLOCK, + GAME_OF_LIFE_BLACK_WHITE, + GAME_OF_LIFE_GRAYSCALE, + GAME_OF_LIFE_COLOR_FADE, + GAME_OF_LIFE_RANDOM_COLOR, + PONG, + SPACE_INVADERS, + NEW_YEAR, +}; + template class Mode : public ModeBase { diff --git a/src/mode/NewYear/NewYear.h b/src/mode/NewYear/NewYear.h index 62c8469..b8fd622 100644 --- a/src/mode/NewYear/NewYear.h +++ b/src/mode/NewYear/NewYear.h @@ -36,7 +36,6 @@ class NewYear : public Mode { display->clear(); if (!ok) { drawNoTime(); - drawSubSecondsBar(info.tm_sec); } else if (info.tm_mon == 1 && info.tm_mday == 1 && info.tm_hour == 0) { drawYear(counter, info.tm_year + 1900); drawFirework(timer->interval);