I'm working on a project that is running off of a Sparkfun Pro Micro 5V. I'm trying to accomplish a color rainbow cycle animation which will have a function when triggered to fade the rainbow to black and fade-in some white LED. I'm using a Adafruit RGBW Neopixel for this. I'm having trouble getting some code that does this as when I try to trigger my fade-out it's turning off faster than expected due to the way I wrote it. I'm hoping someone can point me to a piece of code or suggest an edit that will help.
Constraints for animation:
- Must execute quickly (if at all possible avoid floats or other constructs that take a lot of time; relatively)
- Be able to configure a maximum brightness set on initialization (0 - 255)
- Must not have any "delay" or "while" loops that are designed to hold the code up.
- When triggered by a button press the rainbow should fade to black (i.e. off) and previously off white LED should fade in.
- When button pressed a second time white should fade out and rainbow starts or resumes.
I don't really care how the color animation works. It can cycle to/between specified colors or use whatever. To keep it simple what I tried was to have it just cycle RGB values with each 1/3 out of phase. My cycle includes a "dead" time where the color stays off for a part of the loop to keep the colors more distinct so they don't merge into white through color mixing. I specifically didn't use any sort of HSV conversion or sin/cos/whatever to calculate position in the cycle I am because those require a lot of calculation relative to incrementing a number.
Here is the code I wrote for your review. This has some servo control and the button checking and the LED in it. The bit at the very end is to prevent the pixels from updating during the time the servo pulses are being sent which prevents servo jitter due to the neoPixels.
#include <Servo.h>
#include <Adafruit_NeoPixel.h>
#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8)
class ColorCycle {
private: int maxB;
private: int offH;
private: int stepD;
private: int curD;
private: int rVal;
private: int gVal;
private: int bVal;
private: int wVal;
private: int rHold;
private: int gHold;
private: int bHold;
private: int wHold;
private: bool rDir;
private: bool gDir;
private: bool bDir;
private: bool wDir;
private: bool showWhite;
public: ColorCycle(int maxBrightness, int offHold, int stepDelay) {
maxB = maxBrightness;
offH = offHold;
rVal = gVal = bVal = wVal = 0;
rHold = 0;
gHold = (maxB + offH) * .5;
bHold = (maxB + offH);
wHold = 0;
rDir = gDir = bDir = true;
wDir = false;
stepD = stepDelay;
curD = 0;
}
void moveStep() {
moveSteps(1);
}
void moveSteps(int count) {
if (count == 1) { //going to skip the delay if you are trying to move more than one step intentionally.
if (curD > 0) {
curD--;
return;
}
curD = stepD;
}
for (int s = 0; s < count; s++) {
if (showWhite) {
whiteHold();
wDir = true;
} else {
wDir = false;
}
//setting the direction and holds when appropriate
if (rVal == 0)
rDir = true;
if (rVal == maxB) {
rDir = false;
rHold = offH;
}
if (gVal == 0)
gDir = true;
if (gVal == maxB) {
gDir = false;
gHold = offH;
}
if (bVal == 0)
bDir = true;
if (bVal == maxB) {
bDir = false;
bHold = offH;
}
//stepping the values
if (rDir && rHold == 0)
rVal++;
if (!rDir && rVal > 0)
rVal--;
if (rVal == 0 && rHold > 0) {
rHold--;
}
if (gDir && gHold == 0)
gVal++;
if (!gDir && gVal > 0)
gVal--;
if (gVal == 0 && gHold > 0) {
gHold--;
}
if (bDir && bHold == 0)
bVal++;
if (!bDir && bVal > 0)
bVal--;
if (bVal == 0 && bHold > 0) {
bHold--;
}
if (wDir && wVal < maxB)
wVal++;
if (!wDir && wVal > 0)
wVal--;
}
}
int getRedValue() {
return rVal;
}
int getGreenValue() {
return gVal;
}
int getBlueValue() {
return bVal;
}
int getWhiteValue() {
return 0;
}
void setShowWhite(bool sw) {
showWhite = sw;
}
void whiteHold() {
rHold = 5;
gHold = (maxB + offH) * .5;
bHold = (maxB + offH);
rDir = gDir = bDir = false;
}
};
#define BUTTON_LED_PIN 16
#define BUTTON_SWITCH_PIN 14
Servo lidServo;
int lidServoPosition;
Servo leftServo;
int leftServoPosition;
Servo rightServo;
int rightServoPosition;
#define LID_LED_CONTROL_PIN 3
#define LID_LED_COUNT 8
Adafruit_NeoPixel lidPixel(LID_LED_COUNT, LID_LED_CONTROL_PIN, NEO_RGBW + NEO_KHZ800);
ColorCycle ccLid(50, 50, 10);
#define BASE_LED_CONTROL_PIN 4
#define BASE_LED_COUNT 8
Adafruit_NeoPixel basePixel(BASE_LED_COUNT, BASE_LED_CONTROL_PIN, NEO_RGBW + NEO_KHZ800);
ColorCycle ccBase(50, 50, 10);
void setup() {
pinMode(BUTTON_LED_PIN, OUTPUT);
digitalWrite(BUTTON_LED_PIN, LOW);
pinMode(BUTTON_SWITCH_PIN, INPUT);
lidServo.attach(5);
leftServo.attach(6);
rightServo.attach(9);
lidPixel.begin();
lidPixel.show();
basePixel.begin();
basePixel.show();
Serial.begin(9600); //This pipes to the serial monitor
Serial.println("Starting");
}
void loop() {
if (digitalRead(BUTTON_SWITCH_PIN) == true) {
digitalWrite(BUTTON_LED_PIN, HIGH);
lidServoPosition = 0;
leftServoPosition = 0;
rightServoPosition = 0;
ccLid.setShowWhite(true);
ccBase.setShowWhite(true);
} else {
digitalWrite(BUTTON_LED_PIN, LOW);
lidServoPosition = 180;
leftServoPosition = 180;
rightServoPosition = 180;
ccLid.setShowWhite(false);
ccBase.setShowWhite(false);
}
lidServo.write(lidServoPosition);
leftServo.write(leftServoPosition);
rightServo.write(rightServoPosition);
ccLid.moveStep();
ColorCycle tempCc = ccLid;
for (int i = 0; i < lidPixel.numPixels(); i++) {
lidPixel.setPixelColor(i, lidPixel.Color(tempCc.getGreenValue(), tempCc.getRedValue(), tempCc.getBlueValue(), tempCc.getWhiteValue()));
tempCc.moveSteps(10);
}
ccBase.moveStep();
tempCc = ccBase;
for (int i = 0; i < lidPixel.numPixels(); i++) {
basePixel.setPixelColor(i, basePixel.Color(tempCc.getGreenValue(), tempCc.getRedValue(), tempCc.getBlueValue(), tempCc.getWhiteValue()));
tempCc.moveSteps(10);
}
if (TCNT1 > usToTicks(8192)) {
lidPixel.show();
basePixel.show();
};
}