I'm building a DMX light controller using an Arduino Mega and the SimpleDMX library.
I need some advice about how to write code to achieve a specific effect. I want the lights to just slowly fade from one color to another. I thought it would be as simple as randomly choosing values for the RGB channels and slowly ramping them up and down. But that doesn't produce the look I'm after. Most of the time, this just looks like white light. In order to get distinct colors, I need combos of two (RB or BG or GR for example). Or single colors.
If you don't know DMX, each color channel (R,G,B in this case) is set with a value from 0-255. The higher the value the brighter that particular color is.
So I'd like to do something like:
Start with red at 255
Slowly ramp blue up from 0 to 255
Once blue reaches 255, slowly ramp red down to 0
Once red is 0, slowly ramp green up from 0-255
Once green is 255 slowly ramp blue down to 0
Once blue is zero, slowly ramp red up to 255
Once red is 255, slowly ramp green down to 0
Then we'd be back at the top
I'm stumped about how to do this and could use some advice about where to start. Any advice would be appreciated.
It's not that complicated, you can use the "FadeUp" example from the library as a base.
You just have to repeat for each channel and do the fadedown in reverse.
Fading the individual channels up and down is easy. But I want to fade them up and down in a specific sequence. That's what giving me a hard time. (I did mention this was help a noob time.)
Is the best way to go just a series of nested if/else statements? It seems like there'd be a more elegant way.
If all the program is doing then a series of for loops, one for each up or down sequence using delay()s is good enough.
If the program needs to do anything else whilst the fading up/down is occurring then a different technique can be used. You might want to use the alternative technique anyway, just for experience
It would involve using a Finite State Machine to run only the code for the current fade and millis() for timing
It's doing a lot of other stuff. I have some timers going on for tap tempo sort of things and I can use them for the fades too. It's the structuring and sequencing of the fades that's tripping me up. I guess I can just do a series of if statements, but I figured there had to be a better way.
Here's what I came up with. It works. But seems clunky and overly complicated.
int red=0;
int red_dir=0; //-1=down, 0=stopped, 1=up
int blue=0;
int blue_dir=0; //-1=down, 0=stopped, 1=up
int green=0;
int green_dir=0; //-1=down, 0=stopped, 1=up
int step=5;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
red=255;
blue_dir=1;
}
void loop() {
// put your main code here, to run repeatedly:
if(red_dir==1){
red = red+step;
if (red > 255){
red=255;
red_dir=0;
green_dir=-1;
}
}else if (red_dir==-1){
red = red-step;
if (red < 0){
red=0;
red_dir=0;
green_dir=1;
}
}
if(blue_dir==1){
blue = blue+step;
if (blue > 255){
blue=255;
blue_dir=0;
red_dir=-1;
}
}else if (blue_dir==-1){
blue = blue-step;
if (blue < 0){
blue=0;
blue_dir=0;
red_dir=1;
}
}
if(green_dir==1){
green = green+step;
if (green > 255){
green=255;
green_dir=0;
blue_dir=-1;
}
}else if (green_dir==-1){
green = green-step;
if (green < 0){
green=0;
green_dir=0;
blue_dir=1;
}
}
Serial.print("red=");
Serial.println(red);
Serial.print("blue=");
Serial.println(blue);
Serial.print("green=");
Serial.println(green);
delay(1000);
}
Take a look at the principles of this sketch. The sketch can be in any one of several states and the switch/case will ensure that the code for the current state is executed.
I chose to increment the value written to the LED for each state but you can obviously ramp up or ramp down the value to meet your own needs. Note the use of millis() for timing which allows other code to run in the loop() function. Add as many states as you need. I find the switch/case easier to read than if/else and giving the states meaningful names makes it easier too
const byte ledPins[] = { 3, 5, 6, 9 };
const int period = 20;
const byte LED_COUNT = sizeof(ledPins) / sizeof(ledPins[0]);
enum states
{
fadingLed3,
fadingLed5,
fadingLed6,
fadingLed9
};
byte currentState = fadingLed3;
byte ledLevel = 0;
void setup()
{
Serial.begin(115200);
for (int x = 0; x < LED_COUNT; x++)
{
pinMode(ledPins[x], OUTPUT);
analogWrite(ledPins[x], 255);
}
}
void loop()
{
unsigned long currentTime = millis();
static unsigned long prevTime = currentTime;
switch (currentState)
{
case fadingLed3:
if (currentTime - prevTime >= period)
{
ledLevel++;
analogWrite(ledPins[fadingLed3], ledLevel);
prevTime = currentTime;
if (ledLevel == 255)
{
currentState = fadingLed5;
}
}
break;
case fadingLed5:
if (currentTime - prevTime >= period)
{
ledLevel++;
analogWrite(ledPins[fadingLed5], ledLevel);
prevTime = currentTime;
if (ledLevel == 255)
{
currentState = fadingLed6;
}
break;
}
case fadingLed6:
if (currentTime - prevTime >= period)
{
ledLevel++;
analogWrite(ledPins[fadingLed6], ledLevel);
prevTime = currentTime;
if (ledLevel == 255)
{
currentState = fadingLed9;
}
break;
}
case fadingLed9:
if (currentTime - prevTime >= period)
{
ledLevel++;
analogWrite(ledPins[fadingLed9], ledLevel);
prevTime = currentTime;
if (ledLevel == 255)
{
currentState = fadingLed3;
}
break;
}
}
//any other code that does not block the free running of the loop() function can go in here
}
I think I follow you. And I might be missing something. But I'm missing how to step through the cases in a specified order.
But how would you set which state is active and in which order? And what about fading up and down? I guess I'd need additional states for fade up and fade down. But I still don't know how I'd go through those states one by one without the complicated if/else structure. And this would fade one individual color up and down at a time. But that will make the stage dark when the values are low. And it won't create color combos (like purple with red and blue mixed).
Here's what I'm trying to accomplish. Once I hit the button to put the lights in "fade mode" I want to:
Start with red at 255
Slowly ramp blue up from 0 to 255
Once blue reaches 255, slowly ramp red down to 0
Once red is 0, slowly ramp green up from 0-255
Once green is 255 slowly ramp blue down to 0
Once blue is zero, slowly ramp red up to 255
Once red is 255, slowly ramp green down to 0
Then we're back at our initial state and we'll start over. I do like the idea of using a case structure and agree that's easier to read. But I'm not sure how to apply it to get the desired effect.
For the effect you're describing, you can use a combination of nested loops and conditional statements in your Arduino code. Here's a basic outline to get you started:
cppCopy code
#include <SimpleDMX.h>
// Define DMX channels for R, G, and B
int channelR = 1;
int channelG = 2;
int channelB = 3;
SimpleDMX dmx;
void setup() {
// Initialize DMX
dmx.setMaxChannel(3);
dmx.begin();
}
void loop() {
// Start with red at 255
dmx.write(channelR, 255);
dmx.write(channelG, 0);
dmx.write(channelB, 0);
dmx.update();
// Slowly ramp blue up from 0 to 255
for (int i = 0; i <= 255; i++) {
dmx.write(channelB, i);
dmx.update();
delay(10);
}
// Slowly ramp red down to 0
for (int i = 255; i >= 0; i--) {
dmx.write(channelR, i);
dmx.update();
delay(10);
}
// Repeat the above steps for green and blue ramps
// Add similar loops for green and blue ramps
// Continue the pattern to complete the effect
}
This is a basic example, and you may need to fine-tune the delays and intensity values for your specific setup. Feel free to modify and expand upon this code to achieve the desired effect.