Port of https://github.com/Vasil-Pahomov/ArWs2812 from Arduino to ESP8266 Implementing garland of WS2812 Co-authored-by: Dmitry Blinov <dblinov@blackberry.com>mcspr-patch-1
@ -1,3 +1,5 @@ | |||
*.gz.h -diff -merge | |||
*.gz.h linguist-generated=true | |||
*.ini text eol=lf | |||
*.h text eol=lf | |||
*.cpp text eol=lf |
@ -0,0 +1,484 @@ | |||
/* | |||
GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
Tested on 60 led strip. | |||
!!! For more leds can cause WDT rebot. Need to be carefully tested for more than 60 leds !!! | |||
The most time consuming operation is actually showing leds by Adafruit Neopixel. It take about 1870 mcs. | |||
More long strip can take more time to show. | |||
Currently animation calculation, brightness calculation/transition and showing makes in one loop cycle. | |||
Debug output shows timings. Overal timing should be not more that 3000 ms. | |||
For longer strips have sense to divide entire strip (pixels) on parts about 100 pixels and show one part | |||
at a cycle. | |||
*/ | |||
#include "garland.h" | |||
#if GARLAND_SUPPORT | |||
#include <Adafruit_NeoPixel.h> | |||
#include <vector> | |||
#include "garland/color.h" | |||
#include "garland/palette.h" | |||
#include "garland/scene.h" | |||
#include "ws.h" | |||
const char* NAME_GARLAND_ENABLED = "garlandEnabled"; | |||
const char* NAME_GARLAND_BRIGHTNESS = "garlandBrightness"; | |||
const char* NAME_GARLAND_SPEED = "garlandSpeed"; | |||
const char* NAME_GARLAND_SWITCH = "garland_switch"; | |||
const char* NAME_GARLAND_SET_BRIGHTNESS = "garland_set_brightness"; | |||
const char* NAME_GARLAND_SET_SPEED = "garland_set_speed"; | |||
const char* NAME_GARLAND_SET_DEFAULT = "garland_set_default"; | |||
#define EFFECT_UPDATE_INTERVAL_MIN 5000 // 5 sec | |||
#define EFFECT_UPDATE_INTERVAL_MAX 10000 // 10 sec | |||
bool _garland_enabled = true; | |||
unsigned long _last_update = 0; | |||
unsigned long _interval_effect_update; | |||
// Palette should | |||
Palette pals[] = { | |||
// palettes below are taken from http://www.color-hex.com/color-palettes/ (and modified) | |||
// RGB: Red,Green,Blue sequence | |||
Palette("RGB", {0xFF0000, 0x00FF00, 0x0000FF}), | |||
// Rainbow: Rainbow colors | |||
Palette("Rainbow", {0xFF0000, 0xAB5500, 0xABAB00, 0x00FF00, 0x00AB55, 0x0000FF, 0x5500AB, 0xAB0055}), | |||
// RainbowStripe: Rainbow colors with alternating stripes of black | |||
Palette("Stripe", {0xFF0000, 0x000000, 0xAB5500, 0x000000, 0xABAB00, 0x000000, 0x00FF00, 0x000000, | |||
0x00AB55, 0x000000, 0x0000FF, 0x000000, 0x5500AB, 0x000000, 0xAB0055, 0x000000}), | |||
// Party: Blue purple ping red orange yellow (and back). Basically, everything but the greens. | |||
// This palette is good for lighting at a club or party. | |||
Palette("Party", {0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, 0xE81700, 0xB84700, 0xAB7700, 0xABAB00, | |||
0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9}), | |||
// Heat: Approximate "black body radiation" palette, akin to the FastLED 'HeatColor' function. | |||
// Recommend that you use values 0-240 rather than the usual 0-255, as the last 15 colors will be | |||
// 'wrapping around' from the hot end to the cold end, which looks wrong. | |||
Palette("Heat", {0x700070, 0xFF0000, 0xFFFF00, 0xFFFFCC}), | |||
// Fire: | |||
Palette("Fire", {0x000000, 0x220000, 0x880000, 0xFF0000, 0xFF6600, 0xFFCC00}), | |||
// Blue: | |||
Palette("Blue", {0xffffff, 0x0000ff, 0x00ffff}), | |||
// Sun: Slice Of The Sun | |||
Palette("Sun", {0xfff95b, 0xffe048, 0xffc635, 0xffad22, 0xff930f}), | |||
// Lime: yellow green mix | |||
Palette("Lime", {0x51f000, 0x6fff00, 0x96ff00, 0xc9ff00, 0xf0ff00}), | |||
// Pastel: Pastel Fruity Mixture | |||
Palette("Pastel", {0x75aa68, 0x5960ae, 0xe4be6c, 0xca5959, 0x8366ac}), | |||
// Green: Vibrant greens | |||
Palette("Green", {0x89ff01, 0x42c501, 0x349404, 0x0f6902, 0x004208})}; | |||
constexpr size_t palsSize() { return sizeof(pals)/sizeof(pals[0]); } | |||
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(GARLAND_LEDS, GARLAND_D_PIN, NEO_GRB + NEO_KHZ800); | |||
Scene scene(&pixels); | |||
Anim* anims[] = {new AnimStart(), new AnimPixieDust(), new AnimSparkr(), new AnimRun(), new AnimStars(), | |||
new AnimSpread(), new AnimRandCyc(), new AnimFly(), new AnimComets(), new AnimAssemble()}; | |||
constexpr size_t animsSize() { return sizeof(anims)/sizeof(anims[0]); } | |||
//------------------------------------------------------------------------------ | |||
void garlandDisable() { | |||
pixels.clear(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void garlandEnabled(bool enabled) { | |||
_garland_enabled = enabled; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool garlandEnabled() { | |||
return _garland_enabled; | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Setup | |||
//------------------------------------------------------------------------------ | |||
void _garlandConfigure() { | |||
_garland_enabled = getSetting(NAME_GARLAND_ENABLED, true); | |||
DEBUG_MSG_P(PSTR("[GARLAND] _garland_enabled = %d\n"), _garland_enabled); | |||
byte brightness = getSetting(NAME_GARLAND_BRIGHTNESS, 255); | |||
scene.setBrightness(brightness); | |||
DEBUG_MSG_P(PSTR("[GARLAND] brightness = %d\n"), brightness); | |||
float speed = getSetting(NAME_GARLAND_SPEED, 50); | |||
scene.setSpeed(speed); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void _garlandReload() { | |||
_garlandConfigure(); | |||
} | |||
#if WEB_SUPPORT | |||
//------------------------------------------------------------------------------ | |||
void _garlandWebSocketOnConnected(JsonObject& root) { | |||
root[NAME_GARLAND_ENABLED] = garlandEnabled(); | |||
root[NAME_GARLAND_BRIGHTNESS] = scene.getBrightness(); | |||
root[NAME_GARLAND_SPEED] = scene.getSpeed(); | |||
root["garlandVisible"] = 1; | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool _garlandWebSocketOnKeyCheck(const char* key, JsonVariant& value) { | |||
if (strncmp(key, NAME_GARLAND_ENABLED, strlen(NAME_GARLAND_ENABLED)) == 0) return true; | |||
if (strncmp(key, NAME_GARLAND_BRIGHTNESS, strlen(NAME_GARLAND_BRIGHTNESS)) == 0) return true; | |||
if (strncmp(key, NAME_GARLAND_SPEED, strlen(NAME_GARLAND_SPEED)) == 0) return true; | |||
return false; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void _garlandWebSocketOnAction(uint32_t client_id, const char* action, JsonObject& data) { | |||
if (strcmp(action, NAME_GARLAND_SWITCH) == 0) { | |||
if (data.containsKey("status") && data.is<int>("status")) { | |||
_garland_enabled = (1 == data["status"].as<int>()); | |||
setSetting(NAME_GARLAND_ENABLED, _garland_enabled); | |||
if (!_garland_enabled) { | |||
schedule_function([](){ | |||
pixels.clear(); | |||
pixels.show(); | |||
}); | |||
} | |||
} | |||
} | |||
if (strcmp(action, NAME_GARLAND_SET_BRIGHTNESS) == 0) { | |||
if (data.containsKey("brightness")) { | |||
byte new_brightness = data.get<byte>("brightness"); | |||
DEBUG_MSG_P(PSTR("[GARLAND] new brightness = %d\n"), new_brightness); | |||
setSetting(NAME_GARLAND_BRIGHTNESS, new_brightness); | |||
scene.setBrightness(new_brightness); | |||
} | |||
} | |||
if (strcmp(action, NAME_GARLAND_SET_SPEED) == 0) { | |||
if (data.containsKey("speed")) { | |||
byte new_speed = data.get<byte>("speed"); | |||
DEBUG_MSG_P(PSTR("[GARLAND] new speed = %d\n"), new_speed); | |||
setSetting(NAME_GARLAND_SPEED, new_speed); | |||
scene.setSpeed(new_speed); | |||
} | |||
} | |||
if (strcmp(action, NAME_GARLAND_SET_DEFAULT) == 0) { | |||
scene.setDefault(); | |||
byte brightness = scene.getBrightness(); | |||
setSetting(NAME_GARLAND_BRIGHTNESS, brightness); | |||
byte speed = scene.getSpeed(); | |||
setSetting(NAME_GARLAND_SPEED, speed); | |||
char buffer[128]; | |||
snprintf_P(buffer, sizeof(buffer), PSTR("{\"garlandBrightness\": %d, \"garlandSpeed\": %d}"), brightness, speed); | |||
wsSend(buffer); | |||
} | |||
} | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
// Loop | |||
//------------------------------------------------------------------------------ | |||
void garlandLoop(void) { | |||
if (!garlandEnabled()) | |||
return; | |||
scene.run(); | |||
unsigned long animation_time = millis() - _last_update; | |||
if (animation_time > _interval_effect_update && scene.finishedAnimCycle()) { | |||
_last_update = millis(); | |||
_interval_effect_update = secureRandom(EFFECT_UPDATE_INTERVAL_MIN, EFFECT_UPDATE_INTERVAL_MAX); | |||
static int animInd = 0; | |||
int prevAnimInd = animInd; | |||
while (prevAnimInd == animInd) animInd = secureRandom(1, animsSize()); | |||
static int paletteInd = 0; | |||
int prevPalInd = paletteInd; | |||
while (prevPalInd == paletteInd) paletteInd = secureRandom(palsSize()); | |||
int numShows = scene.getNumShows(); | |||
int frameRate = animation_time > 0 ? numShows * 1000 / animation_time : 0; | |||
DEBUG_MSG_P(PSTR("[GARLAND] Anim: %-10s Pal: %-8s timings: calc: %4d pixl: %3d show: %4d frate: %d\n"), | |||
anims[prevAnimInd]->name(), pals[prevPalInd].name(), | |||
scene.getAvgCalcTime(), scene.getAvgPixlTime(), scene.getAvgShowTime(), frameRate); | |||
DEBUG_MSG_P(PSTR("[GARLAND] Anim: %-10s Pal: %-8s Inter: %d\n"), | |||
anims[animInd]->name(), pals[paletteInd].name(), _interval_effect_update); | |||
scene.setAnim(anims[animInd]); | |||
scene.setPalette(&pals[paletteInd]); | |||
scene.setup(); | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
void garlandSetup() { | |||
_garlandConfigure(); | |||
// Websockets | |||
#if WEB_SUPPORT | |||
wsRegister() | |||
.onConnected(_garlandWebSocketOnConnected) | |||
.onKeyCheck(_garlandWebSocketOnKeyCheck) | |||
.onAction(_garlandWebSocketOnAction); | |||
#endif | |||
espurnaRegisterLoop(garlandLoop); | |||
espurnaRegisterReload(_garlandReload); | |||
pixels.begin(); | |||
scene.setAnim(anims[0]); | |||
scene.setPalette(&pals[0]); | |||
scene.setup(); | |||
_interval_effect_update = secureRandom(EFFECT_UPDATE_INTERVAL_MIN, EFFECT_UPDATE_INTERVAL_MAX); | |||
} | |||
/*####################################################################### | |||
_____ | |||
/ ____| | |||
| (___ ___ ___ _ __ ___ | |||
\___ \ / __| / _ \ | '_ \ / _ \ | |||
____) | | (__ | __/ | | | | | __/ | |||
|_____/ \___| \___| |_| |_| \___| | |||
#######################################################################*/ | |||
#define GARLAND_SCENE_TRANSITION_MS 1000 // transition time between animations, ms | |||
#define GARLAND_SCENE_SPEED_MAX 70 | |||
#define GARLAND_SCENE_SPEED_FACTOR 10 | |||
#define GARLAND_SCENE_DEFAULT_SPEED 50 | |||
#define GARLAND_SCENE_DEFAULT_BRIGHTNESS 255 | |||
Scene::Scene(Adafruit_NeoPixel* pixels) | |||
: _pixels(pixels), | |||
_numLeds(pixels->numPixels()), | |||
_leds1(_numLeds), | |||
_leds2(_numLeds), | |||
_ledstmp(_numLeds), | |||
_seq(_numLeds) { | |||
} | |||
void Scene::setPalette(Palette* palette) { | |||
_palette = palette; | |||
if (setUpOnPalChange) { | |||
setupImpl(); | |||
} | |||
} | |||
void Scene::setBrightness(byte brightness) { | |||
DEBUG_MSG_P(PSTR("[GARLAND] Scene::setBrightness = %d\n"), brightness); | |||
this->brightness = brightness; | |||
} | |||
byte Scene::getBrightness() { | |||
DEBUG_MSG_P(PSTR("[GARLAND] Scene::getBrightness = %d\n"), brightness); | |||
return brightness; | |||
} | |||
// Speed is reverse to cycleFactor and 10x | |||
void Scene::setSpeed(byte speed) { | |||
this->speed = speed; | |||
cycleFactor = (float)(GARLAND_SCENE_SPEED_MAX - speed) / GARLAND_SCENE_SPEED_FACTOR; | |||
DEBUG_MSG_P(PSTR("[GARLAND] Scene::setSpeed %d cycleFactor = %d\n"), speed, (int)(cycleFactor * 1000)); | |||
} | |||
byte Scene::getSpeed() { | |||
DEBUG_MSG_P(PSTR("[GARLAND] Scene::getSpeed %d cycleFactor = %d\n"), speed, (int)(cycleFactor * 1000)); | |||
return speed; | |||
} | |||
void Scene::setDefault() { | |||
speed = GARLAND_SCENE_DEFAULT_SPEED; | |||
cycleFactor = (float)(GARLAND_SCENE_SPEED_MAX - speed) / GARLAND_SCENE_SPEED_FACTOR; | |||
brightness = GARLAND_SCENE_DEFAULT_BRIGHTNESS; | |||
DEBUG_MSG_P(PSTR("[GARLAND] Scene::setDefault speed = %d cycleFactor = %d brightness = %d\n"), speed, (int)(cycleFactor * 1000), brightness); | |||
} | |||
void Scene::run() { | |||
unsigned long iteration_start_time = micros(); | |||
if (state == Calculate || cyclesRemain < 1) { | |||
// Calculate number of cycles for this animation iteration | |||
float cycleSum = cycleFactor * (_anim ? _anim->getCycleFactor() : 1.0) + cycleTail; | |||
cyclesRemain = cycleSum; | |||
if (cyclesRemain < 1) { | |||
cyclesRemain = 1; | |||
cycleSum = 0; | |||
cycleTail = 0; | |||
} else { | |||
cycleTail = cycleSum - cyclesRemain; | |||
} | |||
if (_anim) { | |||
_anim->Run(); | |||
} | |||
sum_calc_time += (micros() - iteration_start_time); | |||
iteration_start_time = micros(); | |||
++calc_num; | |||
state = Transition; | |||
} | |||
if (state == Transition && cyclesRemain < 3) { | |||
// transition coef, if within 0..1 - transition is active | |||
// changes from 1 to 0 during transition, so we interpolate from current | |||
// color to previous | |||
float transc = (float)((long)transms - (long)millis()) / GARLAND_SCENE_TRANSITION_MS; | |||
Color* leds_prev = (_leds == &_leds1[0]) ? &_leds2[0] : &_leds1[0]; | |||
if (transc > 0) { | |||
for (int i = 0; i < _numLeds; i++) { | |||
// transition is in progress | |||
Color c = _leds[i].interpolate(leds_prev[i], transc); | |||
byte r = (int)(bri_lvl[c.r]) * brightness / 256; | |||
byte g = (int)(bri_lvl[c.g]) * brightness / 256; | |||
byte b = (int)(bri_lvl[c.b]) * brightness / 256; | |||
_pixels->setPixelColor(i, _pixels->Color(r, g, b)); | |||
} | |||
} else { | |||
for (int i = 0; i < _numLeds; i++) { | |||
// regular operation | |||
byte r = (int)(bri_lvl[_leds[i].r]) * brightness / 256; | |||
byte g = (int)(bri_lvl[_leds[i].g]) * brightness / 256; | |||
byte b = (int)(bri_lvl[_leds[i].b]) * brightness / 256; | |||
_pixels->setPixelColor(i, _pixels->Color(r, g, b)); | |||
} | |||
} | |||
sum_pixl_time += (micros() - iteration_start_time); | |||
iteration_start_time = micros(); | |||
++pixl_num; | |||
state = Show; | |||
} | |||
if (state == Show && cyclesRemain < 2) { | |||
_pixels->show(); | |||
sum_show_time += (micros() - iteration_start_time); | |||
++show_num; | |||
state = Calculate; | |||
++numShows; | |||
} | |||
--cyclesRemain; | |||
} | |||
void Scene::setupImpl() { | |||
transms = millis() + GARLAND_SCENE_TRANSITION_MS; | |||
// switch operation buffers (for transition to operate) | |||
if (_leds == &_leds1[0]) { | |||
_leds = &_leds2[0]; | |||
} else { | |||
_leds = &_leds1[0]; | |||
} | |||
if (_anim) { | |||
_anim->Setup(_palette, _numLeds, _leds, &_ledstmp[0], &_seq[0]); | |||
} | |||
} | |||
void Scene::setup() { | |||
sum_calc_time = 0; | |||
sum_pixl_time = 0; | |||
sum_show_time = 0; | |||
calc_num = 0; | |||
pixl_num = 0; | |||
show_num = 0; | |||
numShows = 0; | |||
if (!setUpOnPalChange) { | |||
setupImpl(); | |||
} | |||
} | |||
unsigned long Scene::getAvgCalcTime() { return sum_calc_time / calc_num; } | |||
unsigned long Scene::getAvgPixlTime() { return sum_pixl_time / pixl_num; } | |||
unsigned long Scene::getAvgShowTime() { return sum_show_time / show_num; } | |||
/*####################################################################### | |||
_ _ _ | |||
/\ (_) | | (_) | |||
/ \ _ __ _ _ __ ___ __ _ | |_ _ ___ _ __ | |||
/ /\ \ | '_ \ | | | '_ ` _ \ / _` | | __| | | / _ \ | '_ \ | |||
/ ____ \ | | | | | | | | | | | | | (_| | | |_ | | | (_) | | | | | | |||
/_/ \_\ |_| |_| |_| |_| |_| |_| \__,_| \__| |_| \___/ |_| |_| | |||
#######################################################################*/ | |||
Anim::Anim(const char* name) : _name(name) {} | |||
void Anim::Setup(Palette* palette, uint16_t numLeds, Color* leds, Color* ledstmp, byte* seq) { | |||
this->palette = palette; | |||
this->numLeds = numLeds; | |||
this->leds = leds; | |||
this->ledstmp = ledstmp; | |||
this->seq = seq; | |||
SetupImpl(); | |||
} | |||
void Anim::initSeq() { | |||
for (int i = 0; i < numLeds; i++) | |||
seq[i] = i; | |||
} | |||
void Anim::shuffleSeq() { | |||
for (int i = 0; i < numLeds; i++) { | |||
byte ind = (unsigned int)(rngb() * numLeds / 256); | |||
if (ind != i) { | |||
std::swap(seq[ind], seq[i]); | |||
} | |||
} | |||
} | |||
void Anim::glowSetUp() { | |||
braPhaseSpd = secureRandom(4, 13); | |||
if (braPhaseSpd > 8) { | |||
braPhaseSpd = braPhaseSpd - 17; | |||
} | |||
braFreq = secureRandom(20, 60); | |||
} | |||
void Anim::glowForEachLed(int i) { | |||
int8 bra = braPhase + i * braFreq; | |||
bra = BRA_OFFSET + (abs(bra) >> BRA_AMP_SHIFT); | |||
leds[i] = leds[i].brightness(bra); | |||
} | |||
void Anim::glowRun() { braPhase += braPhaseSpd; } | |||
bool operator== (const Color &c1, const Color &c2) | |||
{ | |||
return (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b); | |||
} | |||
unsigned int rng() { | |||
static unsigned int y = 0; | |||
y += micros(); // seeded with changing number | |||
y ^= y << 2; | |||
y ^= y >> 7; | |||
y ^= y << 7; | |||
return (y); | |||
} | |||
// Ranom numbers generator in byte range (256) much faster than secureRandom. | |||
// For usage in time-critical places. | |||
byte rngb() { return (byte)rng(); } | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,16 @@ | |||
/* | |||
GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
*/ | |||
#pragma once | |||
#include "espurna.h" | |||
#if GARLAND_SUPPORT | |||
void garlandSetup(); | |||
#endif |
@ -0,0 +1,76 @@ | |||
/* | |||
Part of the GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
*/ | |||
#pragma once | |||
#include "color.h" | |||
#define BRA_AMP_SHIFT 1 // brigthness animation amplitude shift. true BrA amplitude is calculated | |||
// as (0..127) value shifted right by this amount | |||
#define BRA_OFFSET 127 //(222-64) // brigthness animation amplitude offset | |||
class Palette; | |||
class Anim { | |||
public: | |||
Anim(const char* name); | |||
const char* name() { return _name; } | |||
void Setup(Palette* palette, uint16_t numLeds, Color* leds, Color* _ledstmp, byte* seq); | |||
virtual bool finishedycle() const { return true; }; | |||
virtual void Run() = 0; | |||
virtual void setCycleFactor(float new_cycle_factor) { cycleFactor = new_cycle_factor; } | |||
virtual float getCycleFactor() { return cycleFactor; } | |||
protected: | |||
uint16_t numLeds = 0; | |||
Palette* palette = nullptr; | |||
Color* leds = nullptr; | |||
Color* ledstmp = nullptr; | |||
byte* seq = nullptr; | |||
int phase; | |||
int pos; | |||
int inc; | |||
//brigthness animation (BrA) current initial phase | |||
byte braPhase; | |||
//braPhase change speed | |||
byte braPhaseSpd = 5; | |||
//BrA frequency (spatial) | |||
byte braFreq = 150; | |||
Color curColor = Color(0); | |||
Color prevColor = Color(0); | |||
const Color sparkleColor = Color(0xFFFFFF); | |||
// Reversed analog of speed. To control speed for particular animation (fine tuning for one). | |||
// Lower - faster. Set cycleFactor < 1 speed up animation, while cycleFactor > 1 slow it down. | |||
float cycleFactor = 1.0; | |||
virtual void SetupImpl() = 0; | |||
// helper functions for animations | |||
void initSeq(); | |||
void shuffleSeq(); | |||
//glow animation setup | |||
void glowSetUp(); | |||
//glow animation - must be called for each LED after it's BASIC color is set | |||
//note this overwrites the LED color, so the glow assumes that color will be stored elsewhere (not in leds[]) | |||
//or computed each time regardless previous leds[] value | |||
void glowForEachLed(int i); | |||
//glow animation - must be called at the end of each animaton run | |||
void glowRun(); | |||
private: | |||
const char* _name; | |||
}; | |||
unsigned int rng(); | |||
byte rngb(); |
@ -0,0 +1,54 @@ | |||
#if GARLAND_SUPPORT | |||
#include <list> | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimAssemble : public Anim { | |||
public: | |||
AnimAssemble() : Anim("Assemble") { | |||
cycleFactor = 2; | |||
} | |||
void SetupImpl() override { | |||
inc = 1 + (rngb() >> 5); | |||
if (secureRandom(10) > 5) { | |||
inc = -inc; | |||
} | |||
int p = 0; | |||
for (int i = 0; i < numLeds; i++) { | |||
leds[i] = 0; | |||
Color c = palette->getCachedPalColor((byte)p); | |||
ledstmp[i] = c; | |||
p = p + inc; | |||
if (p >= 256) { | |||
p = p - 256; | |||
} else if (p < 0) { | |||
p = p + 256; | |||
} | |||
} | |||
initSeq(); | |||
shuffleSeq(); | |||
pos = 0; | |||
} | |||
void Run() override { | |||
if (pos < numLeds) { | |||
byte cur_point = seq[pos]; | |||
leds[cur_point] = ledstmp[cur_point]; | |||
++pos; | |||
} else { | |||
int del_pos = pos - numLeds; | |||
byte cur_point = seq[del_pos]; | |||
leds[cur_point] = 0; | |||
if (++pos >= numLeds * 2) | |||
pos = 0; | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,66 @@ | |||
#if GARLAND_SUPPORT | |||
#include <list> | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimComets : public Anim { | |||
public: | |||
AnimComets() : Anim("Comets") { | |||
} | |||
void SetupImpl() override { | |||
comets.clear(); | |||
for (int i = 0; i < 4; ++i) | |||
comets.emplace_back(palette, numLeds); | |||
} | |||
void Run() override { | |||
for (int i = 0; i < numLeds; i++) leds[i] = 0; | |||
for (auto& c : comets) { | |||
int tail = c.head + c.len * -c.dir; | |||
// Check if Comet out of range and generate it again | |||
if ((c.head < 0 && tail < 0) || (c.head >= numLeds && tail >= numLeds)) { | |||
Comet new_comet(palette, numLeds); | |||
std::swap(c, new_comet); | |||
} | |||
for (int l = 0; l < c.len; ++l) { | |||
int p = c.head + l * -c.dir; | |||
if (p >= 0 && p < numLeds) { | |||
leds[p] = c.points[l]; | |||
} | |||
} | |||
c.head = c.head + c.speed * c.dir; | |||
} | |||
} | |||
private: | |||
struct Comet { | |||
float head; | |||
int len = secureRandom(10, 20); | |||
float speed = ((float)secureRandom(4, 10)) / 10; | |||
Color color; | |||
int dir = 1; | |||
std::vector<Color> points; | |||
Comet(Palette* pal, uint16_t numLeds) : head(secureRandom(0, numLeds / 2)), color(pal->getRndInterpColor()), points(len) { | |||
// DEBUG_MSG_P(PSTR("[GARLAND] Comet created head = %d len = %d speed = %g cr = %d cg = %d cb = %d\n"), head, len, speed, color.r, color.g, color.b); | |||
if (secureRandom(10) > 5) { | |||
head = numLeds - head; | |||
dir = -1; | |||
} | |||
for (int i = 0; i < len; ++i) { | |||
points[i] = Color((byte)(color.r * (len - i) / len), (byte)(color.g * (len - i) / len), (byte)(color.b * (len - i) / len)); | |||
} | |||
} | |||
}; | |||
std::list<Comet> comets; | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,48 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimFly : public Anim { | |||
public: | |||
AnimFly() : Anim("Fly") { | |||
} | |||
void SetupImpl() override { | |||
//length of particle tail | |||
pos = secureRandom(2, 15); | |||
//probability of the tail | |||
inc = secureRandom(5, 15); | |||
if (secureRandom(10) > 5) { | |||
inc = -inc; | |||
} | |||
phase = 0; | |||
} | |||
void Run() override { | |||
byte launchpos; | |||
if (inc > 0) { | |||
launchpos = numLeds - 1; | |||
for (int i = 1; i < numLeds; i++) { | |||
leds[i - 1] = leds[i]; | |||
} | |||
} else { | |||
launchpos = 0; | |||
for (int i = numLeds - 2; i >= 0; i--) { | |||
leds[i + 1] = leds[i]; | |||
} | |||
} | |||
if (secureRandom(abs(inc)) == 0) { | |||
curColor = palette->getRndInterpColor(); | |||
phase = pos; | |||
} | |||
leds[launchpos] = Color((int)curColor.r * phase / pos, (int)curColor.g * phase / pos, (int)curColor.b * phase / pos); | |||
if (phase > 0) phase--; | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,68 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
#define DUST_LENGTH 20 | |||
//------------------------------------------------------------------------------ | |||
class AnimPixieDust : public Anim { | |||
public: | |||
AnimPixieDust() : Anim("PixieDust") { | |||
} | |||
void SetupImpl() override { | |||
phase = 0; | |||
curColor = palette->getRndInterpColor(); | |||
prevColor = palette->getRndInterpColor(); | |||
inc = secureRandom(2) * 2 - 1; | |||
if (inc > 0) { | |||
phase = -DUST_LENGTH / 2; | |||
} else { | |||
phase = numLeds + DUST_LENGTH / 2; | |||
} | |||
glowSetUp(); | |||
} | |||
void Run() override { | |||
if (inc > 0) { | |||
for (int i = 0; i < numLeds; i++) { | |||
leds[i] = (i > phase) ? prevColor : curColor; | |||
glowForEachLed(i); | |||
} | |||
phase++; | |||
if (phase >= 4 * numLeds) { | |||
phase = -DUST_LENGTH / 2; | |||
prevColor = curColor; | |||
curColor = palette->getRndInterpColor(); | |||
} | |||
} else { | |||
for (int i = 0; i < numLeds; i++) { | |||
leds[i] = (i < phase) ? prevColor : curColor; | |||
glowForEachLed(i); | |||
} | |||
phase--; | |||
if (phase <= -3 * numLeds) { | |||
phase = numLeds + DUST_LENGTH / 2; | |||
prevColor = curColor; | |||
curColor = palette->getContrastColor(prevColor); | |||
} | |||
} | |||
glowRun(); | |||
for (int k = phase - DUST_LENGTH / 2; k < (phase + DUST_LENGTH / 2); k++) { | |||
if (k >= 0 && k < numLeds) { | |||
int mix = abs(k - phase) * 255 / DUST_LENGTH + ((int)rngb() - 125); | |||
if (mix < 0) { | |||
mix = 0; | |||
} else if (mix > 255) { | |||
mix = 255; | |||
} | |||
leds[k] = sparkleColor.interpolate(leds[k], (float)mix / 255); | |||
} | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,25 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../palette.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimRandCyc : public Anim { | |||
public: | |||
AnimRandCyc() : Anim("RandCyc") { | |||
} | |||
void SetupImpl() override { | |||
for (int i = 0; i < numLeds; i++) | |||
seq[i] = rngb(); | |||
} | |||
void Run() override { | |||
for (int i = 0; i < numLeds; i++) { | |||
leds[i] = palette->getCachedPalColor(seq[i]); | |||
seq[i] += rngb() >> 6; | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,38 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimRun : public Anim { | |||
public: | |||
AnimRun() : Anim("Run") { | |||
} | |||
void SetupImpl() override { | |||
pos = 0; | |||
inc = 1 + (rngb() >> 5); | |||
if (secureRandom(10) > 5) { | |||
inc = -inc; | |||
} | |||
} | |||
void Run() override { | |||
int p = pos; | |||
for (int i = 0; i < numLeds; i++) { | |||
Color c = palette->getCachedPalColor((byte)p); | |||
leds[i] = c; | |||
p = p + inc; | |||
if (p >= 256) { | |||
p = p - 256; | |||
} else if (p < 0) { | |||
p = p + 256; | |||
} | |||
} | |||
pos = pos + 1; | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,51 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
#define SPARK_PROB 3 //probability of spark when in idle plase | |||
//------------------------------------------------------------------------------ | |||
class AnimSparkr : public Anim { | |||
public: | |||
AnimSparkr() : Anim("Sparkr") { | |||
} | |||
void SetupImpl() override { | |||
glowSetUp(); | |||
phase = 0; | |||
curColor = palette->getRndInterpColor(); | |||
prevColor = palette->getRndInterpColor(); | |||
initSeq(); | |||
shuffleSeq(); | |||
} | |||
void Run() override { | |||
for (int i = 0; i < numLeds; i++) { | |||
byte pos = seq[i]; | |||
leds[pos] = (i > phase) ? prevColor | |||
: (i == phase) ? sparkleColor | |||
: curColor; | |||
glowForEachLed(i); | |||
} | |||
glowRun(); | |||
if (phase > numLeds) { | |||
if (secureRandom(SPARK_PROB) == 0) { | |||
int i = (int)rngb() * numLeds / 256; | |||
leds[i] = sparkleColor; | |||
} | |||
} | |||
phase++; | |||
if (phase > 2 * numLeds) { | |||
phase = 0; | |||
prevColor = curColor; | |||
curColor = palette->getContrastColor(prevColor); | |||
shuffleSeq(); | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,73 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
#include "debug.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimSpread : public Anim { | |||
const byte minWidth = 10; | |||
const byte maxWidth = 15; | |||
const byte numTries = 5; | |||
const byte fade_ind = 255 / minWidth; | |||
int GeneratePos() { | |||
int pos = -1; | |||
for (int i = 0; i < numTries; ++i) { | |||
pos = secureRandom(0, numLeds); | |||
for (int j = pos - maxWidth; j < pos + maxWidth; ++j) { | |||
if (j >= 0 && j < numLeds && seq[j] > 0) { | |||
pos = -1; | |||
break; | |||
} | |||
} | |||
if (pos >= 0) break; | |||
} | |||
return pos; | |||
} | |||
public: | |||
AnimSpread() : Anim("Spread") { | |||
cycleFactor = 2; | |||
} | |||
void SetupImpl() override { | |||
inc = secureRandom(2, 4); | |||
// DEBUG_MSG_P(PSTR("[GARLAND] AnimSpread inc = %d\n"), inc); | |||
for (int i = 0; i < numLeds; i++) | |||
seq[i] = 0; | |||
} | |||
void Run() override { | |||
for (int i = 0; i < numLeds; i++) | |||
leds[i] = 0; | |||
for (int i = 0; i < numLeds; i++) { | |||
if (seq[i] > 0) { | |||
byte width = maxWidth - seq[i]; | |||
for (int j = i - width; j <= (i + width); j++) { | |||
Color c = ledstmp[i]; | |||
if (j >= 0 && j < numLeds) { | |||
leds[j].r += c.r; | |||
leds[j].g += c.g; | |||
leds[j].b += c.b; | |||
} | |||
} | |||
ledstmp[i].fade(fade_ind); | |||
seq[i]--; | |||
} | |||
} | |||
if (secureRandom(inc) == 0) { | |||
int pos = GeneratePos(); | |||
if (pos == -1) | |||
return; | |||
ledstmp[pos] = palette->getRndInterpColor(); | |||
seq[pos] = secureRandom(minWidth, maxWidth); | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,53 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
#include "../color.h" | |||
#include "../palette.h" | |||
//seq keeps phases: 0..127 increasing, 128..255 decreasing, ends at 255 (steady off) | |||
//ledstmp keeps color of stars | |||
//------------------------------------------------------------------------------ | |||
class AnimStars : public Anim { | |||
public: | |||
AnimStars() : Anim("Stars") { | |||
} | |||
void SetupImpl() override { | |||
//inc is (average) interval between appearance of new stars | |||
inc = secureRandom(2, 5); | |||
//reset all phases | |||
for (int i = 0; i < numLeds; i++) | |||
seq[i] = 255; | |||
} | |||
void Run() override { | |||
for (byte i = 0; i < numLeds; i++) { | |||
byte phi = seq[i]; | |||
if (phi < 254) { | |||
Color col = ledstmp[i]; | |||
if (phi <= 127) { | |||
leds[i] = col.brightness(phi << 1); | |||
} else { | |||
leds[i] = col.brightness((255 - phi) << 1); | |||
} | |||
seq[i] += 2; | |||
} else { | |||
leds[i].r = 0; | |||
leds[i].g = 0; | |||
leds[i].b = 0; | |||
} | |||
} | |||
if (secureRandom(inc) == 0) { | |||
byte pos = secureRandom(numLeds); | |||
if (seq[pos] > 250) { | |||
seq[pos] = 0; | |||
ledstmp[pos] = palette->getRndInterpColor(); | |||
} | |||
} | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,41 @@ | |||
#if GARLAND_SUPPORT | |||
#include "../anim.h" | |||
//------------------------------------------------------------------------------ | |||
class AnimStart : public Anim { | |||
public: | |||
AnimStart() : Anim("Start") { | |||
} | |||
void SetupImpl() override { | |||
phase = 0; | |||
} | |||
void Run() override { | |||
if (phase < numLeds) { | |||
leds[phase].r = 255; | |||
leds[phase].g = 255; | |||
leds[phase].b = 255; | |||
for (int i = 0; i < numLeds; i++) { | |||
leds[i].fade(50); | |||
} | |||
} else if (phase >= numLeds) { | |||
for (int i = 0; i < numLeds; i++) { | |||
short r = numLeds + 255 - phase + rngb(); | |||
r = min(r, (short)255); | |||
leds[i].r = (byte)max(r, (short)0); | |||
short g = numLeds + 255 - phase + rngb(); | |||
g = min(g, (short)255); | |||
leds[i].g = (byte)max(g, (short)0); | |||
short b = numLeds + 255 - phase + rngb(); | |||
b = min(b, (short)255); | |||
leds[i].b = (byte)max(b, (short)0); | |||
} | |||
phase++; | |||
} | |||
phase++; | |||
} | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,87 @@ | |||
/* | |||
Part of the GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
*/ | |||
#pragma once | |||
#if GARLAND_SUPPORT | |||
#include <Arduino.h> | |||
struct Color | |||
{ | |||
byte r; | |||
byte g; | |||
byte b; | |||
inline Color() : r(0), g(0), b(0) {} | |||
// allow construction from R, G, B | |||
inline Color(uint8_t ir, uint8_t ig, uint8_t ib) | |||
: r(ir), g(ig), b(ib) { | |||
} | |||
// allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code | |||
inline Color(uint32_t colorcode) | |||
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) { | |||
} | |||
//interpolates between this color and provided. | |||
//x is from 0 to 1, 0 gives this color, 1 gives provided color, values between give interpolation | |||
Color interpolate(Color color, float x) const { | |||
int r0 = x * (color.r - r) + r; | |||
int g0 = x * (color.g - g) + g; | |||
int b0 = x * (color.b - b) + b; | |||
return Color(r0, g0, b0); | |||
} | |||
//creates color with decreased brightness | |||
Color brightness(byte k) const { | |||
int r0 = r * (int)k / 255; | |||
int g0 = g * (int)k / 255; | |||
int b0 = b * (int)k / 255; | |||
return Color(r0, g0, b0); | |||
} | |||
//fades (decreases all RGB channels brightness) this color by k | |||
void fade(byte k) { | |||
if (r>=k) { r=r-k; } else { r=0; } | |||
if (g>=k) { g=g-k; } else { g=0; } | |||
if (b>=k) { b=b-k; } else { b=0; } | |||
} | |||
//fades color separately for each channel | |||
void fade3(byte dr, byte dg, byte db) { | |||
if (r>=dr) { r=r-dr; } else { r=0; } | |||
if (g>=dg) { g=g-dg; } else { g=0; } | |||
if (b>=db) { b=b-db; } else { b=0; } | |||
} | |||
//checks whether this color is visually close to given one | |||
bool isCloseTo(Color c) const { | |||
int diff = abs(r - c.r) + abs(g - c.g) + abs(b - c.b); | |||
return diff <= 220; //220 is magic number. Low values give "true" on closer colors, while higher can cause infinite loop while trying to find different color | |||
} | |||
//return value, that can be used to define how close one color to another | |||
int howCloseTo(Color c) const { | |||
return abs(r - c.r) + abs(g - c.g) + abs(b - c.b); | |||
} | |||
bool empty() const { | |||
return r == 0 && g == 0 && b == 0; | |||
} | |||
void println() const { | |||
Serial.print(("r="));Serial.print(r);Serial.print((" ")); | |||
Serial.print(("g="));Serial.print(g);Serial.print( (" ")); | |||
Serial.print(("b="));Serial.println(b); | |||
} | |||
friend bool operator== (const Color &c1, const Color &c2); | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,95 @@ | |||
/* | |||
Part of the GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
*/ | |||
#pragma once | |||
#if GARLAND_SUPPORT | |||
#include <vector> | |||
#include "color.h" | |||
class Palette { | |||
public: | |||
Palette(const char* name, std::vector<Color>&& colors) : _name(name), _numColors(colors.size()), _colors(std::move(colors)), _cache(256) { | |||
} | |||
const char* name() const { return _name; } | |||
/** | |||
* Get the interpolated color from the palette. | |||
* The argument is a floating number between 0 and 1 | |||
* Used to smoothly traverse through palette. | |||
*/ | |||
Color getPalColor(float i) const { | |||
int i0 = (int)(i * _numColors) % (_numColors); | |||
int i1 = (int)(i * _numColors + 1) % (_numColors); | |||
// decimal part is used to interpolate between the two colors | |||
float t0 = i * _numColors - trunc(i * _numColors); | |||
return _colors[i0].interpolate(_colors[i1], t0); | |||
} | |||
Color getCachedPalColor(byte i) { | |||
if (!_cache[i].empty()) | |||
return _cache[i]; | |||
Color col = getPalColor((float)i / 256); | |||
if (col.empty()) | |||
col = 1; | |||
_cache[i] = col; | |||
return col; | |||
} | |||
/** | |||
* Get the interpolated color between two random neighbour colors. | |||
*/ | |||
Color getRndNeighborInterpColor() const { | |||
int i0 = secureRandom(_numColors); | |||
int i1 = (i0 + 1) % (_numColors); | |||
float t0 = (float)(secureRandom(256)) / 256; | |||
return _colors[i0].interpolate(_colors[i1], t0); | |||
} | |||
/** | |||
* Get the interpolated color between two random colors. | |||
*/ | |||
Color getRndInterpColor() const { | |||
int i0 = secureRandom(_numColors); | |||
int i1 = secureRandom(_numColors); | |||
float t0 = (float)(secureRandom(256)) / 256; | |||
return _colors[i0].interpolate(_colors[i1], t0); | |||
} | |||
Color getContrastColor(const Color& prevColor) const { | |||
int tries = 0; | |||
int bestDiff = 0; | |||
Color bestColor; | |||
// 220 is magic number. Low values give "true" on closer colors, while higher can cause infinite loop while trying to find different color | |||
// let's try to find contras enough color but no more than 10 tries | |||
while (bestDiff <= 220 && tries++ < 10) { | |||
Color newColor = getRndInterpColor(); | |||
int diff = prevColor.howCloseTo(newColor); | |||
if (bestDiff < diff) { | |||
bestDiff = diff; | |||
bestColor = newColor; | |||
} | |||
} | |||
return bestColor; | |||
} | |||
private: | |||
const char* _name; | |||
const int _numColors; | |||
std::vector<Color> _colors; | |||
std::vector<Color> _cache; | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1,111 @@ | |||
/* | |||
Part of the GARLAND MODULE | |||
Copyright (C) 2020 by Dmitry Blinov <dblinov76 at gmail dot com> | |||
Inspired by https://github.com/Vasil-Pahomov/ArWs2812 (currently https://github.com/Vasil-Pahomov/Liana) | |||
*/ | |||
#pragma once | |||
#if GARLAND_SUPPORT | |||
#include <array> | |||
#include <vector> | |||
#include "anim.h" | |||
#include "animations/anim_assemble.h" | |||
#include "animations/anim_comets.h" | |||
#include "animations/anim_fly.h" | |||
#include "animations/anim_pixiedust.h" | |||
#include "animations/anim_randcyc.h" | |||
#include "animations/anim_run.h" | |||
#include "animations/anim_sparkr.h" | |||
#include "animations/anim_spread.h" | |||
#include "animations/anim_stars.h" | |||
#include "animations/anim_start.h" | |||
class Adafruit_NeoPixel; | |||
class Palette; | |||
class Scene { | |||
public: | |||
Scene(Adafruit_NeoPixel* pixels); | |||
void setPalette(Palette* palette); | |||
void setBrightness(byte brightness); | |||
byte getBrightness(); | |||
void setSpeed(byte speed); | |||
byte getSpeed(); | |||
void setDefault(); | |||
void setAnim(Anim* anim) { _anim = anim; } | |||
void run(); | |||
void setup(); | |||
bool finishedAnimCycle() { return _anim ? _anim->finishedycle() : true; } | |||
unsigned long getAvgCalcTime(); | |||
unsigned long getAvgPixlTime(); | |||
unsigned long getAvgShowTime(); | |||
int getNumShows() { return numShows; } | |||
private: | |||
Adafruit_NeoPixel* _pixels = nullptr; | |||
uint16_t _numLeds; | |||
//Color arrays - two for making transition | |||
std::vector<Color> _leds1; | |||
std::vector<Color> _leds2; | |||
// array of Colorfor anim to currently work with | |||
Color* _leds = nullptr; | |||
Anim* _anim = nullptr; | |||
//auxiliary colors array for mutual usage of anims | |||
std::vector<Color> _ledstmp; | |||
std::vector<byte> _seq; | |||
Palette* _palette = nullptr; | |||
// millis to transition end | |||
unsigned long transms; | |||
byte brightness = 0; | |||
// Reverse to speed. If more convenient to calculate in this way. | |||
// 1 < cycleFactor < 4 | |||
byte speed = 50; | |||
float cycleFactor = 2.0; | |||
float cycleTail = 0; | |||
int cyclesRemain = 0; | |||
enum State { | |||
Calculate, | |||
Transition, | |||
Show | |||
} state = Calculate; | |||
int numShows = 0; | |||
//whether to call SetUp on palette change | |||
//(some animations require full transition with fade, otherwise the colors would change in a step, some not) | |||
bool setUpOnPalChange = true; | |||
unsigned long sum_calc_time = 0; | |||
unsigned long sum_pixl_time = 0; | |||
unsigned long sum_show_time = 0; | |||
unsigned int calc_num = 0; | |||
unsigned int show_num = 0; | |||
unsigned int pixl_num = 0; | |||
std::array<byte, 256> bri_lvl = {{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, | |||
4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, | |||
10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 17, 17, | |||
17, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, | |||
29, 30, 30, 31, 31, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 39, 39, 40, 41, 42, 42, 43, 44, 45, 45, 46, | |||
47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, | |||
74, 75, 76, 78, 79, 80, 82, 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 98, 99, 101, 102, 104, 106, 108, 109, | |||
111, 113, 115, 117, 119, 121, 122, 124, 126, 129, 131, 133, 135, 137, 139, 142, 144, 146, 149, 151, 153, 156, | |||
158, 161, 163, 166, 169, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 204, 208, 211, 214, 218, 221, | |||
225, 228, 232, 236, 239, 243, 247, 251, 255}}; | |||
void setupImpl(); | |||
}; | |||
#endif // GARLAND_SUPPORT |
@ -0,0 +1 @@ | |||
#define GARLAND_SUPPORT 1 |