Interlocking Relay Code

Hello,

I have a question in regards to interlocking relays. I am looking for program 3 relays and some how interlock them. Meaning when Relay 1 is "ON" Relay 2, and Relay 3 must remain "OFF" until Relay 1 is switched "OFF". After Relay 1 status is "OFF" the program can check to see which of the remaining Relays needs to be turned "ON", at which point the remaining two relays must be "OFF". What is happening now is that multiple relays want to turn "ON" at the same time. This causes the relay board to make all kinds of weird sounds. The code is posted below:

const int ValueHigh1 = 100; // high value for turning Relay 1 off
const int ValueLow1 = 20; // low value for turning Relay 1 on
const int VlaueHigh2 = 90; // high value for turning Relay 2 off
const int ValueLow2 = 30; // low value for turning Relay 2 on
const int VlaueHigh3 = 75; // high value for turning Relay 3 off
const int ValueLow3 = 45; // low value for turning Relay 3 on

int R1=4; //digital pin for relay 1
int R2=5; //digital pin for relay 2
int R3=6; //digital pin for relay 3



void setup() {

  //INITIALIZING RELAY STATES:
  digitalWrite(R1,LOW); // OFF
  pinMode(R1, OUTPUT);
  
  digitalWrite(R2,LOW); // OFF
  pinMode(R2, OUTPUT);
  
  digitalWrite(R3,LOW); // OFF
  pinMode(R3, OUTPUT);
  

}

void loop() {
  // put your main code here, to run repeatedly:


if ( R1 > ValueHigh1) { //sensor values inversed so when hit low turn on
    digitalWrite(R1, HIGH); //on
    digitalWrite(R2,LOW); //off
    digitalWrite(R3,LOW); //off

  }
   if (R1 <= ValueLow1) {
    digitalWrite(R1,LOW); //off 
 }


  if ( R2 > ValueHigh2) { //sensor values inversed so when hit low turn on
    digitalWrite(R2, HIGH); //on
    digitalWrite(R1,LOW); //off 
    digitalWrite(R3,LOW); //off

    
  }
   if (R2 <= ValueLow2) {
    digitalWrite(R2,LOW); //off
 }
 

   if ( R3 > ValueHigh3) { //sensor values inversed so when hit low turn on
    digitalWrite(R3, HIGH); //on
    digitalWrite(R1,LOW); //off    
    digitalWrite(R2,LOW); //off


  }
   if (R3 <= ValueLow3) {
    digitalWrite(R3,LOW); //off
    
 }

}

Please note that I am not using any kind of delay function or loop because i want this to be continuous. Furthermore, R1,R2, and R3 have "ValueHigh" and "ValueLow" values that overlap. Simply changing these values will not solve the problem, because those values must overlap for the project. I am seeking help for the "interlocking" feature/code if one exists. Or any ideas on how to tackle this problem.

Note: I also tried using "and" conditions within my "if" statement. For example, if ( (R1 > ValueHigh1 ) && (R2==LOW) && (R3==LOW) ) then execute desired function. Problem here is it never goes into any of the "if" loops when written as such because there are times when "ValueHigh1" , "ValueHigh2", and "ValueHigh3" are all reached at the same time causing all Relay status' to be HIGH.

Simple solution would be create a state variable that is a combination of the states of the three relays - one bit per relay. Use that in a switch statement. You then have 8 cases, each corresponding to one of the 8 possible states of the relays. Sounds like the NEXT state should be easy to determine within each case.

I have not looked, but I bet you are NOT monitoring the output state of the relays. You know your code runs 1,000 times faster than a relay can change state.

1 Like

Your code doesn't make sense:
R1, R2 and R3 are output pins for the relays but you compare them to various values far higher than anything that could possibly represent a pin being HIGH or LOW, in any case, it's the pin numbers you are comparing, so that's always going to give the same result.

Nowhere do you read anything that could be comparable to your high and low values, and your comments mention a mysterious sensor that is never read.

look this over
(tested using switches and LEDs on multi-function shield)
i'll leave it to you to decide how to set inps []

const byte PinInps [] = { A1, A2, A3 };
const byte PinLeds [] = { 11, 12, 13 };

const int Ninp = sizeof(PinInps);
const int Nled = sizeof(PinLeds);

byte inps [Ninp];

enum { InpOff = HIGH, InpOn = LOW };
enum { RlyOff = HIGH, RlyOn = LOW };

enum { ALL_OFF, Rly1, Rly23 };
int state = ALL_OFF;

// -----------------------------------------------------------------------------
void loop ()
{
    for (unsigned n = 0; n < Ninp; n++)  {
        inps [n] = digitalRead (PinInps [n]);
        Serial.print (inps [n]);
    }

    switch (state) {
    case ALL_OFF:
        if (InpOn == inps [0])  {
            state = Rly1;
            digitalWrite (PinLeds [0], RlyOn);
        }
        else if (InpOn == inps [1] || InpOn == inps [2])
            state = Rly23;
        break;

    case Rly1:
        if (InpOff == inps [0])  {
            state = ALL_OFF;
            digitalWrite (PinLeds [0], RlyOff);
        }
        break;

    case Rly23:
        if (InpOff == inps [1] && InpOff == inps [2])  {
            state = ALL_OFF;
            digitalWrite (PinLeds [1], RlyOff);
            digitalWrite (PinLeds [2], RlyOff);
        }

        if (InpOff == inps [1])
            digitalWrite (PinLeds [1], RlyOff);
        else
            digitalWrite (PinLeds [1], RlyOn);

        if (InpOff == inps [2])
            digitalWrite (PinLeds [2], RlyOff);
        else
            digitalWrite (PinLeds [2], RlyOn);
        break;
    }

    Serial.print  (" ");
    Serial.print  (state);
    Serial.println ();
}

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

    for (unsigned n = 0; n < Ninp; n++)
        pinMode (PinInps [n], INPUT_PULLUP);

    for (unsigned n = 0; n < Nled; n++)  {
        pinMode      (PinLeds [n], OUTPUT);
        digitalWrite (PinLeds [n], RlyOff);
    }
}
1 Like

@ttommttomm - Your first code (when adjusted) has a race condition where relays will be made to fight against each other depending on the input. That is to say, if "value 1" were at a level where it turns relay 2 OFF, but "value 2" is at a level where it turns relay 2 ON, you will have an on/off/on/etc of relays every loop. Make a truth table showing Value 1, 2, and 3 above the high threshold, between high and low threshold, and below the low threshold.... and show the state for all relays.

(val)     REL1   REL2   REL3
1 above   .      .      .
1 between .      .      .
1 below   OFF    OFF    OFF
2 above   .      .      .
2 between .      .      .
2 below   .      .      .
3 above   .      .      .
3 between .      .      .
3 below   .      .      .

Thank you @RayLivingston

Please correct me if im wrong, but what i understood is i should make something like a truth table? for example 0 is OFF and 1 is ON, so table for all the states looks like this:

R1 R2 R3
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

Except from the above table i only want 4 states (000,001,010,100). Sorry if I misunderstood and complicated the matter further, but im not entirely sure what you mean by "create a state variable that is a combination of the states of the three relays - one bit per relay". Coding is not my strong suit :sweat_smile:

@Paul_KD7HB correct, i am not monitoring the output states of the relays. To be honest I never knew i could, but i will look into it. All I know is that I can assume the output of the relay based on whether the digital write is HIGH or LOW.

Thank you @PerryBebbington i was too quick in posting the original code I have edited the code and posted the new one below:

const int ValueHigh = 100; // high value for turning Relay 1 off
const int ValueLow = 20; // low value for turning Relay 1 on


int R1=4; //digital pin for relay 1
int R2=5; //digital pin for relay 2
int R3=6; //digital pin for relay 3

int sensor1 = A1;//Declare a variable for the sensor 
int sensor2 = A2;//Declare a variable for the sensor 
int sensor3 = A3;//Declare a variable for the sensor 

float SensorReading1=0;
float SensorReading2=0;
float SensorReading3=0;



void setup() {

  //INITIALIZING RELAY STATES:
  digitalWrite(R1,LOW); // OFF
  pinMode(R1, OUTPUT);
  
  digitalWrite(R2,LOW); // OFF
  pinMode(R2, OUTPUT);
  
  digitalWrite(R3,LOW); // OFF
  pinMode(R3, OUTPUT);
  

}

void loop() {
  // put your main code here, to run repeatedly:

  //Read sensor values
     SensorReading1 = analogRead(sensor1);
     SensorReading2 = analogRead(sensor2);
     SensorReading3 = analogRead(sensor3);


if ( SensorReading1 > ValueHigh) { 
    digitalWrite(R1, HIGH); //on
    digitalWrite(R2,LOW); //off
    digitalWrite(R3,LOW); //off

  }
   if (SensorReading1 <= ValueLow) {
    digitalWrite(R1,LOW); //off 
 }


  if ( SensorReading2 > ValueHigh) { 
    digitalWrite(R2, HIGH); //on
    digitalWrite(R1,LOW); //off 
    digitalWrite(R3,LOW); //off

    
  }
   if (SensorReading2 <= ValueLow) {
    digitalWrite(R2,LOW); //off
 }
 

   if ( SensorReading3 > ValueHigh) { 
    digitalWrite(R3, HIGH); //on
    digitalWrite(R1,LOW); //off    
    digitalWrite(R2,LOW); //off


  }
   if (SensorReading3 <= ValueLow) {
    digitalWrite(R3,LOW); //off
    
 }

}

Please note that I have also edited the code to now include a single "ValueHigh" and "ValueLow" for the relays to further emphasize the need for the interlock feature as all relays can have the same value at any moment.

This is the problem im trying to solve:

I have a large roof which im fixing temp sensors too. The area of the roof requires 3 sensors to make it effective. I want to cool down the roof by spraying water on it until it reached a certain temp. After which the program can go back to checking all the sensors values again. At any point if any of the temp sensors reached the high temp only that corresponding relay must be activated until the desired low temp has been reached. I do not want interruptions in between where another part of the roof is now being cooled for a few seconds here and there, as this will never achieve the desired cooling effect.

Thank you so much @gcjr it is much appreciated. I see you are using arrays? Im not the greatest coder but I will research it and try my best to understand and alter this code as need be :pray:

Yes, and is probably ok, as long as you don't expect the relay to transfer by the time of the next Arduino instruction.

@xfpd yes that race condition is a great description to what I am facing. I have edited the code to make a single "ValueHigh" and a single "ValueLow" for all relays. The truth table would look like this:

VALUE R1  R2  R3
Low   0   0   0
High  0   0   1
High  0   1   0
High  0   1   1
High  1   0   0
High  1   0   1
High  1   1   0
High  1   1   1

Of these, only 4 are acceptable (000, 001, 010, 100)

Also, if i were to make an "IF" statement using those 4 states wouldnt I be writing something similar to what I mention in my original post? I tried using an "IF" statement that read:

if ((SensorReading1 > ValueHigh ) && (R2 == LOW) && (R3== LOW)){
  digitalWrite(R1, HIGH); //on
}
 if (SensorReading1 <= ValueLow) {
    digitalWrite(R1,LOW); //off
 }

 if ((SensorReading2 > ValueHigh ) && (R1 == LOW) && (R3== LOW)){
  digitalWrite(R2, HIGH); //on
}
 if (SensorReading2 <= ValueLow) {
    digitalWrite(R2,LOW); //off
 }

 if ((SensorReading3 > ValueHigh ) && (R1 == LOW) && (R2== LOW)){
  digitalWrite(R3, HIGH); //on
}
 if (SensorReading1 <= ValueLow) {
    digitalWrite(R3,LOW); //off
 }

This did not work because when the && conditions are not met, its like a self feeding problem where once it jams nothing gets cooled the && condition NEVER gets met since all temp sensors want to be "ON".

My apologies if i misunderstood.

@Paul_KD7HB good point, so would 1 second be a good amount to time to expect the relay to transfer position? If so I can add a loop to delay 1 second (not the delay function)

You cannot use the R1, R2, or R3 directly. They will always return “0”.

Try

if ((SensorReading1 > ValueHigh ) && (digitalRead(R2) == LOW) && (digitalRead(R3) == LOW))

I noticed that you have not made A0, A1, and A2 inputs. I know they default to inputs on reset but it is good programming practice to define them explicitly.

There is no real difference between "delay()" and a loop you write to accomplish the same thing. Both will "block" the execution of any other code. Perhaps 10-20 milliseconds is sufficient. Testing will tell.

Here are files for a simulation on WOKWI.com with the "race condition" for experimenting without frying a relay. Paste these files into the tabs with their filename.

sketch.ino
// https://forum.arduino.cc/t/interlocking-relay-code/1268854

const int ValueHigh1 = 100; // high value for turning Relay 1 off
const int ValueLow1 = 20; // low value for turning Relay 1 on
const int ValueHigh2 = 90; // high value for turning Relay 2 off
const int ValueLow2 = 30; // low value for turning Relay 2 on
const int VlaueHigh3 = 75; // high value for turning Relay 3 off
const int ValueLow3 = 45; // low value for turning Relay 3 on

int R1 = 4; //digital pin for relay 1
int R2 = 5; //digital pin for relay 2
int R3 = 6; //digital pin for relay 3

int V1 = A1; // analog pin
int V2 = A2;
int V3 = A3;

int Value1, Value2, Value3; // analog input values

void setup() {
  //INITIALIZING RELAY STATES:
  digitalWrite(R1, LOW); // OFF
  pinMode(R1, OUTPUT);
  digitalWrite(R2, LOW); // OFF
  pinMode(R2, OUTPUT);
  digitalWrite(R3, LOW); // OFF
  pinMode(R3, OUTPUT);

  pinMode(V1, INPUT); // analog pin as input
  pinMode(V3, INPUT);
  pinMode(V2, INPUT);
}

void loop() {

  Value1 = analogRead(V1); // read analog pins
  Value2 = analogRead(V2);
  Value3 = analogRead(V3);

  // if ( R1 > ValueHigh1) { //sensor values inversed so when hit low turn on
  if ( Value1 > ValueHigh1) { //sensor values inversed so when hit low turn on
    digitalWrite(R1, HIGH); //on
    digitalWrite(R2, LOW); //off
    digitalWrite(R3, LOW); //off
  }
  // if (R1 <= ValueLow1) {
  if (Value1 <= ValueLow1) {
    digitalWrite(R1, LOW); //off
  }
  // if ( R2 > ValueHigh2) { //sensor values inversed so when hit low turn on
  if ( Value2 > ValueHigh2) { //sensor values inversed so when hit low turn on
    digitalWrite(R2, HIGH); //on
    digitalWrite(R1, LOW); //off
    digitalWrite(R3, LOW); //off
  }
  // if (R2 <= ValueLow2) {
  if (Value2 <= ValueLow2) {
    digitalWrite(R2, LOW); //off
  }
  // if ( R3 > VlaueHigh3) { //sensor values inversed so when hit low turn on
  if ( Value3 > VlaueHigh3) { //sensor values inversed so when hit low turn on
    digitalWrite(R3, HIGH); //on
    digitalWrite(R1, LOW); //off
    digitalWrite(R2, LOW); //off
  }
  // if (R3 <= ValueLow3) {
  if (Value3 <= ValueLow3) {
    digitalWrite(R3, LOW); //off
  }
}
diagram.json
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": -78.1, "left": 172.6, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot2", "top": -78.1, "left": 259, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot3", "top": -78.1, "left": 345.4, "attrs": {} },
    { "type": "wokwi-relay-module", "id": "relay1", "top": -143.8, "left": 153.6, "attrs": {} },
    { "type": "wokwi-relay-module", "id": "relay2", "top": -211, "left": 220.8, "attrs": {} },
    { "type": "wokwi-relay-module", "id": "relay3", "top": -278.2, "left": 297.6, "attrs": {} }
  ],
  "connections": [
    [ "nano:GND.1", "pot1:GND", "black", [ "v14.4", "h37.9" ] ],
    [ "nano:5V", "pot1:VCC", "red", [ "v24", "h76.3" ] ],
    [ "nano:GND.1", "pot2:GND", "black", [ "v14.4", "h143.5" ] ],
    [ "nano:GND.1", "pot3:GND", "black", [ "v14.4", "h220.3" ] ],
    [ "nano:5V", "pot2:VCC", "red", [ "v24", "h181.9" ] ],
    [ "nano:5V", "pot3:VCC", "red", [ "v24", "h268.3" ] ],
    [ "pot1:SIG", "nano:A1", "green", [ "v105.6", "h-125.2" ] ],
    [ "pot2:SIG", "nano:A2", "green", [ "v115.2", "h-173.2" ] ],
    [ "pot3:SIG", "nano:A3", "green", [ "v124.8", "h-250" ] ],
    [ "nano:GND.1", "relay1:GND", "black", [ "v0" ] ],
    [ "nano:5V", "relay1:VCC", "red", [ "v0" ] ],
    [ "nano:GND.1", "relay2:GND", "black", [ "v0" ] ],
    [ "nano:GND.1", "relay3:GND", "black", [ "v0" ] ],
    [ "nano:5V", "relay2:VCC", "red", [ "v0" ] ],
    [ "nano:5V", "relay3:VCC", "red", [ "v0" ] ],
    [ "nano:4", "relay1:IN", "green", [ "v0" ] ],
    [ "nano:5", "relay2:IN", "green", [ "v0" ] ],
    [ "nano:6", "relay3:IN", "green", [ "v0" ] ]
  ],
  "dependencies": {}
}

It is easy to do but not with the relays you have. I use DPDT relays and wire 1 side for the load, normally NO. The other half I feed the control in to the common of the first and the NC to the next relays common contact... These are powering the Plus coil side and the switches are low side. This also gives preference to the first relay in the chain. You can turn all on but only the first will activate.

1 Like

Thank you for the amended code, it now makes some kind of sense, even if it's not right.

I'm not clear what you want to achieve, is it:
If sensor 1 is too hot spray sensor 1's roof area, do no spray sensor 2 or sensor 3's roof area until after sensor 1's area has cooled?

Similarly for sensor 2's and sensor 3's roof area? So only ever spray one third of the roof? Is this correct?

These should not be floats, analogue read returns an unsigned int, not a float. Use unsigned int or uint16_t

Simple enough to do using DPDT relays, where the second pole is the enable/disable to the others.