From 2351c42db219b557080527647e3495fed3a7d451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Fri, 31 Dec 2021 15:23:39 +0100 Subject: [PATCH] NewYear --- CMakeLists.txt | 2 +- platformio.ini | 8 +-- src/BASICS.cpp | 14 +--- src/BASICS.h | 2 +- src/display/Display.h | 25 +++++++ src/display/Vector.h | 24 ++++++- src/main.cpp | 66 ++++++++++------- src/mode/GameOfLife/Cell.h | 9 +-- src/mode/Mode.h | 6 +- src/mode/NewYear/Firework.h | 136 +++++++++++++++++++++++++++++++++++ src/mode/NewYear/NewYear.cpp | 3 + src/mode/NewYear/NewYear.h | 120 +++++++++++++++++++++++++++++++ src/mode/Pong/Pong.h | 2 +- src/mode/Timer.h | 3 +- 14 files changed, 362 insertions(+), 58 deletions(-) create mode 100644 src/mode/NewYear/Firework.h create mode 100644 src/mode/NewYear/NewYear.cpp create mode 100644 src/mode/NewYear/NewYear.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 87ef223..2af601f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,4 +30,4 @@ add_custom_target( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -add_executable(Z_DUMMY_TARGET ${SRC_LIST} src/mode/Test/Border.h src/mode/Clock/Clock.h src/mode/SpaceInvaders/SpaceInvaders.h src/mode/Timer.h src/mode/Pong/Pong.cpp src/BASICS.cpp src/display/Display.cpp) +add_executable(Z_DUMMY_TARGET ${SRC_LIST} src/mode/Test/Border.h src/mode/Clock/Clock.h src/mode/SpaceInvaders/SpaceInvaders.h src/mode/Timer.h src/mode/Pong/Pong.cpp src/BASICS.cpp src/display/Display.cpp src/mode/NewYear/NewYear.h src/mode/NewYear/Firework.h src/mode/NewYear/NewYear.cpp) diff --git a/platformio.ini b/platformio.ini index f8f6fcf..d2e6209 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,9 +13,9 @@ platform = espressif32 board = esp32dev framework = arduino lib_deps = https://github.com/adafruit/Adafruit_NeoPixel -upload_port = 10.0.0.120 -upload_protocol = espota -;upload_port = /dev/ttyUSB0 -;upload_speed = 921600 +;upload_port = 10.0.0.120 +;upload_protocol = espota +upload_port = /dev/ttyUSB0 +upload_speed = 921600 monitor_port = /dev/ttyUSB0 monitor_speed = 115200 diff --git a/src/BASICS.cpp b/src/BASICS.cpp index d87542d..13e084f 100644 --- a/src/BASICS.cpp +++ b/src/BASICS.cpp @@ -1,20 +1,10 @@ #include "BASICS.h" -double step(double valueCurrent, double valueMin, double valueMax, microseconds_t timeTotal, microseconds_t timeDelta) { +double doStep(double valueCurrent, double valueMin, double valueMax, long long millisecondsTotal, microseconds_t microsecondsDelta) { double valueRange = valueMax - valueMin; - double timeRatio = (double) timeDelta / (double) timeTotal; + double timeRatio = (double) microsecondsDelta / ((double) millisecondsTotal * 1000.0); double valueStep = valueRange * timeRatio; double valueNew = max(valueMin, min(valueMax, valueCurrent + valueStep)); -// Serial.printf("valueCurrent: %13.3f\n", valueCurrent); -// Serial.printf("valueMin: %13.3f\n", valueMin); -// Serial.printf("valueMax: %13.3f\n", valueMax); -// Serial.printf("valueRange: %13.3f\n", valueRange); -// Serial.printf("timeTotal: %13.3f\n", (double) timeTotal); -// Serial.printf("timeDelta: %13.3f\n", (double) timeDelta); -// Serial.printf("timeRatio: %13.3f\n", timeRatio); -// Serial.printf("valueStep: %13.3f\n", valueStep); -// Serial.printf("valueNew: %13.3f\n", valueNew); -// Serial.println(); return valueNew; } diff --git a/src/BASICS.h b/src/BASICS.h index e18f917..28f2229 100644 --- a/src/BASICS.h +++ b/src/BASICS.h @@ -8,7 +8,7 @@ typedef int64_t microseconds_t; -double step(double valueCurrent, double valueMin, double valueMax, microseconds_t timeTotal, microseconds_t timeDelta); +double doStep(double valueCurrent, double valueMin, double valueMax, microseconds_t millisecondsTotal, microseconds_t microsecondsDelta); bool randomBool(int uncertainty); diff --git a/src/display/Display.h b/src/display/Display.h index 5045dbe..e2ced21 100644 --- a/src/display/Display.h +++ b/src/display/Display.h @@ -3,11 +3,24 @@ #include "Pixel.h" #include "Adafruit_NeoPixel.h" +#include "Vector.h" #define SYMBOL_COUNT 13 #define DISPLAY_CHAR_WIDTH 3 #define DISPLAY_CHAR_HEIGHT 5 +#define FULL 255 +#define ____ 0 +#define rgb(r, g, b) ((((r << 8) | g) << 8) | b) +#define COLOR_RED (rgb(FULL, ____, ____)) +#define COLOR_GREEN (rgb(____, FULL, ____)) +#define COLOR_BLUE (rgb(____, ____, FULL)) +#define COLOR_YELLOW (rgb(FULL, FULL, ____)) +#define COLOR_VIOLET (rgb(FULL, ____, FULL)) +#define COLOR_TURQUOISE (rgb(____, FULL, FULL)) +#define COLOR_WHITE (rgb(FULL, FULL, FULL)) +#define COLOR_BLACK (rgb(____, ____, ____)) + extern bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT]; class Display { @@ -51,6 +64,10 @@ public: print(xPos, yPos, index, 255, 255, 255); } + void print(uint8_t *xPos, uint8_t yPos, uint8_t index, uint32_t color) { + print(xPos, yPos, index, (uint8_t) (color >> 16), (uint8_t) (color >> 8), (uint8_t) color); + } + void print(uint8_t *xPos, uint8_t yPos, uint8_t index, uint8_t r, uint8_t g, uint8_t b) { if (index >= SYMBOL_COUNT) { Serial.printf("Cannot print symbol #%u.\n", index); @@ -77,6 +94,14 @@ public: setPixelColor(x, y, (((r << 8) | g) << 8) | b); } + void set(Vector *pos, uint32_t color) { + setPixelColor((uint8_t) round(pos->x), (uint8_t) round(pos->y), color); + } + + void set(Vector *pos, uint8_t r, uint8_t g, uint8_t b) { + setPixelColor((uint8_t) round(pos->x), (uint8_t) round(pos->y), (((r << 8) | g) << 8) | b); + } + void setPixelColor(uint8_t x, uint8_t y, uint32_t color) { if (x >= width || y >= height) { return; diff --git a/src/display/Vector.h b/src/display/Vector.h index abc2ba6..b61a886 100644 --- a/src/display/Vector.h +++ b/src/display/Vector.h @@ -11,6 +11,11 @@ public: double y; + Vector() : + x(0.0), y(0.0) { + // nothing + } + Vector(double x, double y) : x(x), y(y) { // nothing @@ -22,6 +27,24 @@ public: y = sin(radians) * length; } + Vector *set(Vector that) { + this->x = that.x; + this->y = that.y; + return this; + } + + Vector *set(double _x, double _y) { + this->x = _x; + this->y = _y; + return this; + } + + Vector *add(double _x, double _y) { + this->x += _x; + this->y += _y; + return this; + } + Vector *add(Vector that) { this->x += that.x; this->y += that.y; @@ -45,7 +68,6 @@ public: } return this; } - }; #endif diff --git a/src/main.cpp b/src/main.cpp index ecb2cd5..e660b1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,21 @@ #include #include #include "mode/Mode.h" -#include "mode/GameOfLife/GameOfLife.h" #include "display/Display.h" + +#if PROG_ALL +#include "mode/GameOfLife/GameOfLife.h" #include "mode/Pong/Pong.h" #include "mode/Test/Border.h" #include "mode/Clock/Clock.h" #include "mode/SpaceInvaders/SpaceInvaders.h" +#endif + +#include "mode/NewYear/NewYear.h" enum ModeId { NONE, +#if PROG_ALL BORDER, CLOCK, GAME_OF_LIFE_BLACK_WHITE, @@ -18,11 +24,13 @@ enum ModeId { GAME_OF_LIFE_RANDOM_COLOR, PONG, SPACE_INVADERS, +#endif + NEW_YEAR, }; Display display(32, 8); -ModeId newModeId = PONG; +ModeId newModeId = NEW_YEAR; ModeId currentModeId = NONE; @@ -48,6 +56,7 @@ void setup() { delay(500); Serial.begin(115200); Serial.println("\n\n\nStartup!"); + WiFi.begin("HappyNet", "1Grausame!Sackratte7"); ArduinoOTA.onStart([]() { display.clear(); @@ -68,8 +77,8 @@ void setup() { display.clear(); display.loop(); }); - ArduinoOTA.begin(); + display.setup(); } @@ -156,29 +165,34 @@ void loadNewMode() { switch (currentModeId) { case NONE: break; - case BORDER: - mode = new Border(&display); - break; - case CLOCK: - mode = new Clock(&display); - break; - case GAME_OF_LIFE_BLACK_WHITE: - mode = new GameOfLife(&display, BLACK_WHITE); - break; - case GAME_OF_LIFE_GRAYSCALE: - mode = new GameOfLife(&display, GRAYSCALE); - break; - case GAME_OF_LIFE_COLOR_FADE: - mode = new GameOfLife(&display, COLOR_FADE); - break; - case GAME_OF_LIFE_RANDOM_COLOR: - mode = new GameOfLife(&display, RANDOM_COLOR); - break; - case PONG: - mode = new Pong(&display); - break; - case SPACE_INVADERS: - mode = new SpaceInvaders(&display); +#if PROG_ALL + case BORDER: + mode = new Border(&display); + break; + case CLOCK: + mode = new Clock(&display); + break; + case GAME_OF_LIFE_BLACK_WHITE: + mode = new GameOfLife(&display, BLACK_WHITE); + break; + case GAME_OF_LIFE_GRAYSCALE: + mode = new GameOfLife(&display, GRAYSCALE); + break; + case GAME_OF_LIFE_COLOR_FADE: + mode = new GameOfLife(&display, COLOR_FADE); + break; + case GAME_OF_LIFE_RANDOM_COLOR: + mode = new GameOfLife(&display, RANDOM_COLOR); + break; + case PONG: + mode = new Pong(&display); + break; + case SPACE_INVADERS: + mode = new SpaceInvaders(&display); + break; +#endif + case NEW_YEAR: + mode = new NewYear(&display); break; } Serial.printf("Mode: %s\n", mode == nullptr ? "None" : mode->getName()); diff --git a/src/mode/GameOfLife/Cell.h b/src/mode/GameOfLife/Cell.h index 763c476..58056d3 100644 --- a/src/mode/GameOfLife/Cell.h +++ b/src/mode/GameOfLife/Cell.h @@ -16,15 +16,10 @@ public: void animate(microseconds_t dt) { // TODO "doStep" does not work as expected if (alive) { - fade = step(fade, 0.0, 255.0, 200000, +dt); + fade = doStep(fade, 0.0, 255.0, 200, +dt); } else { - fade = step(fade, 0.0, 255.0, 200000, -dt); + fade = doStep(fade, 0.0, 255.0, 200, -dt); } -// if (alive) { -// fade = min(255.0, fade + (double) dt / 200000.0); -// } else { -// fade = max(0.0, fade - (double) dt / 200000.0); -// } } uint8_t getR() const { diff --git a/src/mode/Mode.h b/src/mode/Mode.h index b870bf8..75f7158 100644 --- a/src/mode/Mode.h +++ b/src/mode/Mode.h @@ -47,12 +47,11 @@ protected: virtual void doStep(microseconds_t dt) {}; - Timer *createTimer(microseconds_t interval, Timer::Callback callback) { + Timer *createTimer(long long millisecondsInterval, Timer::Callback callback) { for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) { if (*timer == nullptr) { - *timer = new Timer(interval, callback); + *timer = new Timer(millisecondsInterval * 1000, callback); timerCount++; - Serial.printf("Timer created: %p (having %u now)\n", *timer, timerCount); return *timer; } } @@ -84,7 +83,6 @@ private: void _destroyTimer(Timer **timer) { timerCount--; free(*timer); - Serial.printf("Timer destroyed: %p (having %u now)\n", *timer, timerCount); *timer = nullptr; } diff --git a/src/mode/NewYear/Firework.h b/src/mode/NewYear/Firework.h new file mode 100644 index 0000000..8aecdc1 --- /dev/null +++ b/src/mode/NewYear/Firework.h @@ -0,0 +1,136 @@ +#ifndef NEW_YEAR_FIREWORK_H +#define NEW_YEAR_FIREWORK_H + +#include "BASICS.h" +#include "display/Vector.h" +#include "display/Display.h" + +class Firework { + + enum State { + INITIAL, RISE, EXPLODE, SPARKLE + }; + + Display *display{}; + + uint32_t color{}; + + State state = RISE; + + Vector position; + + double destinationHeight = 0; + + double explosionRadius = 0; + + double sparkleMax = 0; + + double explosion{}; + + double sparkle{}; + +public: + + void init(Display *_display) { + this->display = _display; + reset(); + } + + void reset() { + position.set((double) random(display->width), display->height); + color = random(16777216); + state = INITIAL; + + destinationHeight = display->height / 2.0 + (double) random(5) - 2; + explosionRadius = (double) random(3) + 1; + sparkleMax = 100; + + explosion = 0.0; + sparkle = 0.0; + } + + void launch() { + if (state != INITIAL) { + Serial.println("ERROR: Cannot start Firework. Already started."); + return; + } + state = RISE; + } + + void step(microseconds_t dt) { + switch (state) { + case INITIAL: + break; + case RISE: + if (position.y <= destinationHeight) { + state = EXPLODE; + } + break; + case EXPLODE: + if (explosion >= explosionRadius) { + state = SPARKLE; + } + break; + case SPARKLE: + if (sparkle >= sparkleMax) { + reset(); + } + break; + } + switch (state) { + case INITIAL: + break; + case RISE: + position.y = doStep(position.y, 0.0, display->height, 1000, -dt); + break; + case EXPLODE: + explosion = doStep(explosion, 0.0, explosionRadius, 500, +dt); + break; + case SPARKLE: + sparkle = doStep(sparkle, 0.0, sparkleMax, 1000, +dt); + break; + } + } + + void draw() { + Vector p; + switch (state) { + case INITIAL: + break; + case RISE: + display->set(&position, COLOR_YELLOW); + break; + case EXPLODE: + drawParticle(p, +0.0, +1.0); + drawParticle(p, +0.7, +0.7); + drawParticle(p, +1.0, +0.0); + drawParticle(p, +0.7, -0.7); + drawParticle(p, +0.0, -1.0); + drawParticle(p, -0.7, -0.7); + drawParticle(p, -1.0, +0.0); + drawParticle(p, -0.7, +0.7); + break; + case SPARKLE: + if (randomBool(2)) drawParticle(p, +0.0, +1.0); + if (randomBool(2)) drawParticle(p, +0.7, +0.7); + if (randomBool(2)) drawParticle(p, +1.0, +0.0); + if (randomBool(2)) drawParticle(p, +0.7, -0.7); + if (randomBool(2)) drawParticle(p, +0.0, -1.0); + if (randomBool(2)) drawParticle(p, -0.7, -0.7); + if (randomBool(2)) drawParticle(p, -1.0, +0.0); + if (randomBool(2)) drawParticle(p, -0.7, +0.7); + break; + } + } + + void drawParticle(Vector &p, double x, double y) { + display->set(p.set(position)->add(x * explosion, y * explosion), color); + } + + bool isAlive() { + return state != INITIAL; + } + +}; + +#endif diff --git a/src/mode/NewYear/NewYear.cpp b/src/mode/NewYear/NewYear.cpp new file mode 100644 index 0000000..112db2e --- /dev/null +++ b/src/mode/NewYear/NewYear.cpp @@ -0,0 +1,3 @@ +#include "NewYear.h" + +NewYear *NewYear::instance = nullptr; \ No newline at end of file diff --git a/src/mode/NewYear/NewYear.h b/src/mode/NewYear/NewYear.h new file mode 100644 index 0000000..a9753dd --- /dev/null +++ b/src/mode/NewYear/NewYear.h @@ -0,0 +1,120 @@ +#ifndef NEWYEAR_H +#define NEWYEAR_H + +#define MAX_FIREWORKS 10 + +#include "mode/Mode.h" +#include "Firework.h" + +class NewYear : public Mode { + + static NewYear *instance; + + Firework fireworksBegin[MAX_FIREWORKS]; + Firework *fireworksEnd = fireworksBegin + MAX_FIREWORKS; + + void launch(Timer *timer, uint32_t counter, uint32_t currentCount) { + for (Firework *firework = fireworksBegin; firework < fireworksEnd; firework++) { + if (!firework->isAlive()) { + firework->launch(); + return; + } + } + } + + void step(Timer *timer, uint32_t counter, uint32_t currentCount) { + display->clear(); + tm t{}; + getLocalTime(&t); + int year = t.tm_year + 1900; + int mon = t.tm_mon + 1; + int day = t.tm_mday; + int hour = t.tm_hour; + int min = t.tm_min; + int sec = t.tm_sec; +#if PROG_ALL +#else + hour = 23; + min = 59; + sec = 55 + (int) (counter * 50 / 1000); + while (sec >= 60) { + min++; + sec -= 60; + } + while (min >= 60) { + hour++; + min -= 60; + } + while (hour >= 24) { + day++; + hour -= 24; + } + while (day > 31) { + mon++; + day -= 31; + } + while (mon > 12) { + year++; + mon -= 12; + } + Serial.printf("%04i-%02i-%02i %02i:%02i:%02i\n", year, mon, day, hour, min, sec); +#endif + if (mon == 12 && day == 31) { + size_t h = (24 - hour - (min > 0 || sec > 0 ? 1 : 0)); + size_t m = (60 - min - (sec > 0 ? 1 : 0)) % 60; + size_t s = (60 - sec) % 60; + uint8_t x = 0; + if (h >= 10) { + display->print(&x, 1, h / 10, COLOR_WHITE); + } else { + x += 3; + } + x++; + display->print(&x, 1, h % 10, COLOR_WHITE); + display->print(&x, 1, 10, COLOR_WHITE); + display->print(&x, 1, m / 10, COLOR_WHITE); + x++; + display->print(&x, 1, m % 10, COLOR_WHITE); + display->print(&x, 1, 10, COLOR_WHITE); + display->print(&x, 1, s / 10, COLOR_WHITE); + x++; + display->print(&x, 1, s % 10, COLOR_WHITE); + } else { + for (Firework *firework = fireworksBegin; firework < fireworksEnd; firework++) { + if (firework->isAlive()) { + firework->step(timer->interval); + firework->draw(); + } + } + uint8_t x = 8; + display->print(&x, 1, year / 1000 % 10, counter % 64 != 0 ? COLOR_WHITE : COLOR_BLACK); + x++; + display->print(&x, 1, year / 100 % 10, counter % 64 != 1 ? COLOR_WHITE : COLOR_BLACK); + x++; + display->print(&x, 1, year / 10 % 10, counter % 64 != 2 ? COLOR_WHITE : COLOR_BLACK); + x++; + display->print(&x, 1, year / 1 % 10, counter % 64 != 3 ? COLOR_WHITE : COLOR_BLACK); + } + } + +public: + + explicit NewYear(Display *display) : + Mode(display) { + createTimer(500, [](Timer *timer, uint32_t counter, uint32_t currentCount) { instance->launch(timer, counter, currentCount); }); + createTimer(50, [](Timer *timer, uint32_t counter, uint32_t currentCount) { instance->step(timer, counter, currentCount); }); + instance = this; + for (Firework *firework = fireworksBegin; firework < fireworksEnd; firework++) { + firework->init(display); + } + } + + ~NewYear() override = default; + + const char *getName() override { + return "NewYear"; + } + +}; + +#endif diff --git a/src/mode/Pong/Pong.h b/src/mode/Pong/Pong.h index ccf93a5..2e83f92 100644 --- a/src/mode/Pong/Pong.h +++ b/src/mode/Pong/Pong.h @@ -133,7 +133,7 @@ public: position(display->width / 2.0, display->height / 2.0), velocity(random(360), exp10(1)) { instance = this; - createTimer(100000, &tick); + createTimer(100, &tick); spawnBall(random(2) == 0 ? -1 : +1); resetPlayer(); } diff --git a/src/mode/Timer.h b/src/mode/Timer.h index 4f293bd..37bf9f2 100644 --- a/src/mode/Timer.h +++ b/src/mode/Timer.h @@ -31,8 +31,9 @@ public: accu += dt; if (accu >= interval) { uint32_t currentCount = accu / interval; + totalCount += currentCount; accu = accu % interval; - callback(this, totalCount++, currentCount); + callback(this, totalCount, currentCount); } }