I'm trying to create a non-blocking sketch (no Delay(), no For loops) to smoothly cross fade an RGB LED from one colour to another using analogWrite() on three pins. It's tricky because the R/G/B steps from the initial PWM value to the next might not be the same, e.g. I want to start with all three off (black, rgb(0,0,0)) and cross fade up to an orange (rgb(248, 117, 49)) in a set amount of time (e.g. 2550 ms). Focusing on one colour first, I can get the Blue LED to fade up from black to pure blue (rgb,0,0,255) when outputting to the serial monitor but not when I comment out the #define
statement at the top of the sketch, or even when removing all the Print() statements - the blue LED does not come on at all.
Arduino Pro Mini (5V, 16Mhz, powered by SparkFun Serial Basic Breakout board)
I've tried:
- Compiling/loading with both the 1.8.13 IDE and the newer 2.0.3 - same result.
- Using other pins on the Arduino Pro Mini - same result
- Removing all Serial.print() lines with the conditional
#ifdef
wrapper - same result
// Comment out the line below for production
// (serial monitoring affects timings)
#define DEBUG_MODE
bool isCommonAnode = true; // Set to false for common cathode
int redPin = 9;
int grnPin = 10;
int bluPin = 11;
int delayBeforeStartupMS = 3000;
int powerUpFadeTimeMS = 2550;
int delayBeforeWarpMS = 4500;
unsigned long timeStampMS = 0;
bool startupSeq = false;
bool gotoWarp = false;
bool startLoop = false;
// Colours
int black[3] = { 0, 0, 0 };
int blue[3] = { 0, 0, 255 };
// Set initial colour values
int redVal = black[0];
int grnVal = black[1];
int bluVal = black[2];
// Initialize colour variables
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;
int delayB = 0;
// R/G/B PWM step values
int stepR;
int stepG;
int stepB;
bool fadingB = false;
void setup() {
#ifdef DEBUG_MODE
Serial.begin(9600);
Serial.println("-----------");
Serial.println("DEVMODE ON");
Serial.println("-----------");
#endif
pinMode(redPin, OUTPUT); // sets the pins as output
pinMode(grnPin, OUTPUT);
pinMode(bluPin, OUTPUT);
// Set initial values (black)
if ( isCommonAnode == true ) {
analogWrite(redPin, 255 - redVal); // Write current values to LED pins
analogWrite(grnPin, 255 - grnVal); // (using common anode, so need to use '255-')
analogWrite(bluPin, 255 - bluVal);
} else {
analogWrite(redPin, redVal); // Write current values to LED pins
analogWrite(grnPin, grnVal);
analogWrite(bluPin, bluVal);
}
}
void loop() {
static unsigned long startTimeBlueMS = millis();
unsigned long currentTimeMS = millis();
static unsigned long timeStampMS = millis();
#ifdef DEBUG_MODE
Serial.print("currentTimeMS = ");
Serial.print(currentTimeMS);
Serial.print(", startTimeBlueMS = ");
Serial.println(startTimeBlueMS);
#endif
if ( (currentTimeMS >= delayBeforeStartupMS) && (startupSeq == false) ) {
startupSeq = true; // don't revert back to false or this will repeat with the next iteration
startLoop = true; // set boolean for first time thru loop
}
// 1. Initial power-up -> dish and impulse crystal fade up to an orange/copper colour
// ==================================================================================
if ( (startupSeq == true) && (gotoWarp == false) ) {
if ( startLoop == true ) {
// Do this once for each cross fade colour
bluVal = blue[2]; // new value for Blue
delayB = calculateDelay(prevB, bluVal, powerUpFadeTimeMS);
stepB = prevB; // set PWM step increment value to previous Blue value
fadingB = true; // set flag for fading Blue
startLoop = false;
#ifdef DEBUG_MODE
Serial.print("bluVal = ");
Serial.print(bluVal);
Serial.print(", delayB = ");
Serial.print(delayB);
Serial.print(", stepB = ");
Serial.print(stepB);
Serial.print(", fadingB = ");
Serial.println(fadingB);
#endif
}
if ( ( (currentTimeMS - startTimeBlueMS) >= delayB ) && ( fadingB == true ) ) {
#ifdef DEBUG_MODE
Serial.print("currentTimeMS = ");
Serial.print(currentTimeMS);
Serial.print(", startTimeBlueMS = ");
Serial.print(startTimeBlueMS);
Serial.print(", stepB = ");
Serial.println(stepB);
#endif
if ( stepB != bluVal ) {
if ( isCommonAnode == true ) {
// analogWrite(redPin, 255 - redVal); // Write current values to LED pins
// analogWrite(grnPin, 255 - grnVal); // (using common anode, so need to use '255-')
if ( bluVal > prevR ) {
analogWrite(bluPin, 255 - (stepB++));
} else {
analogWrite(bluPin, 255 - (stepB--));
}
} else {
// analogWrite(redPin, redVal); // Write current values to LED pins
// analogWrite(grnPin, grnVal);
if ( bluVal > prevR ) {
analogWrite(bluPin, stepB++);
} else {
analogWrite(bluPin, stepB--);
}
}
startTimeBlueMS = currentTimeMS; // set wait timer
} else {
// Done fading this colour
fadingB = false;
}
} else {
// Cross fade complete; update current values for next cross fade
prevR = redVal;
prevG = grnVal;
prevB = bluVal;
gotoWarp = true; // set state variable to begin check for next cross fade
timeStampMS = currentTimeMS; // start timer for pause before going to warp
#ifdef DEBUG_MODE
// Serial.print("prevR = ");
// Serial.print(prevR);
// Serial.print(", prevG = ");
// Serial.print(prevG);
Serial.print(", prevB = ");
Serial.print(prevB);
Serial.print(", gotoWarp = ");
Serial.print(gotoWarp);
Serial.print(", timeStampMS = ");
Serial.println(timeStampMS);
#endif
}
}
// 2. Go to warp
// =============
}
int calculateDelay(int prevValue, int endValue, int overallFadeTimeMS) {
// For delay between PWM steps, value needs to be positive so take absolute value of difference
int delayMS = abs((endValue - prevValue)); // What's the overall gap?
if (delayMS) { // If its non-zero,
delayMS = overallFadeTimeMS/delayMS; // divide by overallFadeTimeMS
}
return delayMS;
}