Difference between revisions of "Arduino and the mechanical relay"

From diychristmas.org wiki
Jump to navigation Jump to search
(Started explaining the code)
m
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
So you bought an arduino and then you got one of those cheap relay boards and you decided to try to do blinky flashy with them. First, it is not a great idea but assuming you want to try it, here you go!
 +
 +
== Second, here's the program code -- comments follow later! ==
 +
 
  <nowiki>
 
  <nowiki>
// *** diychristmas.org free software so long as this line remains in sketch!  
+
// *** diychristmas.org free software so long as this line remains in sketch!
// Vixen/xLights "generic" serial mechanical relay sketch
+
// Basically Vixen "generic" serial mechanical relay sketch
 +
//  - Setup a generic serial controller to use this sketch
 
//  - Uses digitalWrite as this outputs LOW or HIGH no pwm values
 
//  - Uses digitalWrite as this outputs LOW or HIGH no pwm values
 
//  - This is what is needed for a mechanical relay
 
//  - This is what is needed for a mechanical relay
Line 11: Line 16:
  
 
// Vixen information
 
// Vixen information
//  - CHANNEL_COUNT is number of channels it will listen for from Vixen
+
// speed for the com port for talking with player
const int CHANNEL_COUNT = 8;
 
// speed for the com port for talking with vixen
 
 
const long SERIAL_COM_SPEED = 115200L;
 
const long SERIAL_COM_SPEED = 115200L;
 
// Generic Serial controller config - must be present, must match controller setup
 
// Generic Serial controller config - must be present, must match controller setup
const int VIXEN_HEADER[3] = {33, 34, 35}; // This is character !"# (hard to replicate in Vixen sequence)
+
const int CONTROLLER_HEADER[3] = {33, 34, 35}; // This is character !"# (hard to replicate in sequencer)
 +
 
 
// Relays
 
// Relays
 
//  - most mechanical relays turn on when pin is low, off on high
 
//  - most mechanical relays turn on when pin is low, off on high
Line 23: Line 27:
  
 
// these are program variables we need to use in multiple places
 
// these are program variables we need to use in multiple places
const int SIZE_OF_HEADER = sizeof(VIXEN_HEADER) / sizeof(int); // no need to change
+
const int SIZE_OF_HEADER = sizeof(CONTROLLER_HEADER) / sizeof(int); // no need to change
 +
const int CHANNEL_COUNT = sizeof(channels) / sizeof(int); // no need to change
 
int buffer[CHANNEL_COUNT]; // no need to change going to hold relay output values
 
int buffer[CHANNEL_COUNT]; // no need to change going to hold relay output values
  
Line 29: Line 34:
 
{
 
{
 
   Serial.begin(SERIAL_COM_SPEED);
 
   Serial.begin(SERIAL_COM_SPEED);
  Serial.write("Setup");
 
  
 
   // set the channel pins to output mode
 
   // set the channel pins to output mode
Line 42: Line 46:
 
void loop()
 
void loop()
 
{
 
{
   waitForVixenHeader(VIXEN_HEADER);
+
   waitForControllerHeader(CONTROLLER_HEADER);
   readFromVixen();
+
   readSequenceData();
 
   outputToLights();
 
   outputToLights();
 
}
 
}
Line 70: Line 74:
 
}
 
}
  
void readFromVixen()
+
void readSequenceData()
 
{
 
{
 
   char buffer2[CHANNEL_COUNT];
 
   char buffer2[CHANNEL_COUNT];
Line 84: Line 88:
 
}
 
}
  
void waitForVixenHeader(int header[])
+
void waitForControllerHeader(int header[])
 
{
 
{
 
   for (int i = 0; i < SIZE_OF_HEADER; i++) {
 
   for (int i = 0; i < SIZE_OF_HEADER; i++) {
 
     // wait for serial available
 
     // wait for serial available
 
     while (!Serial.available()) {}
 
     while (!Serial.available()) {}
     // Check the byte until it matches the VIXEN_HEADER byte
+
     // Check the byte until it matches the CONTROLLER_HEADER byte
 
     int inByte = Serial.read();
 
     int inByte = Serial.read();
     if (inByte != VIXEN_HEADER[i]) {
+
     if (inByte != CONTROLLER_HEADER[i]) {
 
       i = -1; // wrong data set to "zero"
 
       i = -1; // wrong data set to "zero"
 
     }
 
     }
Line 116: Line 120:
 
   turnLightsOff();
 
   turnLightsOff();
 
}
 
}
 +
 
</nowiki>
 
</nowiki>
  
 
Let's talk about the code...
 
Let's talk about the code...
At the top of the sketch, you will find the definitions of how you are going to use the Arduino with Vixen, xLights or any other serial output. These things are self explanatory but for the "I've never seen an Arduino or Vixen setup" people, here is another try to explain them.
+
At the top of the sketch, you will find the definitions of how you are going to use the Arduino with Vixen or any other serial output. These things are self explanatory but for the "I've never seen an Arduino or Vixen setup" people, here is another try to explain them.
  
 
== Things you might have to change ==
 
== Things you might have to change ==
Line 125: Line 130:
 
int channels[] = {2, 3, 4, 5, 6, 7, 8, 10};
 
int channels[] = {2, 3, 4, 5, 6, 7, 8, 10};
  
This line assigns pins from the arduino to specific "channels" in order. In your software, you would assign channel one to Shrub1, channel two to Shrub2, etc. You will need to map these through the Arduino pins to the specific relay on your relay board and out to the lights.
+
This line assigns pins from the arduino to specific "channels". The order of definition is the order the pins will be turned on or off. In your software, you would assign channel one to Shrub1, channel two to Shrub2, etc. This is done in the element to controller mapping in Vixen. You will need to map these through the Arduino pins to the specific relay on your relay board and out to the lights.
 
It might look like this:
 
It might look like this:
 
{| class="wikitable"
 
{| class="wikitable"
Line 176: Line 181:
  
  
const int CHANNEL_COUNT = 8;
+
const long SERIAL_COM_SPEED = 115200L;
const long VIXEN_COM_SPEED = 115200L;
+
This sets the communication speed back to the player. This will have to match the controller setup in your player. 115,200 baud is equivalent to 115,200 bps which translates to:
const int VIXEN_HEADER[3] = {33, 34, 35};
+
115,200 / 8 bits of data plus 2 bits of overhead = 11,520 bytes per second
 +
11,520 bytes / 20 fps (50 ms) = 576 bytes per frame
 +
 
 +
This is way more than enough for this setup but this becomes more important when we start doing pixels. So, we might as well use the faster speed.
 +
 
 +
const int CONTROLLER_HEADER[3] = {33, 34, 35};
 +
This is how we find the beginning of a packet. This is also set in the controller setup in the player. The player will send these bytes before sending the data bytes. We define them in integer so consult an ASCII chart to find the appropriate values. Using three values and using values that are one digit apart will make it hard to accidentally send these values. The reason we do this is because the communication stream could be interrupted and this will re-sync the stream. This also means that if something is wrong with your lights, you can fix it, turn the power back on and it will find which lights to turn on in order.
 +
 
 
const int ON  = LOW;
 
const int ON  = LOW;
 
const int OFF = HIGH;
 
const int OFF = HIGH;
 +
Most mechanical relays turn on when we send off (LOW or x'00'). If your relays are off when they should be on (and on when they should be off), then you will need to flip the LOW and HIGH.
 +
 +
Everything else in this sketch should just work with generic serial controller. As of June, 2018, this limits you to Vixen 2 or Vixen 3. If you need a Renard sketch, see [[Arduino and the mechanical relay using Renard]].

Latest revision as of 19:51, 3 August 2018

So you bought an arduino and then you got one of those cheap relay boards and you decided to try to do blinky flashy with them. First, it is not a great idea but assuming you want to try it, here you go!

Second, here's the program code -- comments follow later!

// *** diychristmas.org free software so long as this line remains in sketch!
// Basically Vixen "generic" serial mechanical relay sketch
//  - Setup a generic serial controller to use this sketch
//  - Uses digitalWrite as this outputs LOW or HIGH no pwm values
//  - This is what is needed for a mechanical relay

// Hookup the relay in order of channel definition
// Define as many as you need
// put channels (pins) in an array so we can use looping structures to control
int channels[] = {2, 3, 4, 5, 6, 7, 8, 10};

// Vixen information
// speed for the com port for talking with player
const long SERIAL_COM_SPEED = 115200L;
// Generic Serial controller config - must be present, must match controller setup
const int CONTROLLER_HEADER[3] = {33, 34, 35}; // This is character !"# (hard to replicate in sequencer)

// Relays
//  - most mechanical relays turn on when pin is low, off on high
const int ON  = LOW;
const int OFF = HIGH;

// these are program variables we need to use in multiple places
const int SIZE_OF_HEADER = sizeof(CONTROLLER_HEADER) / sizeof(int); // no need to change
const int CHANNEL_COUNT = sizeof(channels) / sizeof(int); // no need to change
int buffer[CHANNEL_COUNT]; // no need to change going to hold relay output values

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

  // set the channel pins to output mode
  for (int channelIndex = 0; channelIndex < CHANNEL_COUNT; channelIndex++) {
    pinMode(channels[channelIndex], OUTPUT);
  }

  turnLightsOff();
  powerOnSelfTest(); // watch your lights to make sure they are all going on in order
}

void loop()
{
  waitForControllerHeader(CONTROLLER_HEADER);
  readSequenceData();
  outputToLights();
}

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

void outputToLights()
{
  for (int channelIndex = 0; channelIndex < CHANNEL_COUNT; channelIndex++) {
    // we should only be seeing 255 and 0 but in case someone tried a fade
    // 48 is used for debug purposes. You can use the serial monitor to test
    // send this string !"#01010101 to turn on every other relay as 0 == dec(48) 1 == dec(49)
    if (buffer[channelIndex] > 48) {
      digitalWrite(channels[channelIndex], ON);
    }
    else {
      digitalWrite(channels[channelIndex], OFF);
    }
  }
}

void readSequenceData()
{
  char buffer2[CHANNEL_COUNT];
  int index = 0;

  while (Serial.available() < CHANNEL_COUNT) {}
  // We have enough data!
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    int inByte = Serial.read();
    buffer[i] = inByte;
    Serial.write(inByte);
  }
}

void waitForControllerHeader(int header[])
{
  for (int i = 0; i < SIZE_OF_HEADER; i++) {
    // wait for serial available
    while (!Serial.available()) {}
    // Check the byte until it matches the CONTROLLER_HEADER byte
    int inByte = Serial.read();
    if (inByte != CONTROLLER_HEADER[i]) {
      i = -1; // wrong data set to "zero"
    }
  }
  // found the header!
}

// powerOnSelfTest - does a couple of checks to make sure everything turns on and off
//  - watch your lights, they should go on in order
void powerOnSelfTest()
{
  // This routines turns on and off each relay in order
  for (int channelIndex = 0; channelIndex < CHANNEL_COUNT; channelIndex++) {
    digitalWrite(channels[channelIndex], ON); // turn on one channel at a time
    delay(2000);
    digitalWrite(channels[channelIndex], OFF);
  }
  // This routines turns on each relay in order leaving them all on for .5 seconds
  for (int channelIndex = 0; channelIndex < CHANNEL_COUNT; channelIndex++) {
    digitalWrite(channels[channelIndex], ON); // all on test
    delay(500); // wait .5 seconds
  }
  delay(2000); // slight pause to check all on
  turnLightsOff();
}


Let's talk about the code... At the top of the sketch, you will find the definitions of how you are going to use the Arduino with Vixen or any other serial output. These things are self explanatory but for the "I've never seen an Arduino or Vixen setup" people, here is another try to explain them.

Things you might have to change

int channels[] = {2, 3, 4, 5, 6, 7, 8, 10};

This line assigns pins from the arduino to specific "channels". The order of definition is the order the pins will be turned on or off. In your software, you would assign channel one to Shrub1, channel two to Shrub2, etc. This is done in the element to controller mapping in Vixen. You will need to map these through the Arduino pins to the specific relay on your relay board and out to the lights. It might look like this:

Description Channel Pin Relay
Shrub1Left 1 2 In1
Shrub2 2 3 In2
Shrub3 3 4 In3
Shrub4Right 4 5 In4
Post1Left 5 6 In5
Post2 6 7 In6
Post3 7 8 In7
Post4Right 8 10 In8


const long SERIAL_COM_SPEED = 115200L; This sets the communication speed back to the player. This will have to match the controller setup in your player. 115,200 baud is equivalent to 115,200 bps which translates to: 115,200 / 8 bits of data plus 2 bits of overhead = 11,520 bytes per second 11,520 bytes / 20 fps (50 ms) = 576 bytes per frame

This is way more than enough for this setup but this becomes more important when we start doing pixels. So, we might as well use the faster speed.

const int CONTROLLER_HEADER[3] = {33, 34, 35}; This is how we find the beginning of a packet. This is also set in the controller setup in the player. The player will send these bytes before sending the data bytes. We define them in integer so consult an ASCII chart to find the appropriate values. Using three values and using values that are one digit apart will make it hard to accidentally send these values. The reason we do this is because the communication stream could be interrupted and this will re-sync the stream. This also means that if something is wrong with your lights, you can fix it, turn the power back on and it will find which lights to turn on in order.

const int ON = LOW; const int OFF = HIGH; Most mechanical relays turn on when we send off (LOW or x'00'). If your relays are off when they should be on (and on when they should be off), then you will need to flip the LOW and HIGH.

Everything else in this sketch should just work with generic serial controller. As of June, 2018, this limits you to Vixen 2 or Vixen 3. If you need a Renard sketch, see Arduino and the mechanical relay using Renard.