Code will turn relay on but won't turn back it off... I'm stumped..

[code]

/* 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 = 10000UL; // read sensors every 10 seconds

unsigned long fanStartTime;   // time fan turned on
const unsigned long minRunTime = 60000UL; //  1 Minutes
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 = 13; //Built in led

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");
    if (fanRunning == true)
    Serial.println("Fan On");
    if (fanRunning == false)
    Serial.println("Fan Off");
  }

  // 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 ((temp) > 30 and (hum) > 20 and (temp) < 90 and (hum) < 55) {
      digitalWrite(relayPin, HIGH);
      fanStartTime = currentTime;
      fanRunning = true;
    }
    
  
    
  
     else if (fanRunning == true)
     if((temp) < 30 and (hum) < 20 and (temp) > 90 and (hum) > 55 and currentTime - fanStartTime >= minRunTime) {
      digitalWrite(relayPin, LOW);
      fanRunning = false;
   
    
   }
  
}

[/code]
A short overview, this project will, when done, turn a relay on when the temperature and humidity get between a certain threshold and will leave the relay on for a period of time(regardless of whether the temperature and humidity drop back below their thresholds). I have put together a functioning version of this with the delay function and it pretty much works perfectly. I wanted to implement millis in case I ever wanted to do something while the hold period was in place. So, here’s my problem, the relay turns on just fine when temp/hum get within their threshold but it won’t turn back off again once it gets out of its threshold. It’s like it completely glosses over the statement to check the temp/hum and time its been running and shut it back off again.

You'll need to change the conditions that you use to check to turn it off. What you have is looking for a temperature that is less than 30 and greater than 90. You will need to use or.

if((temp) < 30 and (hum) < 20 and (temp) > 90 and (hum) > 55

in order for that to be true temp has to be less than 30 and also greater than 90 at the same time.
Same for hum, less than 20 and also greater than 55.

Maybe:

if(((temp) < 30 and (hum) < 20) or ((temp) > 90 and (hum) > 55)

I tried copying and pasting your code in place of mine and it still won't turn off. I'm not doubting that using and instead of or is a problem, but I only used "and" in my sketch with the delay function, just exactly like its laid out there and it worked just fine... Turns on and off exactly when it needs to..

[code]






// library
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_SHT31.h"
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

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

Adafruit_SHT31 sht31 = Adafruit_SHT31();

//Variables
int chk;
int relay = 3;


void setup() {
  Serial.begin(9600);
  sht31.begin(0x44);
  lcd.begin(22, 21);
  pinMode(4, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(2, OUTPUT);
}

void loop() {

  delay(2000);

  //Read humidity and temp
  float hum = sht31.readHumidity();
  float temp = sht31.readTemperature();

  //Prints Humidity and Temp to serial monitor
  Serial.print("humidity:");
  Serial.print(hum);
  Serial.print("%, Temp:");
  Serial.print(temp * 1.8 + 32);
  Serial.println("F");
  lcd.clear();
  lcd.print("T: ");
  lcd.print(temp * 1.8 + 32);
  lcd.print("F");
  lcd.setCursor(0, 1);
  lcd.print("H: ");
  lcd.print(hum);
  lcd.print("%");
  delay(18000UL);

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

  if ((temp * 1.8 + 32) > 35 and (hum) > 20 and (temp * 1.8 + 32) < 90 and (hum) < 70) {
    digitalWrite(13, HIGH);
    delay(8000);
    digitalWrite(4, HIGH);
    if (relay = HIGH) Serial.println("ON");
    lcd.setCursor(14, 1);
    lcd.print("ON");
    delay(1200000UL);
  } else {
    digitalWrite(13, LOW);
    digitalWrite(4, LOW);
  }
}

[/code]
This is my sketch with delay, obviously it’s set up a tad bit differently, but still I’m using “and” and it is functioning fine other than needing reset every once in awhile.

Your old code only has one if controlling the relay. You added a second and broke it.

So are you saying that it's not possible to have more than one if statement?

Not at all. It is unnecessary in this case though. You have an if that tells you to turn the relay on. In your original code, if it wasn't to be turned on, you turned it off.

Now, you check to see if it should be on. If not you use another if to see if it should be off. Problem is, as previously mentioned, to turn it off, you are looking for a temperature that is simultaneously less than 30 and greater than 90. Can you think of such a number?

No I cannot. But I changed it per your instruction and it still isn't functioning. It did seem a bit redundant to me to have both of those if statements but I didn't know how else to do that using millis as the timing function for the hold period instead of delay. I'm all for making code simpler, but since I'm a novice, I just can't wrap my mind around quite how to make that work with just one if statment.

Best to post your code since it isn't working.

Essentially though, use millis to determine when to check relay state. Use the first if as you did before to tell you to turn it on and an else (with no extra if) to turn it off.

Ultimately, you may need to use a bit of hysteresis to avoid rapid switching of the relay when you are near the set points, but try the simple solution first.

[code]

/* 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 = 10000UL; // read sensors every 10 seconds

unsigned long fanStartTime;   // time fan turned on
const unsigned long minRunTime = 120000UL; //  2 Minutes
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 = 13; //Built in led

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");
    if (fanRunning == true)
      Serial.println("Fan On");
    if (fanRunning == false)
      Serial.println("Fan Off");
  }

  // 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 (((temp) > 30 and (hum) > 20) and ((temp) < 90 and (hum) < 55)) {
    digitalWrite(relayPin, HIGH);
    fanStartTime = currentTime;
    fanRunning = true;
  }

  else if ((currentTime - fanStartTime >= minRunTime) and (temp) < 30 or (temp) > 90 and (hum) < 20 or (hum) > 55)
  {
    digitalWrite(relayPin, LOW);
    fanRunning = false;
  }
}

[/code]
OK, I think I was misunderstanding you a bit before. I cleaned my code up a little and tried again, and now it turns off! But it ignores the hold time… This is what I have. I am using an else if at the moment.

So even though the way I currently have it is not ideal, my main question now is why is it ignoring the hold time that I have in there? To me the way I understand it is that everything in that else if statement would have to be true, starting with the hold time before it would turn the relay off. Is that not correct?

if ( currentTime = previousDisplayTime ?

As dumb as that mistake is, unfortunately it is not the cause of the problem. I fixed it and looked for any other such typos and it still just ignores the hold...

I’m sorry can’t see any variables called “hold”.

I’d probably factor the temperature and humidity acceptable range values into a single boolean function.
It would make your code easier to read - I don’t really know why you’ve got parentheses around variable names in some expressions

Sorry, minRunTime is what the variable is called. That is an excellent question to which I do not really have an answer other than to say that is how I had seen it done when I was initially setting up the DHT 22 so I just went with it.

I suspect you may need to have a chat with Mr de Morgan, but I’m off to bed now.

Please post the latest version if you need more help.

I can’t get this to compile because “POSITIVE” in this line:

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

is not defined. As an aside, maybe I don’t have the right I2C library but I don’t see any constructor having that many parameters; are you sure this is correct? As well, you’ve got pins 0 and 1 (hardware serial) assigned duty in this constructor and are using the serial port; is this as intended?

Anyway, try this:

/* 
 *  turn led on if temp is above certain point
 */
// library
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define DHTPIN 7
#define DHTTYPE DHT22

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 = 10000UL; // read sensors every 10 seconds

unsigned long fanStartTime;   // time fan turned on
const unsigned long minRunTime = 120000UL; //  2 Minutes
bool fanRunning = false;

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

//Variables
float hum;//Stores humidity
float temp;//stores temp
const int relayPin = 13; //Built in led

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");
        
        if (fanRunning == true)
            Serial.println("Fan On");
        if (fanRunning == false)
            Serial.println("Fan Off");
    }
    
    // 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 == false )
    {
        if( (temp > 30 && temp < 90) && (hum > 20 && hum < 55) ) 
        {
            digitalWrite(relayPin, HIGH);
            fanStartTime = currentTime;
            fanRunning = true;
            
        }//if
            
    }//if
    else
    {
        if( (currentTime - fanStartTime) >= minRunTime )
        {
            if( (temp < 30 || temp > 90) && (hum < 20 || hum > 55 ) )
            {
                digitalWrite(relayPin, LOW);
                fanRunning = false;
            }
        }
    }
}

There are several libraries named LiquidCrystal_I2C and, unfortunately, not all are the same, making it harder to find the library that matches the code. That is one reason that I use the hd44780 library for my character LCDs.