Compare commits

...

15 Commits

Author SHA1 Message Date
205a0df842 Small fixes.
Some checks failed
continuous-integration/drone/push Build is failing
2019-10-04 15:58:56 +02:00
8bcee1871f Effect dvd now with subpixel rendering. 2019-10-04 15:58:24 +02:00
ef57c5ea2e Settings can now be changed:
* Via HTTP POST request to /settings, providing key=<key_to_change> and value=<new_value>.
  * Via MQTT at <MQTT_TOPIC>/settings.<key>.
2019-10-04 12:11:05 +02:00
0f1d4abe04 Settings are now properly global and can be changed. 2019-10-04 12:10:16 +02:00
2b50691067 Fixed memory leak in random_matrix and rainbow_matrix. 2019-10-04 12:08:01 +02:00
af1314632e sines effect now uses subpixel rendering. 2019-10-04 12:05:54 +02:00
2b7033b685 cycle effect now tracks the heap leakage of the running effects. Cause somewhere's a memory leak leading to restarts of the ESP every about 2 days... 2019-10-04 12:03:03 +02:00
97dd6de280 Fixes for recorder and tests. 2019-10-02 06:21:10 +02:00
54d357e6df Fixes for firework effect. 2019-10-02 06:20:39 +02:00
ac1f758b87 Fixes in blur2d effect. 2019-10-02 06:18:43 +02:00
85aee53462 More debugging output in Animation.cpp 2019-10-02 06:18:15 +02:00
f42b5e1034 Effect big_clock now show the seconds in a calmer way. Divisible-by-5 seconds are in another color. 2019-10-02 06:17:20 +02:00
083564caef Reorganized effect selection stuff: No longer a big case clause comparing CRC32. Instead an array of structs. Much nicer. Also, other code can now see which effects there are. 2019-10-02 06:16:07 +02:00
3f6d4cb0be Moved settings from preprocessor directives to variables. Also added a way to (for now only) display them via HTTP server. 2019-10-02 06:13:55 +02:00
382631d7d7 Effect#loop now gets the time since the last run of the loop in ms. This enables
the effects to show animations that stay fluid independent of the current frame rate.
2019-10-01 06:29:32 +02:00
58 changed files with 563 additions and 287 deletions

View File

@ -95,7 +95,6 @@ If you enabled `DEBUG`, log messages will be sent to `MQTT_TOPIC/log`.
| FastLED (with small modifications) | Daniel Garcia & Mark Kriegsman | https://fastled.io | FastLED (with small modifications) | Daniel Garcia & Mark Kriegsman | https://fastled.io
| NTPClient (with modifications) | | https://github.com/arduino-libraries/NTPClient | NTPClient (with modifications) | | https://github.com/arduino-libraries/NTPClient
| ESP8266WebServer | | https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer | ESP8266WebServer | | https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer
| ErriezCRC32 | Erriez | https://github.com/Erriez/ErriezCRC32
| ESPAsyncTCP | me-no-dev | https://github.com/me-no-dev/ESPAsyncTCP | ESPAsyncTCP | me-no-dev | https://github.com/me-no-dev/ESPAsyncTCP
### Inspirations and stuff ### Inspirations and stuff

View File

@ -9,7 +9,7 @@ protected:
Window* window = Window::getFullWindow(); // Use a full screen window per default. Window* window = Window::getFullWindow(); // Use a full screen window per default.
public: public:
virtual ~Effect() {}; virtual ~Effect() {};
virtual void loop() = 0; virtual void loop(uint16_t ms) = 0;
virtual String get_name() = 0; virtual String get_name() = 0;
boolean supports_window = false; boolean supports_window = false;
virtual boolean can_be_shown_with_clock() { return false; }; virtual boolean can_be_shown_with_clock() { return false; };

View File

@ -3,6 +3,7 @@
#include <Arduino.h> #include <Arduino.h>
#define FASTLED_INTERNAL #define FASTLED_INTERNAL
#include <FastLED.h> #include <FastLED.h>
#include "settings.h"
//#define DEBUG // Uncomment this to enable Debug messages via Serial and, if enabled, MQTT. //#define DEBUG // Uncomment this to enable Debug messages via Serial and, if enabled, MQTT.
//#define CONFIG_USABLE // Uncomment this by removing the // at the beginning! //#define CONFIG_USABLE // Uncomment this by removing the // at the beginning!
@ -49,39 +50,35 @@
#define MONITOR_LOOP_TIME_THRESHOLD 500 #define MONITOR_LOOP_TIME_THRESHOLD 500
#define MONITOR_LOOP_TIME_COUNT_MAX 10 #define MONITOR_LOOP_TIME_COUNT_MAX 10
#define EFFECT_CYCLE_TIME 300 // Time in seconds between cycling effects. // settings.effects.cycle.time = 300; // Time in seconds between cycling effects.
#define EFFECT_CYCLE_RANDOM true // settings.effects.cycle.random = true;
#define EFFECT_MATRIX_LENGTH_MIN 4 // settings.effects.matrix.length_min = 4;
#define EFFECT_MATRIX_LENGTH_MAX 20 // settings.effects.matrix.length_max = 20;
#define EFFECT_MATRIX_SPEED_MIN 50 // settings.effects.matrix.speed_min = 1;
#define EFFECT_MATRIX_SPEED_MAX 135 // settings.effects.matrix.speed_max = 10;
#define EFFECT_SINGLE_DYNAMIC_LOOP_TIME 40 // .dynamic.single_loop_time = 40;
#define EFFECT_MULTI_DYNAMIC_LOOP_TIME 1400 // .dynamic.multi_loop_time = 1400;
#define EFFECT_BIG_DYNAMIC_LOOP_TIME 50 // .dynamic.big_loop_time = 50;
#define EFFECT_BIG_DYNAMIC_SIZE 3 // .dynamic.big_size = 3;
#define EFFECT_CONFETTI_PIXELS_PER_LOOP 2 // .fire.cooldown = 192;
// .fire.spark_chance = 5;
#define EFFECT_SNAKE_DIRECTION_CHANGE 10 // .firework.drag = 255;
#define EFFECT_SNAKE_SLOWDOWN 2 // .firework.bounce = 200;
// .firework.gravity = 10;
// .firework.sparks = 12;
#define EFFECT_FIRE_COOLDOWN 192 // .gol.start_percentage = 90;
#define EFFECT_FIRE_SPARK_CHANCE 5 // .gol.blend_speed = 10;
// .gol.restart_after_steps = 100;
#define EFFECT_FIREWORK_SHOT_CHANCE 200 // .sines.count = 5;
#define EFFECT_FIREWORK_BLUR 200
#define EFFECT_FIREWORK_FADEOUT_SPEED 5
#define EFFECT_GOL_START_PERCENTAGE 90 // .snake.direction_change = 5;
#define EFFECT_GOL_BLEND_SPEED 10 // .snake.slowdown = 2;
#define EFFECT_GOL_RESTART_AFTER_STEPS 100
#define EFFECT_DVD_WIDTH 3
#define EFFECT_DVD_HEIGHT 2
#define EFFECT_SINES_COUNT 5
// Stop editing here // Stop editing here

View File

@ -4,6 +4,6 @@
class AnalogClockEffect : public Effect { class AnalogClockEffect : public Effect {
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "analog_clock"; } String get_name() override { return "analog_clock"; }
}; };

View File

@ -17,6 +17,6 @@ public:
AnimationEffect(const char* name, uint32_t 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); AnimationEffect* setFgColor(uint32_t c);
void loop(); void loop(uint16_t ms);
String get_name() override; String get_name() override;
}; };

View File

@ -9,7 +9,7 @@ private:
CRGB color_off = CRGB(0x000000); CRGB color_off = CRGB(0x000000);
boolean invert = false; boolean invert = false;
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "bell"; } String get_name() override { return "bell"; }
}; };

View File

@ -5,12 +5,13 @@
class BigClockEffect : public Effect { class BigClockEffect : public Effect {
private: private:
CRGB _color_font = CRGB(0xAAAAAA); CRGB _color_font = CRGB(0xAAAAAA);
CRGB _color_seconds = CRGB(0xFF0000); CRGB _color_seconds_light = CRGB(0xFFFF00);
CRGB _color_seconds_dark = CRGB(0xFF0000);
void _draw_seconds(); void _draw_seconds();
void _draw_border_pixel(uint8_t second, CRGB* color); void _draw_border_pixel(uint8_t second, uint8_t part, CRGB* color);
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "big_clock"; } String get_name() override { return "big_clock"; }
}; };

View File

@ -11,7 +11,7 @@ public:
~Blur2DEffect(); ~Blur2DEffect();
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
void loop(); void loop(uint16_t ms);
String get_name() override { return "blur2d"; } String get_name() override { return "blur2d"; }
}; };

View File

@ -11,7 +11,7 @@ protected:
public: public:
~ClockEffect(); ~ClockEffect();
virtual void loop(); virtual void loop(uint16_t ms);
String get_name() override { return "clock"; } String get_name() override { return "clock"; }
void loop_with_invert(bool invert); void loop_with_invert(bool invert);
void loop(boolean invert, CRGB fg_color, CRGB bg_color, uint8_t y); void loop(boolean invert, CRGB fg_color, CRGB bg_color, uint8_t y);
@ -20,5 +20,5 @@ public:
class NightClockEffect : public ClockEffect { class NightClockEffect : public ClockEffect {
public: public:
NightClockEffect(); NightClockEffect();
void loop() override; void loop(uint16_t ms) override;
}; };

View File

@ -7,7 +7,7 @@ class ConfettiEffect : public Effect {
protected: protected:
virtual CRGB _getColor(); virtual CRGB _getColor();
public: public:
void loop(); void loop(uint16_t ms);
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
String get_name() override { return "confetti"; } String get_name() override { return "confetti"; }
}; };

View File

@ -8,6 +8,8 @@ private:
Effect* effect = NULL; Effect* effect = NULL;
uint16_t effect_id = -1; uint16_t effect_id = -1;
unsigned long effectSince = 0; unsigned long effectSince = 0;
uint16_t _heap_free = 0;
uint8_t _effects_count;
public: public:
CycleEffect(); CycleEffect();
~CycleEffect(); ~CycleEffect();
@ -17,5 +19,5 @@ public:
boolean clock_as_mask(); boolean clock_as_mask();
String get_name() override; String get_name() override;
void loop(); void loop(uint16_t ms);
}; };

View File

@ -4,15 +4,15 @@
class DvdEffect : public Effect { class DvdEffect : public Effect {
private: private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
uint8_t _x = 0; saccum78 _x = 0;
uint8_t _y = 0; saccum78 _y = 0;
int8_t _x_dir = 1; int8_t _x_dir = 1;
int8_t _y_dir = 1; int8_t _y_dir = 1;
CRGB _color; CRGB _color;
public: public:
DvdEffect(); DvdEffect();
~DvdEffect(); ~DvdEffect();
void loop() override; void loop(uint16_t ms) override;
bool can_be_shown_with_clock() override; bool can_be_shown_with_clock() override;
String get_name() override { return "dvd"; } String get_name() override { return "dvd"; }
}; };

View File

@ -13,14 +13,14 @@ public:
SingleDynamicEffect(); SingleDynamicEffect();
void init(); void init();
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
virtual void loop(); virtual void loop(uint16_t ms);
void draw(); void draw();
String get_name() override { return "single_dynamic"; } String get_name() override { return "single_dynamic"; }
}; };
class MultiDynamicEffect : public SingleDynamicEffect { class MultiDynamicEffect : public SingleDynamicEffect {
public: public:
void loop(); void loop(uint16_t ms);
String get_name() override { return "multi_dynamic"; } String get_name() override { return "multi_dynamic"; }
}; };
@ -28,7 +28,7 @@ class BigDynamicEffect : public Effect {
private: private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6); Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
public: public:
void loop(); void loop(uint16_t ms);
~BigDynamicEffect(); ~BigDynamicEffect();
boolean can_be_shown_with_clock() override; boolean can_be_shown_with_clock() override;
String get_name() override { return "big_dynamic"; } String get_name() override { return "big_dynamic"; }

View File

@ -15,6 +15,6 @@ private:
public: public:
FireEffect(); FireEffect();
~FireEffect(); ~FireEffect();
void loop(); void loop(uint16_t ms);
String get_name() override { return "fire"; } String get_name() override { return "fire"; }
}; };

View File

@ -6,11 +6,6 @@
enum FireworkDotType { FIREWORK_DOT_NONE, FIREWORK_DOT_SHELL, FIREWORK_DOT_SPARK }; enum FireworkDotType { FIREWORK_DOT_NONE, FIREWORK_DOT_SHELL, FIREWORK_DOT_SPARK };
#define EFFECT_FIREWORK_DRAG 255
#define EFFECT_FIREWORK_BOUNCE 200
#define EFFECT_FIREWORK_GRAVITY 10
#define EFFECT_FIREWORK_SPARKS 12
class FireworkEffect; class FireworkEffect;
class FireworkEffectDot { class FireworkEffectDot {
@ -49,14 +44,14 @@ private:
CRGB _burst_color; CRGB _burst_color;
FireworkEffectDot* _dot; FireworkEffectDot* _dot;
FireworkEffectDot* _sparks[EFFECT_FIREWORK_SPARKS]; FireworkEffectDot** _sparks;
public: public:
FireworkEffect(); FireworkEffect();
~FireworkEffect(); ~FireworkEffect();
void skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c); void skyburst(accum88 x, accum88 y, saccum78 xv, saccum78 yv, CRGB c);
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
void loop(); void loop(uint16_t ms);
String get_name() override { return "firework"; } String get_name() override { return "firework"; }
}; };

View File

@ -17,7 +17,7 @@ private:
public: public:
GolEffect(); GolEffect();
~GolEffect(); ~GolEffect();
void loop(); void loop(uint16_t ms);
bool can_be_shown_with_clock(); bool can_be_shown_with_clock();
String get_name() override { return "gol"; } String get_name() override { return "gol"; }
}; };

View File

@ -12,7 +12,7 @@ private:
public: public:
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
void loop(); void loop(uint16_t ms);
void apply_option(String key, String value) override; void apply_option(String key, String value) override;
String get_name() override { return "marquee"; } String get_name() override { return "marquee"; }
}; };

View File

@ -27,9 +27,9 @@ public:
MatrixEffectColumn(Window* win, uint8_t direction=0, bool random_direction=false); MatrixEffectColumn(Window* win, uint8_t direction=0, bool random_direction=false);
virtual ~MatrixEffectColumn() {}; virtual ~MatrixEffectColumn() {};
void advance(); void advance(uint16_t ms);
void draw(); void draw();
void loop(); void loop(uint16_t ms);
}; };
class RainbowMatrixEffectColumn : public MatrixEffectColumn { class RainbowMatrixEffectColumn : public MatrixEffectColumn {
@ -55,7 +55,7 @@ public:
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
MatrixEffect(); MatrixEffect();
virtual ~MatrixEffect(); virtual ~MatrixEffect();
void loop(); void loop(uint16_t ms);
String get_name() override { return "matrix"; } String get_name() override { return "matrix"; }
}; };

View File

@ -10,7 +10,7 @@ private:
public: public:
PixelClockEffect(); PixelClockEffect();
~PixelClockEffect(); ~PixelClockEffect();
void loop(); void loop(uint16_t ms);
bool can_be_shown_with_clock(); bool can_be_shown_with_clock();
String get_name() override { return "pixel_clock"; } String get_name() override { return "pixel_clock"; }
}; };

View File

@ -30,7 +30,7 @@ public:
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
boolean clock_as_mask(); boolean clock_as_mask();
void loop(); void loop(uint16_t ms);
String get_name() override { return "sinematrix3"; } String get_name() override { return "sinematrix3"; }
}; };

View File

@ -6,28 +6,29 @@
class SinesEffectSinus { class SinesEffectSinus {
private: private:
uint8_t _value; uint16_t _frequency;
uint8_t _frequency; uint16_t _color_frequency;
uint8_t _amplitude; uint16_t _amplitude;
uint8_t _x; uint16_t _x;
uint8_t _step; uint16_t _offset;
Window* _window; Window* _window;
CRGB _color;
public: public:
SinesEffectSinus(Window* w); SinesEffectSinus(Window* w);
void loop(); void loop(uint16_t ms);
}; };
class SinesEffect : public Effect { class SinesEffect : public Effect {
private: private:
SinesEffectSinus* _sinus[EFFECT_SINES_COUNT]; SinesEffectSinus** _sinus;
uint8_t _step = 0; uint8_t _count;
void _init();
void _delete();
public: public:
SinesEffect(); SinesEffect();
~SinesEffect(); ~SinesEffect();
boolean supports_window = true; boolean supports_window = true;
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();
void loop(); void loop(uint16_t ms);
String get_name() override { return "sines"; } String get_name() override { return "sines"; }
}; };

View File

@ -17,7 +17,7 @@ private:
public: public:
SnakeEffect(); SnakeEffect();
~SnakeEffect(); ~SnakeEffect();
void loop(); void loop(uint16_t ms);
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);
boolean can_be_shown_with_clock(); boolean can_be_shown_with_clock();

View File

@ -9,7 +9,7 @@ private:
public: public:
StaticEffect(CRGB col); StaticEffect(CRGB col);
boolean supports_window = true; boolean supports_window = true;
void loop(); void loop(uint16_t ms);
String get_name() override { return "static"; } String get_name() override { return "static"; }
}; };

View File

@ -10,7 +10,7 @@ private:
double _real_center_x = LED_WIDTH / 2; double _real_center_x = LED_WIDTH / 2;
double _real_center_y = LED_HEIGHT / 2; double _real_center_y = LED_HEIGHT / 2;
public: public:
void loop(); void loop(uint16_t ms);
boolean can_be_shown_with_clock() override; boolean can_be_shown_with_clock() override;
boolean clock_as_mask() override; boolean clock_as_mask() override;
String get_name() override { return "twirl"; } String get_name() override { return "twirl"; }

View File

@ -1,17 +1,20 @@
#ifndef effects_H #pragma once
#define effects_H
#include "Effect.h" #include "Effect.h"
#include "effect_clock.h" #include "effect_clock.h"
extern const char* cycle_effects[]; struct EffectEntry {
extern uint8_t cycle_effects_count; const char* name;
bool use_in_cycle;
std::function<Effect*()> create;
};
extern const EffectEntry effects[];
extern const uint8_t effects_size;
extern Effect* current_effect; extern Effect* current_effect;
extern ClockEffect effect_clock; extern ClockEffect effect_clock;
Effect* select_effect(uint32_t c); Effect* select_effect(char* name);
Effect* select_effect(uint8_t id);
bool change_current_effect(String s); bool change_current_effect(String s);
void setup_effects(); void setup_effects();
#endif

View File

@ -2,6 +2,9 @@
#include <Arduino.h> #include <Arduino.h>
extern uint8_t baseHue;
extern char hostname[30];
typedef struct { typedef struct {
uint8_t width; uint8_t width;
uint8_t height; uint8_t height;
@ -26,6 +29,3 @@ typedef struct {
uint16_t x; uint16_t x;
uint16_t y; uint16_t y;
} Coords; } Coords;
extern uint8_t baseHue;
extern char hostname[30];

83
include/settings.h Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include <Arduino.h>
enum SettingType {
TYPE_UINT8,
TYPE_UINT16,
TYPE_BOOL
};
typedef struct {
const char* name;
uint16_t* value;
SettingType type;
} Setting;
struct Settings {
uint16_t fps = 50;
struct /* effects */ {
struct /* cycle */ {
uint16_t time = 300;
uint16_t random = 1;
} cycle ;
struct /* matrix */ {
uint16_t length_min = 4;
uint16_t length_max = 20;
uint16_t speed_min = 1;
uint16_t speed_max = 10;
} matrix;
struct /* confetti */ {
uint16_t pixels_per_loop = 2;
} confetti;
struct /* dvd */ {
uint16_t width = 3;
uint16_t height = 2;
uint16_t speed = 50;
} dvd;
struct /* dynamic */ {
uint16_t single_loop_time = 40;
uint16_t multi_loop_time = 1400;
uint16_t big_loop_time = 50;
uint16_t big_size = 3;
} dynamic;
struct /* fire */ {
uint16_t cooldown = 192;
uint16_t spark_chance = 5;
} fire;
struct /* firework */ {
uint16_t drag = 255;
uint16_t bounce = 200;
uint16_t gravity = 10;
uint16_t sparks = 12;
} firework;
struct /* gol */ {
uint16_t start_percentage = 90;
uint16_t blend_speed = 10;
uint16_t restart_after_steps = 100;
} gol;
struct /* sines */ {
uint16_t count = 5;
} sines;
struct /* snake */ {
uint16_t direction_change = 5;
uint16_t slowdown = 2;
} snake;
} effects;
};
extern Settings settings;
extern Setting all_settings[];
extern const uint8_t all_settings_size;
bool change_setting(const char* key, uint16_t new_value);

View File

@ -18,7 +18,6 @@ lib_deps =
https://github.com/fabianonline/FastLED.git https://github.com/fabianonline/FastLED.git
https://github.com/fabianonline/NTPClient.git https://github.com/fabianonline/NTPClient.git
ESP8266WebServer ESP8266WebServer
ErriezCRC32
ESPAsyncTCP ESPAsyncTCP
[env:ota] [env:ota]

View File

@ -75,7 +75,7 @@ bool Animation::_load_from_file(const char* filename) {
return false; return false;
} }
if (file.available() != size - 6) { if (file.available() < 0 || file.available() + 6 != size) {
LOGln("Animation * Expected file to have %d bytes available, but found %d bytes available.", size - 6, file.available()); LOGln("Animation * Expected file to have %d bytes available, but found %d bytes available.", size - 6, file.available());
file.close(); file.close();
return false; return false;
@ -207,27 +207,29 @@ 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];
LOGln("Deleting _colors...");
LOGln("Animation * Deleting _colors...");
if (_colors) delete [] _colors; if (_colors) delete [] _colors;
LOGln("Deleting fgColor...");
LOGln("Animation * Deleting fgColor...");
if (fgColor != NULL) delete fgColor; if (fgColor != NULL) delete fgColor;
LOGln("Deleting bgColor...");
LOGln("Animation * Deleting bgColor...");
if (bgColor != NULL) delete bgColor; if (bgColor != NULL) delete bgColor;
LOGln("Deleting _frame_data_lengths...");
LOGln("Animation * Deleting _frame_data_lengths...");
if (_frame_data_lengths) delete [] _frame_data_lengths; if (_frame_data_lengths) delete [] _frame_data_lengths;
LOGln("Deleting _frame_times...");
LOGln("Animation * Deleting _frame_times...");
if (_frame_times) delete [] _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];
} }
LOGln("Deleting _frame_data...");
LOGln("Animation * Deleting _frame_data...");
if (_frame_data) delete [] _frame_data; if (_frame_data) delete [] _frame_data;
LOGln("Deleteion done.");
LOGln("Animation * Deletion done.");
} }
void Animation::draw() { void Animation::draw() {

View File

@ -42,13 +42,13 @@ void Window::clear(CRGB* color) {
} }
void Window::drawText(Font* font, uint16_t x, uint16_t y, String text, CRGB* color) { void Window::drawText(Font* font, uint16_t x, uint16_t y, String text, CRGB* color) {
for (int i=0; i<text.length(); i++) { for (uint16_t i=0; i<text.length(); i++) {
drawChar(font, (x+(i*(font->width + 1))<<8), (y<<8), text[i], color); drawChar(font, (x+((i*(font->width + 1))<<8)), (y<<8), text[i], color);
} }
} }
void Window::drawSubText(Font* font, accum88 x, accum88 y, String text, CRGB* color) { void Window::drawSubText(Font* font, accum88 x, accum88 y, String text, CRGB* color) {
for (int i=0; i<text.length(); i++) { for (uint16_t i=0; i<text.length(); i++) {
drawChar(font, x+(i*((font->width + 1)<<8)), y, text[i], color); drawChar(font, x+(i*((font->width + 1)<<8)), y, text[i], color);
} }
} }

View File

@ -2,7 +2,7 @@
#include "my_fastled.h" #include "my_fastled.h"
#include "ntp.h" #include "ntp.h"
void AnalogClockEffect::loop() { void AnalogClockEffect::loop(uint16_t ms) {
window->clear(); window->clear();
CRGB white(0xFFFFFF); CRGB white(0xFFFFFF);
CRGB red(0xFF0000); CRGB red(0xFF0000);

View File

@ -20,7 +20,7 @@ AnimationEffect::~AnimationEffect() {
delete this->animation; delete this->animation;
} }
void AnimationEffect::loop() { void AnimationEffect::loop(uint16_t ms) {
this->animation->drawFrame(); this->animation->drawFrame();
this->animation->advance(); this->animation->advance();
} }

View File

@ -3,7 +3,7 @@
#include "effect_bell.h" #include "effect_bell.h"
#include "sprites.h" #include "sprites.h"
void BellEffect::loop() { void BellEffect::loop(uint16_t ms) {
Serial.println("This is Bell.loop()"); Serial.println("This is Bell.loop()");
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int x = 0; x < 2; x++) { for (int x = 0; x < 2; x++) {

View File

@ -3,7 +3,7 @@
#include "fonts.h" #include "fonts.h"
#include "ntp.h" #include "ntp.h"
void BigClockEffect::loop() { void BigClockEffect::loop(uint16_t ms) {
window->clear(); window->clear();
uint8_t h = ntpClient.getHours(); uint8_t h = ntpClient.getHours();
window->drawChar(&font_numbers3x5_blocky, 6<<8, 2<<8, '0' + (h / 10), &_color_font); window->drawChar(&font_numbers3x5_blocky, 6<<8, 2<<8, '0' + (h / 10), &_color_font);
@ -25,17 +25,28 @@ void BigClockEffect::loop() {
void BigClockEffect::_draw_seconds() { void BigClockEffect::_draw_seconds() {
uint8_t seconds = ntpClient.getSeconds(); uint8_t seconds = ntpClient.getSeconds();
for (int i=1; i<=seconds; i++) { for (int i=1; i<=seconds; i++) {
_draw_border_pixel(i, &_color_seconds); _draw_border_pixel(i, 0, (i%5==0) ? &_color_seconds_light : &_color_seconds_dark);
} }
uint16_t millis = ntpClient.getEpochMillis() % 1000; uint16_t millis = ntpClient.getEpochMillis() % 1000;
/*
// Enable this to have the next pixel move smoothly to its position
if (millis > 0) { if (millis > 0) {
uint8_t part = 60 - ((60 - seconds) * millis / 1000); uint8_t part = 60 - ((60 - seconds) * millis / 1000);
_draw_border_pixel(part, &_color_seconds); _draw_border_pixel(part, &_color_seconds);
} }
*/
uint8_t offset = 5 - ((millis % 1000) / 200);
uint8_t part = scale8(millis % 200, 200);
uint8_t number_to_show = (60 - seconds - offset) / 5 + 1;
for(uint8_t i = 0; i<number_to_show; i++) {
uint8_t pos = seconds + offset + i*5;
_draw_border_pixel(pos, part, (seconds + i + 1)%5==0 ? &_color_seconds_light : &_color_seconds_dark);
}
} }
void BigClockEffect::_draw_border_pixel(uint8_t i, CRGB* color) { void BigClockEffect::_draw_border_pixel(uint8_t i, uint8_t part, CRGB* color) {
uint8_t x, y; /*uint8_t x, y;
if (i<=8) { if (i<=8) {
x = 7 + i; x = 7 + i;
y = 0; y = 0;
@ -48,9 +59,31 @@ void BigClockEffect::_draw_border_pixel(uint8_t i, CRGB* color) {
} else if (i <= 53) { } else if (i <= 53) {
x = 0; x = 0;
y = 15 - i + 38; y = 15 - i + 38;
} else { } else if (i <= 60) {
x = i - 53; x = i - 53;
y = 0; y = 0;
} else {
return;
} }
window->setPixel(x, y, color); window->setPixel(x, y, color);*/
accum88 x, y;
if (i<=8) {
x = ((7+i)<<8) + part;
y = 0;
} else if (i<=23) {
x = 15<<8;
y = ((i-8)<<8) + part;
} else if (i<=38) {
x = ((38-i)<<8) - part;
y = 15<<8;
} else if (i<=53) {
x = 0;
y = ((53-i)<<8) - part;
} else if (i<=60) {
x = ((i-53)<<8) + part;
y = 0;
} else {
return;
}
window->setSubPixel(x, y, color);
} }

View File

@ -4,7 +4,7 @@ boolean Blur2DEffect::can_be_shown_with_clock() {
return true; return true;
} }
void Blur2DEffect::loop() { void Blur2DEffect::loop(uint16_t ms) {
uint8_t blur_amount = dim8_raw(beatsin8(3, 128, 224)); uint8_t blur_amount = dim8_raw(beatsin8(3, 128, 224));
window->blur(blur_amount); window->blur(blur_amount);
@ -17,10 +17,10 @@ void Blur2DEffect::loop() {
uint8_t x3 = beatsin8(11, 0, window->width-1); uint8_t x3 = beatsin8(11, 0, window->width-1);
uint8_t y3 = beatsin8(13, 0, window->height-1); uint8_t y3 = beatsin8(13, 0, window->height-1);
uint16_t ms = millis(); uint16_t time = millis();
CRGB c1 = CHSV(ms / 29, 200, 255); CRGB c1 = CHSV(time / 29, 200, 255);
CRGB c2 = CHSV(ms / 41, 200, 255); CRGB c2 = CHSV(time / 41, 200, 255);
CRGB c3 = CHSV(ms / 73, 200, 255); CRGB c3 = CHSV(time / 73, 200, 255);
window->addPixelColor(x1, y1, &c1); window->addPixelColor(x1, y1, &c1);
window->addPixelColor(x2, y2, &c2); window->addPixelColor(x2, y2, &c2);
window->addPixelColor(x3, y3, &c3); window->addPixelColor(x3, y3, &c3);

View File

@ -8,7 +8,7 @@ NightClockEffect::NightClockEffect() {
window = Window::getFullWindow(); window = Window::getFullWindow();
} }
void NightClockEffect::loop() { void NightClockEffect::loop(uint16_t ms) {
uint16_t minutes = minutes16(); uint16_t minutes = minutes16();
//uint8_t y = minutes % ((window->height - 5) * 2 - 2); //uint8_t y = minutes % ((window->height - 5) * 2 - 2);
//if (y > window->height - 5) y = 2*window->height - 2*y; //if (y > window->height - 5) y = 2*window->height - 2*y;
@ -16,7 +16,7 @@ void NightClockEffect::loop() {
ClockEffect::loop(false, CRGB(0x200000), CRGB(0x000000), y); ClockEffect::loop(false, CRGB(0x200000), CRGB(0x000000), y);
} }
void ClockEffect::loop() { void ClockEffect::loop(uint16_t ms) {
loop_with_invert(false); loop_with_invert(false);
} }

View File

@ -3,9 +3,9 @@
#include "functions.h" #include "functions.h"
#include "prototypes.h" #include "prototypes.h"
void ConfettiEffect::loop() { void ConfettiEffect::loop(uint16_t ms) {
window->fadeToBlackBy(3); window->fadeToBlackBy(3);
for (int i=0; i<EFFECT_CONFETTI_PIXELS_PER_LOOP; i++) { for (int i=0; i<settings.effects.confetti.pixels_per_loop; i++) {
CRGB color = _getColor(); CRGB color = _getColor();
window->addPixelColor(random16(LED_COUNT), &color); window->addPixelColor(random16(LED_COUNT), &color);
} }

View File

@ -3,6 +3,11 @@
#include <ErriezCRC32.h> #include <ErriezCRC32.h>
CycleEffect::CycleEffect() { CycleEffect::CycleEffect() {
_effects_count = 0;
for (uint8_t i=0; i<effects_size; i++) {
if (effects[i].use_in_cycle) _effects_count++;
}
LOGln("Cycle * Found %d effects to use in cycle.", _effects_count);
changeEffect(); changeEffect();
} }
@ -11,20 +16,41 @@ CycleEffect::~CycleEffect() {
} }
void CycleEffect::changeEffect() { void CycleEffect::changeEffect() {
int new_id; uint8_t new_id;
if (EFFECT_CYCLE_RANDOM) { if (settings.effects.cycle.random && _effects_count>1) {
do { do {
new_id = random8(cycle_effects_count); new_id = random8(_effects_count);
} while (new_id == effect_id); } while (new_id == effect_id);
} else { } else {
new_id = (effect_id + 1) % cycle_effects_count; new_id = (effect_id + 1) % _effects_count;
} }
LOGln("CycleEffect * Changing effect from #%d to #%d", effect_id, new_id); LOGln("CycleEffect * Changing effect from #%d to #%d", effect_id, new_id);
delay(25); delay(25);
if (effect) delete effect; if (effect) delete effect;
LOGln("CycleEffect * Searching for new effect '%s'", cycle_effects[new_id]);
int16_t diff;
uint16_t old_heap = _heap_free;
_heap_free = ESP.getFreeHeap();
if (old_heap) {
// diff positive = More heap used (baad)
// diff negative = Less heap used (good-ish)
diff = old_heap - _heap_free;
LOGln("CycleEffect * Heap usage: #%d,%d,%+d", effect_id, _heap_free, diff);
}
delay(25); delay(25);
effect = select_effect( crc32String(cycle_effects[new_id]) ); LOGln("CycleEffect * Searching for new effect #%d", new_id);
uint8_t count = 0;
for (uint8_t i=0; i<effects_size; i++) {
if (effects[i].use_in_cycle) {
if (count == new_id) {
effect = effects[i].create();
break;
}
count++;
}
}
if (effect) { if (effect) {
effect_id = new_id; effect_id = new_id;
effectSince = millis(); effectSince = millis();
@ -41,13 +67,13 @@ boolean CycleEffect::clock_as_mask() {
return effect->clock_as_mask(); return effect->clock_as_mask();
}; };
void CycleEffect::loop() { void CycleEffect::loop(uint16_t ms) {
if (!effect) changeEffect(); // If this is the first run, we have to select an effect first! if (!effect) changeEffect(); // If this is the first run, we have to select an effect first!
effect->loop(); effect->loop(ms);
// Don't use EVERY_N_SECONDS(config_effect_cycle_time) here because that function isn't relly made // Don't use EVERY_N_SECONDS(config_effect_cycle_time) here because that function isn't relly made
// to be used with changing values. // to be used with changing values.
EVERY_N_SECONDS(1) { EVERY_N_SECONDS(1) {
if (effectSince + EFFECT_CYCLE_TIME*1000 < millis()) { if (effectSince + settings.effects.cycle.time*1000 < millis()) {
changeEffect(); changeEffect();
} }
} }

View File

@ -1,29 +1,43 @@
#include "effect_dvd.h" #include "effect_dvd.h"
#include "my_fastled.h" #include "my_fastled.h"
void DvdEffect::loop() { void DvdEffect::loop(uint16_t ms) {
bool dir_changed = false; bool dir_changed = false;
EVERY_N_MILLISECONDS( 250 ) {
_x += _x_dir;
_y += _y_dir;
if (_x == 0 || _x + EFFECT_DVD_WIDTH >= window->width) { _x += _x_dir * settings.effects.dvd.speed;
_y += _y_dir * settings.effects.dvd.speed;
if (_x <= 0) {
_x = -_x;
_x_dir = -_x_dir;
dir_changed = true;
//LOGln("speed: %d", settings.effects.dvd.speed);
} else if (_x + (settings.effects.dvd.width << 8) >= (window->width << 8)) {
_x -= 2*settings.effects.dvd.speed;
_x_dir = -_x_dir; _x_dir = -_x_dir;
dir_changed = true; dir_changed = true;
} }
if (_y == 0 || _y + EFFECT_DVD_HEIGHT >= window->height) {
if (_y <= 0) {
_y = -_y;
_y_dir = -_y_dir;
dir_changed = true;
} else if (_y + (settings.effects.dvd.height << 8) >= (window->height << 8)) {
_y -= 2*settings.effects.dvd.speed;
_y_dir = -_y_dir; _y_dir = -_y_dir;
dir_changed = true; dir_changed = true;
}
} }
window->clear(); 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); if (dir_changed) _color = (CRGB)CHSV(random8(), 255, 255);
for (int x=0; x<settings.effects.dvd.width; x++) for (int y=0; y<settings.effects.dvd.height; y++) {
window->setSubPixel(_x + (x<<8), _y + (y<<8), (CRGB*)&_color);
}
for (int x=1; x<settings.effects.dvd.width; x++) for (int y=1; y<settings.effects.dvd.height; y++) {
window->setPixel((_x>>8) + x, (_y>>8) + y, (CRGB*)&_color);
}
} }
bool DvdEffect::can_be_shown_with_clock() { return true; } bool DvdEffect::can_be_shown_with_clock() { return true; }

View File

@ -10,8 +10,8 @@ void SingleDynamicEffect::init() {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255); for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
} }
void SingleDynamicEffect::loop() { void SingleDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( EFFECT_SINGLE_DYNAMIC_LOOP_TIME ) { EVERY_N_MILLISECONDS( settings.effects.dynamic.single_loop_time ) {
tiles[random8(tile_count)] = CHSV(baseHue + random8(64), 180, 255); tiles[random8(tile_count)] = CHSV(baseHue + random8(64), 180, 255);
} }
this->draw(); this->draw();
@ -28,8 +28,8 @@ boolean SingleDynamicEffect::can_be_shown_with_clock() {
return true; return true;
} }
void MultiDynamicEffect::loop() { void MultiDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( EFFECT_MULTI_DYNAMIC_LOOP_TIME ) { EVERY_N_MILLISECONDS( settings.effects.dynamic.multi_loop_time ) {
for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255); for (int i=0; i<tile_count; i++) tiles[i] = CHSV(baseHue + random8(64), 180, 255);
} }
this->draw(); this->draw();
@ -39,24 +39,16 @@ BigDynamicEffect::~BigDynamicEffect() {
delete window; delete window;
} }
void BigDynamicEffect::loop() { void BigDynamicEffect::loop(uint16_t ms) {
EVERY_N_MILLISECONDS( EFFECT_BIG_DYNAMIC_LOOP_TIME ) { EVERY_N_MILLISECONDS( settings.effects.dynamic.big_loop_time ) {
uint8_t x = random8(0, window->width - EFFECT_BIG_DYNAMIC_SIZE + 1); uint8_t x = random8(0, window->width - settings.effects.dynamic.big_size + 1);
uint8_t y = random8(0, window->height - EFFECT_BIG_DYNAMIC_SIZE + 1); uint8_t y = random8(0, window->height - settings.effects.dynamic.big_size + 1);
CRGB color = CHSV(random8(), 255, 255); CRGB color = CHSV(random8(), 255, 255);
CRGB black(0x000000); 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++) { for (uint8_t ix=0; ix<settings.effects.dynamic.big_size; ix++) for (uint8_t iy=0; iy<settings.effects.dynamic.big_size; iy++) {
window->setPixel(x+ix, y+iy, &color); 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

@ -14,7 +14,7 @@ FireEffect::~FireEffect() {
delete [] this->data; delete [] this->data;
} }
void FireEffect::loop() { void FireEffect::loop(uint16_t ms) {
cooldown(); cooldown();
spark(); spark();
propagate(); propagate();
@ -22,11 +22,11 @@ void FireEffect::loop() {
} }
void FireEffect::cooldown() { void FireEffect::cooldown() {
for(int i=0; i<(this->window->width * this->window->height); i++) this->data[i] = scale8(this->data[i], EFFECT_FIRE_COOLDOWN); // 240 or something for(int i=0; i<(this->window->width * this->window->height); i++) this->data[i] = scale8(this->data[i], settings.effects.fire.cooldown);
} }
void FireEffect::spark() { void FireEffect::spark() {
for(int x=0; x<this->window->width; x++) if (random8(EFFECT_FIRE_SPARK_CHANCE)==0) this->data[x] = this->spark_temp(); for(int x=0; x<this->window->width; x++) if (random8(settings.effects.fire.spark_chance)==0) this->data[x] = this->spark_temp();
} }
inline uint8_t FireEffect::spark_temp() { inline uint8_t FireEffect::spark_temp() {

View File

@ -49,20 +49,20 @@ void FireworkEffectDot::draw() {
dim8_video( scale8( scale8( _color.g, ye), xe)), dim8_video( scale8( scale8( _color.g, ye), xe)),
dim8_video( scale8( scale8( _color.b, ye), xe))); dim8_video( scale8( scale8( _color.b, ye), xe)));
_window->addPixelColor(ix, iy, &c00); _window->addPixelColor(ix, iy, &c00);
_window->addPixelColor(ix, iy+1, &c01); _window->addPixelColor(ix, iy-1, &c01);
_window->addPixelColor(ix+1, iy, &c10); _window->addPixelColor(ix+1, iy, &c10);
_window->addPixelColor(ix+1, iy+1, &c11); _window->addPixelColor(ix+1, iy-1, &c11);
} }
void FireworkEffectDot::move() { void FireworkEffectDot::move() {
if (!show) return; if (!show) return;
_yv -= EFFECT_FIREWORK_GRAVITY; _yv -= settings.effects.firework.gravity;
_xv = _scale15by8_local(_xv, EFFECT_FIREWORK_DRAG); _xv = _scale15by8_local(_xv, settings.effects.firework.drag);
_yv = _scale15by8_local(_yv, EFFECT_FIREWORK_DRAG); _yv = _scale15by8_local(_yv, settings.effects.firework.drag);
if (type == FIREWORK_DOT_SPARK) { if (type == FIREWORK_DOT_SPARK) {
_xv = _scale15by8_local(_xv, EFFECT_FIREWORK_DRAG); _xv = _scale15by8_local(_xv, settings.effects.firework.drag);
_yv = _scale15by8_local(_yv, EFFECT_FIREWORK_DRAG); _yv = _scale15by8_local(_yv, settings.effects.firework.drag);
_color.nscale8(255); _color.nscale8(255);
if (!_color) { if (!_color) {
show = 0; show = 0;
@ -70,12 +70,12 @@ void FireworkEffectDot::move() {
} }
// Bounce if we hit the ground // Bounce if we hit the ground
if (_xv < 0 && _y - _window->height < (-_yv)) { if (_yv < 0 && _y - _window->height < (-_yv)) {
if (type == FIREWORK_DOT_SPARK) { if (type == FIREWORK_DOT_SPARK) {
show = 0; show = 0;
} else { } else {
_yv = -_yv; _yv = -_yv;
_yv = _scale15by8_local(_yv, EFFECT_FIREWORK_BOUNCE); _yv = _scale15by8_local(_yv, settings.effects.firework.bounce);
if (_yv < 500) { if (_yv < 500) {
show = 0; show = 0;
} }
@ -139,11 +139,11 @@ boolean FireworkEffect::can_be_shown_with_clock() {
return true; return true;
} }
void FireworkEffect::loop() { void FireworkEffect::loop(uint16_t ms) {
window->clear(); window->clear();
_dot->move(); _dot->move();
_dot->draw(); _dot->draw();
for (int i=0; i<EFFECT_FIREWORK_SPARKS; i++) { for (int i=0; i<settings.effects.firework.sparks; i++) {
_sparks[i]->move(); _sparks[i]->move();
_sparks[i]->draw(); _sparks[i]->draw();
} }
@ -159,7 +159,7 @@ void FireworkEffect::loop() {
} }
if (_skyburst) { if (_skyburst) {
int nsparks = random8(EFFECT_FIREWORK_SPARKS / 2, EFFECT_FIREWORK_SPARKS + 1); int nsparks = random8(settings.effects.firework.sparks / 2, settings.effects.firework.sparks + 1);
for (int i=0; i<nsparks; i++) { for (int i=0; i<nsparks; i++) {
_sparks[i]->sky_burst(_burst_x, _burst_y, _burst_yv, _burst_color); _sparks[i]->sky_burst(_burst_x, _burst_y, _burst_yv, _burst_color);
_skyburst = 0; _skyburst = 0;
@ -169,14 +169,15 @@ void FireworkEffect::loop() {
FireworkEffect::FireworkEffect() { FireworkEffect::FireworkEffect() {
_dot = new FireworkEffectDot(window, this); _dot = new FireworkEffectDot(window, this);
for (int i=0; i<EFFECT_FIREWORK_SPARKS; i++) { _sparks = new FireworkEffectDot*[settings.effects.firework.sparks];
for (int i=0; i<settings.effects.firework.sparks; i++) {
_sparks[i] = new FireworkEffectDot(window, this); _sparks[i] = new FireworkEffectDot(window, this);
} }
} }
FireworkEffect::~FireworkEffect() { FireworkEffect::~FireworkEffect() {
delete window; delete window;
for (int i=0; i<EFFECT_FIREWORK_SPARKS; i++) { for (int i=0; i<settings.effects.firework.sparks; i++) {
delete _sparks[i]; delete _sparks[i];
} }
delete _dot; delete _dot;

View File

@ -16,7 +16,7 @@ bool GolEffect::can_be_shown_with_clock() { return true; }
void GolEffect::_initialize() { void GolEffect::_initialize() {
for(uint16_t i=0; i<this->window->count; i++) { for(uint16_t i=0; i<this->window->count; i++) {
_data[i] = random8() < EFFECT_GOL_START_PERCENTAGE ? 1 : 0; _data[i] = random8() < settings.effects.gol.start_percentage ? 1 : 0;
} }
_old_hue = _hue; _old_hue = _hue;
_hue = random8(); _hue = random8();
@ -29,12 +29,12 @@ GolEffect::~GolEffect() {
delete window; delete window;
} }
void GolEffect::loop() { void GolEffect::loop(uint16_t ms) {
if (EFFECT_GOL_BLEND_SPEED + _blend > 255) { if (settings.effects.gol.blend_speed + _blend > 255) {
_blend = 0; _blend = 0;
_advance(); _advance();
} else { } else {
_blend += EFFECT_GOL_BLEND_SPEED; _blend += settings.effects.gol.blend_speed;
} }
_draw(); _draw();
@ -43,7 +43,7 @@ void GolEffect::loop() {
void GolEffect::_advance() { void GolEffect::_advance() {
_step++; _step++;
_old_hue = _hue; _old_hue = _hue;
if (_step >= EFFECT_GOL_RESTART_AFTER_STEPS) { if (_step >= settings.effects.gol.restart_after_steps) {
_initialize(); _initialize();
} else { } else {
for(uint16_t i=0; i<this->window->count; i++) { for(uint16_t i=0; i<this->window->count; i++) {

View File

@ -5,8 +5,7 @@ boolean MarqueeEffect::can_be_shown_with_clock() {
return true; return true;
} }
void MarqueeEffect::loop() { void MarqueeEffect::loop(uint16_t ms) {
static int loop_counter = 0;
window->clear(); window->clear();
CRGB color = CHSV(0, 255, 255); CRGB color = CHSV(0, 255, 255);
uint16_t width = _text.length() * 6; uint16_t width = _text.length() * 6;

View File

@ -38,27 +38,27 @@ void MatrixEffectColumn::restart(bool completely_random) {
} }
} }
length = random8(EFFECT_MATRIX_LENGTH_MIN, EFFECT_MATRIX_LENGTH_MAX); length = random8(settings.effects.matrix.length_min, settings.effects.matrix.length_max);
running = true; running = true;
speed = random8(EFFECT_MATRIX_SPEED_MIN, EFFECT_MATRIX_SPEED_MAX); speed = random8(settings.effects.matrix.speed_min, settings.effects.matrix.speed_max);
} }
void MatrixEffectColumn::advance() { void MatrixEffectColumn::advance(uint16_t ms) {
switch(_direction) { switch(_direction) {
case DIR_NORTH: case DIR_NORTH:
y-=speed; y-=speed * ms;
if ((y>>8) > window->height && (y>>8) + length > window->height) running=false; if ((y>>8) > window->height && (y>>8) + length > window->height) running=false;
break; break;
case DIR_EAST: case DIR_EAST:
x+=speed; x+=speed * ms;
if ((x>>8) - length > window->width) running=false; if ((x>>8) - length > window->width) running=false;
break; break;
case DIR_SOUTH: case DIR_SOUTH:
y+=speed; y+=speed * ms;
if ((y>>8) - length > window->height) running=false; if ((y>>8) - length > window->height) running=false;
break; break;
case DIR_WEST: case DIR_WEST:
x-=speed; x-=speed * ms;
if ((x>>8) > window->width && (y>>8) + length > window->width) running=false; if ((x>>8) > window->width && (y>>8) + length > window->width) running=false;
break; break;
} }
@ -79,14 +79,14 @@ void MatrixEffectColumn::draw() {
} }
} }
void MatrixEffectColumn::loop() { void MatrixEffectColumn::loop(uint16_t ms) {
if (!running) { if (!running) {
if (random8() < 20) { if (random8() < 20) {
// Start the column again. // Start the column again.
restart(false); restart(false);
} }
} else { } else {
advance(); advance(ms);
draw(); draw();
} }
} }
@ -144,13 +144,19 @@ MatrixEffect::MatrixEffect() {
} }
RandomMatrixEffect::RandomMatrixEffect() { RandomMatrixEffect::RandomMatrixEffect() {
_columns = new MatrixEffectColumn* [window->width]; // No need to initialize _columns, because that will have been done by ctor of MatrixEffect.
for (int i=0; i<window->width; i++) _columns[i] = new RandomMatrixEffectColumn(window, random8(4), true); for (int i=0; i<window->width; i++) {
delete _columns[i];
_columns[i] = new RandomMatrixEffectColumn(window, random8(4), true);
}
} }
RainbowMatrixEffect::RainbowMatrixEffect() { RainbowMatrixEffect::RainbowMatrixEffect() {
_columns = new MatrixEffectColumn* [window->width]; // No need to initialize _columns, because that will have been done by ctor of MatrixEffect.
for (int i=0; i<window->width; i++) _columns[i] = new RainbowMatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH); for (int i=0; i<window->width; i++) {
delete _columns[i];
_columns[i] = new RainbowMatrixEffectColumn(window, MatrixEffectColumn::DIR_SOUTH);
}
} }
MatrixEffect::~MatrixEffect() { MatrixEffect::~MatrixEffect() {
@ -160,7 +166,7 @@ MatrixEffect::~MatrixEffect() {
delete[] _columns; delete[] _columns;
} }
void MatrixEffect::loop() { void MatrixEffect::loop(uint16_t ms) {
window->clear(); window->clear();
for (int i=0; i<window->width; i++) _columns[i]->loop(); for (int i=0; i<window->width; i++) _columns[i]->loop(ms);
} }

View File

@ -13,7 +13,7 @@ PixelClockEffect::~PixelClockEffect() {
delete window; delete window;
} }
void PixelClockEffect::loop() { void PixelClockEffect::loop(uint16_t ms) {
uint8_t x, y; // Temporary variables for calculating positions uint8_t x, y; // Temporary variables for calculating positions
window->clear(); window->clear();
// Seconds // Seconds

View File

@ -5,7 +5,7 @@
boolean Sinematrix3Effect::can_be_shown_with_clock() { return true; }; boolean Sinematrix3Effect::can_be_shown_with_clock() { return true; };
boolean Sinematrix3Effect::clock_as_mask() { return true; }; boolean Sinematrix3Effect::clock_as_mask() { return true; };
void Sinematrix3Effect::loop() { void Sinematrix3Effect::loop(uint16_t ms) {
pangle = addmodpi( pangle, 0.0133 + (angle / 256) ); pangle = addmodpi( pangle, 0.0133 + (angle / 256) );
angle = cos(pangle) * PI; angle = cos(pangle) * PI;
sx = addmodpi( sx, 0.00673 ); sx = addmodpi( sx, 0.00673 );

View File

@ -2,47 +2,56 @@
SinesEffectSinus::SinesEffectSinus(Window* w) { SinesEffectSinus::SinesEffectSinus(Window* w) {
_window = w; _window = w;
_frequency = random8(40)+8; _frequency = random16(6<<8, 30<<8);
_amplitude = random(5)+2; _color_frequency = random16(128, 2<<8);
_x = random8(_window->width); _x = random16(1<<8, (_window->width-2)<<8);
_step = 0; accum88 diff = (_window->width<<8) - _x;
_color = CHSV(random8(), 255, 255); if (_x > diff) diff=_x;
_amplitude = random16(1<<8, diff);
_offset = random16();
} }
void SinesEffectSinus::loop() { void SinesEffectSinus::loop(uint16_t ms) {
_value += _frequency; accum88 x = beatsin88(_frequency, _x-_amplitude, _x+_amplitude, _offset);
if ((_value == 0 || _value==128) && random8(16)==0) { CRGB color = CHSV(beat88(_color_frequency, _offset)>>8, 255, 255);
int8_t sign = _value == 0 ? -1 : +1; _window->setSubPixel(x, 0, &color);
if (_x > 200) sign = -1;
else if (_x >= _window->width) sign = 1;
_amplitude = random(3)+2;
_frequency = random8(40)+8;
_color = CHSV(random8(), 255, 255);
_x = _x - sign*_amplitude;
}
uint8_t x = _x + ((sin8(_value) - 128) * _amplitude / 128);
_window->setPixel(x, 0, &_color);
} }
SinesEffect::SinesEffect() { SinesEffect::SinesEffect() {
for (int i=0; i<EFFECT_SINES_COUNT; i++) { _init();
}
void SinesEffect::_init() {
_count = settings.effects.sines.count;
_sinus = new SinesEffectSinus*[_count];
for (int i=0; i<_count; i++) {
_sinus[i] = new SinesEffectSinus(window); _sinus[i] = new SinesEffectSinus(window);
} }
} }
SinesEffect::~SinesEffect() { SinesEffect::~SinesEffect() {
for (int i=0; i<EFFECT_SINES_COUNT; i++) { delete _sinus[i]; } _delete();
}
void SinesEffect::_delete() {
for (int i=0; i<_count; i++) {
delete _sinus[i];
}
delete [] _sinus;
} }
boolean SinesEffect::can_be_shown_with_clock() { boolean SinesEffect::can_be_shown_with_clock() {
return true; return true;
} }
void SinesEffect::loop() { void SinesEffect::loop(uint16_t ms) {
if (settings.effects.sines.count != _count) {
_delete();
_init();
}
// do stuff // do stuff
if (_step++ % 4) return; // Skip 3 out of 4 steps.
window->shift_down_and_blur(); window->shift_down_and_blur();
for (int i=0; i<EFFECT_SINES_COUNT; i++) { for (int i=0; i<_count; i++) {
_sinus[i]->loop(); _sinus[i]->loop(ms);
} }
} }

View File

@ -10,9 +10,9 @@ SnakeEffect::~SnakeEffect() {
delete window; delete window;
} }
void SnakeEffect::loop() { void SnakeEffect::loop(uint16_t ms) {
if (run++ % EFFECT_SNAKE_SLOWDOWN == 0) { // Change the coordinates only on every n-th run. if (run++ % settings.effects.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(settings.effects.snake.direction_change)==0 || is_turn_needed()) turn_random();
this->coords = update_position(this->coords, this->direction); this->coords = update_position(this->coords, this->direction);
} }

View File

@ -6,7 +6,7 @@ StaticEffect::StaticEffect(CRGB col) {
color = col; color = col;
} }
void StaticEffect::loop() { void StaticEffect::loop(uint16_t ms) {
EVERY_N_SECONDS(1) { EVERY_N_SECONDS(1) {
window->clear(&color); window->clear(&color);
} }

View File

@ -4,7 +4,7 @@
boolean TwirlEffect::can_be_shown_with_clock() { return true; }; boolean TwirlEffect::can_be_shown_with_clock() { return true; };
boolean TwirlEffect::clock_as_mask() { return true; }; boolean TwirlEffect::clock_as_mask() { return true; };
void TwirlEffect::loop() { void TwirlEffect::loop(uint16_t ms) {
double center_x = _real_center_x; // - (cos8(_center_offset_angle)>>6); double center_x = _real_center_x; // - (cos8(_center_offset_angle)>>6);
double center_y = _real_center_y; // + (sin8(_center_offset_angle)>>6); double center_y = _real_center_y; // + (sin8(_center_offset_angle)>>6);
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++) {

View File

@ -1,6 +1,6 @@
#include "effects.h" #include "effects.h"
#include "config.h"
#include "my_fastled.h" #include "my_fastled.h"
#include <ErriezCRC32.h>
#include "effect_bell.h" #include "effect_bell.h"
#include "effect_sinematrix3.h" #include "effect_sinematrix3.h"
#include "effect_big_clock.h" #include "effect_big_clock.h"
@ -27,41 +27,50 @@ Effect* current_effect;
ClockEffect effect_clock; ClockEffect effect_clock;
Effect* select_effect(uint32_t code) { const EffectEntry effects[] = {
switch (code) { /* 0 */ {"sinematrix3", true, [](){ return new Sinematrix3Effect(); }},
// use e.g. https://crccalc.com/ for the conversion of name to crc. /* 1 */ {"big_clock", true, [](){ return new BigClockEffect(); }},
case 0: case 0xD682E3C8 /* sinematrix3 */ : return new Sinematrix3Effect(); /* 2 */ {"clock", false, [](){ return new ClockEffect(); }},
case 1: case 0x90A887DA /* big_clock */ : return new BigClockEffect(); /* 3 */ {"bell", false, [](){ return new BellEffect(); }},
case 2: case 0xBE7BBE92 /* clock */ : return new ClockEffect(); /* 4 */ {"off", false, [](){ return new StaticEffect(0x000000); }},
case 3: case 0x733BE087 /* bell */ : return new BellEffect(); //(new AnimationEffect("/bell.pia", 0x000000, 0, 0))->setFgColor(0xFFFF00); /* 5 */ {"single_dynamic", true, [](){ return new SingleDynamicEffect(); }},
case 4: case 0x2BBC5D43 /* off */ : return new StaticEffect(0x000000); /* 6 */ {"multi_dynamic", true, [](){ return new MultiDynamicEffect(); }},
case 5: case 0x1D84F231 /* koopa */ : return new AnimationEffect("/koopa.pia", CRGB(0x000000), 0, 0); /* 7 */ {"big_dynamic", true, [](){ return new BigDynamicEffect(); }},
case 6: case 0xAC43BCF1 /* couple_rain */ : return new AnimationEffect("/couple_rain.pia", CRGB(0x000000), -8, -16); /* 8 */ {"matrix", true, [](){ return new MatrixEffect(); }},
case 7: case 0xF1B117F7 /* single_dynamic */ : return new SingleDynamicEffect(); /* 9 */ {"random_matrix", true, [](){ return new RandomMatrixEffect(); }},
case 8: case 0xF52F2804 /* multi_dynamic */ : return new MultiDynamicEffect(); /* 10 */ {"rainbow_matrix", true, [](){ return new RainbowMatrixEffect(); }},
case 9: case 0xF83341CF /* matrix */ : return new MatrixEffect(); /* 11 */ {"cycle", false, [](){ return new CycleEffect(); }},
case 10: case 0xD2B79DD0 /* rainbow_matrix */ : return new RainbowMatrixEffect(); /* 12 */ {"twirl", true, [](){ return new TwirlEffect(); }},
case 11: case 0xE8DD3433 /* random_matrix */ : return new RandomMatrixEffect(); /* 13 */ {"confetti", true, [](){ return new ConfettiEffect(); }},
case 12: case 0xB086D193 /* cycle */ : return new CycleEffect(); /* 14 */ {"random_confetti", true, [](){ return new RandomConfettiEffect(); }},
case 13: case 0x2293EF9F /* twirl */ : return new TwirlEffect(); /* 15 */ {"snake", true, [](){ return new SnakeEffect(); }},
case 14: case 0x60ECC3E6 /* heart */ : return new AnimationEffect("/heart.pia", CRGB(0x000000), 0, 0); /* 16 */ {"firework", true, [](){ return new FireworkEffect(); }},
case 15: case 0x42090A49 /* confetti */ : return new ConfettiEffect(); /* 17 */ {"gol", true, [](){ return new GolEffect(); }},
case 16: case 0x516D6B9E /* snake */ : return new SnakeEffect(); /* 18 */ {"pixel_clock", false, [](){ return new PixelClockEffect(); }},
case 17: case 0x58DE09CF /* fire */ : return new FireEffect(); /* 19 */ {"dvd", false, [](){ return new DvdEffect(); }},
case 18: case 0x08BA9C08 /* firework */ : return new FireworkEffect(); /* 20 */ {"analog_clock", false, [](){ return new AnalogClockEffect(); }},
case 19: case 0x14B85EAC /* gol */ : return new GolEffect(); /* 21 */ {"sines", true, [](){ return new SinesEffect(); }},
case 20: case 0xFA13015D /* cake */ : return new AnimationEffect("/cake.pia", CRGB(0x000000), 0, 0); /* 22 */ {"blur2d", true, [](){ return new Blur2DEffect(); }},
case 21: case 0xA2B0D68B /* pixel_clock */ : return new PixelClockEffect(); /* 23 */ {"marquee", 0, [](){ return new MarqueeEffect(); }},
case 22: case 0x2C0E6962 /* big_dynamic */ : return new BigDynamicEffect(); /* 24 */ {"night_clock", false, [](){ return new NightClockEffect(); }}
case 23: case 0xDA6F31A5 /* random_confetti */ : return new RandomConfettiEffect(); };
case 24: case 0x8325C1DF /* dvd */ : return new DvdEffect(); const uint8_t effects_size = 25;
case 25: case 0x8CA97519 /* analog_clock */ : return new AnalogClockEffect();
case 26: case 0xADB18CC5 /* sines */ : return new SinesEffect();
case 27: case 0x0407881E /* blur2d */ : return new Blur2DEffect(); Effect* select_effect(const char* name) {
case 28: case 0x935CFA7C /* marquee */ : return new MarqueeEffect(); for(int i=0; i<effects_size; i++) {
case 29: case 0xE27D739E /* night_clock */ : return new NightClockEffect(); if (strcmp(effects[i].name, name)==0) {
default : return NULL; return effects[i].create();
}; }
}
return NULL;
}
Effect* select_effect(uint8_t id) {
if (id < effects_size) {
return effects[id].create();
}
return NULL;
} }
bool change_current_effect(String payload) { bool change_current_effect(String payload) {
@ -74,7 +83,7 @@ bool change_current_effect(String payload) {
LOGln("Effects * Cleaned effect name: %s", payload.c_str()); LOGln("Effects * Cleaned effect name: %s", payload.c_str());
} }
Effect* new_effect = select_effect( crc32String(payload.c_str()) ); Effect* new_effect = select_effect( payload.c_str() );
if (new_effect == NULL) { if (new_effect == NULL) {
LOGln("Effects * Could not find effect with name %s", payload.c_str()); LOGln("Effects * Could not find effect with name %s", payload.c_str());
return false; return false;
@ -101,21 +110,6 @@ bool change_current_effect(String payload) {
return true; return true;
} }
const char* cycle_effects[] = {
"sinematrix3",
"single_dynamic", "multi_dynamic", "big_dynamic",
"matrix", "rainbow_matrix", "random_matrix",
"confetti", "random_confetti",
"snake",
"gol",
"twirl",
"sines",
"blur2d",
"firework",
"big_clock",
"dvd"};
uint8_t cycle_effects_count = 17;
void setup_effects() { void setup_effects() {
current_effect = new CycleEffect(); current_effect = new CycleEffect();
} }

View File

@ -42,7 +42,7 @@ void http_server_setup() {
PGM_P text_plain = PSTR("text/plain"); PGM_P text_plain = PSTR("text/plain");
http_server.on("/", HTTP_GET, [&](){ http_server.on("/", HTTP_GET, [&](){
LOGln("HTTP * GET /"); LOGln("HTTP * GET /");
String message = "<html><head><title>Pitrix</title></head><body><h1>Pitrix</h1><p>Known animations:</p>"; String message = "<html><head><title>Pitrix</title></head><body><h1>Pitrix</h1><a href='/settings'>Settings</a><p>Known animations:</p>";
if (!SPIFFS.begin()) { if (!SPIFFS.begin()) {
message += "<strong>No SPIFFS file system found.</strong>"; message += "<strong>No SPIFFS file system found.</strong>";
} else { } else {
@ -57,6 +57,32 @@ void http_server_setup() {
message += "</body></html>"; message += "</body></html>";
http_server.send(200, "text/html", message); http_server.send(200, "text/html", message);
}); });
http_server.on("/settings", HTTP_GET, [&]() {
String message = "<html><head><title>Pitrix settings</title></head><body><h1>Pitrix settings</h1><a href='/'>Back to main page</a><table>";
for (int i=0; i<all_settings_size; i++) {
message += "<tr><td>";
message += all_settings[i].name;
message += "</td><td>";
message += *all_settings[i].value;
message += "</td></tr>";
}
message += "</table></body></html>";
http_server.send(200, "text/html", message);
});
http_server.on("/settings", HTTP_POST, [&]() {
if (!http_server.hasArg("key") || !http_server.hasArg("value")) {
http_server.send(400, "text/plain", "Missing argument.");
return;
}
String name = http_server.arg("key");
uint16_t value = http_server.arg("value").toInt();
if (change_setting(name.c_str(), value)) {
http_server.send(200, "text/plain", "OK");
} else {
http_server.send(400, "text/plain", "Could not change setting.");
}
});
http_server.on("/delete", HTTP_GET, [&]() { http_server.on("/delete", HTTP_GET, [&]() {
LOGln("HTTP * GET /delete"); LOGln("HTTP * GET /delete");
if (http_server.args()==0) { if (http_server.args()==0) {

View File

@ -71,10 +71,15 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
tests::run(); tests::run();
return; return;
} }
long value = payload.toInt(); long value = payload.toInt();
LOGln("MQTT * Payload as number: %d", value); LOGln("MQTT * Payload as number: %d", value);
if (topic.compareTo("brightness")==0) { if (topic.startsWith("settings.")) {
topic.remove(0, 9);
change_setting(topic.c_str(), value);
return;
} else if (topic.compareTo("brightness")==0) {
if (value > 0 && value <= 255) { if (value > 0 && value <= 255) {
LOGln("MQTT * Changing brightness..."); LOGln("MQTT * Changing brightness...");
FastLED.setBrightness(value); FastLED.setBrightness(value);

View File

@ -16,6 +16,7 @@ int loop_timeouts = 0;
long loop_started_at = 0; long loop_started_at = 0;
uint8_t baseHue = 0; // defined as extern in prototypes.h uint8_t baseHue = 0; // defined as extern in prototypes.h
char hostname[30]; // defined as extern in prototypes.h char hostname[30]; // defined as extern in prototypes.h
unsigned long _last_effect_loop_finished_at = 0;
#ifdef RECORDER_ENABLE #ifdef RECORDER_ENABLE
Recorder* recorder; Recorder* recorder;
#endif #endif
@ -87,9 +88,20 @@ void loop() {
} }
EVERY_N_MILLISECONDS(1000 / FPS) { EVERY_N_MILLISECONDS(1000 / FPS) {
//LOGln("Core * loop running"); // Calculate the delay since the last time loop() was called.
current_effect->loop(); // This way, the effect can handle varying frame rates.
//LOGln("Core * loop ran"); uint16_t last_loop_ago;
unsigned long now = millis();
if (now > _last_effect_loop_finished_at && _last_effect_loop_finished_at) {
last_loop_ago = now - _last_effect_loop_finished_at;
} else {
last_loop_ago = 0;
}
current_effect->loop(last_loop_ago);
// Save the time for the next run.
_last_effect_loop_finished_at = now;
if (current_effect->can_be_shown_with_clock()) { if (current_effect->can_be_shown_with_clock()) {
effect_clock.loop_with_invert(current_effect->clock_as_mask()); effect_clock.loop_with_invert(current_effect->clock_as_mask());

View File

@ -36,7 +36,7 @@ Recorder::Recorder() {
LOGln("Recorder * Sending data to port %d", _client_port); LOGln("Recorder * Sending data to port %d", _client_port);
} }
} else if (*(char*)data == 'E') { } else if (*(char*)data == 'E') {
String effect = String(((char*)(data+1))); String effect = String((char*)data+1);
LOGln("Recorder * Setting effect %s", effect.c_str()); LOGln("Recorder * Setting effect %s", effect.c_str());
Window::getFullWindow()->clear(); Window::getFullWindow()->clear();
change_current_effect(effect); change_current_effect(effect);

74
src/settings.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "settings.h"
#include "config.h"
Settings settings;
Setting all_settings[] = {
{"fps", &settings.fps, TYPE_UINT8},
{"effects.confetti.pixels_per_loop", &settings.effects.confetti.pixels_per_loop, TYPE_UINT8},
{"effects.cycle.random", &settings.effects.cycle.random, TYPE_BOOL},
{"effects.cycle.time", &settings.effects.cycle.time, TYPE_UINT16},
{"effects.dvd.width", &settings.effects.dvd.width, TYPE_UINT8},
{"effects.dvd.height", &settings.effects.dvd.height, TYPE_UINT8},
{"effects.dvd.speed", &settings.effects.dvd.speed, TYPE_UINT8},
{"effects.dynamic.single_loop_time", &settings.effects.dynamic.single_loop_time, TYPE_UINT16},
{"effects.dynamic.multi_loop_time", &settings.effects.dynamic.multi_loop_time, TYPE_UINT16},
{"effects.dynamic.big_loop_time", &settings.effects.dynamic.big_loop_time, TYPE_UINT16},
{"effects.dynamic.big_size", &settings.effects.dynamic.big_size, TYPE_UINT8},
{"effects.fire.cooldown", &settings.effects.fire.cooldown, TYPE_UINT8},
{"effects.fire.spark_chance", &settings.effects.fire.spark_chance, TYPE_UINT8},
{"effects.firework.drag", &settings.effects.firework.drag, TYPE_UINT8},
{"effects.firework.bounce", &settings.effects.firework.bounce, TYPE_UINT8},
{"effects.firework.gravity", &settings.effects.firework.gravity, TYPE_UINT8},
{"effects.firework.sparks", &settings.effects.firework.sparks, TYPE_UINT8},
{"effects.gol.start_percentage", &settings.effects.gol.start_percentage, TYPE_UINT8},
{"effects.gol.blend_speed", &settings.effects.gol.blend_speed, TYPE_UINT8},
{"effects.gol.restart_after_steps", &settings.effects.gol.restart_after_steps, TYPE_UINT8},
{"effects.matrix.length_min", &settings.effects.matrix.length_min, TYPE_UINT8},
{"effects.matrix.length_max", &settings.effects.matrix.length_max, TYPE_UINT8},
{"effects.matrix.speed_min", &settings.effects.matrix.speed_min, TYPE_UINT8},
{"effects.matrix.speed_max", &settings.effects.matrix.speed_max, TYPE_UINT8},
{"effects.sines.count", &settings.effects.sines.count, TYPE_UINT8},
{"effects.snake.direction_change", &settings.effects.snake.direction_change, TYPE_UINT8}
};
const uint8_t all_settings_size = 25;
bool change_setting(const char* key, uint16_t new_value) {
LOGln("Settings * Trying to set setting %s to new value %d...", key, new_value);
Setting* s = NULL;
for (uint8_t i=0; i<all_settings_size; i++) {
if (strcmp(key, all_settings[i].name)==0) {
s = &all_settings[i];
break;
}
}
if (s==NULL) {
LOGln("Settings * No setting matching the name %s found.", key);
return false;
}
// Check data size
if (s->type == TYPE_BOOL && new_value > 1) {
LOGln("Settings * Data type of %s is boolean, but new value is > 1.", key);
return false;
}
if (s->type == TYPE_UINT8 && new_value>0xFF) {
LOGln("Settings * Data type of %s is uint8_t, but new value is > 0xFF.", key);
return false;
}
*(s->value) = new_value;
LOGln("Settings * Success. New value for %s is %d.", key, new_value);
return true;
}

View File

@ -11,15 +11,18 @@ namespace tests {
int i=0; int i=0;
Effect* effect; Effect* effect;
int32_t diffs[3] = {0, 0, 0}; int32_t diffs[3] = {0, 0, 0};
String effect_name;
while (1) { while (1) {
for (int j=0; j<3; j++) { for (int j=0; j<3; j++) {
int free_at_start = ESP.getFreeHeap(); int free_at_start = ESP.getFreeHeap();
effect = select_effect(i); effect = select_effect(i);
effect->loop(1);
if (effect == NULL) return; if (effect == NULL) return;
effect_name = effect->get_name();
delete effect; delete effect;
diffs[i] = ESP.getFreeHeap() - free_at_start; diffs[i] = ESP.getFreeHeap() - free_at_start;
} }
LOGln("Tests * Memory leakage of effect #%d: %d, %d, %d", i, diffs[0], diffs[1], diffs[2]); LOGln("Tests * Memory leakage of effect %s: %d, %d, %d", effect_name.c_str(), diffs[0], diffs[1], diffs[2]);
i++; i++;
} }
} }