The "blink-without-delay"-pattern shows how this can be solved but with several time periods and logic this becomes complex. Some abstraction is needed. The Timemark library provides a solution. Belows is the logic for a single sensor-relay pair with a state trace to serial:
#include <Timemark.h>
const uint32_t TURN_OFF_TIMEOUT = 30000L;
Timemark turnOff(TURN_OFF_TIMEOUT);
const int sensorPin = 2;
const int relayPin = 3;
void setup()
{
Serial.begin(9600);
while (!Serial);
pinMode(sensorPin, INPUT_PULLUP);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
trace.start();
}
void loop()
{
if (trace.expired()) {
Serial.print(millis());
Serial.print(F(":sensor="));
Serial.print(digitalRead(sensorPin));
Serial.print(F(", relay="));
Serial.println(digitalRead(relayPin));
}
if (digitalRead(sensorPin) == LOW) {
digitalWrite(relayPin, HIGH);
turnOff.start();
}
else if (turnOff.expired()) {
digitalWrite(relayPin, LOW);
}
}
Expanding to three sensor-relays becomes:
#include <Timemark.h>
const uint32_t TURN_OFF_TIMEOUT = 30000L;
const int CONTROL_MAX = 3;
Timemark turnOff[CONTROL_MAX];
const int sensorPin[CONTROL_MAX] = { 2, 4, 6 };
const int relayPin[CONTROL_MAX] = { 3, 5, 7 };
void setup()
{
for (int i = 0; i < CONTROL_MAX; i++) {
turnOff[i].limitMillis(TURN_OFF_TIMEOUT);
pinMode(sensorPin[i], INPUT_PULLUP);
pinMode(relayPin[i], OUTPUT);
digitalWrite(relayPin[i], LOW);
}
}
void loop()
{
for (int i = 0; i < CONTROL_MAX; i++) {
if (digitalRead(sensorPin[i]) == LOW) {
digitalWrite(relayPin[i], HIGH);
turnOff[i].start();
}
else if (turnOff[i].expired()) {
digitalWrite(relayPin[i], LOW);
}
}
}
An alternative solution is to use the Scheduler library. Below is a rewrite using template functions and a controller task per sensor-relay pair.
#include <Scheduler.h>
const uint32_t TURN_OFF_TIMEOUT = 30000L;
const uint32_t DEBOUNCE_TIMEOUT = 40L;
template<const int sensorPin, const int relayPin> void setupController()
{
pinMode(sensorPin, INPUT_PULLUP);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
}
template<const int sensorPin, const int relayPin> void loopController()
{
if (digitalRead(sensorPin) == LOW) {
digitalWrite(relayPin, HIGH);
while (digitalRead(sensorPin) == LOW) delay(DEBOUNCE_TIMEOUT);
delay(TURN_OFF_TIMEOUT);
digitalWrite(relayPin, LOW);
}
yield();
}
void setup()
{
Scheduler.start(setupController<2,3>, loopController<2,3>);
Scheduler.start(setupController<4,5>, loopController<4,5>);
Scheduler.start(setupController<6,7>, loopController<6,7>);
}
void loop()
{
yield();
}
Please note that the Scheduler requires yield() or delay() to switch between the tasks.
Cheers!