This commit is contained in:
Patrick Haßel 2021-12-31 12:45:29 +01:00
parent 4030b9136a
commit 72e5946a0e
18 changed files with 479 additions and 151 deletions

View File

@ -30,4 +30,4 @@ add_custom_target(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 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) 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)

View File

@ -13,7 +13,9 @@ platform = espressif32
board = esp32dev board = esp32dev
framework = arduino framework = arduino
lib_deps = https://github.com/adafruit/Adafruit_NeoPixel lib_deps = https://github.com/adafruit/Adafruit_NeoPixel
upload_port = /dev/ttyUSB0 upload_port = 10.0.0.120
upload_speed = 921600 upload_protocol = espota
;upload_port = /dev/ttyUSB0
;upload_speed = 921600
monitor_port = /dev/ttyUSB0 monitor_port = /dev/ttyUSB0
monitor_speed = 115200 monitor_speed = 115200

23
src/BASICS.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "BASICS.h"
double step(double valueCurrent, double valueMin, double valueMax, microseconds_t timeTotal, microseconds_t timeDelta) {
double valueRange = valueMax - valueMin;
double timeRatio = (double) timeDelta / (double) timeTotal;
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;
}
bool randomBool(int uncertainty) {
return random(uncertainty) == 0;
}

View File

@ -8,26 +8,8 @@
typedef int64_t microseconds_t; typedef int64_t microseconds_t;
double step(double valueCurrent, double valueMin, double valueMax, microseconds_t timeTotal, microseconds_t timeDelta) { double step(double valueCurrent, double valueMin, double valueMax, microseconds_t timeTotal, microseconds_t timeDelta);
double valueRange = valueMax - valueMin;
double timeRatio = (double) timeDelta / (double) timeTotal;
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;
}
bool randomBool(int uncertainty) { bool randomBool(int uncertainty);
return random(uncertainty) == 0;
}
#endif #endif

95
src/display/Display.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "Display.h"
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,
},
{
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, _, _,
},
{
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, _,
X, X, X,
X, _, X,
},
};

View File

@ -4,6 +4,12 @@
#include "Pixel.h" #include "Pixel.h"
#include "Adafruit_NeoPixel.h" #include "Adafruit_NeoPixel.h"
#define SYMBOL_COUNT 13
#define DISPLAY_CHAR_WIDTH 3
#define DISPLAY_CHAR_HEIGHT 5
extern bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT];
class Display { class Display {
public: public:
@ -33,6 +39,36 @@ public:
clear(); clear();
} }
void print(uint8_t xPos, uint8_t yPos, uint8_t index, uint8_t r, uint8_t g, uint8_t b) {
print(&xPos, yPos, index, r, g, b);
}
void print(uint8_t xPos, uint8_t yPos, uint8_t index) {
print(&xPos, yPos, index, 255, 255, 255);
}
void print(uint8_t *xPos, uint8_t yPos, uint8_t index) {
print(xPos, yPos, index, 255, 255, 255);
}
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);
return;
}
bool *symbolBit = SYMBOLS[index];
for (uint8_t y = 0; y < DISPLAY_CHAR_HEIGHT; ++y) {
for (uint8_t x = 0; x < DISPLAY_CHAR_WIDTH; ++x) {
if (*(symbolBit++)) {
set(*xPos + x, yPos + y, r, g, b);
} else {
set(*xPos + x, yPos + y, 0, 0, 0);
}
}
}
*xPos += 3;
}
void set(uint8_t x, uint8_t y, uint32_t color) { void set(uint8_t x, uint8_t y, uint32_t color) {
setPixelColor(x, y, color); setPixelColor(x, y, color);
} }
@ -67,6 +103,10 @@ public:
return leds.getBrightness(); return leds.getBrightness();
} }
void setIndex(uint16_t index, uint8_t r, uint8_t g, uint8_t b) {
leds.setPixelColor(index, r, g, b);
}
}; };
#endif #endif

View File

@ -22,6 +22,30 @@ public:
y = sin(radians) * length; y = sin(radians) * length;
} }
Vector *add(Vector that) {
this->x += that.x;
this->y += that.y;
return this;
}
Vector *bounceInBox(uint8_t width, uint8_t height) {
while (x < 0 || x >= width) {
if (x < 0) {
x = -x;
} else if (x >= width) {
x = 2 * width - x;
}
}
while (y < 0 || y >= height) {
if (y < 0) {
y = -y;
} else if (y >= height) {
y = 2 * height - y;
}
}
return this;
}
}; };
#endif #endif

View File

@ -22,7 +22,7 @@ enum ModeId {
Display display(32, 8); Display display(32, 8);
ModeId newModeId = SPACE_INVADERS; ModeId newModeId = PONG;
ModeId currentModeId = NONE; ModeId currentModeId = NONE;
@ -30,7 +30,7 @@ microseconds_t lastMicros = 0;
Mode *mode = nullptr; Mode *mode = nullptr;
double speed = 4.0; double speed = 1.0;
bool connected = false; bool connected = false;
@ -49,6 +49,26 @@ void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println("\n\n\nStartup!"); Serial.println("\n\n\nStartup!");
WiFi.begin("HappyNet", "1Grausame!Sackratte7"); WiFi.begin("HappyNet", "1Grausame!Sackratte7");
ArduinoOTA.onStart([]() {
display.clear();
display.loop();
});
ArduinoOTA.onProgress([](unsigned int total, unsigned int progress) {
double ratio = (double) progress / (double) total;
auto index = (uint16_t) round(ratio * (double) display.pixelCount);
auto color = (uint8_t) round(ratio * 255.0);
display.setIndex(index, 255 - color, color, 0);
display.loop();
});
ArduinoOTA.onEnd([]() {
display.clear();
display.loop();
});
ArduinoOTA.onError([](int error) {
display.clear();
display.loop();
});
ArduinoOTA.begin(); ArduinoOTA.begin();
display.setup(); display.setup();
} }

View File

@ -5,88 +5,6 @@
class Clock : public Mode { class Clock : public Mode {
private:
bool SYMBOLS[11][35] = {
{
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,
},
{
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, _, _,
},
{
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, _,
_, _, _,
},
};
public: public:
explicit Clock(Display *display) : explicit Clock(Display *display) :
@ -100,37 +18,22 @@ public:
return "Clock"; return "Clock";
} }
void step(microseconds_t dt) override { void doStep(microseconds_t dt) override {
tm time{}; tm time{};
getLocalTime(&time); getLocalTime(&time);
display->clear(); display->clear();
uint8_t x = 0; uint8_t x = 0;
print(&x, time.tm_hour / 10); display->print(&x, 1, time.tm_hour / 10);
x++; x++;
print(&x, time.tm_hour % 10); display->print(&x, 1, time.tm_hour % 10);
print(&x, 10); display->print(&x, 1, 10);
print(&x, time.tm_min / 10); display->print(&x, 1, time.tm_min / 10);
x++; x++;
print(&x, time.tm_min % 10); display->print(&x, 1, time.tm_min % 10);
print(&x, 10); display->print(&x, 1, 10);
print(&x, time.tm_sec / 10); display->print(&x, 1, time.tm_sec / 10);
x++; x++;
print(&x, time.tm_sec % 10); display->print(&x, 1, time.tm_sec % 10);
}
void print(uint8_t *pos, uint8_t index) {
if (index > 10) {
Serial.printf("Cannot print symbol #%u.", index);
return;
}
bool *symbolBit = SYMBOLS[index];
for (uint8_t y = 0; y < 5; ++y) {
for (uint8_t x = 0; x < 3; ++x) {
uint8_t value = *(symbolBit++) ? 255 : 0;
display->set(*pos + x, y, value, value, value);
}
}
*pos += 3;
} }
}; };

View File

@ -14,7 +14,7 @@ public:
uint32_t color = 0; uint32_t color = 0;
void animate(microseconds_t dt) { void animate(microseconds_t dt) {
// TODO "step" does not work as expected // TODO "doStep" does not work as expected
if (alive) { if (alive) {
fade = step(fade, 0.0, 255.0, 200000, +dt); fade = step(fade, 0.0, 255.0, 200000, +dt);
} else { } else {

View File

@ -69,7 +69,7 @@ public:
return "???"; return "???";
} }
void step(microseconds_t dt) override { void doStep(microseconds_t dt) override {
runtime += dt; runtime += dt;
if (runtime >= 500000) { if (runtime >= 500000) {
runtime = 0; runtime = 0;

View File

@ -1,29 +1,93 @@
#ifndef MODE_H #ifndef MODE_H
#define MODE_H #define MODE_H
#define TIMER_COUNT 10
#include "BASICS.h" #include "BASICS.h"
#include "display/Display.h" #include "display/Display.h"
#include "Timer.h"
class Mode { class Mode {
private:
Timer *timers[TIMER_COUNT]{};
uint8_t timerCount = 0;
protected: protected:
Display *display; Display *display;
microseconds_t clock;
public: public:
explicit Mode(Display *display) : explicit Mode(Display *display) :
display(display) { display(display) {
// nothing for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
*timer = nullptr;
}
} }
virtual ~Mode() = default; virtual ~Mode() {
destroyAllTimers();
};
virtual const char *getName() = 0; virtual const char *getName() = 0;
virtual void step(microseconds_t dt) = 0; void step(microseconds_t dt) {
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
if (*timer != nullptr) {
(*timer)->step(dt);
}
}
doStep(dt);
}
protected:
virtual void doStep(microseconds_t dt) {};
Timer *createTimer(microseconds_t interval, Timer::Callback callback) {
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
if (*timer == nullptr) {
*timer = new Timer(interval, callback);
timerCount++;
Serial.printf("Timer created: %p (having %u now)\n", *timer, timerCount);
return *timer;
}
}
Serial.printf("ERROR: Cannot create more than %d timers!\n", TIMER_COUNT);
return nullptr;
}
bool destroyTimer(Timer *t) {
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
if (*timer == t) {
_destroyTimer(timer);
return true;
}
}
Serial.printf("ERROR: No such timer.\n");
return false;
}
void destroyAllTimers() {
for (Timer **timer = timers; timer < timers + TIMER_COUNT; timer++) {
if (*timer != nullptr) {
_destroyTimer(timer);
}
}
}
private:
void _destroyTimer(Timer **timer) {
timerCount--;
free(*timer);
Serial.printf("Timer destroyed: %p (having %u now)\n", *timer, timerCount);
*timer = nullptr;
}
}; };
#endif #endif

View File

@ -7,10 +7,28 @@ class Player {
public: public:
int8_t position = 0; uint8_t position = 0;
uint8_t size = 2;
uint8_t score = 0; uint8_t score = 0;
bool moveUp = false;
void randomMove(uint8_t height) {
if (moveUp) {
position--;
if (position <= 0 || randomBool(20)) {
moveUp = !moveUp;
}
} else {
position++;
if (position + size >= height || randomBool(20)) {
moveUp = !moveUp;
}
}
}
}; };
#endif #endif

12
src/mode/Pong/Pong.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "Pong.h"
Pong *Pong::instance = nullptr;
void Pong::resetPlayer() {
player0.size = 3;
player0.score = 0;
player0.position = (display->height - player0.size) / 2;
player1.size = 3;
player1.score = 0;
player1.position = (display->height - player1.size) / 2;
}

View File

@ -9,6 +9,12 @@ class Pong : public Mode {
private: private:
enum Status {
SCORE, PLAY, OVER
};
static Pong *instance;
Player player0; Player player0;
Player player1; Player player1;
@ -17,17 +23,119 @@ private:
Vector velocity; Vector velocity;
Status status = PLAY;
microseconds_t timeout = 0;
static void tick(Timer *timer, uint32_t totalCount, uint32_t currentCount) {
instance->_tick(timer, totalCount, currentCount);
}
void resetPlayer();
void _tick(Timer *timer, uint32_t totalCount, uint32_t currentCount) {
switch (status) {
case SCORE:
timeout -= currentCount * timer->interval;
if (timeout <= 0) {
status = PLAY;
}
break;
case PLAY:
position.add(velocity);
player0.randomMove(display->height);
player1.randomMove(display->height);
paddleBounce();
checkScoring();
topBottomBounce();
draw();
break;
case OVER:
timeout -= currentCount * timer->interval;
if (timeout <= 0) {
resetPlayer();
status = SCORE;
timeout = 2000000;
}
break;
}
}
void topBottomBounce() {
position.bounceInBox(display->width, display->height);
}
void checkScoring() {
if (position.x < 0) {
player1.score++;
spawnBall(+1);
} else if (position.x >= display->width) {
player0.score++;
spawnBall(-1);
}
if (player0.score >= 10 || player1.score >= 10) {
status = OVER;
timeout = 2000000;
}
}
void paddleBounce() {
if (position.x == 1 && player0.position <= position.y && position.y < player0.position + player0.size) {
velocity.x = -velocity.x;
position.x = 3;
} else if (position.x == display->width - 2 && player1.position <= position.y && position.y < player1.position + player1.size) {
velocity.x = -velocity.x;
position.x = display->width - 4;
}
}
void spawnBall(int direction) {
position.x = (double) display->width / 2.0;
position.y = (double) display->height / 2.0;
velocity.x = direction;
velocity.y = 0;
status = SCORE;
timeout = 2000000;
}
void draw() {
display->clear();
switch (status) {
case SCORE:
display->print(1, 1, player0.score, 0, 255, 0);
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, player1.score, 255, 0, 0);
break;
case PLAY:
for (int i = 0; i < player0.size; ++i) {
display->set(1, (uint8_t) round(player0.position) + i, 0, 255, 0);
}
for (int i = 0; i < player1.size; ++i) {
display->set(display->width - 2, (uint8_t) round(player1.position) + i, 255, 0, 0);
}
display->set((uint8_t) round(position.x), (uint8_t) round(position.y), 255, 255, 255);
break;
case OVER:
if (player0.score > player1.score) {
display->print(1, 1, 11, 0, 255, 0);
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 12, 255, 0, 0);
} else if (player0.score < player1.score) {
display->print(1, 1, 12, 255, 0, 0);
display->print(display->width - 1 - DISPLAY_CHAR_WIDTH, 1, 11, 0, 255, 0);
}
break;
}
}
public: public:
explicit Pong(Display *display) : explicit Pong(Display *display) :
Mode(display), Mode(display),
position(display->width / 2.0, display->height / 2.0), position(display->width / 2.0, display->height / 2.0),
velocity(random(360), exp10(1)) { velocity(random(360), exp10(1)) {
// nothing instance = this;
} createTimer(100000, &tick);
spawnBall(random(2) == 0 ? -1 : +1);
static int8_t randomSignum() { resetPlayer();
return random(2) == 0 ? -1 : +1;
} }
~Pong() override = default; ~Pong() override = default;
@ -36,10 +144,6 @@ public:
return "Pong"; return "Pong";
} }
void step(microseconds_t dt) override {
}
}; };
#endif #endif

View File

@ -68,7 +68,7 @@ public:
return "Space Invaders"; return "Space Invaders";
} }
void step(microseconds_t dt) override { void doStep(microseconds_t dt) override {
stepRockets(dt); stepRockets(dt);
stepInvaders(dt); stepInvaders(dt);
stepHero(dt); stepHero(dt);
@ -80,7 +80,7 @@ public:
Serial.println("WINNER!"); Serial.println("WINNER!");
reset(); reset();
} }
if (swarmY + (invadersCountY - 1) * 2 >= display->height - 1) { // TODO this is only correct if there still are any invaders in the last row (otherwise we "Game Over" too early) if (swarmY + (invadersCountY - 1) * 2 >= display->height - 1) { // TODO this is only correct if there still are any invaders in the accu row (otherwise we "Game Over" too early)
Serial.println("GAME OVER"); Serial.println("GAME OVER");
reset(); reset();
} }

View File

@ -18,7 +18,7 @@ public:
return "Border"; return "Border";
} }
void step(microseconds_t dt) override { void doStep(microseconds_t dt) override {
for (int y = 0; y < display->height; y++) { for (int y = 0; y < display->height; y++) {
for (int x = 0; x < display->width; x++) { for (int x = 0; x < display->width; x++) {
if (x == 0 || x == display->width - 1 || y == 0 || y == display->height - 1) { if (x == 0 || x == display->width - 1 || y == 0 || y == display->height - 1) {

41
src/mode/Timer.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef TIMER_H
#define TIMER_H
#include "BASICS.h"
class Timer {
public:
typedef void (*Callback)(Timer *timer, uint32_t counter, uint32_t currentCount);
private:
Callback callback;
microseconds_t accu = 0;
uint32_t totalCount = 0;
public:
microseconds_t interval;
Timer(microseconds_t interval, Callback callback) :
callback(callback),
interval(interval) {
// nothing
}
void step(microseconds_t dt) {
accu += dt;
if (accu >= interval) {
uint32_t currentCount = accu / interval;
accu = accu % interval;
callback(this, totalCount++, currentCount);
}
}
};
#endif