DMX Lighting help

Help a noob time.

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.

Are you able to turn-on one (or two) colors at a time, or is it just the fading that's giving you trouble?

I don't see why that would give you white... You never have all 3 colors on at the same time.

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.

It won't give me white if I can pull off what I outlined. But that's what I'm having trouble conceptualizing in code.

The fading up and down isn't a problem. It's doing it in a specific order that I'm having trouble with.

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.

There is. I will put together a small example but it might not be today

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.

When the end conditions for a state are satisfied you set the next state to move to

For example, when in state fadingLed3

               if (ledLevel == 255)
                {
                    currentState = fadingLed5;
                }

maybe something like this..

/*
  https://forum.arduino.cc/t/dmx-lighting-help/1198060/10
*/
#define BTN_PIN 2
#define RED_PIN 6
#define GREEN_PIN 5
#define BLUE_PIN 3

//used in loop and fadeleds..
unsigned long now;
//loops vars for button debouncing/state..
unsigned long lastPush;
int intervalPush = 50;
byte btnState = 1;
//fading state toggles with each button press..
bool fading = false;



//vars used in FadeLeds..
unsigned long lastCycle;
int intervalCycle = 5;
byte state = 0;
int counter = 0;


void setup() {
  Serial.begin(115200);
  Serial.println("Ready..");
  pinMode(BTN_PIN, INPUT_PULLUP);

}

void loop() {

  now = millis();

  if (now - lastPush >= intervalPush) {
    byte b = digitalRead(BTN_PIN);
    if (b != btnState) {
      lastPush = now;
      btnState = b;
      if (btnState == LOW) {
        fading = !fading;
        if (!fading) {
          analogWrite(RED_PIN, 0);
          analogWrite(GREEN_PIN, 0);
          analogWrite(BLUE_PIN, 0);
        } else {
          analogWrite(RED_PIN, 255);
          state = 0;
          counter = 0;
        }
      }
    }
  }



  if (fading) {
    FadeLeds();
  }


}

void FadeLeds() {
  if (now - lastCycle >= intervalCycle) {
    lastCycle = now;
    switch (state) {
      case 0: if (counter <= 255) {
          analogWrite(BLUE_PIN, counter);
          counter++;
        } else {
          state++;
          counter = 255;
        }
        break;
      case 1: if (counter > 1) {
          analogWrite(RED_PIN, counter);
          counter--;
        } else {
          state++;
          counter = 0;
        }
        break;
      case 2: if (counter <= 255) {
          analogWrite(GREEN_PIN, counter);
          counter++;
        } else {
          state++;
          counter = 255;
        }
        break;
      case 3: if (counter > 1) {
          analogWrite(BLUE_PIN, counter);
          counter--;
        } else {
          state++;
          counter = 0;
        }
        break;
      case 4: if (counter <= 255) {
          analogWrite(RED_PIN, counter);
          counter++;
        } else {
          state++;
          counter = 255;
        }
        break;
      case 5: if (counter > 1) {
          analogWrite(GREEN_PIN, counter);
          counter--;
        } else {
          state = 0;
          counter = 0;
        }
        break;
    }
  }
}

Simmed here..

Led fading in it's own function needs to be called every loop and delay is now your enemy..
added a button for stopping and starting..

should be easy to follow and expand, well, i think anyways..

good luck.. ~q

1 Like

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.

Hope this helps!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.