Cycle timer without delay

Hi, I have just startet using the Arduino, and are trying to set up a program with timers. I have search all over the internet and cannot find any useful codes.

What I want to do is I want to have a button controlling a relay. When the push button is being pressed I want he relay to switch on for 12 hours, then off for 12 hours and continue with this loop until the button is being pressed again. Then I want another button to turn the relay on for 48 hours, after that it will continue with the loop above.

Can anyone help me?

Welcome to the forum tergut.

What is your background?
Do you have a sketch that we can look at to see your progress?
Attach your code using the </> icon on the left side of the posting menu.

I used your assignment as exercise.

Bounce2 for the buttons, a small state-machine as implementation.

I used 1.2 and 4.8 seconds for testing.

// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

enum States { sIdle, sOn12, sOff12, sOn48 };

// for test use 1.2 and 4.8 secs
const unsigned long durCycle1 = 12 * 100ul; // * 60 * 60 * 10
const unsigned long durOnce = 48 * 100ul; // * 60 * 60 * 10

const byte pinButton1 = 5;  // button for 1st action
const byte pinButton2 = 6;  // button for 2nd action
const byte pinRelay = 7;

unsigned long LoopEntered;
unsigned long lastRelayChange;
byte State = sIdle;

Bounce bCycle12;
Bounce b48Cycle12;

bool condRelayState(bool cond, bool relayState, byte newState) {
  if (cond) {
    lastRelayChange = LoopEntered;
    digitalWrite(pinRelay, relayState);
    State = newState;
  }
  return !cond;
}

void setup() {
  Serial.begin(115200);
  pinMode(pinRelay, OUTPUT);
  bCycle12.attach(pinButton1, INPUT_PULLUP);
  b48Cycle12.attach(pinButton2, INPUT_PULLUP);
}

void loop() {
  LoopEntered = millis();
  bCycle12.update();
  b48Cycle12.update();

  if (State == sIdle) {

    if (condRelayState(bCycle12.fell(), HIGH, sOn12)) // start 12 cycle
      condRelayState(b48Cycle12.fell(), HIGH, sOn48); // start 48 onetime

  } else if (State == sOn12) {

    if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
      condRelayState(LoopEntered - lastRelayChange > durCycle1, LOW, sOff12); // if done, next phase

  } else if (State == sOff12) {

    if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
      condRelayState(LoopEntered - lastRelayChange > durCycle1, HIGH, sOn12); // if done, next phase

  } else if (State == sOn48) {

    if (condRelayState(b48Cycle12.fell(), LOW, sIdle)) // stop on  key
      condRelayState(LoopEntered - lastRelayChange > durOnce, HIGH, sOn12); // if done, start 1st cycle
  }
}

Hi Larry D, Thank you. My background is electrical and instrument engineering. I haven't hot a sketch yet to work, but I will try the sketch from Whandall, tanks alot Whandall.

I will try this once I get all my components hooked up.

I tested it with LEDs and buttons closing to GND with internal PULLUP, to drive a relay you will probably have to reverse the actor (LED / relay) logic.

Hi Whandall, I have now tested it with two pushbuttons and a relay module. It runs really nice, thanks alot.

The only thing is when I have pressed button 1, (12 hour cycle) I cannot press button 2. I have to stop 12 hour cycle and then I can start 48 hours cycle.

Do you have any ideas?

tergut:
When the push button is being pressed I want he relay to switch on for 12 hours,
then off for 12 hours and continue with this loop until the button is being pressed again.
Then I want another button to turn the relay on for 48 hours, after that it will continue with the loop above.

It is written after your specification.

If you changed your mind and want to have the 48 stop the 12 in any cycle, here you go:

// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

enum States { sIdle, sOn12, sOff12, sOn48 };

// for test use 1.2 and 4.8 secs
const unsigned long durCycle1 = 12 * 100ul; // * 60 * 60 * 10
const unsigned long durOnce = 48 * 100ul; // * 60 * 60 * 10

const byte pinButton1 = 5;  // button for 1st action
const byte pinButton2 = 6;  // button for 2nd action
const byte pinRelay = 7;

unsigned long LoopEntered;
unsigned long lastRelayChange;
byte State = sIdle;

Bounce bCycle12;
Bounce b48Cycle12;

bool condRelayState(bool cond, bool relayState, byte newState) {
  if (cond) {
    lastRelayChange = LoopEntered;
    digitalWrite(pinRelay, relayState);
    State = newState;
  }
  return !cond;
}

void setup() {
  Serial.begin(115200);
  pinMode(pinRelay, OUTPUT);
  bCycle12.attach(pinButton1, INPUT_PULLUP);
  b48Cycle12.attach(pinButton2, INPUT_PULLUP);
}

void loop() {
  LoopEntered = millis();
  bCycle12.update();
  b48Cycle12.update();

  if (State == sIdle) {

    if (condRelayState(bCycle12.fell(), HIGH, sOn12)) // start 12 cycle
      condRelayState(b48Cycle12.fell(), HIGH, sOn48); // start 48 onetime

  } else if (State == sOn12) {

    if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
      if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
        condRelayState(LoopEntered - lastRelayChange > durCycle1, LOW, sOff12); // if done, next phase

  } else if (State == sOff12) {

    if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
      if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
        condRelayState(LoopEntered - lastRelayChange > durCycle1, HIGH, sOn12); // if done, next phase

  } else if (State == sOn48) {

    if (condRelayState(b48Cycle12.fell(), LOW, sIdle)) // stop on  key
      condRelayState(LoopEntered - lastRelayChange > durOnce, HIGH, sOn12); // if done, start 1st cycle
  }
}

Hi Whandall, Thanks alot, it worked exellent.

Is it possible to use the same logic to set an output high for lets say 6 hours, then low for 18 in a continues cycle?

Is it possible to use the same logic to set an output high for lets say 6 hours, then low for 18 in a continues cycle?

Why don't you give it a try using the same principles as in the existing program ?

tergut: Hi Whandall, Thanks alot, it worked exellent.

Is it possible to use the same logic to set an output high for lets say 6 hours, then low for 18 in a continues cycle?

hours6 = 6 * 60 * 60 * 1000ul ; hours18 = 18 * 60 * 60 * 1000ul ;

;)

Error: missing semicolons

:o
images.jpg

Hi,

Thank you all for excellent tips.
I have now upgraded my code and implemented a potentiometer for controlling the time on the first button.
I have some errors that I cannot figure out, can someone help me?

What I want to do:
I have 3 switches and 1 pot meter for controlling a relay.

Button 1: Starts a continues loop on and off for relay output
Button 2: Set the relay output HIGH for 24 hours
Button 3: Set the relay output HIGH for 48 hours
Pot meter: Controlls the time for Button 1, with maximum 24 hours. If the pot meter is set to 50% it will be HIGH for 12 hours and LOW for 12 hours, if it is set to 25 % it will be HIGH for 6 hours and LOW for 18 hours.

Here is my error messages:
Arduino:1.6.5 (Windows 7), “Arduino Nano, ATmega328”

pump_control_pot.ino: In function ‘void loop()’:
pump_control_pot:64: error: ‘turnTimerOff’ was not declared in this scope
pump_control_pot:65: error: ‘turnTimerOn’ was not declared in this scope
pump_control_pot:78: error: ‘timerOff’ was not declared in this scope
pump_control_pot:85: error: ‘timerOn’ was not declared in this scope
pump_control_pot:95: error: a function-definition is not allowed here before ‘{’ token
pump_control_pot:103: error: a function-definition is not allowed here before ‘{’ token
pump_control_pot:162: error: expected ‘}’ at end of input
‘turnTimerOff’ was not declared in this scope

Here is my code:

</>
// GitHub - thomasfredericks/Bounce2: Debouncing library for Arduino and Wiring
#include <Bounce2.h>

enum States { sIdle, sOn12, sOff12, sOn24, sOn48 };

// for test use 1.2 and 4.8 secs
//const unsigned long durCycle1 = 12 * 100ul * 60 * 60 * 10; // Not in use with pot meter
const unsigned long durOnce24 = 24 * 100ul; // * 60 * 60 * 10
const unsigned long durOnce48 = 48 * 100ul; // * 60 * 60 * 10

const byte pinButton1 = 2; // button for 1st action
const byte pinButton2 = 3; // button for 2nd action
const byte pinButton3 = 4; // button for 3rd action
const byte pinRelay = 13; // output for relay

unsigned long LoopEntered;
unsigned long lastRelayChange;

unsigned long ONE_SECOND = 1000; //1 second is 1000 microSeconds
unsigned long ONE_MINUTE = ONE_SECOND * 60;
unsigned long ONE_HOUR = ONE_MINUTE * 60;
int MAX_ADC_COUNTS = 1024; //ADC
unsigned long SENSOR_READ_DELY_TIME = ONE_SECOND;

unsigned long MAX_CYCLE_TIME = 24 * ONE_HOUR; // This controls the total amount of time for a period (both 1 one and 1 off time)
unsigned long IGNORE_MINIMUM = 1 * ONE_SECOND; // In case you are controlling something that you don’t want cycled too fast.
int ALARM_COUNTS = 250; //blink rate. Each is on for this amount of time.
int TIMER_RATIO_INPUT_PIN = A0; // this is the onboard potentiometer that controls the ratio of on to off

int TIMER_ON = HIGH;
int TIMER_OFF = LOW;

byte State = sIdle;

Bounce bCycle12;
Bounce b24Cycle12;
Bounce b48Cycle12;

bool condRelayState(bool cond, bool relayState, byte newState) {
if (cond) {
lastRelayChange = LoopEntered;
digitalWrite(pinRelay, relayState);
State = newState;
}
return !cond;
}

void setup() {
Serial.begin(115200);
pinMode(pinRelay, OUTPUT);
pinMode(pinButton1, INPUT_PULLUP);
pinMode(pinButton2, INPUT_PULLUP);
pinMode(pinButton3, INPUT_PULLUP);
bCycle12.attach(pinButton1);
b24Cycle12.attach(pinButton2);
b48Cycle12.attach(pinButton3);
}

void loop() {
LoopEntered = millis();
bCycle12.update();
b24Cycle12.update();
b48Cycle12.update();
turnTimerOff(); //de-energize the relay
turnTimerOn(); //energize the relay

if (State == sIdle) {

if (condRelayState(bCycle12.fell(), HIGH, sOn12)) // start 12 cycle
condRelayState(b24Cycle12.fell(), HIGH, sOn24); // start 24 onetime
condRelayState(b48Cycle12.fell(), HIGH, sOn48); // start 48 onetime

} else if (State == sOn12) {

if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
if (condRelayState(b24Cycle12.fell(), HIGH, sOn24)) // start on key
if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
condRelayState(LoopEntered - lastRelayChange - timerOff, LOW, sOff12); // if done, next phase

} else if (State == sOff12) {

if (condRelayState(bCycle12.fell (), LOW, sIdle)) // stop on key
if (condRelayState(b24Cycle12.fell(), HIGH, sOn24)) // start on key
if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
condRelayState(LoopEntered - lastRelayChange - timerOn, HIGH, sOn12); // if done, next phase

} else if (State == sOn24) {

if (condRelayState(b24Cycle12.fell(), LOW, sIdle)) // stop on key
condRelayState(LoopEntered - lastRelayChange > durOnce24, HIGH, sOn12); // if done, start 1st cycle
} else if (State == sOn48) {
if (condRelayState(b48Cycle12.fell(), LOW, sIdle)) // stop on key
condRelayState(LoopEntered - lastRelayChange > durOnce48, HIGH, sOn12); // if done, start 1st cycle
}
float getSetpointRatio() { //rather than counts of 0 – 1023 (range of the onboard ADC), return the 0% - 100% of range
float ratio = 0.0;
int sensorValue = 0;

sensorValue = analogRead(TIMER_RATIO_INPUT_PIN);
ratio = float(sensorValue) / float(MAX_ADC_COUNTS);
return ratio;
}
void turnTimerOn () {
//time controlled, not temperature controlled
unsigned long cumulativeOnTime = 0;
unsigned long timerOnTime = 0;
unsigned long timerOffTime = 0; //special case; keep on if no off time
float ratio;
do {
ratio = getSetpointRatio();
timerOnTime = calcOnTime(ratio);
if(timerOnTime < IGNORE_MINIMUM) { //need to refactor this
return;
}
timerOn(); //energize the relay
delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx on time.
cumulativeOnTime += SENSOR_READ_DELAY_TIME;
} while(cumulativeOnTime < timerOnTime);
}
void turnTimerOff() {
unsigned long cumulativeOffTime = 0;
unsigned long timerOffTime = 0;
float ratio = 0.0;
do {
ratio = getSetpointRatio();
timerOffTime = calcOffTime(ratio);
if(timerOffTime < IGNORE_MINIMUM) { //need to refactor this
return;
}
timerOff(); //de-energize the relay
delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx off time.
cumulativeOffTime += SENSOR_READ_DELAY_TIME;
} while(cumulativeOffTime < timerOffTime);
}
unsigned long calcOnTime(float ratio) {
//time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
unsigned long onTime = 0;
onTime = ratio * MAX_CYCLE_TIME;

return onTime;
}

unsigned long calcOffTime(float ratio) {
//time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
unsigned long offTime = 0;
float offRatio = 0.0;

offTime = calcOnTime(ratio);
offTime = MAX_CYCLE_TIME - offTime;

return offTime;
}

void timerOn() {
digitalWrite(pinRelay, TIMER_ON); // sink current
}

void timerOff() {
digitalWrite(pinRelay, TIMER_OFF);
}
}

You don’t write this symbol, </>, you press the button with that symbol to get code tags.

Look carefully at where the closing brace for the loop() function is placed.

Thank you UKHeliBob, that sorted a lot of my errors.
Now I am down to these errors:

pump_control_pot.ino: In function ‘void loop()’:
pump_control_pot:78: error: invalid operands of types ‘long unsigned int’ and ‘void()’ to binary 'operator-
pump_control_pot:85: error: invalid operands of types ‘long unsigned int’ and ‘void()’ to binary ‘operator-’
pump_control_pot.ino: At global scope:
pump_control_pot:110: error: expected unqualified-id before ‘do’
invalid operands of types ‘long unsigned int’ and ‘void()’ to binary ‘operator-’

This is how my code is at the moment:

// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

enum States { sIdle, sOn12, sOff12, sOn24, sOn48 };

// for test use 1.2 and 4.8 secs
//const unsigned long durCycle1 = 12 * 100ul * 60 * 60 * 10; // Not in use with pot meter
const unsigned long durOnce24 = 24 * 100ul; // * 60 * 60 * 10
const unsigned long durOnce48 = 48 * 100ul; // * 60 * 60 * 10

const byte pinButton1 = 2; // button for 1st action
const byte pinButton2 = 3; // button for 2nd action
const byte pinButton3 = 4; // button for 3rd action
const byte pinRelay = 13; // output for relay

unsigned long LoopEntered;
unsigned long lastRelayChange;

unsigned long ONE_SECOND = 1000; //1 second is 1000 microSeconds
unsigned long ONE_MINUTE = ONE_SECOND * 60;
unsigned long ONE_HOUR = ONE_MINUTE * 60;
int MAX_ADC_COUNTS = 1024; //ADC
unsigned long SENSOR_READ_DELY_TIME = ONE_SECOND;

unsigned long MAX_CYCLE_TIME = 24 * ONE_HOUR; // This controls the total amount of time for a period (both 1 one and 1 off time)
unsigned long IGNORE_MINIMUM = 1 * ONE_SECOND; // In case you are controlling something that you don't want cycled too fast.
int ALARM_COUNTS = 250; //blink rate. Each is on for this amount of time.
int TIMER_RATIO_INPUT_PIN = A0; // this is the onboard potentiometer that controls the ratio of on to off

int TIMER_ON = HIGH;
int TIMER_OFF = LOW;

byte State = sIdle;

Bounce bCycle12;
Bounce b24Cycle12;
Bounce b48Cycle12;

bool condRelayState(bool cond, bool relayState, byte newState) {
if (cond) {
lastRelayChange = LoopEntered;
digitalWrite(pinRelay, relayState);
State = newState;
}
return !cond;
}

void setup() {
Serial.begin(115200);
pinMode(pinRelay, OUTPUT);
pinMode(pinButton1, INPUT_PULLUP);
pinMode(pinButton2, INPUT_PULLUP);
pinMode(pinButton3, INPUT_PULLUP);
bCycle12.attach(pinButton1);
b24Cycle12.attach(pinButton2);
b48Cycle12.attach(pinButton3);
}

void loop() {
LoopEntered = millis();
bCycle12.update();
b24Cycle12.update();
b48Cycle12.update();
turnTimerOff(); //de-energize the relay
turnTimerOn(); //energize the relay

if (State == sIdle) {

if (condRelayState(bCycle12.fell(), HIGH, sOn12)) // start 12 cycle
condRelayState(b24Cycle12.fell(), HIGH, sOn24); // start 24 onetime
condRelayState(b48Cycle12.fell(), HIGH, sOn48); // start 48 onetime

} else if (State == sOn12) {

if (condRelayState(bCycle12.fell(), LOW, sIdle)) // stop on key
if (condRelayState(b24Cycle12.fell(), HIGH, sOn24)) // start on key
if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
condRelayState(LoopEntered - lastRelayChange - timerOff, LOW, sOff12); // if done, next phase

} else if (State == sOff12) {

if (condRelayState(bCycle12.fell (), LOW, sIdle)) // stop on key
if (condRelayState(b24Cycle12.fell(), HIGH, sOn24)) // start on key
if (condRelayState(b48Cycle12.fell(), HIGH, sOn48)) // start on key
condRelayState(LoopEntered - lastRelayChange - timerOn, HIGH, sOn12); // if done, next phase

} else if (State == sOn24) {

if (condRelayState(b24Cycle12.fell(), LOW, sIdle)) // stop on key
condRelayState(LoopEntered - lastRelayChange > durOnce24, HIGH, sOn12); // if done, start 1st cycle
} else if (State == sOn48) {
if (condRelayState(b48Cycle12.fell(), LOW, sIdle)) // stop on key
condRelayState(LoopEntered - lastRelayChange > durOnce48, HIGH, sOn12); // if done, start 1st cycle
}
}
float getSetpointRatio() { //rather than counts of 0 – 1023 (range of the onboard ADC), return the 0% - 100% of range
float ratio = 0.0;
int sensorValue = 0;

sensorValue = analogRead(TIMER_RATIO_INPUT_PIN);
ratio = float(sensorValue) / float(MAX_ADC_COUNTS);
return ratio;
}
void turnTimerOn() {
//time controlled, not temperature controlled
unsigned long cumulativeOnTime = 0;
unsigned long timerOnTime = 0;
unsigned long timerOffTime = 0; //special case; keep on if no off time
float ratio;}
do {
ratio = getSetpointRatio();
timerOnTime = calcOnTime(ratio);
if(timerOnTime < IGNORE_MINIMUM) { //need to refactor this
return;
}
timerOn(); //energize the relay
delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx on time.
cumulativeOnTime += SENSOR_READ_DELAY_TIME;
} while(cumulativeOnTime < timerOnTime);

void turnTimerOff() {
unsigned long cumulativeOffTime = 0;
unsigned long timerOffTime = 0;
float ratio = 0.0;
do {
ratio = getSetpointRatio();
timerOffTime = calcOffTime(ratio);
if(timerOffTime < IGNORE_MINIMUM) { //need to refactor this
return;
}
timerOff(); //de-energize the relay
delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx off time.
cumulativeOffTime += SENSOR_READ_DELAY_TIME;
} while(cumulativeOffTime < timerOffTime);
}
unsigned long calcOnTime(float ratio) {
//time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
unsigned long onTime = 0;
onTime = ratio * MAX_CYCLE_TIME;

return onTime;
}

unsigned long calcOffTime(float ratio) {
//time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
unsigned long offTime = 0;
float offRatio = 0.0;

offTime = calcOnTime(ratio);
offTime = MAX_CYCLE_TIME - offTime;

return offTime;
}

void timerOn() {
digitalWrite(pinRelay, TIMER_ON); // sink current
}

void timerOff() {
digitalWrite(pinRelay, TIMER_OFF); 
}
condRelayState(LoopEntered - lastRelayChange - timerOff, LOW, sOff12); // if done, next phase

timerOff is the name of a function. It's not a number. How do you expect to subtract it from a number? What is a number minus the name of a function supposed to equal?

do {
  ratio = getSetpointRatio();
  timerOnTime = calcOnTime(ratio);
  if (timerOnTime < IGNORE_MINIMUM) { //need to refactor this
    return;
  }
  timerOn(); //energize the relay
  delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx on time.
  cumulativeOnTime += SENSOR_READ_DELAY_TIME;
} while (cumulativeOnTime < timerOnTime);

Check your braces again. This bit is outside of any function. You can’t have a do loop that isn’t in a function. Did you mean to have this as part of the turnTimerOn function?

Using autoformat (control t) on your code will help line things up and help you find where the braces are and what they actually surround. It will also make your code much more readable.

Can somebody help me with this code, I cant get it to work the way I want. I have combined to exellent codes but I cannot get them to work together.

I have three pushbuttons, one potentiometer and one relay card.

Pushbutton 1: Start and stop a continues loop controlled by the potentiometer which divides a total of 24 hours.

Pushbutton 2: Start and stop a 24 hour loop

Pushbutton 3: Start and stop a 48 hour loop

Here is my code:

// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

enum States { sIdle, sOn12, sOff12, sOn24, sOn48, onTIme, offTime};

// for test use 1.2 and 4.8 secs
//const unsigned long durCycle1 = 12 * 100ul * 60 * 60 * 10; // Not in use with pot meter
const unsigned long durOnce24 = 24 * 100ul; // * 60 * 60 * 10
const unsigned long durOnce48 = 48 * 100ul; // * 60 * 60 * 10

const byte pinButton1 = 2; // button for 1st action
const byte pinButton2 = 3; // button for 2nd action
const byte pinButton3 = 4; // button for 3rd action
const byte pinRelay = 13; // output for relay

unsigned long LoopEntered;
unsigned long lastRelayChange;

unsigned long ONE_SECOND = 1000; //1 second is 1000 microSeconds
unsigned long ONE_MINUTE = ONE_SECOND * 60;
unsigned long ONE_HOUR = ONE_MINUTE * 60;
int MAX_ADC_COUNTS = 1024; //ADC
unsigned long SENSOR_READ_DELAY_TIME = ONE_SECOND;

//MAX_CYCLE_TIME for test use 24 * ONE_SECOND
unsigned long MAX_CYCLE_TIME = 24 * ONE_SECOND; // This controls the total amount of time for a period (both 1 one and 1 off time)
unsigned long IGNORE_MINIMUM = 1 * ONE_SECOND; // In case you are controlling something that you don't want cycled too fast.
int ALARM_COUNTS = 250; //blink rate. Each is on for this amount of time.
int TIMER_RATIO_INPUT_PIN = A0; // this is the onboard potentiometer that controls the ratio of on to off

int TIMER_ON = HIGH;
int TIMER_OFF = LOW;

byte State = sIdle;

Bounce bCycle12;
Bounce b24Cycle12;
Bounce b48Cycle12;

bool condRelayState(bool cond, bool relayState, byte newState, bool potState) {
  if (cond) {
    lastRelayChange = LoopEntered;
    digitalWrite(pinRelay, relayState);
    State = newState;
  }
  return !cond;
}

bool condRelayStateOnce(bool cond, bool relayState, byte newState) {
  if (cond) {
    lastRelayChange = LoopEntered;
    digitalWrite(pinRelay, relayState);
    State = newState;
  }
  return !cond;
}
void setup() {
  Serial.begin(115200);
  pinMode(pinRelay, OUTPUT);
  pinMode(pinButton1, INPUT_PULLUP);
  pinMode(pinButton2, INPUT_PULLUP);
  pinMode(pinButton3, INPUT_PULLUP);
  bCycle12.attach(pinButton1);
  b24Cycle12.attach(pinButton2);
  b48Cycle12.attach(pinButton3);
}

void loop() {
  LoopEntered = millis();
  bCycle12.update();
  b24Cycle12.update();
  b48Cycle12.update();
  turnTimerOff(); //de-energize the relay
  turnTimerOn(); //energize the relay

  if (State == sIdle) {

    if (condRelayStateOnce(bCycle12.fell(), HIGH, sOn12)) // start 12 cycle
      condRelayStateOnce(b24Cycle12.fell(), HIGH, sOn24); // start 24 onetime
    condRelayStateOnce(b48Cycle12.fell(), HIGH, sOn48); // start 48 onetime

  } else if (State == sOn12) {

    if (condRelayStateOnce(bCycle12.fell(), LOW, sIdle)) // stop on key
      if (condRelayStateOnce(b24Cycle12.fell(), HIGH, sOn24)) // start on key
        if (condRelayStateOnce(b48Cycle12.fell(), HIGH, sOn48)) // start on key
          condRelayStateOnce(LoopEntered - lastRelayChange, LOW, sOff12); // if done, next phase

  } else if (State == sOff12) {

    if (condRelayStateOnce(bCycle12.fell (), LOW, sIdle)) // stop on key
      if (condRelayStateOnce(b24Cycle12.fell(), HIGH, sOn24)) // start on key
        if (condRelayStateOnce(b48Cycle12.fell(), HIGH, sOn48)) // start on key
          condRelayStateOnce(LoopEntered - lastRelayChange, HIGH, sOn12); // if done, next phase

  } else if (State == sOn24) {

    if (condRelayStateOnce(b24Cycle12.fell(), LOW, sIdle)) // stop on key
      condRelayStateOnce(LoopEntered - lastRelayChange > durOnce24, HIGH, sOn12); // if done, start 1st cycle
      
  } else if (State == sOn48) {
    
    if (condRelayStateOnce(b48Cycle12.fell(), LOW, sIdle)) // stop on key
      condRelayStateOnce(LoopEntered - lastRelayChange > durOnce48, HIGH, sOn12); // if done, start 1st cycle
  }
}
float getSetpointRatio() { //rather than counts of 0 - 1023 (range of the onboard ADC), return the 0% - 100% of range
  float ratio = 0.0;
  int sensorValue = 0;

  sensorValue = analogRead(TIMER_RATIO_INPUT_PIN);
  ratio = float(sensorValue) / float(MAX_ADC_COUNTS);
  return ratio;
}
void turnTimerOn() {
  //time controlled, not temperature controlled
  unsigned long cumulativeOnTime = 0;
  unsigned long timerOnTime = 0;
  unsigned long timerOffTime = 0; //special case; keep on if no off time
  float ratio;

  do {
    ratio = getSetpointRatio();
    timerOnTime = calcOnTime(ratio);
    if (timerOnTime < IGNORE_MINIMUM) { //need to refactor this
      return;
    }
    timerOn(); //energize the relay
    delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx on time.
    cumulativeOnTime += SENSOR_READ_DELAY_TIME;
  } while (cumulativeOnTime < timerOnTime);
}
void turnTimerOff() {
  unsigned long cumulativeOffTime = 0;
  unsigned long timerOffTime = 0;
  float ratio = 0.0;
  do {
    ratio = getSetpointRatio();
    timerOffTime = calcOffTime(ratio);
    if (timerOffTime < IGNORE_MINIMUM) { //need to refactor this
      return;
    }
    timerOff(); //de-energize the relay
    delay(SENSOR_READ_DELAY_TIME); //simple way to be able to provide approx off time.
    cumulativeOffTime += SENSOR_READ_DELAY_TIME;
  } while (cumulativeOffTime < timerOffTime);
}
unsigned long calcOnTime(float ratio) {
  //time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
  unsigned long onTime = 0;
  onTime = ratio * MAX_CYCLE_TIME;

  return onTime;
}

unsigned long calcOffTime(float ratio) {
  //time is a function of MAX_CYCLE_TIME; if this is seconds, then return seconds; if mins, then return mins
  unsigned long offTime = 0;
  float offRatio = 0.0;

  offTime = calcOnTime(ratio);
  offTime = MAX_CYCLE_TIME - offTime;

  return offTime;
}

void timerOn() {
  digitalWrite(TIMER_ON, sOn12); // sink current
}

void timerOff() {
  digitalWrite(TIMER_OFF, sOff12);
}