Skip to content

Commit

Permalink
added transition as effect
Browse files Browse the repository at this point in the history
simplified IEffect interface
  • Loading branch information
depuits committed Dec 22, 2017
1 parent 2e5cfac commit a17d0f6
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 240 deletions.
213 changes: 42 additions & 171 deletions mqtt_esp8266_light/ColorState.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,23 @@
#define __COLORSTATE_H_INCLUDED__

class ColorState: public IEffect {
public:
//TODO check which variables should be private
const bool includeRgb = (CONFIG_STRIP == RGB) || (CONFIG_STRIP == RGBW);
const bool includeWhite = (CONFIG_STRIP == BRIGHTNESS) || (CONFIG_STRIP == RGBW);

// Maintained state for reporting to HA
byte red = 255;
byte green = 255;
byte blue = 255;
byte white = 255;
byte brightness = 255;

// Real values to write to the LEDs (ex. including brightness and state)
byte realRed = 0;
byte realGreen = 0;
byte realBlue = 0;
byte realWhite = 0;

bool stateOn = false;

// Globals for fade/transitions
bool startFade = false;
unsigned long lastLoop = 0;
int transitionTime = 0;
bool inFade = false;
int loopCount = 0;
int stepR, stepG, stepB, stepW;
int redVal, grnVal, bluVal, whtVal;
void applyColor(int inR, int inG, int inB, int inW) {
// when the light is off it should be off
if (!stateOn) {
inR = 0;
inG = 0;
inB = 0;
inW = 0;
}

void setColor(int inR, int inG, int inB, int inW) {
if (CONFIG_INVERT_LED_LOGIC) {
inR = (255 - inR);
inG = (255 - inG);
inB = (255 - inB);
inW = (255 - inW);
}

// when the light is off it should be off
if (!stateOn) {
inR = 0;
inG = 0;
inB = 0;
inW = 0;
}

if (includeRgb) {
analogWrite(CONFIG_PIN_RED, inR);
Expand All @@ -57,7 +30,7 @@ class ColorState: public IEffect {
analogWrite(CONFIG_PIN_WHITE, inW);
}

if (CONFIG_DEBUG) {
/*if (CONFIG_DEBUG) {
Serial.print("Setting LEDs: {");
if (includeRgb) {
Serial.print("r: ");
Expand All @@ -77,39 +50,45 @@ class ColorState: public IEffect {
}
Serial.println("}");
}
}*/
}

public:
//TODO check which variables should be private
const bool includeRgb = (CONFIG_STRIP == RGB) || (CONFIG_STRIP == RGBW);
const bool includeWhite = (CONFIG_STRIP == BRIGHTNESS) || (CONFIG_STRIP == RGBW);

byte red = 255;
byte green = 255;
byte blue = 255;
byte white = 255;

byte brightness = 255;

bool stateOn = false;

void setColor(int inR, int inG, int inB, int inW) {
red = inR;
green = inG;
blue = inB;
white = inW;
}

virtual bool processJson(JsonObject& root) {
if (includeRgb && root.containsKey("color")) {
bool processJson(JsonObject& root) {
if (root.containsKey("color")) {
red = root["color"]["r"];
green = root["color"]["g"];
blue = root["color"]["b"];
}

if (includeWhite && root.containsKey("white_value")) {
if (root.containsKey("white_value")) {
white = root["white_value"];
}

if (root.containsKey("brightness")) {
brightness = root["brightness"];
}

if (root.containsKey("transition")) {
transitionTime = root["transition"];
}
else {
transitionTime = 0;
}

// Update lights
realRed = map(red, 0, 255, 0, brightness);
realGreen = map(green, 0, 255, 0, brightness);
realBlue = map(blue, 0, 255, 0, brightness);
realWhite = map(white, 0, 255, 0, brightness);

startFade = true;

return true;
}

Expand All @@ -129,124 +108,16 @@ class ColorState: public IEffect {
root["brightness"] = brightness;
}

virtual void update() {
if (startFade) {
Serial.println(transitionTime);
// If we don't want to fade, skip it.
if (transitionTime == 0) {
setColor(realRed, realGreen, realBlue, realWhite);

redVal = realRed;
grnVal = realGreen;
bluVal = realBlue;
whtVal = realWhite;

startFade = false;
}
else {
loopCount = 0;
stepR = calculateStep(redVal, realRed);
stepG = calculateStep(grnVal, realGreen);
stepB = calculateStep(bluVal, realBlue);
stepW = calculateStep(whtVal, realWhite);

inFade = true;
}
}

if (inFade) {
startFade = false;
unsigned long now = millis();
if (now - lastLoop > transitionTime) {
if (loopCount <= 1020) {
lastLoop = now;

redVal = calculateVal(stepR, redVal, loopCount);
grnVal = calculateVal(stepG, grnVal, loopCount);
bluVal = calculateVal(stepB, bluVal, loopCount);
whtVal = calculateVal(stepW, whtVal, loopCount);

setColor(redVal, grnVal, bluVal, whtVal); // Write current values to LED pins

//Serial.print("Loop count: ");
//Serial.println(loopCount);
loopCount++;
}
else {
inFade = false;
}
}
}
}

// From https://www.arduino.cc/en/Tutorial/ColorCrossfader
/* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS
*
* The program works like this:
* Imagine a crossfade that moves the red LED from 0-10,
* the green from 0-5, and the blue from 10 to 7, in
* ten steps.
* We'd want to count the 10 steps and increase or
* decrease color values in evenly stepped increments.
* Imagine a + indicates raising a value by 1, and a -
* equals lowering it. Our 10 step fade would look like:
*
* 1 2 3 4 5 6 7 8 9 10
* R + + + + + + + + + +
* G + + + + +
* B - - -
*
* The red rises from 0 to 10 in ten steps, the green from
* 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps.
*
* In the real program, the color percentages are converted to
* 0-255 values, and there are 1020 steps (255*4).
*
* To figure out how big a step there should be between one up- or
* down-tick of one of the LED values, we call calculateStep(),
* which calculates the absolute gap between the start and end values,
* and then divides that gap by 1020 to determine the size of the step
* between adjustments in the value.
*/
int calculateStep(int prevValue, int endValue) {
int step = endValue - prevValue; // What's the overall gap?
if (step) { // If its non-zero,
step = 1020/step; // divide by 1020
}

return step;
}

/* The next function is calculateVal. When the loop value, i,
* reaches the step size appropriate for one of the
* colors, it increases or decreases the value of that color by 1.
* (R, G, and B are each calculated separately.)
*/
int calculateVal(int step, int val, int i) {
if ((step) && i % step == 0) { // If step is non-zero and its time to change a value,
if (step > 0) { // increment the value if step is positive...
val += 1;
}
else if (step < 0) { // ...or decrement it if step is negative
val -= 1;
}
}

// Defensive driving: make sure val stays in the range 0-255
if (val > 255) {
val = 255;
}
else if (val < 0) {
val = 0;
}
void update() {
// Update lights
byte realRed = map(red, 0, 255, 0, brightness);
byte realGreen = map(green, 0, 255, 0, brightness);
byte realBlue = map(blue, 0, 255, 0, brightness);
byte realWhite = map(white, 0, 255, 0, brightness);

return val;
applyColor (realRed, realGreen, realBlue, realWhite);
}

virtual void end() {}

virtual bool isRunning() { return true; }
virtual const char* getName() { return ""; }
void end() {}
};

#endif
5 changes: 2 additions & 3 deletions mqtt_esp8266_light/IEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ class IEffect {
public:
// process the json from the message and return true when activated
virtual bool processJson(JsonObject& root) = 0;
virtual void populateJson(JsonObject& root) = 0;

virtual void update() = 0;
virtual void end() = 0;

virtual bool isRunning() = 0;
virtual const char* getName() = 0;

virtual ~IEffect() {}
};

Expand Down
43 changes: 25 additions & 18 deletions mqtt_esp8266_light/effects/ColorFade.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class ColorFade: public IEffect {
const char* nameFast = "colorfade_fast";

ColorState& state;
Transition& transition;

bool running = false;
int currentColor = 0;
Expand All @@ -22,21 +23,22 @@ class ColorFade: public IEffect {
};

public:
ColorFade(ColorState& s):
state(s)
ColorFade(ColorState& s, Transition& t):
state(s),
transition(t)
{
}

virtual bool processJson(JsonObject& root) {
bool processJson(JsonObject& root) {
if (state.includeRgb && root.containsKey("effect") &&
(strcmp(root["effect"], nameSlow) == 0 || strcmp(root["effect"], nameFast) == 0)) {
running = true;
currentColor = 0;
if (strcmp(root["effect"], nameSlow) == 0) {
state.transitionTime = CONFIG_COLORFADE_TIME_SLOW; //TODO preserve original transition time for returning to other effect
transition.time = CONFIG_COLORFADE_TIME_SLOW; //TODO preserve original transition time for returning to other effect
}
else {
state.transitionTime = CONFIG_COLORFADE_TIME_FAST;
transition.time = CONFIG_COLORFADE_TIME_FAST;
}
return true;
}
Expand All @@ -49,26 +51,31 @@ class ColorFade: public IEffect {

return false;
}
virtual void update() {
if (running && !state.inFade) {
state.realRed = map(colors[currentColor][0], 0, 255, 0, state.brightness);
state.realGreen = map(colors[currentColor][1], 0, 255, 0, state.brightness);
state.realBlue = map(colors[currentColor][2], 0, 255, 0, state.brightness);
state.realWhite = map(colors[currentColor][3], 0, 255, 0, state.brightness);
void populateJson(JsonObject& root) {
if (running) {
root["effect"] = getName();
}
}

void update() {
if (running && !transition.running) {
byte red = colors[currentColor][0];
byte green = colors[currentColor][1];
byte blue = colors[currentColor][2];
byte white = colors[currentColor][3];

currentColor = (currentColor + 1) % numColors;
state.startFade = true;

transition.start(red, green, blue, white);
}
}
virtual void end() {
void end() {
Serial.println("stopping colorfade");
running = false;
}

virtual bool isRunning() {
return running;
}
virtual const char* getName() {
if (state.transitionTime == CONFIG_COLORFADE_TIME_SLOW) {
const char* getName() {
if (transition.time == CONFIG_COLORFADE_TIME_SLOW) {
return nameSlow;
}
return nameFast;
Expand Down
Loading

0 comments on commit a17d0f6

Please sign in to comment.