Christmas Lights Control System -Arduino Mega - 16 Channel Relay Board

Here is the Arduino 1.0.1 code for controlling the lights via Vixen. It also includes a random mode for when you just want the lights to do something without having to have a Vixen program running.

#define CHANNEL01  2
#define CHANNEL02  3
#define CHANNEL03  4
#define CHANNEL04  5
#define CHANNEL05  6
#define CHANNEL06  7
#define CHANNEL07  8
#define CHANNEL08  9
#define CHANNEL09  10
#define CHANNEL10  11
#define CHANNEL11  12
#define CHANNEL12  13
#define CHANNEL13  44
#define CHANNEL14  45
#define CHANNEL15  46
#define CHANNEL16  47

#define RANDOM_MODE_PININ 52
#define RANDOM_MODE_PINOUT 53
#define RANDOM_MODE_SPEED 1000

int channels[] = {CHANNEL01,CHANNEL02,CHANNEL03,CHANNEL04,CHANNEL05,CHANNEL06,CHANNEL07,CHANNEL08,CHANNEL09,
CHANNEL10,CHANNEL11,CHANNEL12,CHANNEL13,CHANNEL14,CHANNEL15,CHANNEL16};

#define CHANNEL_COUNT 16
#define VIXEN_COM_SPEED 57600
#define PC_COM_SPEED 57600

boolean startingVixen = true;

void setup()
{
  Serial.begin(PC_COM_SPEED);
  Serial1.begin(VIXEN_COM_SPEED);
  
  pinMode(CHANNEL01,OUTPUT);
  pinMode(CHANNEL02,OUTPUT);
  pinMode(CHANNEL03,OUTPUT);
  pinMode(CHANNEL04,OUTPUT);
  pinMode(CHANNEL05,OUTPUT);
  pinMode(CHANNEL06,OUTPUT);
  pinMode(CHANNEL07,OUTPUT);
  pinMode(CHANNEL08,OUTPUT);
  pinMode(CHANNEL09,OUTPUT);
  pinMode(CHANNEL10,OUTPUT);
  pinMode(CHANNEL11,OUTPUT);
  pinMode(CHANNEL12,OUTPUT);
  pinMode(CHANNEL13,OUTPUT);
  pinMode(CHANNEL14,OUTPUT);
  pinMode(CHANNEL15,OUTPUT);
  pinMode(CHANNEL16,OUTPUT);
  
  // set up the switch for Vixen or Random mode
  pinMode(RANDOM_MODE_PININ, INPUT);
  digitalWrite(RANDOM_MODE_PININ,HIGH); // turn on the internal pull-up resistor
  pinMode(RANDOM_MODE_PINOUT, OUTPUT);
  
  turnLightsOff();  
}

void loop()
{
  if(digitalRead(RANDOM_MODE_PININ)==LOW){ // blink at random mode
    startingVixen=true;
    doRandomLights();
  }else{ // play from Vixen mode
    if(startingVixen==true)
      turnLightsOff();
    readFromVixen();
  }
}

void turnLightsOff()
{
  //turn them all off
  for(int channelIndex=0;channelIndex<16;channelIndex++){
    analogWrite(channels[channelIndex], 0);
  }
}

void doRandomLights()
{
    randomSeed(analogRead(0));
    Serial.println("Writting random values.");
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      int randNumber = random(255);
      analogWrite(channels[channelIndex], randNumber);
      Serial.print(randNumber, DEC);
      Serial.print(",");
    }
    Serial.println("");
    delay(random(100,RANDOM_MODE_SPEED));
}

void outputToLights(unsigned char* buffer)
{
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      analogWrite(channels[channelIndex], buffer[channelIndex]);
      Serial.print(buffer[channelIndex], DEC);
      Serial.print(",");
    }
    Serial.println("");
}

void readFromVixen()
{
  Serial.println("Waiting for data from Vixen.");
  startingVixen = false;
  char *footer="VIXEN_END";
  unsigned char buffer[CHANNEL_COUNT];
  char buffer2[CHANNEL_COUNT];
  int index=0;
  unsigned long time = millis();

  waitForVixenHeader();
  while (true) {
    int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    buffer[index] = inByte;
    buffer2[index] = inByte;
    buffer[index+1] = 0;
    buffer2[index+1] = 0;
    index++;
    if(index==9 && strcmp(footer,buffer2)==0){
      Serial.println(footer);
      return;
    }
    else if(index==CHANNEL_COUNT){
      outputToLights(buffer);
      index=0;
    }
  }
  Serial.println("");
}

void waitForVixenHeader()
{
  char *header="VIXEN_START";
  char buffer[12];
  int index = 0;
  unsigned long time = millis();

  while (true) {
    int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    buffer[index] = inByte;
    if(buffer[index]!=header[index]) {// not the right sequence restart
      index=-1;
    }
    buffer[index+1] = 0; // add null
    index++;
    if(index==11 && strcmp(header,buffer)==0){
      Serial.println(header);
      return;
    }
  }
}
  pinMode(CHANNEL01,OUTPUT);
  pinMode(CHANNEL02,OUTPUT);
  pinMode(CHANNEL03,OUTPUT);
  pinMode(CHANNEL04,OUTPUT);
  pinMode(CHANNEL05,OUTPUT);
  pinMode(CHANNEL06,OUTPUT);
  pinMode(CHANNEL07,OUTPUT);
  pinMode(CHANNEL08,OUTPUT);
  pinMode(CHANNEL09,OUTPUT);
  pinMode(CHANNEL10,OUTPUT);
  pinMode(CHANNEL11,OUTPUT);
  pinMode(CHANNEL12,OUTPUT);
  pinMode(CHANNEL13,OUTPUT);
  pinMode(CHANNEL14,OUTPUT);
  pinMode(CHANNEL15,OUTPUT);
  pinMode(CHANNEL16,OUTPUT);

You put these numbers in an array. Use a for loop!

Yep, i put them in the array so I could easily rearrange the channels. I get you point though. :slight_smile:

Here is an update to the code. I've added a power on self test that runs through each channel one at a time turning it on for 1.5 seconds.

#define CHANNEL01  2
#define CHANNEL02  3
#define CHANNEL03  4
#define CHANNEL04  5
#define CHANNEL05  6
#define CHANNEL06  7
#define CHANNEL07  8
#define CHANNEL08  9
#define CHANNEL09  10
#define CHANNEL10  11
#define CHANNEL11  12
#define CHANNEL12  13
#define CHANNEL13  44
#define CHANNEL14  45
#define CHANNEL15  46
#define CHANNEL16  47

#define RANDOM_MODE_PININ 52
#define RANDOM_MODE_PINOUT 53
#define RANDOM_MODE_SPEED 1000

int channels[] = {CHANNEL01,CHANNEL02,CHANNEL03,CHANNEL04,CHANNEL05,CHANNEL06,CHANNEL07,CHANNEL08,CHANNEL09,
CHANNEL10,CHANNEL11,CHANNEL12,CHANNEL13,CHANNEL14,CHANNEL15,CHANNEL16};

#define CHANNEL_COUNT 16
#define VIXEN_COM_SPEED 57600
#define PC_COM_SPEED 57600

boolean startingVixen = true;

void setup()
{
  Serial.begin(PC_COM_SPEED);
  Serial1.begin(VIXEN_COM_SPEED);
  
  pinMode(CHANNEL01,OUTPUT);
  pinMode(CHANNEL02,OUTPUT);
  pinMode(CHANNEL03,OUTPUT);
  pinMode(CHANNEL04,OUTPUT);
  pinMode(CHANNEL05,OUTPUT);
  pinMode(CHANNEL06,OUTPUT);
  pinMode(CHANNEL07,OUTPUT);
  pinMode(CHANNEL08,OUTPUT);
  pinMode(CHANNEL09,OUTPUT);
  pinMode(CHANNEL10,OUTPUT);
  pinMode(CHANNEL11,OUTPUT);
  pinMode(CHANNEL12,OUTPUT);
  pinMode(CHANNEL13,OUTPUT);
  pinMode(CHANNEL14,OUTPUT);
  pinMode(CHANNEL15,OUTPUT);
  pinMode(CHANNEL16,OUTPUT);
  
  // set up the switch for Vixen or Random mode
  pinMode(RANDOM_MODE_PININ, INPUT);
  digitalWrite(RANDOM_MODE_PININ,HIGH); // turn on the internal pull-up resistor
  pinMode(RANDOM_MODE_PINOUT, OUTPUT);
  
  turnLightsOff();  
  powerOnSelfTest();
}

// !!!! note the PWM values that need to be sent to the relay board are reversed from the
// values comming in from Vixen.  Vixen 0-255 (off-on), Relays 255-0 (off-on)

void loop()
{
  if(digitalRead(RANDOM_MODE_PININ)==LOW){ // blink at random mode
    startingVixen=true;
    doRandomLights();
  }else{ // play from Vixen mode
    if(startingVixen==true)
      turnLightsOff();
    readFromVixen();
  }
}

void powerOnSelfTest()
{
    Serial.println("Power on self test running.");
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      analogWrite(channels[channelIndex], 0); // turn on one channel at a time
      delay(1500); // wait 1.5 seconds
      analogWrite(channels[channelIndex], 255);
    }
    turnLightsOff(); 
}

void turnLightsOff()
{
  //turn them all off
  for(int channelIndex=0;channelIndex<16;channelIndex++){
    analogWrite(channels[channelIndex], 255);
  }
}

void doRandomLights()
{
    randomSeed(analogRead(0));
    Serial.println("Writting random values.");
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      int randNumber = random(255);
      randNumber = map(randNumber, 0, 255, 255, 0);
      analogWrite(channels[channelIndex], randNumber);
      Serial.print(randNumber, DEC);
      Serial.print(",");
    }
    Serial.println("");
    delay(random(100,RANDOM_MODE_SPEED));
}

void outputToLights(unsigned char* buffer)
{
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      analogWrite(channels[channelIndex], buffer[channelIndex]);
      Serial.print(buffer[channelIndex], DEC);
      Serial.print(",");
    }
    Serial.println("");
}

void readFromVixen()
{
  Serial.println("Waiting for data from Vixen.");
  startingVixen = false;
  char *footer="VIXEN_END";
  unsigned char buffer[CHANNEL_COUNT];
  char buffer2[CHANNEL_COUNT];
  int index=0;
  unsigned long time = millis();

  waitForVixenHeader();
  while (true) {
    int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    int lightByte = map(inByte, 0, 255, 255, 0);
    buffer[index] = lightByte;
    buffer2[index] = inByte;
    buffer[index+1] = 0;
    buffer2[index+1] = 0;
    index++;
    if(index==9 && strcmp(footer,buffer2)==0){
      Serial.println(footer);
      return;
    }
    else if(index==CHANNEL_COUNT){
      outputToLights(buffer);
      index=0;
    }
  }
  Serial.println("");
}

void waitForVixenHeader()
{
  char *header="VIXEN_START";
  char buffer[12];
  int index = 0;
  unsigned long time = millis();

  while (true) {
    int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    buffer[index] = inByte;
    if(buffer[index]!=header[index]) {// not the right sequence restart
      index=-1;
    }
    buffer[index+1] = 0; // add null
    index++;
    if(index==11 && strcmp(header,buffer)==0){
      Serial.println(header);
      return;
    }
  }
}

BTW the project is done, there will be new videos up on youtube tonight.

Here is the last of the project build videos, will have another video or two of it running with my lights later.

Sorry for constantly updating this thread but I keep making code changes.

// which pins control which channels
#define CHANNEL01  2
#define CHANNEL02  3
#define CHANNEL03  4
#define CHANNEL04  5
#define CHANNEL05  6
#define CHANNEL06  7
#define CHANNEL07  8
#define CHANNEL08  9
#define CHANNEL09  10
#define CHANNEL10  11
#define CHANNEL11  12
#define CHANNEL12  13
#define CHANNEL13  44
#define CHANNEL14  45
#define CHANNEL15  46
#define CHANNEL16  47

// Which pins is the random/Vixen mode switch using
#define RANDOM_MODE_PININ 52
#define RANDOM_MODE_PINOUT 53
#define RANDOM_MODE_SPEED 5000

int channels[] = {CHANNEL01,CHANNEL02,CHANNEL03,CHANNEL04,CHANNEL05,CHANNEL06,CHANNEL07,CHANNEL08,CHANNEL09,
CHANNEL10,CHANNEL11,CHANNEL12,CHANNEL13,CHANNEL14,CHANNEL15,CHANNEL16};

// how many channel will vixen be sending
#define CHANNEL_COUNT 16

// speed for the com port for talking with vixen
#define VIXEN_COM_SPEED 57600

// speed for talking with the serial monitor in the IDE
#define PC_COM_SPEED 57600

// setup your choice of dimming values or just on/off values
// the relays don't seem to be able to dim the lights so it looks
// like I will have to build dimmer circuits for next year. The
// doesn't change, just have to remove the relay bord and replace
// it with a dimmer circuit for each relay.
#define MODE_DIMMING 0
#define MODE_FULL 1
#define MODE MODE_FULL

boolean startingVixen = true;

void setup()
{
  Serial.begin(PC_COM_SPEED);
  Serial1.begin(VIXEN_COM_SPEED);
  
  // set the channel pins to output mode
  for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
    pinMode(channels[channelIndex],OUTPUT);
  }
  
  // set up the switch for Vixen or Random mode
  pinMode(RANDOM_MODE_PININ, INPUT);
  digitalWrite(RANDOM_MODE_PININ,HIGH); // turn on the internal pull-up resistor
  pinMode(RANDOM_MODE_PINOUT, OUTPUT);
  
  turnLightsOff();  
  powerOnSelfTest();
}

// !!!! note the PWM values that need to be sent to the relay board are reversed from the
// values comming in from Vixen.  Vixen 0-255 (off-on), Relays 255-0 (off-on)
void loop()
{
  if(digitalRead(RANDOM_MODE_PININ)==LOW){ // blink at random mode
    startingVixen=true;
    doRandomLights();
  }else{ // play from Vixen mode
    if(startingVixen==true)
      turnLightsOff();
    readFromVixen();
  }
}

void powerOnSelfTest()
{
    Serial.println("Power on self test running.");
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      Serial.print("Channel: ");
      Serial.println(channelIndex+1,DEC);
      analogWrite(channels[channelIndex], 0); // turn on one channel at a time
      delay(500); // wait .5 seconds
      analogWrite(channels[channelIndex], 255);
    }
    turnLightsOff(); 
}

void turnLightsOff()
{
  //turn them all off
  for(int channelIndex=0;channelIndex<16;channelIndex++){
    analogWrite(channels[channelIndex], 255);
  }
}

void doRandomLights()
{
    randomSeed(analogRead(0));
    Serial.println("Writting random values.");
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      if(MODE == MODE_DIMMING)
      {
        int randNumber = random(255);
        randNumber = map(randNumber, 0, 255, 255, 0);
        analogWrite(channels[channelIndex], randNumber);
        Serial.print(randNumber, DEC);
        Serial.print(",");
      }
      else // not dimming, just on or off
      {
        int randNumber = random(0, 255);
        randNumber = map(randNumber, 0, 255, 255, 0);
        if(randNumber<=128)
          analogWrite(channels[channelIndex], 0);
        else
          analogWrite(channels[channelIndex], 255);
        Serial.print(randNumber, DEC);
        Serial.print(",");
      }
    }
    Serial.println("");
    delay(random(100,RANDOM_MODE_SPEED));
}

void outputToLights(unsigned char* buffer)
{
    for(int channelIndex=0;channelIndex<CHANNEL_COUNT;channelIndex++){
      analogWrite(channels[channelIndex], buffer[channelIndex]);
      Serial.print(buffer[channelIndex], DEC);
      Serial.print(",");
    }
    Serial.println("");
}

void readFromVixen()
{
  Serial.println("Waiting for data from Vixen.");
  startingVixen = false;
  char *footer="VIXEN_END";
  unsigned char buffer[CHANNEL_COUNT];
  char buffer2[CHANNEL_COUNT];
  int index=0;
  unsigned long time = millis();

  waitForVixenHeader();
  while (true) {
    int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    int lightByte = map(inByte, 0, 255, 255, 0);
    buffer[index] = lightByte;
    buffer2[index] = inByte;
    buffer[index+1] = 0;
    buffer2[index+1] = 0;
    index++;
    if(index==9 && strcmp(footer,buffer2)==0){
      Serial.println(footer);
      return;
    }
    else if(index==CHANNEL_COUNT){
      outputToLights(buffer);
      index=0;
    }
  }
  Serial.println("");
}

void waitForVixenHeader()
{
  char *header="VIXEN_START";
  char buffer[12];
  int index = 0;
  unsigned long time = millis();

  while (true) {
  int inByte = Serial1.read();
    if(inByte==-1){
      if(index==0 && millis()-time>1000) // we haven't read anything in a second
        return;
      continue;
    }
    time = millis();
    buffer[index] = inByte;
    if(buffer[index]!=header[index]) {// not the right sequence restart
      index=-1;
    }
    buffer[index+1] = 0; // add null
    index++;
    if(index==11 && strcmp(header,buffer)==0){
      Serial.println(header);
      return;
    }
  }
}
1 Like

I've had a few questions from some folks about the project wiring so a made a little video which I'll be uploading to youtube shortly to try to explain it. Here is a little textual explanation that covers the most important points. I don't do very well trying to explain things verbally sometimes, which you will notice if you watch the new video. :stuck_out_tongue:

Basically the key to the project is this:

There are 3 common power bus lines. Ground, Positive and Negative.

The electrical sockets all have their positive terminals connected together on the common positive bus.

The electrical sockets all have their ground terminals connected on the common ground bus.

Each electrical socket has its negative terminal connected to one of the relays with the center screw terminal of the relay.

The third screw terminal (common open of the relay) of every relay is connected to the common negative bus.

When a relay closes it connects the electrical socket's negative terminal to the common negative bus thus allowing power to flow through the circuit for that individual electrical socket, causing the lights plugged into that socket to light up.

There are two power sources for the project. The power source supplying power to the electrical sockets (the orange plug in the project) and the power supply that is powering the relay board and the Arduino board (that little HP power supply in the project).

1 Like

Anyone know if it is possible to replace the mechanical relays on the relay board with equivalent solid state relays (non-zero crossing) and if so what would the part number be for the SSRs?

Well I've pretty much come to the conclusion that even if I could find SSRs to replace the mechanical relays I still wouldn't get the dimming control I want. For now I'm leaving the project as is, it works and is a good start for me. That gives me a year to figure out how to create dimming circuits, lol. The project was fun though. :slight_smile:

Will the latest code work on a arduino uno but using only 13 channels?

i can't think of any reason it wouldn't. If you have issues let me know.

I commented on your Youtube video regarding running your sketch using (Just a USB) I still could not make it work by removing the serial.print and changing Serial1 to Serial. I decided to write my own which only has one problem which only has one problem I was hoping you might help me with. All the lights turn on and my "squares" in vixen only turn them off. So it actually is working opposite of how it should. I did put in your code that turns all the lights off and the powerOnSelfTest and all that works fine. It only screws up when vixen runs.

/// Channel_Count must be > 0 and < 17
int Channel_Count = 16;
int Pin_Offset = 2;

void setup(){
    Serial.begin(57600);

    for (int channel_index=Pin_Offset; channel_index < Channel_Count + Pin_Offset; channel_index++)
    {
        pinMode(channel_index, OUTPUT); //set the pin mode for each channel
        
        //test the channels to see if they are all working
        analogWrite(channel_index, 0); //turn channel at [channel_index] on
        delay(250); // wait
        analogWrite(channel_index, 255); //turn channel at [channel_index] off
    }

    Serial.print("System Ready");
    turn_all_lights_off(); //All the lights come on by default, turn them off
}

void loop()
{
    if (Serial.available() >= Channel_Count)
    {
        for (int channel_index=Pin_Offset; channel_index < Channel_Count + Pin_Offset; channel_index++)
        {
            analogWrite(channel_index, Serial.read());
        }
   
        Serial.println("System Ready");
    }
}

//Force all the lights off
void turn_all_lights_off()
{
  
  for(int channel_index=Pin_Offset;channel_index<Channel_Count + Pin_Offset;channel_index++){
    analogWrite(channel_index, 255);
  }
}

Thank you so much for your help. You did an Amazing job on your build!!!

Hello zparticle

I saw your postings originally on you-tube and followed you to here. Awesome job and thanks to you I am inspired to do a set of my own this year.
Don't know if you have up graded to Vixen3, but I was not able to get your code to run correctly on Vixen3 until I changed the footer command to "END" instead of 'VIXEN_END" also so had to change the line for the 9 count buffer referring to footer. Apparently the new Vixen doesn't support that many characters on the footer, I didn't have to change the header.
Once I did that it came to life.

Thanks again for your post and you should probably put some videos of the finished product here

Rodney

Hello, I'm having a strange problem here. I have everything connected, the arduino communicates with the relay board. I know this because the self test works. When you connect power to the arduino, it cycles through all the relay switches, 1 - 16. It also does this when you press the reset button on the arduino. But when i try running a sequence in vixen, nothing happens. I have the COM ports set up correctly, COM2, Baud 57600. I can't seem to figure out why its not communicating.

I've tried using both Vixen 2 and Vixen 3 and no luck. Any help would be awesome, thank you.

hi
same problem
com2 baud 57600
and nothing
test vixen 3

tx for help

richer2003:
Hello, I'm having a strange problem here. I have everything connected, the arduino communicates with the relay board. I know this because the self test works. When you connect power to the arduino, it cycles through all the relay switches, 1 - 16. It also does this when you press the reset button on the arduino. But when i try running a sequence in vixen, nothing happens. I have the COM ports set up correctly, COM2, Baud 57600. I can't seem to figure out why its not communicating.

I've tried using both Vixen 2 and Vixen 3 and no luck. Any help would be awesome, thank you.

Been beating my head against the wall for a couple of nights now with a very similar issue. When V 2 connects with my mega 2560 the lights (which are on) turn off until the sequence has run, then they come back on. Extremely frustrating. Did you find a resolution for your problem?

Hi all .. it is still summer but I am in full swing of my Christmas build.
I am going to be using led strings and rope light. No pixels this year. I am using arduino 2560 boards connected to many relay boards. Hooking up many arduinos 2560 in parallel to the pc via w5100 ethernet boards to a hub. Have been doing lots of reading the last 6months and this appears to be the easiest for what I am doing.. lights on and off only.

I have found the sketches for the arduino to vixen and relays. I am not having good luck finding information on setting up the w5100 to the pc and vixen. Any help, suggestions and guidance would be appreciated.
Thanks very much
Garnet

I don't if this helps, but I have relay boards controlled by sending data to a shift register which then controls relay coils, couple form factors: Uno shield, Mega shield, and standalone designed for easy daisychaining.
http://www.crossroadsfencing.com/BobuinoRev17/


Here's a couple videos.