Using millis in 4 channel relay, set relay state?

Hi,

I am very new to programming, i'm studying for an MSc in Biotech and decided to include some automation in my thesis, i've tried to ensure this post is correct, apologies if its not!!

I'm setting up a relay module to control 3 pumps and a fan, i just need to turn them on and off at specific times (the timings of which vary between the channels). I got the relay to work using the delay function, however this isnt suitable as i need the pumps to be working at the same time; therefore i think i need to use millis.

I have tried searching online for tutorials / previous questions but most just describe how to use millis with LED's or servos, this has given me an understanding of how millis works but im not sure how to apply it here. I have written the following code (example only includes code for 2 channels because of its size):

int relay1=  8;      // the number of the relay pin 
unsigned long previousMillis1 = 0;        // stores last time relay was active(on)
long OnTime1 = 2500;           // milliseconds of on-time
long OffTime1 = 7500;          // milliseconds of off-time
 
int relay2 =  7;      // the number of relay pin
unsigned long previousMillis2 = 0;        // stores last time relay was active(on) 
long OnTime2 = 3300;           // milliseconds of on-time
long OffTime2 = 4000;          // milliseconds of off-time
 
void setup() 
{
  // set the digital pin as output:
  pinMode(relay1, OUTPUT);      
  pinMode(relay2, OUTPUT);   
  digitalWrite (relay1, HIGH); // relay is active low so setting to high, to avoid
                               // activating pumps on set up 
  digitalWrite (relay2, HIGH); 
}
 
void loop()
{
  // check to see if it's time to turn on relay 
  unsigned long currentMillis = millis();
 
  if (currentMillis - previousMillis1 >= OnTime1)
  {
    relay1 = LOW;  // Turn it on
    previousMillis1 = currentMillis;  // Remember the time
    digitalWrite(relay1, LOW);  // Update the relay
  }
  else if (currentMillis - previousMillis1 >= OffTime1)
  {
    relay1 = HIGH;  // turn it off
    previousMillis1 = currentMillis;   // Remember the time
    digitalWrite(relay1, HIGH);    // Update the relay
  }
  
  if (currentMillis - previousMillis2 >= OnTime2)
  {
    relay2 = LOW;  // Turn it on
    previousMillis2 = currentMillis;  // Remember the time
    digitalWrite(relay2, LOW);  // Update the relay
  }
  else if (currentMillis - previousMillis2 >= OffTime2)
  {
    relay2 = HIGH;  // turn it off
    previousMillis2 = currentMillis;   // Remember the time
    digitalWrite(relay2, HIGH);   // Update the relay
  }
}

When running this on the arduino/relay....nothing happens, at all. I think i need to include a line for relay state, as ive seen most LED millis programmes using 'ledState' but i dont know how this transfers to relay.

Again, sorry if this is a stupidly easy question, i have tried to solve it without posting but this is my last resort! Any help would be greatly appreciated.

B

You have some flaws in your code. First you are modifying the variables holding the pin numbers, and this makes your code set a wrong pin number. Pin numbers should be stored in constants to prevent them from being modifyed by accident. Second you must check the current state of a relay before changing it. This should turn on relay1 for 2500 millis and then turn it off for 7500 millis:

#define RELAY1_PIN 8 //could be "const byte RELAY1_PIN = 8;"

//Read the state of the relay:
byte relayState = digitalRead(RELAY1_PIN);

//Check if relay is off (HIGH?) and the off period has elapsed:
if ((relayState == HIGH) && (currentMillis - previousMillis1 >= OffTime1))
{
  //relay1 = LOW; // This is wrong
  previousMillis1 = currentMillis;  // Remember the time
  digitalWrite(RELAY1_PIN, LOW);  // Update the relay
}
else if ((relayState == LOW) && (currentMillis - previousMillis1 >= OnTime1))
{
  //relay1 = HIGH;  // This is wrong
  previousMillis1 = currentMillis;   // Remember the time
  digitalWrite(RELAY1_PIN, HIGH);    // Update the relay
}

For a start you can add the world 'const' before all the variable declarations of variable than mustn't change. This word lock the value.
Second you can start using the best variable types. The rule is "use the smallest type that contiens tje highest value of a variable". The types are
Char (max +-128)
Byte max 0/255
Int max -+2^15+unsigned 0/2^16
Long max -+2^31+unsigned 0/2^32(millis())

ButlerJJ:
I have tried searching online for tutorials / previous questions but most just describe how to use millis with LED's

Turning on an LED is probably identical to turning on a relay.

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

...R

You should also consider to “sleekify” your code with some arrays:

#define RELAY_COUNT 2

//Comma separated list of relay pins
const byte RELAY_PINS[RELAY_COUNT] = { 8, 7 };

//On intervals
const unsigned int ON_MILLIS[RELAY_COUNT] = { 2500, 3300 };

//Off intervals
const unsigned int OFF_MILLIS[RELAY_COUNT] = { 7500, 4000 };

//Last relay change
unsigned long relayLastChange[RELAY_COUNT] = {0};

//Obvious
unsigned long currentMillis;

//Method to handle the relays by array index
void handleRelay(byte relayIdx)
{
  byte relayState = digitalRead(RELAY_PINS[relayIdx]);
  if (currentMillis - relayLastChange[relayIdx] >= (relayState == LOW) ? ON_MILLIS[relayIdx] : OFF_MILLIS[relayIdx])
  {
    digitalWrite(RELAY_PINS[relayIdx], relayState ^ 1); // ^ 1 means invert
    relayLastChange[relayIdx] = currentMillis;
  }
}

void loop()
{
  currentMillis = millis();
  for (byte i = 0; i < RELAY_COUNT; i++) handleRelay(i);
}

Danois90:
You should also consider to "sleekify" your code with some arrays: [quote/]

I spent some time reading and decided to use a class that gets updated each cycle. It seems to be working but im not sure if it will keep time correctly.

class Switcher 

{
// class member variables
  int relayPin; // number of pin for relay
  long OnTime; 
  long OffTime;

int relayState; // set relay state (active low)
  unsigned long previousMillis; // set time since last update

public:
Switcher(int pin, long on, long off)
{
  relayPin = pin;
  pinMode(relayPin, OUTPUT);

OnTime = on;
  OffTime = off;

relayState = HIGH;
  previousMillis = 0;
 
}

void Update()
{
// check the time to see if relays need to be turned on
// or off
  unsigned long currentMillis = millis();

if ((relayState == LOW) && (currentMillis - previousMillis>= OnTime))
  {
  relayState = HIGH; // Turn it off
  previousMillis = currentMillis; // Remember the time
  digitalWrite(relayPin, relayState); //update the relay
  }
  else if ((relayState == HIGH) && (currentMillis - previousMillis >= OffTime))
  {
  relayState = LOW; // turn it on
  previousMillis = currentMillis;
  digitalWrite(relayPin, relayState);
  }
}
};

Switcher relay1(8, 3000, 15000);
Switcher relay2(7, 3000, 10000);

void setup() {
  // put your setup code here, to run once:
  digitalWrite(8, OUTPUT);
  digitalWrite(7, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  relay1.Update();
  relay2.Update();

}

[code/]

I think the revised code works, thanks to all who helped, i had a bit more to understand than i realised!

Encapsulation in classes is always great because it is easier to re-use in other projects. You should also consider to use the smallest data type that fits the bill. Using an "int" to store a pin-number is a waste of one byte of memory - it may not sound as much, but it sums up :slight_smile:

Danois90:
Encapsulation in classes is always great because it is easier to re-use in other projects. You should also consider to use the smallest data type that fits the bill. Using an "int" to store a pin-number is a waste of one byte of memory - it may not sound as much, but it sums up :slight_smile:

Have since changed to byte, best practice from the start is probably advisable! Thanks!