2 buttons and 8 relays(or more)

Hi everybody
I am quite new to Arduino, i have made a couple of projects before but the one I'm attempting now is a lot harder for me and i am not even sure it can be done. So if you could take a look and tell me if it is possible and where to start i would appreciate it. I will try to describe as best as i can what i want to do.

I have a relay-board with 8 relays and 2 buttons (or if it is easier just 1 button) and i want to do this:

When i press button1
relay 1 turns on and stays on
delay 30 second or so
relay 2 turns on and stays on
delay 30 seconds or so
relay 3 turns on and stays on
delay 30 second or so
relay 4 turns on and stays on
delay 30 seconds or so
Relay 5-8 turns on and stays on for randomly 1-5 minutes then turns off for randomly 1-5 minutes and this cycle repeats "forever"

When i press button 2 (or button 1 again)
All relays should "freeze" in the state they are and then starting with relay 8 they turn of sequentially with a delay of a minute or so between and then stays off.

Then the Arduino should just sit and wait with everything off for when i press button 1 again

Thank you for reading and hopefully answering this :grinning:

It's plausible. You will need to use millis for timing, not delay or you won't pick up the second button press without a long wait.

I would try it with LEDs instead of relays to start with so you don't have to fight with electrical issues while you're writing the logic.

Start with two leds and one button and build up gradually to the full thing.

Has this any practical application ? It sounds sounds like a homework assignment to me.

Yes it have a practical use for me :grinning:
And no it is not for homework, i it is about 40 years since i was in school :joy:
I am going to use it on my model railroad for controlling the street and city lights in night mode.

I already figured i couldnt use delay, this is what i have so far, it is not working as intended, it just goes right in to the "nattLjus" function and stops there, then if i press button when it steps through that function one step for every buttonpress.
I know the code is very uggly and probably with a lot of problems, but as i said i am new to this :grinning:

const int ledPin1 = 13;
const int ledPin2 = 12;
const int ledPin3 = 11;
const int ledPin4 = 10;
int buttonState1 = 0;
int buttonState2 = 0;

unsigned long previousMillis1;
unsigned long previousMillis2;
unsigned long previousMillis3;
unsigned long previousMillis4;

unsigned long interval1 = 200UL;   // not a constant, it will be changed
unsigned long interval2 = 200UL;
unsigned long interval3 = 200UL;
unsigned long interval4 = 200UL;

void setup() 
{
  pinMode( ledPin1, OUTPUT);
  pinMode( ledPin2, OUTPUT);
  pinMode( ledPin3, OUTPUT);
  pinMode( ledPin4, OUTPUT);
  pinMode(2,INPUT);
  pinMode(5,INPUT);
  
}

void loop() {

  buttonState1 = digitalRead(2);
  if(buttonState1==HIGH){
    nattLjus();
    }
  buttonState2 = digitalRead(5);
  if(buttonState2==HIGH){
    dagLjus();
    }
    }

void nattLjus(){

  unsigned long currentMillis1 = millis();

  if( currentMillis1 - previousMillis1 >= interval1) 
  {
    previousMillis1 = currentMillis1;
    interval1 = random( 2000, 10000);   // set the new interval

    // toggle
    digitalWrite( ledPin1, digitalRead( ledPin1) == HIGH ? LOW : HIGH);
  }
  
  unsigned long currentMillis2 = millis(); 

  if( currentMillis2 - previousMillis2 >= interval2) 
  {
    previousMillis2 = currentMillis2;
    interval2 = random( 2000, 10000);   // set the new interval

    // toggle
    digitalWrite( ledPin2, digitalRead( ledPin1) == HIGH ? LOW : HIGH);
  }
  
  
  unsigned long currentMillis3 = millis();

  if( currentMillis3 - previousMillis3 >= interval3) 
  {
    previousMillis3 = currentMillis3;
    interval3 = random( 2000, 10000);   // set the new interval

    // toggle
    digitalWrite( ledPin3, digitalRead( ledPin3) == HIGH ? LOW : HIGH);
  }
  
  unsigned long currentMillis4 = millis();

  if( currentMillis4 - previousMillis4 >= interval4) 
  {
    previousMillis4 = currentMillis4;
    interval4 = random( 2000, 10000);   // set the new interval

    // toggle
    digitalWrite( ledPin4, digitalRead( ledPin4) == HIGH ? LOW : HIGH);
  }
  return;
  }
  
void dagLjus(){
  digitalWrite(10,LOW);
  delay(1000);
  digitalWrite(11,LOW);
  delay(1000);
  digitalWrite(12,LOW);
  delay(1000);
  digitalWrite(13,LOW);
  return;
  }

You need to detect when the button becomes pressed not if it is currently being pressed. Look at the state change example in the IDE.

You also need to write the code as a state machine, see the blink without delay example in the IDE, or Google State machine for many more examples like this one.
State Machine

Or this post
https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158/105

This in not absolutely beginner stuff.

Thanks for the answer, i have looked at some of them and i will read and learn more.
I know it's not beginner stuff and when i studied programming the pc was not even invented yet so i have a lot to learn :joy:

This stuff:

unsigned long previousMillis1;
unsigned long previousMillis2;
unsigned long previousMillis3;
unsigned long previousMillis4;

Says: "I need to learn about arrays".

It's probably overkill in your situation, but if I were doing this, I'd put the data about what the LEDs are to do in an array of structs and make the code an engine that works through that data. Hard coding it is going to be messy.

consider


enum { Off = HIGH, On = LOW };

byte pinBut = A1;
byte butState;

enum { None, TurnOn, Delay30, Rand };

struct Rly {
    int     pin;
    int     op [2];
};

Rly rlys [] = {
    { 10, { TurnOn, Delay30 }},
    { 11, { TurnOn, Delay30 }},
    { 12, { Rand,   Rand }},
    { 13, { Rand,   Rand }},
};
#define N_RLY      (sizeof(rlys)/sizeof(Rly))

int  idx;
int  step;
#define N_STEP  2

#define DelayMsec       5000
#define MinRand         5000
#define MaxRand         15000
unsigned long  msecLst;

bool run;

// -----------------------------------------------------------------------------
void
reset (void)
{
    for (unsigned n = 0; n < N_RLY; n++)
        digitalWrite (rlys [n].pin, Off);
}

// -----------------------------------------------------------------------------
void
loop (void)
{
    byte but = digitalRead (pinBut);
    if (butState != but)  {
        butState = but;
        delay (10);         // debounce

        if (LOW == but)  {
            run = ! run;
            Serial.println ("press");
        }
    }

    if (! run)
        return;

    unsigned long msec = millis ();
    if (msec > msecLst)  {
        Rly *r = & rlys [idx];

        char s [80];
        sprintf (s, " idx %d, step %d, pin %d, step %d %d",
                    idx, step, r->pin, r->op [0], r->op [1]);
        Serial.println (s);

        switch (r->op [step])  {
        case TurnOn:
            digitalWrite (r->pin, On);
            sprintf (s, "TurnOn pin %d %d", r->pin, On);
            Serial.println (s);
            break;

        case Delay30:
            msecLst = msec + DelayMsec;
            Serial.println (DelayMsec);
            break;

        case Rand:
            digitalWrite (r->pin, ! digitalRead (r->pin));
            msecLst = random (MinRand, MaxRand);
            Serial.println (msecLst);
            msecLst += msec;
            break;
        }

        if (++step >= N_STEP)  {
            step = 0;
            idx  = (idx + 1) % N_RLY;
            if (0 == idx)
                reset ();
        }
    }
}

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

    for (unsigned n = 0; n < N_RLY; n++)  {
        digitalWrite (rlys [n].pin, Off);
        pinMode      (rlys [n].pin, OUTPUT);
    }

    pinMode (pinBut, INPUT_PULLUP);
    butState = digitalRead (pinBut);
}

Hi,
Welcome to the forum.
This is a nice project to start Arduino--ising your layout.
I make cardboard models, not enough room for a railway.
But I like making railway based models, here is a Scalescenes factory I built with some lighting effects.
The factory electrics are a bit dodgey so when the arcwelder fires up, the office lights filcker.

Thanks for setting out your timing requirments, it helps when deciding the approach to how the code will be structured.

Tom..... :smiley: :+1: :coffee: :australia:

2 Likes

Hello
I did it again in C++. Missing the part to read and to work on buttons. But I think these actions can be simple added to this given frame work by yourself. And if not don´t hesitate to ask.

// BLOCK COMMENT
// ATTENTION: This Sketch contains elements of C++.
// https://www.learncpp.com/cpp-tutorial/
// https://forum.arduino.cc/t/2-buttons-and-8-relays-or-more/906777
#define ProjectName "2 buttons and 8 relays(or more)"
// CONSTANT DEFINITION
// you may need to change these constants to your hardware and needs.
constexpr byte Input_[] {A0, A1};                       // portPin o---|button|---GND
constexpr byte Output_[] {2, 3, 4, 5, 6, 7, 8, 9};		  // portPin o---|220|---|LED|---GND
// VARIABLE DECLARATION AND DEFINTION
enum {One, Two, Three, Four, Five, Six, Seven, Eight};
unsigned long currentTime;
struct TIMER {
  unsigned long duration;
  bool repeat_;
  bool control_;
  unsigned long stamp;
};
struct BLOCK {
  byte name_;
  byte pin;
  TIMER time_;
} blocks [] = {
  {One, Output_[One], 1000, false, true,  0},
  {Two, Output_[Two], 1000, false, false,  0},
  {Three, Output_[Three], 1000, false, false,  0},
  {Four, Output_[Four], 1000, false, false,  0},
  {Five, Output_[Five], 1000, true, false,  0},
  {Six, Output_[Six], 1000, true, false,  0},
  {Seven, Output_[Seven], 1000, true, false,  0},
  {Eight, Output_[Eight], 1000, true, false,  0},
};
// FUNCTIONS
bool checkTimer(TIMER & time_) {  // generic time handler using TIME struct
  if (currentTime - time_.stamp >= time_.duration && time_.control_) {
    if (time_.repeat_) time_.stamp = currentTime;
    else time_.control_ = false;
    return true;
  } else return false;
}
void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);
  for (auto Input : Input_) pinMode(Input, INPUT_PULLUP);
  for (auto Output : Output_) pinMode(Output, OUTPUT);
  // check outputs
  for (auto Output : Output_) digitalWrite(Output, HIGH);
  delay(1000);
  for (auto Output : Output_) digitalWrite(Output, LOW);
}
void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &block : blocks) {
    if (checkTimer(block.time_))
      switch (block.name_) {
        case One ... Three:
          block.time_.control_ = false;
          digitalWrite(block.pin, HIGH);
          blocks[block.name_ + 1].time_.control_ = true;
          blocks[block.name_ + 1].time_.stamp = currentTime;
          break;
        case Four:
          for (unsigned int n = Five; n < (sizeof(blocks) / sizeof(blocks[0])); n++) {
            blocks[n].time_.control_ = true;
            blocks[n].time_.stamp = currentTime;
            blocks[n].time_.duration = random(200, 1000);
          }
          block.time_.control_ = false;
          digitalWrite(block.pin, HIGH);
          break;
        case Five ... Eight:
          block.time_.duration = random(200, 1000);
          digitalWrite(block.pin, !digitalRead(block.pin));
          break;
      }
  }
}

Have a nice day and enjoy coding in C++.

Hi
Thanks for the answer, that looks very interesting, i will try it out later.
since i'm net good att write code from scratch i'm very grateful for all the help from you guys.
If i have a starting point it is much easier for me to change it to do exactly what i want

Hi and thank you
Yes i think so too, i already have a couple of arduinos controlling traffic lights and other stuff.
But this one was a bit over my skillevel. The next project will be controlling steppers and servos to animate objects and figures.
Your model looks impressive, nice job :grinning:
I can see your problem, not enough power to have them lighted at the same time?

Hi
Thank you soo much for that, i will look more closely and try it out tonight, it is still morning here i Sweden :grinning:

Hello
I´m looking from the otherside of the baltic sea. :wave:

Aha, then its just an hour time difference :joy:

@pixelmeister between your description and codeand the several programs presented as partial solutions I am yet confused about exactly what you are trying to do.

I have read your post carefully and perused and, where possible, executed the code presented in previous posts.

  • turn on relays 1-4 step by step until all are on
  • turn on and off all relays 5-8 with random on and off times

The button initiates this process. The button would stop and reset this process, but:

If the button is pressed in the 1-4 phase, step backwards until all 1-4 are off.

If the button is pressed in the 5-8 phase, is it important that the currently ON relays 5-8 turn off in reverse order? If 5-8 are all turning on and off randomly, there may be 0-4 of them on.

Perhaps any that are on should be left to turn off when they would have, and after all 5-8 are off, then step backwards turning off 4-1. After a delay, all 5-8 OFF, all 1-4 ON.

Or did you even mean that 5-8 are randomly and asynchronous going on and off? A supposed to going on 5, 6, 7, 8 and off 8, 7, 6, 5 with random times atween?

@paulpaulson's code in #11 works well except it goes into the 5-8 phase immediately after 4 is tuned on. And obvsly or as stated does not handle a button nor the reverse process.

TBH I just put the #11 code in a box and have not examined it closely; I assume it would be easy to correct.

Nothing so far handles the unwinding, and the unwinding specification is a bit unclear, hence my question for how that should go.

And for that matter, how an additional button press during the unwinding should be handled - start the winding up (or waiting for all 5-8 relays to be OFF) and just resume the winding up (1-4) or operating (5-8) phases?

I tend to wait until some questions like this are dead clear before spilling code.

a7

Hi
Sorry for the confusion, my english is not so good when trying to explain things like this.

turn on relays 1-4 step by step until all are on
turn on and off all relays 5-8 with random on and off times
The button initiates this process. The button would stop and reset this process, but:

If the button is pressed in the 1-4 phase, step backwards until all 1-4 are off.

If the button is pressed in the 5-8 phase, is it important that the currently ON relays 5-8 turn off in reverse order? If 5-8 are all turning on and off randomly, there may be 0-4 of them on.

Perhaps any that are on should be left to turn off when they would have, and after all 5-8 are off, then step backwards turning off 4-1. After a delay, all 5-8 OFF, all 1-4 ON.

Or did you even mean that 5-8 are randomly and asynchronous going on and off? A supposed to going on 5, 6, 7, 8 and off 8, 7, 6, 5 with random times atween?

I'm going to use this on a model railroad to control the lights on the streets and in buildings.
I want to use relay 1 & 2 to turn on street lights just so it looks a bit more realistic that not all of them turn on at the same time that's why i want a short delay between them.
Relay 3 will be used for lighted commercial signs/neon signs and similar.
relay 4 will be for base interior lights in the houses.
relay 5-8 will be for individual room lighting in houses to get a sense of people moving between rooms.
I will have around 300-500 (at least) leds divided in 8 different circuits , that's why i need relays, it is too much for the arduino to handle i think.

After i push the "night light" button (button 1) that mode will probably run for 15-30 minutes at least so the risk of interrupting the 1-4 phase is not likely, and at what time i interrupt the 5-8 phase doesn't matter. I just wanted the "morning" phase (button 2) to be somewhat realistic, the houses turns of first and then signage and lastly street lights. I don't think it is really necessary to have the random timing between 5-8 when turning them off, i think that when turning off relays 8-1 it is enough to just have a delay between them just to make a little more realistic.
I hope i you understand a bit more what i want to do.

Hello to Skandinanvia
You may shall think about WS2812B LED Strip or equiv for your project. Take a view here.

I'd take an approach similar to @gcjr with the addition to the struct of a time offset from when the button press occurred. Then you can easily see when it's time to operate a relay. It also means that the interval between each can be different.

I'd have two arrays of those structs: one for night falling and one for dawn.