Help with delay without delay sort of code

Hi guys,

Im developing a timer for a home appliance (a dehumidifier) in which i need to control what time it operates (easy) and add a duty cycle to that (not so easy).

Say for example, the appliance is set to work from 1PM to 4PM. This is easily accomplished by converting the time into minutes past midnight and comparing those with the values programmed.

Where I am having a problem is adding a duty cycle to that, for example, during the on time the device should stay on for 5 minutes and off for 10 to defrost.

So this is the code I’m using:

#include <Time.h>  
int internal_clock = 0;
 
// Set the on and off times for output 1
//--------------------------------------------------------------------
  #define dehumidifier_enable_hour                 14
  #define dehumidifier_enable_minute               30
  #define dehumidifier_disable_hour                18
  #define dehumidifier_disable_minute              30

// Used for the output duty cycle function
  byte time_on = 3;
  byte time_off = 10;
  int dehumidifier_on = 0;
//--------------------------------------------------------------------

// These directly convert the user variables into a time of the day in minutes, which is more easily processed.
  int enable = ((dehumidifier_enable_hour*60)+dehumidifier_enable_minute);
  int disable = ((dehumidifier_disable_hour*60)+dehumidifier_disable_minute);

void setup()

{
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);
  Serial.begin(9600); 
  setTime(14,28,00,10,02,16);
  
}

void loop()
{

  if (!second())
  {

  // update Time
    internal_clock = ((hour()*60)+minute());     // This provides a single variable showing the time of the day in minutes elapsed since midnight, for internal use
   
        // This loop executes at startup, adds a given operational time and waits for the cycle to complete before executing again
      if (((internal_clock>enable)&&(internal_clock<disable))&&(internal_clock>(dehumidifier_on+time_off)))
        // Now set the duty cycle.
        {
          //Serial.println("Kitchen Dehumidifier Operating");
          dehumidifier_on = (internal_clock+time_on+time_off-1);
          digitalWrite(5, HIGH);
          Serial.println("On");
        }
        
      // Same as above, but expires once the "on time" elapses. This sets the "off portion" of the cycle
      else if ((internal_clock>disable)||(internal_clock>dehumidifier_on))
        {
          digitalWrite(5, LOW);
          Serial.println("Off");
        }
  }
}

I cant get the code to work as I wish. Any ideas for improvements?

Basically whenever ((internal_clock>enable)&&(internal_clock<disable)) the device is within the operating time. The first time this loop is run the output is set high and the dehumidifier_on variable is loaded with the current time + the total on and off time. This variable should not be executed any more until this time is elapsed

The second part of the loop should be executed once the on time has expired and basically sets the output low for as long as the total time hasn’t elapsed (on and off) or whenever the device is outside the set operating hours.

When the total time has elapsed the first portion of the loop should execute again if the device is within operational hours and again loads the dehumidifier_on variable.

Problem is I cant quite get the variables to work. They more or less work, but I always have one minute more or one minute less. or the loop doesn’t execute properly when the device is powered up (first cycle)

Any suggestions for improvement?

Just to go through some concrete figures for this:

HH:MM:SS 00:00:00 off (if not already off) 14:30:00 on 14:33:00 off 14:43:00 on 14:46:00 off 14:56:00 on ... repeated ... 18:30:00 off (and later ...)

Is that what you want ?

Yes, in essence, that’s how it works, with the given example times (3 minutes on, 10 minutes off)

I just updated the code a bit:

#include <Time.h>  
int internal_clock = 0;
 
// Set the on and off times for output 1
//--------------------------------------------------------------------
  #define dehumidifier_enable_hour                 14
  #define dehumidifier_enable_minute               30
  #define dehumidifier_disable_hour                18
  #define dehumidifier_disable_minute              30

// Used for the output duty cycle function
  byte time_on = 1;
  byte time_off = 1;
  int dehumidifier_on = 0;
  byte deh_first_run = 0;                              // To Run the loop the first time
//--------------------------------------------------------------------

// These directly convert the user variables into a time of the day in minutes, which is more easily processed.
  int enable = ((dehumidifier_enable_hour*60)+dehumidifier_enable_minute);
  int disable = ((dehumidifier_disable_hour*60)+dehumidifier_disable_minute);

void setup()

{
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);
  Serial.begin(9600); 
  setTime(14,28,00,10,02,16);
  
}

void loop()
{

  if (!second())
  {
    delay(1000);       //This ensures loop only runs once
  // update Time
    internal_clock = ((hour()*60)+(minute())+1);     // This provides a single variable showing the time of the day in minutes elapsed since midnight, for internal use
   
        // This loop executes whenever the set time is valid
      if ((internal_clock>enable)&&(internal_clock<disable))
      {
        Serial.print("Within scheadule. Time now is");
        Serial.println(((hour()*100)+(minute())));
        if (!deh_first_run)
          {
            deh_first_run = 1;
            dehumidifier_on = (internal_clock+time_on+time_off);
          }
        if ((internal_clock+time_on+time_off)>dehumidifier_on)
          {
            dehumidifier_on = (internal_clock+time_on+time_off);
            digitalWrite(5, HIGH);
            Serial.println("On");
            Serial.println(dehumidifier_on);
          }
        else if ((internal_clock+time_on)>dehumidifier_on)
        {
          // Set the "off-portion" of the duty cycle
          digitalWrite(5, LOW);
          Serial.println("Off");
          delay(300);
        }     
      }
        
      // Same as above, but expires once the "on time" elapses. This sets the "off portion" of the cycle. Equally, disables output once the set time is no longer active
      else
        {
          Serial.print("Outside scheadule. Time now is");
          Serial.println(((hour()*100)+(minute())));
          digitalWrite(5, LOW);
          Serial.println("Off");
          delay(300);
        }
  }
}

This has a 1 minute on, 1 minute off, but seem to be stuck always on and I haven’t quite figured why…

Who would have said such a simple thing would be so troublesome

You seem to have converted the time of day into a number of minutes since midnight which is very sensible.

You can use that value in exactly the same way that millis() is used in Several Things at a Time

...R

I would have simplified it to something like the following (untested):
It uses a trick with modulus arithmetic (%) to calculate the time on/ time off series.

#include <Time.h>
int internal_clock = 0;

// Set the on and off times for output 1
//--------------------------------------------------------------------
#define dehumidifier_enable_hour                 14
#define dehumidifier_enable_minute               30
#define dehumidifier_disable_hour                18
#define dehumidifier_disable_minute              30

// Used for the output duty cycle function
byte time_on = 1;
byte time_off = 1;
int dehumidifier_on = 0;
byte deh_first_run = 0;                              // To Run the loop the first time
//--------------------------------------------------------------------

// These directly convert the user variables into a time of the day in minutes, which is more easily processed.
int enable = ((dehumidifier_enable_hour * 60) + dehumidifier_enable_minute);
int disable = ((dehumidifier_disable_hour * 60) + dehumidifier_disable_minute);

void setup()

{
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);
  Serial.begin(9600);
  setTime(14, 28, 00, 10, 02, 16);

}

void loop()
{

  if (!second())
  {
    delay(1000);       //This ensures loop only runs once
    // update Time
    internal_clock = ((hour() * 60) + (minute()) + 1); // This provides a single variable showing the time of the day in minutes elapsed since midnight, for internal use

    if ((internal_clock > enable) && (internal_clock < disable) && ( internal_clock % (time_on + time_off) < time_on )  )
    {
      digitalWrite(5, HIGH);
      Serial.println("On");
    }
    else {
      digitalWrite(5, LOW);
      Serial.println("Off");
    }
  }
}

I use the Time library and Unix time for such things. Then things like alarms and duty cycles become a simple matter of integer arithmetic (and it handles time down to the second).

6v6gt: I would have simplified it to something like the following (untested): It uses a trick with modulus arithmetic (%) to calculate the time on/ time off series.

Interesting approach, dividing the time trough the total minutes and using the excess as a number up to the total on+off time!

Function wise, it works 100%. At times it comes on during the off cycle depending on the calculations, so the load takes a few minutes more to be activated, but acceptable.

aarg: I use the Time library and Unix time for such things. Then things like alarms and duty cycles become a simple matter of integer arithmetic (and it handles time down to the second).

Interesting, Ill check that out.

The period of the duty cycle in your examples is relatively short compared with the main enable/disable window of the dehumidifier,
but if do you want the start of the start of the duty cycle ‘on period’ to be aligned with the start of the main enable/disable window,
you could do this:

   if ((internal_clock > enable) && (internal_clock < disable) && ( ( 1 + time_on + time_off + internal_clock - enable ) % (time_on + time_off) < time_on )  )
    {
         .....
    }

This shifts the start of the duty cycle to the beginning of the main enable/disable window.
The “1” is required because you have the test: (internal_clock > enable).
If you changed that to : (internal_clock >= enable) it would not be necessary and you’d also then remove the “1” from: internal_clock = ((hour() * 60) + (minute()) + 1);
The extra “time_on + time_off” is to ensure that the modulus expression never yields a negative during the main enable/disable window.

8:59:00 = (8+60)+59 9:00"00 = (9*60) 9:00:59 = (9*60)

the difference in minutes between 8:59:00 and 9:00:59

is 1

your resolution is +/- 59 seconds your comment that you are to the minute +/- 1 minute would prove this out.

also, you cannot have a timer that is less than 2 minutes. due to the window of +/-1 minute

dave-in-nj: also, you cannot have a timer that is less than 2 minutes. due to the window of +/-1 minute

If that matters it would be trivial to change the whole thing to use seconds rather than minutes.

...R

Robin2: If that matters it would be trivial to change the whole thing to use seconds rather than minutes.

...R

he said he was either plus 1 minute, or late by 1 minute and could not get it to work with a window of 1 minute and did not know why. now we know, and you are correct, he could either change his int to long, then use seconds for the entire day, leave it at int and only count seconds till overflow a little after 10:00 AM, or start counting seconds at the first window of operation, or set the starting time at the first window and have seconds run for a little over 10 hours. or just not worry about the 2 minute window. personally, I am not a fan of non-feedback control. just running the dehumidifier, whether it needs to run or not, then shutting it off, regardless of the humidity, in my book, is not a good implementation of control.

dave-in-nj: personally, I am not a fan of non-feedback control. just running the dehumidifier, whether it needs to run or not, then shutting it off, regardless of the humidity, in my book, is not a good implementation of control.

I agree completely. But that is a separate issue.

...R

For what I am trying to achieve, using seconds is a bit overkill. The concern with the +/- 1 minute is just in terms of code correct operation.

I had it working with the first code 6v6gt has posted and that seems to do the trick nicely.

dave-in-nj: personally, I am not a fan of non-feedback control. just running the dehumidifier, whether it needs to run or not, then shutting it off, regardless of the humidity, in my book, is not a good implementation of control.

The process is controlled using a temperature and humidity sensor. The duty cycle is used to add a defrost cycle if the temperature is low, as to avoid a solid block of ICE on the evaporator. It essentially varies with temperature.

casemod: For what I am trying to achieve, using seconds is a bit overkill.

Presenting something at the functional level is not the same as using it at an internal level. If using seconds internally makes counting minutes possible at the functional level, it's perfectly respectable design methodology to do so.