Compare commits

...

12 Commits

Author SHA1 Message Date
5b70511570 Added options to config.sample.h
Some checks failed
continuous-integration/drone/push Build is failing
2019-06-19 22:31:11 +02:00
77fdba213a I'm tired and forgot to add two files to previous commits. :-/ 2019-06-19 22:29:11 +02:00
ead076f9a3 You can now set options for effects via MQTT and HTTP. Basic syntax is: <mode>,<option1>=<value1>,<option2>=<value2>,... 2019-06-19 22:28:38 +02:00
82fbc7be43 MQTT: Small fixes. Most notably, MQTT_TOPIC/status will now contain hostname and IP of pitrix. 2019-06-19 22:27:33 +02:00
90c0df093e New Effect: BigDynamicEffect. 2019-06-19 22:26:52 +02:00
26df11fc47 Added tests. Start them by sending a message to MQTT_TOPIC/run_tests. First test will test all effects for memory leaks. 2019-06-19 22:26:38 +02:00
0163bbef6c Some memory leaks fixed. 2019-06-19 22:23:49 +02:00
9eeb4b50fd Missed a patch for the previous commit. 2019-06-19 22:22:37 +02:00
d2c0268dce MatrixEffect can now have multiple columns in the same column. In RandomMatrix, they even run in all directions. 2019-06-19 22:22:03 +02:00
e897c6bdcd New Effect: DvdEffect 2019-06-19 22:17:39 +02:00
f1821b0b85 New Effect: RandomConfettiEffect 2019-06-19 22:17:10 +02:00
41af01ee0b Better handling of Animations, their members and the destructor. 2019-06-19 22:16:06 +02:00
29 changed files with 381 additions and 102 deletions

View File

@ -41,7 +41,7 @@ protected:
unsigned long currentFrameSince; unsigned long currentFrameSince;
uint8_t currentFrame = 0; uint8_t currentFrame = 0;
uint8_t* animation_data; uint8_t* animation_data;
CRGB** _colors; CRGB** _colors = NULL;
CRGB* fgColor = NULL; CRGB* fgColor = NULL;
CRGB* bgColor = new CRGB(0x000000); CRGB* bgColor = new CRGB(0x000000);
int8_t xOffset = 0; int8_t xOffset = 0;
@ -51,22 +51,22 @@ protected:
Window* _window; Window* _window;
uint8_t _width; uint8_t _width;
uint8_t _height; uint8_t _height;
uint8_t _frame_count; uint8_t _frame_count = 0;
uint8_t _color_count; uint8_t _color_count = 0;
uint16_t* _frame_times; uint16_t* _frame_times = NULL;
uint16_t* _frame_data_lengths; uint16_t* _frame_data_lengths = NULL;
uint8_t** _frame_data; uint8_t** _frame_data = NULL;
bool _data_valid = false; bool _data_valid = false;
virtual CRGB* getColor(uint8_t color_index); virtual CRGB* getColor(uint8_t color_index);
void drawPixel(int index, CRGB* color); void drawPixel(int index, CRGB* color);
uint16_t getFrameDelay(int frame); uint16_t getFrameDelay(int frame);
bool _load_from_file(const char* c); bool _load_from_file(const char* c);
public: public:
Animation(const char* filename, Window* win); Animation(const char* filename, Window* win);
void setFgColor(CRGB*); void setFgColor(uint32_t c);
void setBgColor(CRGB* bg_color); void setBgColor(uint32_t c);
bool invert(); bool invert();
void setOffsets(int8_t x, int8_t y); void setOffsets(int8_t x, int8_t y);
void setStartFrame(uint8_t sf); void setStartFrame(uint8_t sf);

View File

@ -17,6 +17,7 @@ public:
void setWindow(Window* win) { void setWindow(Window* win) {
window = win; window = win;
}; };
virtual void apply_option(String key, String value) {};
}; };
#endif #endif

View File

@ -9,7 +9,7 @@ public:
const uint8_t width, height; const uint8_t width, height;
uint16_t count; uint16_t count;
static Window* getFullWindow(); static Window* getFullWindow();
Window(): Window(0, 0, LED_WIDTH, LED_HEIGHT) {}; Window(): Window(0, 0, LED_WIDTH, LED_HEIGHT) {};
Window(uint8_t x, uint8_t y) : Window(x, y, LED_WIDTH-x, LED_HEIGHT-y) {}; Window(uint8_t x, uint8_t y) : Window(x, y, LED_WIDTH-x, LED_HEIGHT-y) {};
Window(uint8_t x, uint8_t y, uint8_t width, uint8_t height) : x(x), y(y), width(width), height(height) { count = width*height; }; Window(uint8_t x, uint8_t y, uint8_t width, uint8_t height) : x(x), y(y), width(width), height(height) { count = width*height; };
@ -18,6 +18,7 @@ public:
void clear(CRGB* color); void clear(CRGB* color);
void setPixel(uint8_t x, uint8_t y, CRGB* color); void setPixel(uint8_t x, uint8_t y, CRGB* color);
void setPixelByIndex(uint16_t index, CRGB* color); void setPixelByIndex(uint16_t index, CRGB* color);
void raisePixel(uint8_t x, uint8_t y, CRGB* color);
void line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, CRGB* color); void line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, CRGB* color);
void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t length, CRGB* color); void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t length, CRGB* color);
void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color); void lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color);

View File

@ -56,6 +56,8 @@
#define EFFECT_SINGLE_DYNAMIC_LOOP_TIME 40 #define EFFECT_SINGLE_DYNAMIC_LOOP_TIME 40
#define EFFECT_MULTI_DYNAMIC_LOOP_TIME 1400 #define EFFECT_MULTI_DYNAMIC_LOOP_TIME 1400
#define EFFECT_BIG_DYNAMIC_LOOP_TIME 50
#define EFFECT_BIG_DYNAMIC_SIZE 3
#define EFFECT_CONFETTI_PIXELS_PER_LOOP 2 #define EFFECT_CONFETTI_PIXELS_PER_LOOP 2
@ -73,6 +75,9 @@
#define EFFECT_GOL_BLEND_SPEED 10 #define EFFECT_GOL_BLEND_SPEED 10
#define EFFECT_GOL_RESTART_AFTER_STEPS 100 #define EFFECT_GOL_RESTART_AFTER_STEPS 100
#define EFFECT_DVD_WIDTH 3
#define EFFECT_DVD_HEIGHT 2
// Stop editing here // Stop editing here
#ifdef DEBUG #ifdef DEBUG

View File

@ -8,13 +8,13 @@
class AnimationEffect : public Effect { class AnimationEffect : public Effect {
private: private:
Animation *animation; Animation *animation;
CRGB *bg_color;
uint16_t xOffset; uint16_t xOffset;
uint16_t yOffset; uint16_t yOffset;
public: public:
AnimationEffect(const char* name) : AnimationEffect(name, new CRGB(0x000000), 0, 0) {} AnimationEffect(const char* name) : AnimationEffect(name, 0x000000, 0, 0) {}
AnimationEffect(const char* name, CRGB* background_color) : AnimationEffect(name, background_color, 0, 0) {} AnimationEffect(const char* name, uint32_t bg_color) : AnimationEffect(name, bg_color, 0, 0) {}
AnimationEffect(const char* name, CRGB* bg_color, int x, int y); AnimationEffect(const char* name, uint32_t bg_color, int x, int y);
~AnimationEffect(); ~AnimationEffect();
AnimationEffect* setFgColor(uint32_t c);
void loop(); void loop();
}; };

View File

@ -10,6 +10,7 @@ private:
Window* window = new Window(0, LED_HEIGHT - 6, LED_WIDTH, 6); Window* window = new Window(0, LED_HEIGHT - 6, LED_WIDTH, 6);
public: public:
~ClockEffect();
void loop(); void loop();
void loop(boolean invert, CRGB fg_color, CRGB bg_color); void loop(boolean invert, CRGB fg_color, CRGB bg_color);
}; };

View File

@ -5,8 +5,16 @@
#include "my_fastled.h" #include "my_fastled.h"
class ConfettiEffect : public Effect { class ConfettiEffect : public Effect {
protected:
virtual CRGB _getColor();
public:
void loop(); void loop();
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
}; };
class RandomConfettiEffect : public ConfettiEffect {
protected:
CRGB _getColor() override;
};
#endif #endif

17
include/effect_dvd.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "Effect.h"
class DvdEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
uint8_t _x = 0;
uint8_t _y = 0;
int8_t _x_dir = 1;
int8_t _y_dir = 1;
CRGB _color;
public:
DvdEffect();
~DvdEffect();
void loop() override;
bool can_be_shown_with_clock() override;
};

View File

@ -21,3 +21,11 @@ class MultiDynamicEffect : public SingleDynamicEffect {
public: public:
void loop(); void loop();
}; };
class BigDynamicEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
public:
void loop();
~BigDynamicEffect();
};

View File

@ -9,16 +9,23 @@
class MatrixEffectColumn { class MatrixEffectColumn {
protected: protected:
Window* window; Window* window;
int x, y; uint8_t x, y;
int length = 1; uint8_t length = 1;
uint8_t _direction = 2;
bool _random_direction = false;
virtual CRGB _getColor(uint8_t height); virtual CRGB _getColor(uint8_t height);
virtual void restart(); virtual void restart(bool completely_random);
private: private:
uint16_t speed; uint16_t speed;
boolean running; boolean running;
unsigned long last_move = 0; unsigned long last_move = 0;
public: public:
MatrixEffectColumn(Window* win, int xPos); static const uint8_t DIR_NORTH = 0;
static const uint8_t DIR_EAST = 1;
static const uint8_t DIR_SOUTH = 2;
static const uint8_t DIR_WEST = 3;
MatrixEffectColumn(Window* win, uint8_t direction=0, bool random_direction=false);
virtual ~MatrixEffectColumn() {}; virtual ~MatrixEffectColumn() {};
void advance(); void advance();
void draw(); void draw();
@ -29,16 +36,16 @@ class RainbowMatrixEffectColumn : public MatrixEffectColumn {
protected: protected:
CRGB _getColor(uint8_t height) override; CRGB _getColor(uint8_t height) override;
public: public:
RainbowMatrixEffectColumn(Window* win, int xPos) : MatrixEffectColumn(win, xPos) {}; RainbowMatrixEffectColumn(Window* win, uint8_t dir, bool rnd=false) : MatrixEffectColumn(win, dir, rnd) {};
}; };
class RandomMatrixEffectColumn : public MatrixEffectColumn { class RandomMatrixEffectColumn : public MatrixEffectColumn {
protected: protected:
uint8_t _hue = 42; uint8_t _hue = 42;
CRGB _getColor(uint8_t height) override; CRGB _getColor(uint8_t height) override;
void restart() override; void restart(bool completely_random) override;
public: public:
RandomMatrixEffectColumn(Window* win, int xPos) : MatrixEffectColumn(win, xPos) {}; RandomMatrixEffectColumn(Window* win, uint8_t dir, bool rnd=false) : MatrixEffectColumn(win, dir, rnd) {};
}; };
class MatrixEffect : public Effect { class MatrixEffect : public Effect {

View File

@ -17,6 +17,7 @@ private:
bool is_direction_okay(uint8_t direction); bool is_direction_okay(uint8_t direction);
public: public:
SnakeEffect(); SnakeEffect();
~SnakeEffect();
void loop(); void loop();
boolean valid_position(Coords c); boolean valid_position(Coords c);
Coords update_position(Coords c, uint8_t direction); Coords update_position(Coords c, uint8_t direction);

View File

@ -11,7 +11,7 @@ extern uint8_t cycle_effects_count;
extern Effect* current_effect; extern Effect* current_effect;
extern ClockEffect effect_clock; extern ClockEffect effect_clock;
Effect* string_to_effect(String s); Effect* select_effect(uint32_t c);
bool change_current_effect(String s); bool change_current_effect(String s);
void setup_effects(); void setup_effects();

6
include/tests.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace tests {
void run();
void test_effects_for_memory_leaks();
}

View File

@ -143,7 +143,7 @@ bool Animation::_load_from_file(const char* filename) {
} }
file.close(); file.close();
LOGln("Animation * Loading completed successfully."); LOGln("Animation * Loading completed successfully.");
return true; return true;
@ -160,12 +160,14 @@ Animation::Animation(const char* filename, Window* win) {
} }
} }
void Animation::setFgColor(CRGB* fg_color) { void Animation::setFgColor(uint32_t c) {
this->fgColor = fg_color; if (this->fgColor) delete this->fgColor;
this->fgColor = new CRGB(c);
} }
void Animation::setBgColor(CRGB* bg_color) { void Animation::setBgColor(uint32_t c) {
this->bgColor = bg_color; if (this->bgColor) delete this->bgColor;
this->bgColor = new CRGB(c);
} }
bool Animation::invert() { bool Animation::invert() {
@ -205,15 +207,27 @@ void Animation::setSingleFrame(uint8_t frame) {
Animation::~Animation() { Animation::~Animation() {
for (int i=0; i<_color_count; i++) delete _colors[i]; for (int i=0; i<_color_count; i++) delete _colors[i];
delete [] _colors; LOGln("Deleting _colors...");
if (fgColor) delete fgColor; if (_colors) delete [] _colors;
delete bgColor; LOGln("Deleting fgColor...");
delete [] _frame_data_lengths;
delete [] _frame_times; if (fgColor != NULL) delete fgColor;
LOGln("Deleting bgColor...");
if (bgColor != NULL) delete bgColor;
LOGln("Deleting _frame_data_lengths...");
if (_frame_data_lengths) delete [] _frame_data_lengths;
LOGln("Deleting _frame_times...");
if (_frame_times) delete [] _frame_times;
for (int i=0; i<_frame_count; i++) { for (int i=0; i<_frame_count; i++) {
delete [] _frame_data[i]; delete [] _frame_data[i];
} }
delete [] _frame_data; LOGln("Deleting _frame_data...");
if (_frame_data) delete [] _frame_data;
LOGln("Deleteion done.");
} }
void Animation::draw() { void Animation::draw() {
@ -230,7 +244,7 @@ void Animation::drawFrame(uint8_t frame_index) {
CRGB red(0xFF0000); CRGB red(0xFF0000);
CRGB black(0x000000); CRGB black(0x000000);
for (int x=0; x<_window->width; x++) for (int y=0; y<_window->height; y++) { for (int x=0; x<_window->width; x++) for (int y=0; y<_window->height; y++) {
_window->setPixel(x, y, (y*_window->width+x) % 2 ? &red : &black); _window->setPixel(x, y, (y*_window->width+x + y) % 2 ? &red : &black);
} }
return; return;
} }

View File

@ -11,6 +11,11 @@ void Window::setPixel(uint8_t x, uint8_t y, CRGB* color) {
leds[this->coordsToGlobalIndex(x, y)] = *color; leds[this->coordsToGlobalIndex(x, y)] = *color;
} }
void Window::raisePixel(uint8_t x, uint8_t y, CRGB* color) {
if (x>=this->width || y>=this->height) return;
leds[this->coordsToGlobalIndex(x, y)] |= *color;
}
void Window::setPixelByIndex(uint16_t index, CRGB* color) { void Window::setPixelByIndex(uint16_t index, CRGB* color) {
uint8_t x = index % this->width; uint8_t x = index % this->width;
uint8_t y = index / this->width; uint8_t y = index / this->width;
@ -69,7 +74,7 @@ void Window::line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, CRGB* color) {
int8_t sy = y1<y2 ? 1 : -1; int8_t sy = y1<y2 ? 1 : -1;
int16_t err = dx + dy; int16_t err = dx + dy;
int16_t e2; int16_t e2;
while (1) { while (1) {
setPixel(x1, y1, color); setPixel(x1, y1, color);
if (x1==x2 && y1==y2) break; if (x1==x2 && y1==y2) break;
@ -86,12 +91,12 @@ void Window::circle(uint8_t x0, uint8_t y0, uint8_t radius, CRGB* color) {
int16_t ddF_y = -2 * radius; int16_t ddF_y = -2 * radius;
uint8_t x = 0; uint8_t x = 0;
uint8_t y = radius; uint8_t y = radius;
setPixel(x0, y0 + radius, color); setPixel(x0, y0 + radius, color);
setPixel(x0, y0 - radius, color); setPixel(x0, y0 - radius, color);
setPixel(x0 + radius, y0, color); setPixel(x0 + radius, y0, color);
setPixel(x0 - radius, y0, color); setPixel(x0 - radius, y0, color);
while (x < y) { while (x < y) {
if (f >= 0) { if (f >= 0) {
y--; y--;
@ -101,12 +106,12 @@ void Window::circle(uint8_t x0, uint8_t y0, uint8_t radius, CRGB* color) {
x++; x++;
ddF_x += 2; ddF_x += 2;
f += ddF_x + 1; f += ddF_x + 1;
setPixel(x0 + x, y0 + y, color); setPixel(x0 + x, y0 + y, color);
setPixel(x0 - x, y0 + y, color); setPixel(x0 - x, y0 + y, color);
setPixel(x0 + x, y0 - y, color); setPixel(x0 + x, y0 - y, color);
setPixel(x0 - x, y0 - y, color); setPixel(x0 - x, y0 - y, color);
setPixel(x0 + y, y0 + x, color); setPixel(x0 + y, y0 + x, color);
setPixel(x0 - y, y0 + x, color); setPixel(x0 - y, y0 + x, color);
setPixel(x0 + y, y0 - x, color); setPixel(x0 + y, y0 - x, color);
@ -121,14 +126,14 @@ void Window::lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t length,
void Window::lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color) { void Window::lineWithAngle(uint8_t x, uint8_t y, uint8_t angle, uint8_t startdist, uint8_t length, CRGB* color) {
int16_t x1 = x; int16_t x1 = x;
int16_t y1 = y; int16_t y1 = y;
if (startdist > 0) { if (startdist > 0) {
x1 = x + scale8(startdist, cos8(angle)); x1 = x + scale8(startdist, cos8(angle));
y1 = y + scale8(startdist, sin8(angle)); y1 = y + scale8(startdist, sin8(angle));
} }
int16_t x2 = x + scale8(startdist + length, cos8(angle)); int16_t x2 = x + scale8(startdist + length, cos8(angle));
int16_t y2 = y + scale8(startdist + length, sin8(angle)); int16_t y2 = y + scale8(startdist + length, sin8(angle));
line(x1, y1, x2, y2, color); line(x1, y1, x2, y2, color);
} }

View File

@ -1,16 +1,20 @@
#include "effect_animation.h" #include "effect_animation.h"
#include "functions.h" #include "functions.h"
AnimationEffect::AnimationEffect(const char* name, CRGB* bg, int x, int y) { AnimationEffect::AnimationEffect(const char* name, uint32_t bg, int x, int y) {
this->bg_color = bg;
this->xOffset = x; this->xOffset = x;
this->yOffset = y; this->yOffset = y;
this->animation = new Animation(name, window); this->animation = new Animation(name, window);
this->animation->setBgColor(this->bg_color); this->animation->setBgColor(bg);
this->animation->setOffsets(this->xOffset, this->yOffset); this->animation->setOffsets(this->xOffset, this->yOffset);
} }
AnimationEffect* AnimationEffect::setFgColor(uint32_t c) {
animation->setFgColor(c);
return this;
}
AnimationEffect::~AnimationEffect() { AnimationEffect::~AnimationEffect() {
delete this->animation; delete this->animation;
} }

View File

@ -40,3 +40,7 @@ void ClockEffect::loop(boolean invert, CRGB fg_color, CRGB bg_color) {
window->setPixel(7, 4, &fg_color); window->setPixel(7, 4, &fg_color);
} }
} }
ClockEffect::~ClockEffect() {
delete window;
}

View File

@ -6,9 +6,17 @@
void ConfettiEffect::loop() { void ConfettiEffect::loop() {
window->fadeToBlackBy(3); window->fadeToBlackBy(3);
for (int i=0; i<EFFECT_CONFETTI_PIXELS_PER_LOOP; i++) { for (int i=0; i<EFFECT_CONFETTI_PIXELS_PER_LOOP; i++) {
CRGB color(CHSV(baseHue + random8(64), 200, 255)); CRGB color = _getColor();
window->addPixelColor(random16(LED_COUNT), &color); window->addPixelColor(random16(LED_COUNT), &color);
} }
} }
CRGB ConfettiEffect::_getColor() {
return CHSV(baseHue + random8(64), 255, 255);
}
CRGB RandomConfettiEffect::_getColor() {
return CHSV(random8(), 255, 255);
}
boolean ConfettiEffect::can_be_shown_with_clock() { return true; }; boolean ConfettiEffect::can_be_shown_with_clock() { return true; };

View File

@ -1,5 +1,6 @@
#include "effect_cycle.h" #include "effect_cycle.h"
#include "effects.h" #include "effects.h"
#include <ErriezCRC32.h>
CycleEffect::CycleEffect() { CycleEffect::CycleEffect() {
changeEffect(); changeEffect();
@ -23,7 +24,7 @@ void CycleEffect::changeEffect() {
if (effect) delete effect; if (effect) delete effect;
LOGln("CycleEffect * Searching for new effect '%s'", cycle_effects[new_id]); LOGln("CycleEffect * Searching for new effect '%s'", cycle_effects[new_id]);
delay(25); delay(25);
effect = string_to_effect(cycle_effects[new_id]); effect = select_effect( crc32String(cycle_effects[new_id]) );
effect_id = new_id; effect_id = new_id;
effectSince = millis(); effectSince = millis();
} }

37
src/effect_dvd.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "effect_dvd.h"
#include "my_fastled.h"
void DvdEffect::loop() {
bool dir_changed = false;
EVERY_N_MILLISECONDS( 250 ) {
_x += _x_dir;
_y += _y_dir;
if (_x == 0 || _x + EFFECT_DVD_WIDTH >= window->width) {
_x_dir = -_x_dir;
dir_changed = true;
}
if (_y == 0 || _y + EFFECT_DVD_HEIGHT >= window->height) {
_y_dir = -_y_dir;
dir_changed = true;
}
}
window->clear();
for (int x=0; x<EFFECT_DVD_WIDTH; x++) for (int y=0; y<EFFECT_DVD_HEIGHT; y++) {
window->setPixel(_x + x, _y + y, (CRGB*)&_color);
}
if (dir_changed) _color = (CRGB)CHSV(random8(), 255, 255);
}
bool DvdEffect::can_be_shown_with_clock() { return true; }
DvdEffect::DvdEffect() {
_color = CHSV(random8(), 255, 255);
}
DvdEffect::~DvdEffect() {
delete window;
}

View File

@ -34,3 +34,28 @@ void MultiDynamicEffect::loop() {
} }
this->draw(); this->draw();
} }
BigDynamicEffect::~BigDynamicEffect() {
delete window;
}
void BigDynamicEffect::loop() {
EVERY_N_MILLISECONDS( EFFECT_BIG_DYNAMIC_LOOP_TIME ) {
uint8_t x = random8(0, window->width - EFFECT_BIG_DYNAMIC_SIZE + 1);
uint8_t y = random8(0, window->height - EFFECT_BIG_DYNAMIC_SIZE + 1);
CRGB color = CHSV(random8(), 255, 255);
CRGB black(0x000000);
for (uint8_t ix=0; ix<EFFECT_BIG_DYNAMIC_SIZE; ix++) for (uint8_t iy=0; iy<EFFECT_BIG_DYNAMIC_SIZE; iy++) {
window->setPixel(x+ix, y+iy, &color);
}
/*for (uint8_t ix=0; ix<EFFECT_BIG_DYNAMIC_SIZE+2; ix++) {
window->setPixel(x-1+ix, y-1, &black);
window->setPixel(x-1+ix, y+EFFECT_BIG_DYNAMIC_SIZE+1, &black);
}
for (uint8_t iy=0; iy<EFFECT_BIG_DYNAMIC_SIZE+2; iy++) {
window->setPixel(x-1, y-1+iy, &black);
window->setPixel(x+EFFECT_BIG_DYNAMIC_SIZE+1, y-1+iy, &black);
}*/
}
}

View File

@ -3,7 +3,7 @@
GolEffect::GolEffect() { GolEffect::GolEffect() {
this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
_data = new uint8_t[this->window->count]; _data = new uint8_t[this->window->count];
_old = new uint8_t[this->window->count]; _old = new uint8_t[this->window->count];
for(uint16_t i=0; i<this->window->count; i++) { for(uint16_t i=0; i<this->window->count; i++) {
@ -26,6 +26,7 @@ void GolEffect::_initialize() {
GolEffect::~GolEffect() { GolEffect::~GolEffect() {
delete[] _data; delete[] _data;
delete[] _old; delete[] _old;
delete window;
} }
void GolEffect::loop() { void GolEffect::loop() {
@ -35,7 +36,7 @@ void GolEffect::loop() {
} else { } else {
_blend += EFFECT_GOL_BLEND_SPEED; _blend += EFFECT_GOL_BLEND_SPEED;
} }
_draw(); _draw();
} }
@ -52,14 +53,14 @@ void GolEffect::_advance() {
uint16_t changes = 0; uint16_t changes = 0;
for(uint8_t x=0; x<this->window->width; x++) for(uint8_t y=0; y<this->window->height; y++) { for(uint8_t x=0; x<this->window->width; x++) for(uint8_t y=0; y<this->window->height; y++) {
uint16_t index = y*w + x; uint16_t index = y*w + x;
uint8_t count = uint8_t count =
(x>0 && y>0 && _old[index - w - 1]) + (x>0 && y>0 && _old[index - w - 1]) +
(y>0 && _old[index - w]) + (y>0 && _old[index - w]) +
(x<this->window->width-1 && y>0 && _old[index - w + 1]) + (x<this->window->width-1 && y>0 && _old[index - w + 1]) +
(x>0 && _old[index - 1]) + (x>0 && _old[index - 1]) +
(x<this->window->width-1 && _old[index + 1]) + (x<this->window->width-1 && _old[index + 1]) +
(x>0 && y<this->window->height-1 && _old[index + w - 1]) + (x>0 && y<this->window->height-1 && _old[index + w - 1]) +
(y<this->window->height-1 && _old[index + w]) + (y<this->window->height-1 && _old[index + w]) +
(x<this->window->width-1 && y<this->window->height-1 && _old[index + w + 1]); (x<this->window->width-1 && y<this->window->height-1 && _old[index + w + 1]);
@ -79,7 +80,7 @@ void GolEffect::_advance() {
} }
} }
} }
if (changes == 0) { if (changes == 0) {
_initialize(); _initialize();
} }

View File

@ -2,29 +2,80 @@
#include "my_color_palettes.h" #include "my_color_palettes.h"
#include "functions.h" #include "functions.h"
MatrixEffectColumn::MatrixEffectColumn(Window* win, int xPos) { MatrixEffectColumn::MatrixEffectColumn(Window* win, uint8_t dir, bool rand) {
window = win; window = win;
x = xPos; _direction = dir;
restart(); _random_direction = rand;
y = random8(0, window->height); restart(true);
} }
void MatrixEffectColumn::restart() { void MatrixEffectColumn::restart(bool completely_random) {
y=-1; if (_random_direction) {
_direction = random8(4);
}
if (completely_random) {
x = random8(window->width);
y = random8(window->height);
} else {
switch(_direction) {
case DIR_NORTH:
x = random8(window->width);
y = window->height - 1;
break;
case DIR_EAST:
x = 0;
y = random8(window->height);
break;
case DIR_SOUTH:
x = random8(window->width);
y = 0;
break;
case DIR_WEST:
x = window->width - 1;
y = random8(window->height);
break;
}
}
length = random8(EFFECT_MATRIX_LENGTH_MIN, EFFECT_MATRIX_LENGTH_MAX); length = random8(EFFECT_MATRIX_LENGTH_MIN, EFFECT_MATRIX_LENGTH_MAX);
running = true; running = true;
speed = random8(EFFECT_MATRIX_SPEED_MIN, EFFECT_MATRIX_SPEED_MAX); speed = random8(EFFECT_MATRIX_SPEED_MIN, EFFECT_MATRIX_SPEED_MAX);
} }
void MatrixEffectColumn::advance() { void MatrixEffectColumn::advance() {
y++; switch(_direction) {
if (y-length > window->height) running = false; case DIR_NORTH:
y--;
if (y > window->height && y + length > window->height) running=false;
break;
case DIR_EAST:
x++;
if (x - length > window->width) running=false;
break;
case DIR_SOUTH:
y++;
if (y - length > window->height) running=false;
break;
case DIR_WEST:
x--;
if (x > window->width && y + length > window->width) running=false;
break;
}
} }
void MatrixEffectColumn::draw() { void MatrixEffectColumn::draw() {
int8_t xdir = 0;
int8_t ydir = 0;
switch (_direction) {
case DIR_NORTH: ydir = 1; break;
case DIR_EAST: xdir = 1; break;
case DIR_SOUTH: ydir = -1; break;
case DIR_WEST: xdir = -1; break;
}
for(int i=0; i<length; i++) { for(int i=0; i<length; i++) {
CRGB color = _getColor(i); CRGB color = _getColor(i);
window->setPixel(x, y-i, &color); window->raisePixel(x+(xdir*i), y+(ydir*i), &color);
} }
} }
@ -32,7 +83,7 @@ void MatrixEffectColumn::loop() {
if (!running) { if (!running) {
if (random8() < 20) { if (random8() < 20) {
// Start the column again. // Start the column again.
restart(); restart(false);
} }
} else { } else {
if (millis() - last_move > speed) { if (millis() - last_move > speed) {
@ -75,8 +126,8 @@ CRGB RandomMatrixEffectColumn::_getColor(uint8_t i) {
return color; return color;
} }
void RandomMatrixEffectColumn::restart() { void RandomMatrixEffectColumn::restart(bool completely_random) {
MatrixEffectColumn::restart(); MatrixEffectColumn::restart(completely_random);
_hue = random8(); _hue = random8();
} }
@ -97,15 +148,15 @@ MatrixEffect::MatrixEffect() {
} }
void MatrixEffect::_init() { void MatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new MatrixEffectColumn(window, i); for (int i=0; i<window->width; i++) _columns[i] = new MatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH);
} }
void RandomMatrixEffect::_init() { void RandomMatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new RandomMatrixEffectColumn(window, i); for (int i=0; i<window->width; i++) _columns[i] = new RandomMatrixEffectColumn(window, random8(4), true);
} }
void RainbowMatrixEffect::_init() { void RainbowMatrixEffect::_init() {
for (int i=0; i<window->width; i++) _columns[i] = new RainbowMatrixEffectColumn(window, i); for (int i=0; i<window->width; i++) _columns[i] = new RainbowMatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH);
} }
MatrixEffect::~MatrixEffect() { MatrixEffect::~MatrixEffect() {

View File

@ -10,6 +10,7 @@ PixelClockEffect::PixelClockEffect() {
PixelClockEffect::~PixelClockEffect() { PixelClockEffect::~PixelClockEffect() {
delete _color_seconds; delete _color_seconds;
delete _color_minutes; delete _color_minutes;
delete window;
} }
void PixelClockEffect::loop() { void PixelClockEffect::loop() {

View File

@ -6,6 +6,10 @@ SnakeEffect::SnakeEffect() {
this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
} }
SnakeEffect::~SnakeEffect() {
delete window;
}
void SnakeEffect::loop() { void SnakeEffect::loop() {
if (run++ % EFFECT_SNAKE_SLOWDOWN == 0) { // Change the coordinates only on every n-th run. if (run++ % EFFECT_SNAKE_SLOWDOWN == 0) { // Change the coordinates only on every n-th run.
if (random8(EFFECT_SNAKE_DIRECTION_CHANGE)==0 || is_turn_needed()) turn_random(); if (random8(EFFECT_SNAKE_DIRECTION_CHANGE)==0 || is_turn_needed()) turn_random();

View File

@ -17,46 +17,77 @@
#include "effect_firework.h" #include "effect_firework.h"
#include "effect_gol.h" #include "effect_gol.h"
#include "effect_pixelclock.h" #include "effect_pixelclock.h"
#include "effect_dvd.h"
Effect* current_effect; Effect* current_effect;
ClockEffect effect_clock; ClockEffect effect_clock;
Effect* string_to_effect(String name) { Effect* select_effect(uint32_t code) {
uint32_t crc = crc32String(name.c_str()); switch (code) {
switch (crc) {
// use e.g. https://crccalc.com/ for the conversion of name to crc. // use e.g. https://crccalc.com/ for the conversion of name to crc.
case 0xD682E3C8 /* sinematrix3 */ : return new Sinematrix3Effect(); case 0: case 0xD682E3C8 /* sinematrix3 */ : return new Sinematrix3Effect();
case 0x90A887DA /* big_clock */ : return new BigClockEffect(); case 1: case 0x90A887DA /* big_clock */ : return new BigClockEffect();
case 0xBE7BBE92 /* clock */ : return new ClockEffect(); case 2: case 0xBE7BBE92 /* clock */ : return new ClockEffect();
case 0x733BE087 /* bell */ : return new BellEffect(); case 3: case 0x733BE087 /* bell */ : return new BellEffect(); //(new AnimationEffect("/bell.pia", 0x000000, 0, 0))->setFgColor(0xFFFF00);
case 0x2BBC5D43 /* off */ : return new StaticEffect(0x000000); case 4: case 0x2BBC5D43 /* off */ : return new StaticEffect(0x000000);
case 0x1D84F231 /* koopa */ : return new AnimationEffect("/koopa.pia", new CRGB(0x000000), 0, 0); case 5: case 0x1D84F231 /* koopa */ : return new AnimationEffect("/koopa.pia", CRGB(0x000000), 0, 0);
case 0xAC43BCF1 /* couple_rain */ : return new AnimationEffect("/couple_rain.pia", new CRGB(0x000000), -8, -16); case 6: case 0xAC43BCF1 /* couple_rain */ : return new AnimationEffect("/couple_rain.pia", CRGB(0x000000), -8, -16);
case 0xF1B117F7 /* single_dynamic */ : return new SingleDynamicEffect(); case 7: case 0xF1B117F7 /* single_dynamic */ : return new SingleDynamicEffect();
case 0xF52F2804 /* multi_dynamic */ : return new MultiDynamicEffect(); case 8: case 0xF52F2804 /* multi_dynamic */ : return new MultiDynamicEffect();
case 0xF83341CF /* matrix */ : return new MatrixEffect(); case 9: case 0xF83341CF /* matrix */ : return new MatrixEffect();
case 0xD2B79DD0 /* rainbow_matrix */ : return new RainbowMatrixEffect(); case 10: case 0xD2B79DD0 /* rainbow_matrix */ : return new RainbowMatrixEffect();
case 0xE8DD3433 /* random_matrix */ : return new RandomMatrixEffect(); case 11: case 0xE8DD3433 /* random_matrix */ : return new RandomMatrixEffect();
case 0xB086D193 /* cycle */ : return new CycleEffect(); case 12: case 0xB086D193 /* cycle */ : return new CycleEffect();
case 0x2293EF9F /* twirl */ : return new TwirlEffect(); case 13: case 0x2293EF9F /* twirl */ : return new TwirlEffect();
case 0x60ECC3E6 /* heart */ : return new AnimationEffect("/heart.pia", new CRGB(0x000000), 0, 0); case 14: case 0x60ECC3E6 /* heart */ : return new AnimationEffect("/heart.pia", CRGB(0x000000), 0, 0);
case 0x42090A49 /* confetti */ : return new ConfettiEffect(); case 15: case 0x42090A49 /* confetti */ : return new ConfettiEffect();
case 0x516D6B9E /* snake */ : return new SnakeEffect(); case 16: case 0x516D6B9E /* snake */ : return new SnakeEffect();
case 0x58DE09CF /* fire */ : return new FireEffect(); case 17: case 0x58DE09CF /* fire */ : return new FireEffect();
case 0x08BA9C08 /* firework */ : return new FireworkEffect(); case 18: case 0x08BA9C08 /* firework */ : return new FireworkEffect();
case 0x14B85EAC /* gol */ : return new GolEffect(); case 19: case 0x14B85EAC /* gol */ : return new GolEffect();
case 0xFA13015D /* cake */ : return new AnimationEffect("/cake.pia", new CRGB(0x000000), 0, 0); case 20: case 0xFA13015D /* cake */ : return new AnimationEffect("/cake.pia", CRGB(0x000000), 0, 0);
case 0xA2B0D68B /* pixel_clock */ : return new PixelClockEffect(); case 21: case 0xA2B0D68B /* pixel_clock */ : return new PixelClockEffect();
case 22: case 0x2C0E6962 /* big_dynamic */ : return new BigDynamicEffect();
case 23: case 0xDA6F31A5 /* random_confetti */ : return new RandomConfettiEffect();
case 24: case 0x8325C1DF /* dvd */ : return new DvdEffect();
default : return NULL; default : return NULL;
}; };
} }
bool change_current_effect(String name) { bool change_current_effect(String payload) {
Effect* new_effect = string_to_effect(name); int pos = payload.indexOf(",");
if (new_effect == NULL) return false; String options = "";
if (pos != -1) {
LOGln("Effects * Effect comes with options.");
options = payload.substring(pos+1);
payload.remove(pos);
LOGln("Effects * Cleaned effect name: %s", payload.c_str());
}
Effect* new_effect = select_effect( crc32String(payload.c_str()) );
if (new_effect == NULL) {
LOGln("Effects * Could not find effect with name %s", payload.c_str());
return false;
}
delete current_effect; delete current_effect;
current_effect = new_effect; current_effect = new_effect;
if (options.length() > 0) {
LOGln("Effects * Parsing options: %s", options.c_str());
options += ",";
int p_colon;
while ((p_colon = options.indexOf(",")) >= 0) {
int p_equal = options.indexOf("=");
if (p_equal >= 0 && p_equal < p_colon) {
String key = options.substring(0, p_equal);
String value = options.substring(p_equal + 1, p_colon);
LOGln("Effects * Applying option: %s = %s", key.c_str(), value.c_str());
current_effect->apply_option(key, value);
}
options.remove(0, p_colon + 1);
}
}
return true; return true;
} }

View File

@ -10,6 +10,7 @@
#include "Effect.h" #include "Effect.h"
#include "effects.h" #include "effects.h"
#include "functions.h" #include "functions.h"
#include "tests.h"
WiFiClient wifi; WiFiClient wifi;
PubSubClient mqtt_client(wifi); PubSubClient mqtt_client(wifi);
@ -57,12 +58,20 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
if(topic.compareTo("mode")==0) { if(topic.compareTo("mode")==0) {
LOGln("MQTT * Changing mode..."); LOGln("MQTT * Changing mode...");
change_current_effect(payload); bool result = change_current_effect(payload);
if (result) {
LOGln("MQTT * Effect changed.");
} else {
LOGln("MQTT * Could not change effect.");
}
return; return;
} else if (topic.compareTo("reboot")==0) { } else if (topic.compareTo("reboot")==0) {
LOGln("MQTT * Rebooting"); LOGln("MQTT * Rebooting");
ESP.restart(); ESP.restart();
return; // Will never be reached, but anyway... return; // Will never be reached, but anyway...
} else if (topic.compareTo("run_tests")==0) {
tests::run();
return;
} }
long value = payload.toInt(); long value = payload.toInt();
LOGln("MQTT * Payload as number: %d", value); LOGln("MQTT * Payload as number: %d", value);
@ -81,10 +90,12 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
} }
boolean mqtt_connect() { boolean mqtt_connect() {
LOG("MQTT * Connecting to MQTT server with client id %s", hostname); LOGln("MQTT * Connecting to MQTT server with client id %s", hostname);
if (mqtt_client.connect(hostname, MQTT_USER, MQTT_PASS, MQTT_TOPIC "status", 0, true, "OFFLINE", true)) { if (mqtt_client.connect(hostname, MQTT_USER, MQTT_PASS, MQTT_TOPIC "status", 0, true, "OFFLINE", true)) {
LOGln("MQTT * Connected."); LOGln("MQTT * Connected.");
mqtt_client.publish(MQTT_TOPIC "status", "ONLINE", true); char buffer[40];
snprintf(buffer, 40, "ONLINE %s %s", hostname, WiFi.localIP().toString().c_str());
mqtt_client.publish(MQTT_TOPIC "status", buffer, true);
mqtt_client.subscribe(MQTT_TOPIC "+"); mqtt_client.subscribe(MQTT_TOPIC "+");
mqtt_client.subscribe(MQTT_TOPIC_WEATHER "#"); mqtt_client.subscribe(MQTT_TOPIC_WEATHER "#");
} }

View File

@ -1,5 +1,4 @@
#include <Arduino.h> #include <Arduino.h>
#include <SimpleList.h>
#include "ntp.h" #include "ntp.h"
#include "config.h" #include "config.h"
@ -20,7 +19,7 @@ char hostname[30]; // defined as extern in prototypes.h
void setup() { void setup() {
Serial.begin(74880); Serial.begin(74880);
LOGln("Core * Starting"); LOGln("Core * Starting");
int chipid; int chipid;
#if defined( ESP8266 ) #if defined( ESP8266 )
chipid = ESP.getChipId(); chipid = ESP.getChipId();
@ -28,7 +27,7 @@ void setup() {
chipid = ESP.getEfuseMac() & 0xFFFFFF; chipid = ESP.getEfuseMac() & 0xFFFFFF;
#endif #endif
snprintf(hostname, 30, HOSTNAME, chipid); snprintf(hostname, 30, HOSTNAME, chipid);
setup_effects(); setup_effects();
wifi_setup(); wifi_setup();
ntp_setup(); ntp_setup();

28
src/tests.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "tests.h"
#include "effects.h"
namespace tests {
void run() {
LOGln("Tests * Running test for memory leaks...");
test_effects_for_memory_leaks();
}
void test_effects_for_memory_leaks() {
int i=0;
Effect* effect;
int32_t diffs[3] = {0, 0, 0};
int32_t usage = 0;
while (1) {
for (int j=0; j<3; j++) {
int free_at_start = ESP.getFreeHeap();
effect = select_effect(i);
if (j==0) usage = free_at_start - ESP.getFreeHeap();
if (effect == NULL) return;
delete effect;
diffs[j] = free_at_start - ESP.getFreeHeap();
}
LOGln("Tests * Memory usage of effect #%d: %d, leakage %d, %d, %d", i, usage, diffs[0], diffs[1], diffs[2]);
i++;
}
}
}