Programming help - switch off relays

Hi all,

I've adapted a project that was on Nick Gammon's switches page to do something similar but different.

The project Nick Wrote takes 2 switches and uses his switchmanager library to flash either a left indicator, right indicator or hazards.
Nick's code that was starting point
Nick's page on his switch library - also includes example that was starting point

I'm not looking to flash the output. I'm wanting to switch relay 1 or relay 2 on for 500ms & then turn off again if switch 1, or switch 2 are pressed. If both switches are pressed relay 3 turns on for 500ms. Whatever is pressed the respective relay only comes on once.

With the modification to the code on Nick's page I've gotten to the point where the respective relays turn on - and off again on a subsequent button press.

I can't get to the point where the relays simply come on for 500ms & go off without the subsequent button press.

The end goal is that they need to come on once for 500ms and then go off.

Any advice on the key change I'm missing please?

Have tried a number of things without success.

Code below,

Thanks

Dave

// Adopted from here https://arduino.stackexchange.com/questions/13488/how-to-detect-if-more-than-one-button-was-pressed
// https://www.gammon.com.au/switches


#include <SwitchManager.h>

typedef enum {
    NONE,
    LOW_DOWN,
    HIGH_DOWN,
    LOW_RELAY_ON,
    HIGH_RELAY_ON,
    BOTH
};

const unsigned long BLINK_INTERVAL = 500; // ms

// pin assignments
const byte LOW_SWITCH_PIN = 7;
const byte HIGH_SWITCH_PIN = 8;
const byte LOW_RELAY = A0;
const byte HIGH_RELAY = A1;
const byte DOUBLE_RELAY = 6;

SwitchManager LOWswitch; 
SwitchManager HIGHswitch; 
 
byte state = NONE;
 
void handleLOWPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  // switch down?
  if (newState == LOW)
     {
     switch (state)
       {
       // if other switch down, switch to double mode
       case HIGH_DOWN:
         state = BOTH;
         break;
         
       // if already on or double signal, turn all off
       case LOW_RELAY_ON:
       case BOTH:
         state = NONE;
         break;
         
       // otherwise switch is now down, but not yet released
       default:
         state = LOW_DOWN;
         break;
       }  // end of switch
     return;
     }  // end of LH switch down
  
  // switch must be up

  if (state == LOW_DOWN)  // if down, switch to down-and-released mode
    state = LOW_RELAY_ON;  
  }  // end of handleLHPress
 
void handleHIGHPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  // switch down?
  if (newState == LOW)
     {
     switch (state)
       {
       // if other switch down, switch to double mode
       case LOW_DOWN:
         state = BOTH;
         break;
         
       // if already on or double signal, turn all off
       case HIGH_RELAY_ON:
       case BOTH:
         state = NONE;
         break;
         
       // otherwise switch is now down, but not yet released
       default:
         state = HIGH_DOWN;
         break;
       }  // end of switch
     return;
     }  // end of RH switch down
  
  // switch must be up

  if (state == HIGH_DOWN)  // if down, switch to down-and-released mode
    state = HIGH_RELAY_ON;  
  }  // end of handleRHPress
  
void setup ()
  {
  LOWswitch.begin (LOW_SWITCH_PIN, handleLOWPress);
  HIGHswitch.begin (HIGH_SWITCH_PIN, handleHIGHPress);
  pinMode (LOW_RELAY, OUTPUT);
  pinMode (HIGH_RELAY, OUTPUT);
  pinMode (DOUBLE_RELAY, OUTPUT);
  }  // end of setup

unsigned long lastBlink;
bool onCycle;
 
void blinkLights ()
  {
  lastBlink = millis ();
//  onCycle = !onCycle;
  
  // default to off
  digitalWrite (LOW_RELAY, HIGH);
  digitalWrite (HIGH_RELAY, HIGH);
  digitalWrite (DOUBLE_RELAY, HIGH);

  // every second time, turn them all off
  if (LOW_RELAY == LOW || HIGH_RELAY == LOW || DOUBLE_RELAY == LOW)


    return;
    
  // blink light
  switch (state)
    {
    case NONE:
      break;
    
    case LOW_DOWN:
    case LOW_RELAY_ON:
      digitalWrite (LOW_RELAY, LOW);


      break;
      
    case HIGH_DOWN:
    case HIGH_RELAY_ON:
      digitalWrite (HIGH_RELAY, LOW);

   
      break;
      
    case BOTH:
      digitalWrite (DOUBLE_RELAY, LOW);


      break;
    
    }  // end of switch on state
    
  }  // end of blinkLights
  
void loop ()
  {
  LOWswitch.check ();  // check for presses
  HIGHswitch.check ();  // check for presses

  if (millis () - lastBlink >= BLINK_INTERVAL)
   
      blinkLights ();
   
       // other stuff
    } // end of loop

Yep. Okay. That was dumb. Been playing with this for hours and that was a later change.

If I was going to do it like that I'd need to declare some variables that are set or cleared at different points. That was a bit of a desperate attempt to see if I could get it to take a different path if any of the relays are already on.

Once I've written the relay value to that output if I'm right I could just digital read that pin to get whether a relay is on?

Any suggestions that I might adopt. I'm at the point where I can't see past my own nose to work it out I've tried that many things.

Thanks though, it's a point to start from in a fresh session.

Dave

I want to ask a detail about this:

If a human beeing is pressing two buttons the two buttons will not close in the exact same millisecond.

Pressing button 1 then pressing button 2 2 seconds later is somehow
"both buttons are pressed"

How much time do you want to define that the code will react with
"both buttons pressed" ==> switch on relay3 for 500 milliseconds

In other words what will be the maximum time-difference between pressing button 1 and button 2 which the code will accept as
"both buttons pressed" ?

It would help a lot of you describe the complete application = your complete project

Your functionality has different modes of operation

waiting for low-switch or high-switch ON

if low-switch or high-switch is on
check if the second switch comes on within a short time too
which then is a double-ON

This means as soon as one switch is on
you have to check if the other switch comes on withing a short time

depending on

  • a: low-switch beeing ON

  • b: high-switch beeing ON

  • c: low-switch beeing ON AND high-switch beeing ON

  • a: switch low-relais on

  • b: switch high-relais on

  • c: switch double-relais on

after 500 milliseconds switch off

This is a medium complex functionality which needs - of course -
medium complex code to create it.

Your original code uses the switch-case-break-statement
This code does this too

The code creates the above written functionality
Your functionality has a medium number of conditions
this results in a medium number of if-statements

It will take some time for you to analyse and understand it.
Very normal if you are new to switch-case-break state-machines

const byte LOW_SWITCH_PIN  = 7;
const byte HIGH_SWITCH_PIN = 8;
const byte LOW_RELAY       = A0;
const byte HIGH_RELAY      = A1;
const byte DOUBLE_RELAY    = 6;

const byte OnBoard_LED = 13;

const byte switchIsOn = HIGH;

const byte OutPutOn  = HIGH;    // is output "active" with IO-pin beeing LOW or HIGH?

// the attention-mark ! is the not-operator which inverts the value
// !true = false  !false = true     !HIGH = LOW    !LOW = HIGH
const byte OutPutOff = !OutPutOn;

// constants of the state-machine
const byte sm_waitForButtonPress        = 0;
const byte sm_checkForSecondButtonPress = 1;
const byte sm_wait500msecs              = 2;
const byte sm_switchOff                 = 3;
const byte sm_lowRelayOn                = 4;
const byte sm_highRelaiyOn              = 5;
const byte sm_doubleRelayOn             = 6;

unsigned long secondButtonTimer;
unsigned long relayOnTimer;

const unsigned long max2ndPressTime = 300;

byte MonoFlopState = sm_waitForButtonPress; // state-variable of the state-machine


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  pinMode(LOW_SWITCH_PIN,  INPUT_PULLUP);
  pinMode(HIGH_SWITCH_PIN, INPUT_PULLUP);

  pinMode(LOW_RELAY, OUTPUT);
  pinMode(HIGH_RELAY, OUTPUT);
  pinMode(DOUBLE_RELAY, OUTPUT);
}


void MonoFlopStateMachine() {

  static byte lowSwitchState;
  static byte highSwitchState;

  switch (MonoFlopState) {

    case sm_waitForButtonPress:
      lowSwitchState  = digitalRead(LOW_SWITCH_PIN);
      highSwitchState = digitalRead(HIGH_SWITCH_PIN);

      // check if low-switch is pressed
      if ( lowSwitchState == switchIsOn) {
        secondButtonTimer = millis();
        MonoFlopState = sm_checkForSecondButtonPress;
        Serial.println( F("low-switch is ON check for high-switch") );
      }

      // check if high-switch is pressed
      if ( highSwitchState == switchIsOn) {
        secondButtonTimer = millis();
        MonoFlopState = sm_checkForSecondButtonPress;
        Serial.println( F("high-switch is ON check for low-switch") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_checkForSecondButtonPress:
      // in case low-switch is on only read in high-switch
      if ( lowSwitchState == switchIsOn) {
        highSwitchState = digitalRead(HIGH_SWITCH_PIN);
      }

      // in case high-switch is on only read in low-switch
      if ( highSwitchState == switchIsOn) {
        lowSwitchState  = digitalRead(LOW_SWITCH_PIN);
      }

      // when maxTime for double-press is over
      if ( TimePeriodIsOver(secondButtonTimer, max2ndPressTime) ) {
        // check for double-switching low-switch and high-switch
        if ( (lowSwitchState == switchIsOn) && ( highSwitchState == switchIsOn)) {
          MonoFlopState = sm_doubleRelayOn;
        }

        // if still only low-switch is ON
        if ( lowSwitchState == switchIsOn ) {
          MonoFlopState = sm_lowRelayOn;
        }
        // if still only high-switch is ON
        if ( highSwitchState == switchIsOn) {
          MonoFlopState = sm_highRelaiyOn;
        }
      }
      break; // IMMIDIATELY jump down to END OF SWITCH


    case sm_lowRelayOn:
      digitalWrite(LOW_RELAY, OutPutOn);
      relayOnTimer = millis();
      MonoFlopState = sm_wait500msecs;
      break; // IMMIDIATELY jump down to END OF SWITCH


    case sm_highRelaiyOn:
      digitalWrite(HIGH_RELAY, OutPutOn);
      relayOnTimer = millis();
      MonoFlopState = sm_wait500msecs;
      break; // IMMIDIATELY jump down to END OF SWITCH


    case sm_doubleRelayOn:
      digitalWrite(DOUBLE_RELAY, OutPutOn);
      relayOnTimer = millis();
      MonoFlopState = sm_wait500msecs;
      break; // IMMIDIATELY jump down to END OF SWITCH


    case sm_wait500msecs:
      if ( TimePeriodIsOver(relayOnTimer, 500) ) {
        digitalWrite(HIGH_RELAY,   OutPutOff);
        digitalWrite(LOW_RELAY,    OutPutOff);
        digitalWrite(DOUBLE_RELAY, OutPutOff);
        MonoFlopState = sm_waitForButtonPress;
      }
      break; // IMMIDIATELY jump down to END OF SWITCH
  } // END OF SWITCH

}



void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

  // run down code of state-machine over and over again
  // all the logic for reading in switches and switching output ON/OFF
  // is inside the function
  MonoFlopStateMachine();
}

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

Hi Stefan,

Thanks for your help. Does your code still need Nick's Switchmanager.h library included? Or is it debounced some other way in your code?

The function required is adapting one piece of equipment to another.

A friend has a target release system for clay target shooting. He can preselect 3 states

  1. whether he wants Trap 1 to throw target LOW,
  2. whether he wants Trap 2 to throw target HIGH
  3. both switches close by device to throw both HIGH & LOW simultaneously.

It's a three wire system. Two plus a common. It either closes 1 & 3 for LOW, 2 & 3 for HIGH or both together to throw both.

The problem is he needs to interface to a micro controller driven sequencer that has been programmed to read a common & 3 wires to do the same function

Pin

  1. Common
  2. Both targets
  3. High
  4. Low

So I'm trying to make an Arduino Nano take the inputs from a voice controlled micro controlling relaysw with the following output states - HIGH switch closes, Low switch closes or BOTH close simultaneously, to switch 3 relays that are wired to a plug with the 4 wire pinout above.

So yes, when both close they should be simultaneous or within microseconds of each other.

Does that help?

Thanks

Dave

Hello dkjonesau

What the task of the sketch in real life?

won't one switch be recognized and some action taken before the 2nd switch is recognized?

how would you expect to avoid that from happening?

would is be acceptable to delay the recognition that a switch is pressed for a brief period of time so that both switches can be recognized if pressed within some period of time (e.g. 1/2 sec)?

This is confusing because it seems contradictionary
I do not understand what is the inputside and what is the outputside

You are talking of a microcontroller-interface without specifiying if this is the microcontroller-interface is the one that the code is for or a second microcontroller ????

system A (how many wires---->--->--Arduino nano-->-->---how many wires->--->--systemB

Best thing if you draw a hand-drawn schematic where for each part
you draw each and every wire that goes from where to what
including the voltages and ground-connections or if it is a potential free contact

with inputsystem on the left and output-system on the right.

Can draw it. But the Nano sits in the middle of old & new interpreting an older style hand release input based voice control to a newer proprietary micro based target management system.

So to clarify, the old way of doing this was to use 3 momentary push buttons on a lead with three wires. Very basic. Many aftermarket voice releases use the same three wire system as a result.

It predates micros. They’ve used leads like this since the 60s.

Close switch A & target A is released. Call it HIGH (black & white)
Close switch B & target B is released. Call it LOW (red & white)
Close switch C & both targets A&B are released. Call that DOUBLE (red & black both closed to white simultaneously)

The challenge is mating that older style lead to a new proprietary micro controller based target release system.

(NOTE that’s not what I’m building. The proprietary system is already in situ. )

I’m just mating the two using the Nano to control the relays that become the new switches for the four wire system.

This is because the new micro based system just looks a four wire lead with a common & three inputs that look for a closed contact for about 100ms.

The new style lead runs a 4 wire system previously described.

Plug has 4 pins. Has a three button 4 wire hand piece that I’m trying to replace with the Nano & 3 relays.

1 common
2 double
3 A
4 B

Close switch/relay 1 - 2 & target A&B are both released by the proprietary micro based system as DOUBLE simultaneously
Close switch/relay 1 - 3 & target A is released by the proprietary micro based system
Close switch/relay 1 - 4 & target B is released by the proprietary micro based system

I couldn’t think of an easy way to mate the old & new system using discrete components.

Seemed easier to make up a Nano controlling a 4 relay board just using 3 channels.

Cheers

Dave

that circuit requires that 2 switches be pressed at the same time

sorry, that's not correct

when not pressed, red and black are connected
when the top sw is pressed, red and white
when mid sw is pressed, black and white
when bottom sw pressed, white and red

not clear how the black white and red leads are connected to the Arduino

not clear how they should be monitored by the Arduino. pins may need to be configured as both INPUT_PULLUP and OUTPUT

Yes and you should really draw that thing that shows
what does your Arduino Nano have as inputs and what does your arduino have as outputs

If you are unable to draw this
wait for whoemever wants to help you with your word-rich riddlings

Hello dkjonesau

I'd rather write you a proposal for a solution than what doesn't work.

You should design a logic table and a state transition diagram.
I think one solution is a button manager that evaluates the transitions of the buttons from:
released->pressed and pressed->released.

Nope. If you close the Doubles switch it'll close both the other switches circuits. Those hand release leads with that wiring have been in production by Western White Flyer for decades. It relies on the NC contacts of the High & Low switches.

Double - closes common (White) to both Black & Red
High - closes only common (White) & Red
Low - closes only common (White) & Black

That's why I need to read two inputs near simultaneously and have the nano give me one of three different outputs.

DJ

According to your description, if each button does something, won't both happen when you press both?

At least with the same difference in exactly when you prsssed.

If you need A and B to be precisely identally invoked, you need a third button. If you added also a third relay, you could just mimic the same switch closures as the original interface requires for its three actions.

a7


So all I want to do is have the Nano read the switch states from this diagram - either HIGH, LOW or BOTH (Double is closed - red circuit path.)

And give a simpler output that replaces this new style hand release where High, Low & Double are their own discrete switch.

Does that make more sense?

Dave

Actually, if you diode isolated the buttons, a third button could run both inputs at the exact same moment, down to how long the software takes between seeing one and the other.

Which software could be written to look first and A, B or A + B later.

a7

but how do you wire them to the Arduino ???

no. your switch circuit only has 3 leads

This is the schematic for another project that I built. I'm using it for this. It has four inputs of which I only need to use 2 on D7 & D8. (D9 & D10 not needed here.)

It drives three relays on pins A0, A1 & D6

I built the PCB so you could have input from external switches to terminal blocks & the switches are only installed on test boards. The strip inputs & pushbuttons are just parallel.

Desired situation
If D7 is on, A0 relay is on
If D8 is on, A1 relay is on
If D7 & D8 are both on, D6 relay is on

DJ