Implementing millis() in place of delay()

This is my first time posting so forgive me if I don’t have my sketch uploaded correctly. I am an arduino novice by far, just learning as I go, I am also a farmer so I decided I wanted to make a controller for my grain bin fans based on temperature and humidity readings to control a relay which would in turn control the fan. I have succeeded in accomplishing that but I wanted to refine it a bit more and use millis() to tell the ardruino that once the temp/humidity threshold has been reached, to leave the fan on for xx amount of time before it shuts off even if the temp/humidity drops back below the threshold during that time. I think I mostly understand the millis() function but I can’t seem to figure out how exactly to implement it in place of delay() in my code. Its the last several lines in those if statements, where it says delay(600UL) thats where I want to utilize millis. If possible anyway. Thank you in advance for any help!

 /* turn led on if temp is above certain point

*/
// global constants

unsigned long previousTime = 0;
const unsigned long minRunTime = 1,200,000UL;



// library
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

#define DHTPIN 7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

//Variables
int chk;
float hum;//Stores humidity
float temp;//stores temp
int relay = 3;

void setup() {
  Serial.begin(9600);
  lcd.begin(16,2);
  dht.begin();
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);
}

void loop() {

  unsigned long currentTime = millis();

  delay(2000);

  //Read humidity and temp
  hum = dht.readHumidity();
  temp = dht.readTemperature(true);

  //Prints Humidity and Temp to serial monitor
  Serial.print("humidity:");
  Serial.print(hum);
  Serial.print("%, Temp:");
  Serial.print(temp);
  Serial.println("F");
  lcd.clear();
  lcd.print("HUM: ");
  lcd.print(hum);
  lcd.print("%");
  lcd.setCursor(0, 1);
  lcd.print("TEMP: ");
  lcd.print(temp);
  lcd.print("F");
  delay(10000);

  //Turn fan on if temp and humidity is within the threshold

  if ((temp) > 30 and (hum) > 20 and (temp) < 90 and (hum) < 50) {
    digitalWrite(3, HIGH);
    if (relay = HIGH) delay(600UL);
  } else {
    digitalWrite(3, LOW);
  }
}

Fan_Controller.ino (1.38 KB)

Did you have a look at the Blink Without Delay sketch? (File->Examples->-02.Digital->BlinkWithoutDelay). It will show you have to use millis() properly.

Then, read the sticky post at the top of the forum and learn now to properly add your code inside code tags.

If you get stuck, post back and please do it properly.

I think I got the code posted correctly now. At least it is on my screen anyway. I have looked at Bwod, but that and every other example I have found is just telling it to do print or read something every xx amount of time. I am reading sensors and then based off the data that is returned from said sensor, I may or may not want it to wait for a certain amount of time. The reason I want to use millis is so I can still have up to date temp and humidity readings even when its in that delay period. Obviously if you use delay it all just stops and waits until that time period is up so I can't get my readings every 10 seconds like I want to during that time.

The sample code explains it pretty well.

Instead of delay, you set a variable, say startTime = millis().
In loop, test the startTime against millis(), which increments every millisecond.

if ((millis() - startTime) > myDelay) {
 //do whatever you want to happen when the time has elapsed
}

As a first time poster - you deserve ++karma for making the effort.

Now to the millis() thing. It seems unintuitive at first glance to a beginner, but you need to get your head around the idea that loop() is running all the time - fast. You can set, check & do things on separate passes each time around - which [u]appear[/u] to be happening 'instantly'... in reality they may occur milliseconds apart - but you and most projects don't care about that. (Later - you may learn some other, more advanced tricks, or ultimately step up to a more complex platform than the Arduino IDE and 8-bit chips.)

Put your project aside for a few hours, load up and work through the BWoD examples, just blinking LEDs... tweak them and try to understand why they do what they do, and how. It's not so tricky when you spend a few minutes figuring it out.

Good luck.

For extra information and examples look at Using millis() for timing. A beginners guide and Several things at the same time

Thanks for the responses! Maybe I didn't word it correctly or maybe I am just not quite wrapping my mind around those examples, but they aren't what I need. All those examples are just timing based to blink an LED or something of that sort, there isn't any other data being used in those statements. What I need is to have a relay remain on for 20 minutes once its turned on. So do I need an if statement within an if statement to make millis work in this case? I've already got an if statement checking the data from the DHT22 and then what it to remain on for 20 minutes if the temp and humidity is within the threshold even if it drops back below said threshold.

What I need is to have a relay remain on for 20 minutes once its turned on. So do I need an if statement within an if statement to make millis work in this case?

Turn it on. Save the current value of millis(). Check each time through loop whether the current value of millis() minus the start value equals or is greater than the required period. If so then do something like turning it off, otherwise keep going round loop()

If you want other factors to influence whether it turns on then introduce a boolean to the program and turn on the relay only if the boolean is true. If for some reason it must not turn on then set the boolean to false and it won't happen

This is mostly classic BlinkWithoutDelay as exemplified by the example of that name

Farmboy1994: Thanks for the responses! Maybe I didn't word it correctly or maybe I am just not quite wrapping my mind around those examples, but they aren't what I need. All those examples are just timing based to blink an LED or something of that sort, there isn't any other data being used in those statements. What I need is to have a relay remain on for 20 minutes once its turned on. So do I need an if statement within an if statement to make millis work in this case? I've already got an if statement checking the data from the DHT22 and then what it to remain on for 20 minutes if the temp and humidity is within the threshold even if it drops back below said threshold.

Always test the timer in each iteration of loop(). Write come code and try to get it working, then come back with your sketch and any questions.

This might give you some ideas:

 //Blink without Delay skeleton 
//4 examples demonstrated
//

//LED wiring options
//=============================================
//Depending which way your LEDs are wired, uncomment the next line.
//#define PlusEqualsON 

#ifdef PlusEqualsON
//wired so +5V turns LED ON
#define ledON  HIGH
#define ledOFF LOW
//=========================
#else
//wired so +5V turns LED OFF
#define ledON  LOW
#define ledOFF HIGH
//=========================
#endif

//switch wiring options
//=============================================
//Depending which way your switches are wired, uncomment the next line.
#define PushEqualsLOW 

#ifdef PushEqualsLOW 
//pushing the switch makes pin LOW
#define Pushed   LOW
#define Released HIGH
//=========================
#else
//pushing the switch makes pin HIGH
#define Pushed   HIGH
#define Released LOW
//=========================
#endif

//=============================================
unsigned long currentMillis;
unsigned long pin13Millis;
unsigned long pin12Millis;
unsigned long pin11Millis;
unsigned long SwitchMillis;

//if these are not changed in the sketch, they can be const
unsigned long debounceMillis = 100UL;     //100ms
unsigned long ledOnTime      = 5*1000UL;  //5 seconds

byte laststartSwitchState    = HIGH;
byte buttonState             = HIGH;
byte counter                 = 0;

//the following are enable/disable flags
//some of these might not be used in this sketch
boolean flag13 = true;
boolean flag12 = true;
boolean flag11 = true;
boolean flag10 = true;

const byte startSwitch = 2; //pushed = LOW
const byte testSwitch  = 3; //pushed = LOW

//**********************************************************************

void setup()
{
  Serial.begin(9600);
  
  digitalWrite(13,ledOFF);
  pinMode(13, OUTPUT); 

  digitalWrite(12,ledOFF);
  pinMode(12, OUTPUT);

  digitalWrite(11,ledOFF);
  pinMode(11, OUTPUT);
  
  digitalWrite(10,ledOFF);
  pinMode(10, OUTPUT);

  pinMode(startSwitch, INPUT_PULLUP); //pushed = LOW
  pinMode(testSwitch,  INPUT_PULLUP); //pushed = LOW

} //  >>>>>>>>>>>>>> E N D  O F  s e t u p ( ) <<<<<<<<<<<<<<<<<

void loop()
{
  //save the current time
  currentMillis = millis();

  //************************************* E x a m p l e  1
  //toggle pin 13 every 200mS
  //has 200ms or more gone by?
  if (currentMillis - pin13Millis >= 200UL)
  {
    //code here runs every 200ms
    //get ready for next iteration
    pin13Millis = pin13Millis + 200UL;
    //toggle pin 13
    digitalWrite(13,!digitalRead(13));
  }

  //************************************* E x a m p l e  2
  //at power up, pin 12 LED goes ON, after 3 seconds goes OFF and stays OFF
  //could be used as a powerup reset signal
  if (flag12 == true && currentMillis - pin12Millis <= 3000UL)
  {
    //code here runs for 3 seconds after power up, then stops
    digitalWrite(12,ledON);
  }
  else
  {
    digitalWrite(12,ledOFF);
    //disable further pin 12 control
    flag12 = false;
  }

  //************************************* E x a m p l e  3
  //if testSwitch is pushed and released
  //pin 11 LED goes ON for 5 seconds, then goes OFF 
  buttonState = digitalRead(testSwitch);
  
  //are we are allowed to check the switch and is it pressed?
  if(flag11 == true && buttonState == Pushed)
  {    
    //enable timing of LED on pin 11
    flag11 = false; //false --> timing is enabled
    //turn LED ON
    digitalWrite(11,ledON);
    //record the time LED turned ON
    pin11Millis = currentMillis;
  }
    
  //are we allowed and is it time to control pin 11
  if (flag11 == false && currentMillis - pin11Millis >= ledOnTime)
  {
    //if enabled, code here runs after ledOnTime ms goes by
    digitalWrite(11,ledOFF);
    //allow switch press detection again
    flag11 = true; //true --> switch monitoring is enabled
  }

  //************************************* E x a m p l e  4
  //is it time to check the switches?
  //in particular, pushing startSwitch will turn ON/OFF (toggle) an output pin 10
  //is it time to check the switches
  if (currentMillis - SwitchMillis >= debounceMillis)
  {
    //code here runs every debounceMillis ms
    //get ready for the next iteration
    SwitchMillis += debounceMillis; 
    //go and check the switches
    checkSwitches();    
  } 

  //*********************************
  //put other non-blocking stuff here
  //*********************************

} //  >>>>>>>>>>>>>> E N D  O F  l o o p ( ) <<<<<<<<<<<<<<<<<


//======================================================================
//                      F U N C T I O N S
//======================================================================


//****************** c h e c k S w i t c h e s ( ) *********************
//switches are checked every debounceValue milli seconds 
//no minimum switch press time is validated with this code (i.e. No glitch filter)
void checkSwitches()  
{
  //re-usable for all the switches  
  boolean thisState;    

  //************************************* E x a m p l e  Push ON push OFF (toggle)   
  //check if this switch has changed state
  thisState = digitalRead(startSwitch);
  if (thisState != laststartSwitchState)
  {  
    //update the switch state
    laststartSwitchState = thisState;  

    //this switch position has changed so do some stuff

    //"HIGH condition code"
    //switch went from LOW to HIGH
    if(thisState == HIGH)        
    {
      //Do some HIGH switch stuff here
    }

    //"LOW condition code"
    //switch went from HIGH to LOW
    else                          
    {
      //Do some LOW switch stuff here  
      digitalWrite(10, !digitalRead(10));
      //print number of pushes
      counter++;
      Serial.println(counter);
    }

  } //END of startSwitch code

  //*****************************************  
  //similar code for other switches goes here 
  //*****************************************  

} //END of checkSwitches()

//**********************************************************************

//======================================================================
//                      E N D  O F  C O D E
//======================================================================

Can you please post some code that will compile? It will save us a lot of time, as the compiler can help you out.

This [untested] code should get you close

/* turn led on if temp is above certain point

*/
// global constants

unsigned long previousDisplayTime;  // screen update time
const unsigned long displayDelay = 10000UL;  // update screen every 10 seconds

unsigned long previousReadTime;   // last sensor read time
const unsigned long readDelay = 2000; // read sensors every 2 seconds

unsigned long fanStartTime;   // time fan turned on
const unsigned long minRunTime = 1000UL * 60 * 2; // 20 minute
bool fanRunning = false;

// library
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

#define DHTPIN 7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

//Variables
float hum;//Stores humidity
float temp;//stores temp
const int relayPin = 3;

void setup() {
  Serial.begin(9600);
  lcd.begin(16, 2);
  dht.begin();
  pinMode(relayPin, OUTPUT);
  pinMode(2, OUTPUT);
}

void loop() {

  unsigned long currentTime = millis();

  // check if it is time to read sensors
  if ( currentTime - previousReadTime >= readDelay ) {
    previousReadTime = currentTime;

    //Read humidity and temp
    hum = dht.readHumidity();
    temp = dht.readTemperature(true);

    //Prints Humidity and Temp to serial monitor
    Serial.print("humidity:");
    Serial.print(hum);
    Serial.print("%, Temp:");
    Serial.print(temp);
    Serial.println("F");
  }

  // check if it is time to update lcd
  if ( currentTime = previousDisplayTime >= displayDelay ) {
    previousDisplayTime = currentTime;
    lcd.clear();
    lcd.print("HUM: ");
    lcd.print(hum);
    lcd.print("%");
    lcd.setCursor(0, 1);
    lcd.print("TEMP: ");
    lcd.print(temp);
    lcd.print("F");
  }

  //check fan status

  if ( fanRunning == true ) {
    // fan already running so test how long it has been running and turn off if needed
    if ( currentTime - fanStartTime >= minRunTime ) {
      // turn it off
      fanRunning = false;
      digitalWrite(relayPin, LOW);
    }
  }
  else {
    // fan is not running so test if we need to turn it on
    if ((temp) > 30 and (hum) > 20 and (temp) < 90 and (hum) < 50) {
      digitalWrite(relayPin, HIGH);
      fanStartTime = currenTime;
      fanRunning = true;
    }
  }
}

Farmboy1994: What I need is to have a relay remain on for 20 minutes once its turned on.

Isn't that exactly the same as having an LED stay on for 20 minutes?

...R

Robin2: Isn't that exactly the same as having an LED stay on for 20 minutes?

...R

Only if it's powered from a separate power supply. ;)

aarg: Only if it's powered from a separate power supply. ;)

I was referring to the program logic and the OP's comment in Reply #6

All those examples are just timing based to blink an LED or something of that sort,

...R