Compare commits

...

6 Commits

25 changed files with 151 additions and 41 deletions

View File

@ -6,7 +6,7 @@
class Effect {
protected:
Window* window = Window::getFullWindow(); // Use a full screen window per default.
Window* window = &Window::window_full; // Use a full screen window per default.
public:
virtual ~Effect() {};
virtual void loop(uint16_t ms) = 0;

View File

@ -14,10 +14,13 @@ private:
void _circle_point(int x0, int y0, int x1, int y1, CRGB* color);
void _subpixel_render(uint8_t x, uint8_t y, CRGB* color, SubpixelRenderingMode m);
public:
static Window window_full;
static Window window_with_clock;
static Window window_clock;
const uint8_t x, y;
const uint8_t width, height;
uint16_t count;
static Window* getFullWindow();
Window(): Window(0, 0, LED_WIDTH, LED_HEIGHT) {};
Window(uint8_t x, uint8_t y) : Window(x, y, LED_WIDTH-x, LED_HEIGHT-y) {};

View File

@ -16,7 +16,7 @@ public:
class Blur2DEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* window = &Window::window_with_clock;
uint8_t _count;
Blur2DBlob* _blobs;
public:

View File

@ -7,7 +7,7 @@
class ClockEffect : public Effect {
protected:
Window* window = new Window(0, LED_HEIGHT - 6, LED_WIDTH, 6);
Window* window = &Window::window_clock;
public:
virtual ~ClockEffect();

11
include/effect_diamond.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "Effect.h"
class DiamondEffect : public Effect {
private:
Window* window = &Window::window_with_clock;
public:
void loop(uint16_t ms) override;
bool can_be_shown_with_clock() override;
String get_name() override { return "diamond"; }
};

View File

@ -3,7 +3,7 @@
class DvdEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* window = &Window::window_with_clock;
saccum78 _x = 0;
saccum78 _y = 0;
int8_t _x_dir = 1;

View File

@ -26,7 +26,7 @@ public:
class BigDynamicEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* window = &Window::window_with_clock;
public:
void loop(uint16_t ms);
~BigDynamicEffect();

View File

@ -34,7 +34,7 @@ public:
class FireworkEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* window = &Window::window_with_clock;
bool _skyburst = 0;
accum88 _burst_x;

View File

@ -6,7 +6,7 @@
class MarqueeEffect : public Effect {
private:
Window* window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* window = &Window::window_with_clock;
String _text = String("No text set +++ ");
saccum78 _position = (window->width<<8);
public:

View File

@ -14,6 +14,7 @@ class SnakeEffect : public Effect {
private:
Coords _pos;
Coords _apple;
Coords _tail;
int8_t _dir = SNAKE_DIR_NORTH;
uint8_t* _map;
uint16_t _pixels;
@ -32,11 +33,16 @@ private:
const uint32_t _weights[36] = {0xbd25943f, 0xbf279d81, 0x3e25d128, 0x3ec62438, 0x3f0e719c, 0x3eefbea9, 0x3e947ed4, 0xbe5323c1, 0xbf2d4796, 0xbf3f0a75, 0x3f0e45d9, 0xbf0253ca, 0xbedca2f2, 0xbd79073c, 0x3ede80ec, 0xbd4b97b6, 0x3f69a6be, 0xbe4b8bf2, 0x3eccf87d, 0xbf301113, 0xbf62b6e8, 0xbf71daf6, 0xbf204130, 0xbf222609, 0x3e26c03c, 0xbf497837, 0xbee4d175, 0x3ec601de, 0x3e4954eb, 0x3eef2619, 0xbe849370, 0xbf18fb2b, 0x3f25bbd1, 0xbf3b4e44, 0x3f484d59, 0x3edd6a8a};
// Round 193, 164.8 points, length 36, 6% stopped, 94% died
//const uint32_t _weights[36] = {0x3e872ffb, 0xbea57262, 0xbee269bf, 0x3ed790a3, 0xbf54014f, 0x3ecde0a6, 0xbf240a93, 0xbe9e4782, 0x3f205106, 0xbf4465c2, 0xbf79579a, 0xbf07f122, 0x3ed0e1bc, 0xbf7a5a09, 0xbf0fc70b, 0xbf6d1971, 0xbe0f5585, 0xbec94b12, 0x3f51f7a9, 0x3eaac42b, 0xbe6aafa6, 0x3d3e3ce3, 0xbf7c4232, 0xbe634103, 0x3f800000, 0x3eff886c, 0x3deae1e8, 0x3eea6988, 0xbf800000, 0xbf426a20, 0x3e3a0a45, 0xbe848803, 0x3e84e8c9, 0x3ef9fabc, 0xbe7733e6, 0xbecda633};
// Round 13650, 244.8 points, length 42, 34% stopped, 66% died
//const uint32_t _weights[36] = {0xbeb99de3, 0x3e6ca488, 0xbe3e9dad, 0xbed38d4e, 0x3f279fc1, 0xbd367111, 0xbf473843, 0xbf800000, 0x3f614edc, 0xbecc734f, 0xbe59b29d, 0x3d479078, 0x3efa7ca6, 0xbedc6ce6, 0x3f4626a1, 0x3e9d8c2c, 0x3f29e25c, 0x3ebde05d, 0x3e8f3e29, 0xbe8ad92c, 0xbe148f2d, 0x3d4a3ca7, 0xbf800000, 0x3d9cd634, 0x3f29802e, 0xbf2cc57e, 0xbcbfafff, 0x3e280b8a, 0x3f5ff9a3, 0xbf4e29c9, 0x3e8936d2, 0xbf49dda9, 0xbe9bf4c7, 0x3e203ea8, 0xbd4edf4d, 0xbf4e3c05};
const uint8_t _net_layout[3] = {6, 4, 3};
const uint8_t _net_layers = 3;
const uint8_t _net_total_size = 36;
uint8_t _head_rounds = 0;
uint8_t _tail_rounds = 0;
uint16_t _xy2i(uint8_t x, uint8_t y);
uint16_t _xy2i(Coords c);
@ -49,6 +55,7 @@ private:
void _place_apple();
void _init();
void _decide();
uint8_t _coords_to_field_id(Coords);
int8_t _manual_decision();
void _move();
void _draw();

View File

@ -3,7 +3,7 @@
class TvStaticEffect : public Effect {
private:
Window* _window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window* _window = &Window::window_with_clock;
public:
~TvStaticEffect();
void loop(uint16_t ms) override;

View File

@ -1,10 +1,9 @@
#include "Window.h"
#include "functions.h"
Window* Window::getFullWindow() {
static Window win;
return &win;
}
Window Window::window_full = Window();
Window Window::window_with_clock = Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
Window Window::window_clock = Window(0, LED_HEIGHT-6, LED_WIDTH, 6);
void Window::setPixel(uint8_t x, uint8_t y, CRGB* color) {
if (x>=this->width || y>=this->height) return;

View File

@ -45,5 +45,4 @@ void Blur2DEffect::_delete() {
Blur2DEffect::~Blur2DEffect() {
_delete();
delete window;
}

View File

@ -52,5 +52,4 @@ void ClockEffect::loop(boolean invert, CRGB fg_color, CRGB bg_color, uint8_t yPo
}
ClockEffect::~ClockEffect() {
delete window;
}

14
src/effect_diamond.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "effect_diamond.h"
#include "my_fastled.h"
void DiamondEffect::loop(uint16_t ms) {
for (int x=0; x<window->width; x++) {
for (int y=0; y<window->height; y++) {
uint8_t distance = abs(window->height/2 - y) + abs(window->width/2 - x);
CRGB col = CHSV(distance*8 - (millis()>>5)%255, 255, 255);
window->setPixel(x, y, &col);
}
}
}
bool DiamondEffect::can_be_shown_with_clock() { return true; }

View File

@ -47,5 +47,4 @@ DvdEffect::DvdEffect() {
}
DvdEffect::~DvdEffect() {
delete window;
}

View File

@ -36,7 +36,6 @@ void MultiDynamicEffect::loop(uint16_t ms) {
}
BigDynamicEffect::~BigDynamicEffect() {
delete window;
}
void BigDynamicEffect::loop(uint16_t ms) {

View File

@ -176,7 +176,6 @@ FireworkEffect::FireworkEffect() {
}
FireworkEffect::~FireworkEffect() {
delete window;
for (int i=0; i<settings.effects.firework.sparks; i++) {
delete _sparks[i];
}

View File

@ -2,7 +2,7 @@
#include "my_fastled.h"
GolEffect::GolEffect() {
this->window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
this->window = &Window::window_with_clock;
_data = new uint8_t[this->window->count];
_old = new uint8_t[this->window->count];
@ -26,7 +26,6 @@ void GolEffect::_initialize() {
GolEffect::~GolEffect() {
delete[] _data;
delete[] _old;
delete window;
}
void GolEffect::loop(uint16_t ms) {

View File

@ -4,12 +4,11 @@
#include "prototypes.h"
LightspeedEffect::LightspeedEffect() {
window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
window = &Window::window_with_clock;
_init();
}
LightspeedEffect::~LightspeedEffect() {
delete window;
_delete();
}

View File

@ -2,7 +2,7 @@
#include "ntp.h"
PixelClockEffect::PixelClockEffect() {
window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
window = &Window::window_with_clock;
_color_seconds = new CRGB(0x00FF00);
_color_minutes = new CRGB(0xFFFF00);
}
@ -10,7 +10,6 @@ PixelClockEffect::PixelClockEffect() {
PixelClockEffect::~PixelClockEffect() {
delete _color_seconds;
delete _color_minutes;
delete window;
}
void PixelClockEffect::loop(uint16_t ms) {

View File

@ -2,7 +2,7 @@
#include "functions.h"
SnakeEffect::SnakeEffect() {
window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
window = &Window::window_with_clock;
_pixels = window->width * window->height;
_map = new uint8_t[_pixels];
_init();
@ -25,15 +25,10 @@ void SnakeEffect::_init() {
}
SnakeEffect::~SnakeEffect() {
delete window;
delete _map;
}
void SnakeEffect::_place_apple() {
if (SNAKE_DEBUG) {
_apple = {3, 3};
return;
}
if (_length < _pixels) {
uint8_t start = random8(_pixels);
for (int i=0; i<_pixels; i++) {
@ -60,7 +55,7 @@ void SnakeEffect::_decide() {
inputs[3] = a_l;
inputs[4] = a_s;
inputs[5] = a_r;
if (SNAKE_DEBUG) LOGln("SnakeEffect * Position: %d, %d - Inputs: %3.1f %3.1f %3.1f %3.1f %3.1f %3.1f", _pos.x, _pos.y, inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5]);
if (SNAKE_DEBUG) DBG("SnakeEffect * Position: %d, %d - Inputs: %3.1f %3.1f %3.1f %3.1f %3.1f %3.1f", _pos.x, _pos.y, inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5]);
float* outputs = NULL;
uint8_t i=0;
for (uint8_t layer=1; layer<_net_layers; layer++) {
@ -92,7 +87,7 @@ void SnakeEffect::_decide() {
decision = decision - 1;
delete outputs;
if (SNAKE_DEBUG) LOGln("SnakeEffect * Decision: %d", decision);
if (SNAKE_DEBUG) DBG("SnakeEffect * Decision: %d", decision);
_dir += decision;
if (_dir < 0) _dir += 4;
@ -124,6 +119,65 @@ int8_t SnakeEffect::_manual_decision() {
return 0;
}*/
/* This uses a predefined meandering path through the complete field.
The snake always tries to reach the field matching following criteria:
(1) Being free.
(2) Having a number smaller than the field the apple is on. (Watch out for "overflows".)
*/
int8_t SnakeEffect::_manual_decision() {
uint8_t head_index = _coords_to_field_id(_pos);
uint8_t apple_index = _coords_to_field_id(_apple);
uint8_t tail_index = _coords_to_field_id(_tail);
if (SNAKE_DEBUG) DBG("SnakeEffect * Decision. head: %d, apple: %d, tail: %d", head_index, apple_index, tail_index);
uint16_t best_distance = 0xFFFF;
int8_t decision = 0;
for (int i=-1; i<=1; i++) { // Test all thre possible directions (left, ahead, right)
Coords new_pos = _new_pos(_dir + i);
uint8_t new_index = _coords_to_field_id(new_pos);
int16_t distance;
if (apple_index >= new_index) {
distance = apple_index - new_index;
} else {
distance = (window->width * window->height) - apple_index + new_index;
}
if (SNAKE_DEBUG) DBG("SnakeEffect * Decision: %d would have distance %d", i, distance);
if (distance < best_distance && _is_free(_dir + i)) {
best_distance = distance;
decision = i;
}
}
if (SNAKE_DEBUG) DBG("SnakeEffect * Decision taken: %d with distance %d", decision, best_distance);
/* apple_index > new_index && head_index > apple_index
apple_index > new_index && head_index < apple_index && new_index > head_index
uint16_t head_index = (_head_rounds<<8) | _coords_to_field_id(_pos);
uint16_t apple_index = (_head_rounds<<8) | _coords_to_field_id(_apple);
uint16_t tail_index = (_tail_rounds<<8) | _coords_to_field_id(_tail);
if (apple_index < head_index) apple_index += 0x100;
uint8_t best_dist = 0xFF;
int8_t decision = 0;
for (int i=-1; i<=1; i++) {
Coords new_pos = _new_pos(_dir + i);
uint16_t theoretical_index = (_head_rounds<<8) | _coords_to_field_id(new_pos);
if (theoretical_index < head_index) theoretical_index += 0x100;
int16_t dist = apple_index - theoretical_index;
if (dist < 0) dist += window->height * window->width;
if (dist < best_dist && _is_free(_dir + i) && theoretical_index<tail_index && theoretical_index<tail_index) {
decision = i;
best_dist = dist;
}
}*/
_dir = (_dir + decision) % 4;
return decision;
}
bool SnakeEffect::_is_free(uint8_t dir) {
return _free_spaces(dir)!=0;
}
@ -150,6 +204,17 @@ uint8_t SnakeEffect::_free_spaces(uint8_t dir) {
}
}
uint8_t SnakeEffect::_coords_to_field_id(Coords c) {
if (c.y==0) return window->width - c.x;
if (c.x % 2 == 0) {
// even columns
return window->width + c.x*(window->height - 1) + c.y - 1;
} else {
// odd columns
return window->width + (c.x+1)*(window->height-1) - c.y;
}
}
uint8_t SnakeEffect::_to_apple(uint8_t dir) {
uint8_t d = dir % 4;
int8_t d_x = _apple.x - _pos.x;
@ -202,12 +267,12 @@ void SnakeEffect::_move() {
}
unsigned long now = millis();
if (_last_move_at < now && now - _last_move_at < 0) {
if (_last_move_at < now && now - _last_move_at < 50) {
return;
}
_round++;
_last_move_at = now;
_decide();
_manual_decision();
if (_dying==0 && !_is_free(_dir)) {
_dying = 150;
@ -215,6 +280,10 @@ void SnakeEffect::_move() {
}
_pos = _new_pos(_dir);
uint8_t index_head = _coords_to_field_id(_pos);
uint8_t index_tail = _coords_to_field_id(_tail);
if (SNAKE_DEBUG) LOGln("SnakeEffect * new_pos: %d, %d", _pos.x, _pos.y);
if (SNAKE_DEBUG) LOGln("SnakeEffect * apple: %d, %d", _apple.x, _apple.y);
if (_pos.x==_apple.x && _pos.y==_apple.y) {
@ -222,13 +291,26 @@ void SnakeEffect::_move() {
_length++;
}
for (int i=0; i<_pixels; i++) {
if (_map[i]>0 && _map[i]<_length-1) _map[i]++;
if (_map[i]>0 && _map[i]<_length-1) {
_map[i]++;
if (_map[i]==_length-1) {
_tail = _i2xy(i);
}
}
else _map[i]=0;
}
_map[_xy2i(_pos)] = 1;
if (_pos.x==_apple.x && _pos.y==_apple.y) {
_place_apple();
}
if (index_head > _coords_to_field_id(_pos)) _head_rounds++;
if (index_tail > _coords_to_field_id(_tail)) _tail_rounds++;
if (_head_rounds > 0 && _head_rounds > 0) {
uint8_t min = (_head_rounds < _tail_rounds) ? _tail_rounds : _head_rounds;
_head_rounds -= min;
_tail_rounds -= min;
}
}
void SnakeEffect::_draw() {

View File

@ -24,6 +24,7 @@
#include "effect_blur2d.h"
#include "effect_tv_static.h"
#include "effect_lightspeed.h"
#include "effect_diamond.h"
Effect* current_effect;
@ -51,7 +52,7 @@ EffectEntry effects[] = {
/* 16 */ {"firework", true, [](){ return new FireworkEffect(); }},
/* 17 */ {"gol", true, [](){ return new GolEffect(); }},
/* 18 */ {"pixel_clock", 0, [](){ return new PixelClockEffect(); }},
/* 19 */ {"dvd", 0, [](){ return new DvdEffect(); }},
/* 19 */ {"dvd", true, [](){ return new DvdEffect(); }},
/* 20 */ {"analog_clock", 0, [](){ return new AnalogClockEffect(); }},
/* 21 */ {"sines", true, [](){ return new SinesEffect(); }},
/* 22 */ {"blur2d", true, [](){ return new Blur2DEffect(); }},
@ -64,8 +65,9 @@ EffectEntry effects[] = {
/* 29 */ {"koopa", 0, [](){ return new AnimationEffect("/koopa.pia"); }},
/* 30 */ {"cake", 0, [](){ return new AnimationEffect("/cake.pia"); }},
/* 31 */ {"kid", 0, [](){ return new AnimationEffect("/kid.pia"); }},
/* 32 */ {"diamond", true, [](){ return new DiamondEffect(); }},
};
const uint8_t effects_size = 32;
const uint8_t effects_size = 33;
Effect* select_effect(const char* name) {

View File

@ -31,7 +31,7 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
if (topic.startsWith(MQTT_TOPIC_WEATHER)) {
// Weather stuff
topic.remove(0, strlen(MQTT_TOPIC_WEATHER));
LOGln("MQTT * Weather stuff.");
DBG("MQTT * Weather stuff.");
if (topic.startsWith("icons/")) {
topic.remove(0, 6);
uint8_t id = topic.toInt();
@ -39,7 +39,7 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
uint8_t val = payload.toInt();
if (val==0) return;
weather_icon_ids[id] = val;
LOGln("Set weather_icon_ids[%d] to value %d", id, val);
DBG("Set weather_icon_ids[%d] to value %d", id, val);
} else if (topic.startsWith("temperatures/")) {
topic.remove(0, 13);
uint8_t id = topic.toInt();
@ -47,7 +47,7 @@ void mqtt_callback(char* original_topic, byte* pl, unsigned int length) {
uint8_t val = payload.toInt();
if (val==0) return;
weather_temperatures[id] = val;
LOGln("Set weather_temperatures[%d] to value %d", id, val);
DBG("Set weather_temperatures[%d] to value %d", id, val);
}
return;
} else if (topic.equals(MQTT_TOPIC_TIMER)) {

View File

@ -73,7 +73,7 @@ void loop() {
EVERY_N_SECONDS(1) {
Serial.print("Core * Waiting for OTA... "); Serial.println(starting_up);
starting_up--;
Window* w = Window::getFullWindow();
Window* w = &Window::window_full;
CRGB color(0xFF0000);
w->clear();
for (int i=0; i<starting_up; i++) {