Help using Relays with greenhouse

Hi everyone,

I'm no expert at this programming stuff and I've been cracking my head for a few days now to figure out how to incorporate a temperature and humidity controlled relay.

Here lies the problem :

Right now I have yet to put in my digital pins because I have already tried to insert then and were running with the delay of my DHT readings, turning on during the reading and instantly turns off!!! I've read about the Blink Without Delay, switch case and a lot more other options... Now I'm just not sure how to make multiple readings and different reactions for different situations...

Just some input would be great!!!

Thank you for your help

//Library for SainSmart LCD 20x4 I2C 
#include <FastIO.h>
#include <I2CIO.h>
#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal_SR.h>
#include <LiquidCrystal_SR2W.h>
#include <LiquidCrystal_SR3W.h>

#define lcdAddr 0x27

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

//Library for DHT Sensor
#include "DHT.h"
#define DHTTYPE DHT11
#define DHTPIN 2
DHT dht(DHTPIN, DHTTYPE);

//Library for Button 
#include <Button.h>

//Library for Datalogger
#include <SD.h>
#include <SPI.h>
const int chipSelect = 10;

//Basic Libraries
#include <Wire.h>

void setup() {

//Setup for DHT
  Serial.begin(9600);
  Serial.println("DHT Readings");

dht.begin();

//Setup for Datalogging

Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
//Setup for LCD Display
  
  lcd.begin(20,4);

//Relay pins
}
  

void loop() {
//Loop for DHT Sensor
 delay (5000);
 
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float f = dht.readTemperature(true);

   if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  
  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");

//Loop for datalogger

   String dataString = "";
   
   for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
}    
File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
    }
  else {
    Serial.println("error opening datalog.txt");
  }


//Water Level Analog Sensor
  
//Relay loop

//LCD Display

  lcd.setBacklight(1);
  
  lcd.home();
  lcd.print("Temperature");
  lcd.setCursor(0,1);
  lcd.print(t);
  lcd.print("*C");
  
  lcd.setCursor(0,2);
  lcd.print("Humidity");
  lcd.setCursor(0,3);
  lcd.print(h);
  lcd.print("*%");

}

Greenhouse_Controller_v_1.ino (2.54 KB)

Ideally, you'd have formatted your code and posted it between code tags, but here it is:

//Library for SainSmart LCD 20x4 I2C
#include <FastIO.h>
#include <I2CIO.h>
#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal_SR.h>
#include <LiquidCrystal_SR2W.h>
#include <LiquidCrystal_SR3W.h>

#define lcdAddr 0x27

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

//Library for DHT Sensor
#include "DHT.h"
#define DHTTYPE DHT11
#define DHTPIN 2
DHT dht(DHTPIN, DHTTYPE);


//Library for Button
#include <Button.h>

//Library for Datalogger
#include <SD.h>
#include <SPI.h>
const int chipSelect = 10;

//Basic Libraries
#include <Wire.h>

//Analog Sensors
const int WaterLevelPin = A0;
int sensorValue = 0;

//Relays
const int relay1Pin = 12;
int relay1State = LOW;


void setup() {

  //Setup for DHT
  Serial.begin(9600);
  Serial.println("DHT Readings");

  dht.begin();

  //Setup for Datalogging

  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  //Setup for LCD Display

  lcd.begin(20, 4);

  //Relay pins
}


void loop() {
  //Loop for DHT Sensor
  delay (5000);

  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float f = dht.readTemperature(true);

  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");

  //Loop for datalogger

  String dataString = "";

  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
  }
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }
  else {
    Serial.println("error opening datalog.txt");
  }


  //Water Level Analog Sensor

  //Relay loop

  //LCD Display

  lcd.setBacklight(1);

  lcd.home();
  lcd.print("Temperature");
  lcd.setCursor(0, 1);
  lcd.print(t);
  lcd.print("*C");

  lcd.setCursor(0, 2);
  lcd.print("Humidity");
  lcd.setCursor(0, 3);
  lcd.print(h);
  lcd.print("*%");

}

Thanks I just discovered the functions xD

OK. Under what conditions do you wish the relay controlled by the DHT sensor to operate ?

  1. When the temp is too low, turns on heater, start the low setting on a two speed fan
  2. When the humidity is too low, start two speed fan at High, humidifier turns on, aux fan stops
  3. When temp is too high, start two speed fan at high, start aux fan, start humidifier
  4. When all parameters are within threshold, every hour start aux fan for 5 minutes, then stops

I have just read about millis() but I cant figure where in the code to utilize it

To get rid of delay, you can use the following

void loop()
{
  // current time;
  unsigned long currentTime = millis();
  // start time of read delay
  static unsigned long startTime = 0;

  // if delay not started, start delay and read dht
  if (startTime == 0)
  {
    // set the start time
    startTime = currentTime;

    // read dht
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    float f = dht.readTemperature(true);

    if (isnan(h) || isnan(t) || isnan(f))
    {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
  }
  // if delay lapsed
  else if(currentTime - startTime > 5000)
  {
    // reset start time
    startTime = 0;
  }
  
  your other code
  ...
  ...
}

'startTime' is used both as a flag and to remember the time that the required delay started.
If 'startTime' equals zero, the start time of the delay is set and the measurement is done.
Else the current time is compared to the start time and if greater than the required delay, 'startTime' is set to zero so the next iteration of loop() the reading will be done again.

Mmm... Interesting but it does not work because without my data apparently I cannot declare my variable elsewhere in the code as shown

//Library for SainSmart LCD 20x4 I2C 
#include <FastIO.h>
#include <I2CIO.h>
#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal_SR.h>
#include <LiquidCrystal_SR2W.h>
#include <LiquidCrystal_SR3W.h>

#define lcdAddr 0x27

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

//Library for DHT Sensor
#include "DHT.h"
#define DHTTYPE DHT11
#define DHTPIN 2
DHT dht(DHTPIN, DHTTYPE);


//Library for Button 
#include <Button.h>

//Library for Datalogger
#include <SD.h>
#include <SPI.h>
const int chipSelect = 10;

//Basic Libraries
#include <Wire.h>

//Analog Sensors
const int WaterLevelPin = A0;
int sensorValue = 0;

//Relays
const int relay1Pin = 12;
int relay1State = LOW;


void setup() {

//Setup for DHT
  Serial.begin(9600);
  Serial.println("DHT Readings");

dht.begin();

//Setup for Datalogging

Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
//Setup for LCD Display
  
  lcd.begin(20,4);

//Relay pins
}
  

void loop() {
//Loop for DHT Sensor
  unsigned long currentTime = millis();
  static unsigned long startTime = 0;

  if (startTime == 0){
    startTime = currentTime;
  
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float f = dht.readTemperature(true);

   if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
}
  else if(currentTime - startTime > 5000) {
    startTime = 0;
  }
  
  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");

//Loop for datalogger

   String dataString = "";
   for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
}    
File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
    }
  else {
    Serial.println("error opening datalog.txt");
  }


//Water Level Analog Sensor
  
//Relay loop

//LCD Display

  lcd.setBacklight(1);
  
  lcd.home();
  lcd.print("Temperature");
  lcd.setCursor(0,1);
  lcd.print(t);
  lcd.print("*C");
  
  lcd.setCursor(0,2);
  lcd.print("Humidity");
  lcd.setCursor(0,3);
  lcd.print(h);
  lcd.print("*%");

}

I understand do what the concept of this millis does but, I may need some kind of data relay for taking that of the reading and taking it out of the brackets to use elsewhere... Maybe there is a function for that?

Sorry, my mistake.

void loop()
{
  // current time;
  unsigned long currentTime = millis();
  // start time of read delay
  static unsigned long startTime = 0;

  // humidity, tempC, tempF
  float h, t, f;

  // if delay not started, start delay and read dht
  if (startTime == 0)
  {
    // set the start time
    startTime = currentTime;

    // read dht
    h = dht.readHumidity();
    t = dht.readTemperature();
    f = dht.readTemperature(true);

    if (isnan(h) || isnan(t) || isnan(f))
    {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
  }
  // if delay lapsed
  else if(currentTime - startTime > 5000)
  {
    // reset start time
    startTime = 0;
  }
 
  your other code
  ...
  ...
}

Wow... how simple was that :o thank you very much sterretje it has me flying thru my coding... a lot of understanding for a construction guy xD so do I still use the millis for all my other function?

Not quite sure what you mean?

here is one I desire to understand : due to the nature of the timing, will I have to assign multiple start times for every area or will one time starter and multiple independent sequencing be sufficient

In other words, does the current time affect only one structure or will it be a base for every structure to run their own time for their loop?

You can share currentTime. You need a second startTime variable for point 4 in reply #4. Change the name of your current startTime to e.g. startTimeSensordelay (ok, it's a bit long but it reflects exactly what it is) and add a second variable startTimeFandelay that you use for the fan.

Sounds like what im doing right now but... ran into another problem... See the timer works but keeps generating data over and over at a rapid pace, im trying to keep incoming data at a 5 second interval xD I know maybe it is over the top but I would desire that for my data logging, or should I keep my sensor always on and only take the data that I need at every 5 seconds, because i am guessing in this scenario that my relay is functioning in the same way that my sensor is?

Please post your latest code.

Here we go :

//Library for SainSmart LCD 20x4 I2C 
#include <FastIO.h>
#include <I2CIO.h>
#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal_SR.h>
#include <LiquidCrystal_SR2W.h>
#include <LiquidCrystal_SR3W.h>

#define lcdAddr 0x27

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

//Library for DHT Sensor
#include "DHT.h"
#define DHTTYPE DHT11
#define DHTPIN 2
DHT dht(DHTPIN, DHTTYPE);


//Library for Button 
#include <Button.h>

//Library for Datalogger
#include <SD.h>
#include <SPI.h>
const int chipSelect = 10;

//Basic Libraries
#include <Wire.h>

//Analog Sensors
const int WaterLevelPin = A0;
int sensorValue = 0;

//Relays
const int relay1Pin = 12;
const int relay2Pin = 11;
const int relay3Pin = 10;
const int relay4Pin = 9;
int relay1State = LOW;
int relay2State = LOW;
int relay3State = LOW;
int relay4State = LOW;

void setup() {

//Setup for DHT
  Serial.begin(9600);
  Serial.println("DHT Readings");

dht.begin();

//Setup for Datalogging

Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
//Setup for LCD Display
  
  lcd.begin(20,4);

//Relay pins

  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);
  pinMode(relay3Pin, OUTPUT);
  pinMode(relay4Pin, OUTPUT);
}
void loop() {
  
//Loop for DHT Sensor
  unsigned long currentdataTime = millis();
  static unsigned long startdataTime = 0;
  
  float h, t, f;

  if (startdataTime == 0){
   
   startdataTime = currentdataTime;
   
  h = dht.readHumidity();
  t = dht.readTemperature();
  f = dht.readTemperature(true);

   if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  
  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");  
  
  String dataString = "";
   for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
      
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
    }
  else {
    Serial.println("error opening datalog.txt");
  }
  }
 else if(currentdataTime-startdataTime > 5000) {
    startdataTime = 0;
  }
//Loop for datalogger

 
    }
}    



//Water Level Analog Sensor
  
//Relay loop

  unsigned long currentcycleTime = millis();
  static unsigned long startcycleTime = 0;

if (startcycleTime == 0) {
  currentcycleTime = startcycleTime;

  relay1State = digitalRead(relay1Pin);
  relay2State = digitalRead(relay2Pin);
  
  if (t<=28) {
    digitalWrite(relay1State, HIGH);
    digitalWrite(relay2State, HIGH);
  }
  else if (currentcycleTime - startcycleTime > 300000) {
    startcycleTime = 0;
  }
}

//LCD Display

  lcd.setBacklight(1);
  
  lcd.home();
  lcd.print("Temperature");
  lcd.setCursor(0,1);
  lcd.print(t);
  lcd.print("*C");
  
  lcd.setCursor(0,2);
  lcd.print("Humidity");
  lcd.setCursor(0,3);
  lcd.print(h);
  lcd.print("*%");

}

I have change names for some timers but I am not comprehending why it is going erratic like that

As I said, you can share the 'currentTime' variable.

Jay_Swan:
I have change names for some timers but I am not comprehending why it is going erratic like that

What do you mean by 'erratic'? Does the code not work? If so describe what you expect and describe what actually happens.

Maybe compare the two code parts

  if (startdataTime == 0)
  {
    startdataTime = currentdataTime;
    ...
    ...

versus

  if (startcycleTime == 0)
  {
    currentcycleTime = startcycleTime;
    ...
    ...

Do you see a difference :wink:

I also think that you have the 'else if' in the wrong place (both for the dht reading and the relay control). Currently the 'else if' is not the alternative for 'if(startTime == 0)'. Have a close look at the code I presented and compare it with your code.
My code (stripped)

  // if delay not started, start delay and read dht
  if (startTime == 0)
  {
    ...
    ...
  }
  // if delay lapsed
  else if (currentTime - startTime > 5000)
  {
    ...
    ...
  }

  your other code
  ...
  ...
}

Your code (stripped) for the relay (same problem for the dht)

  if (startcycleTime == 0)
  {
    ...
    ...
    if (t <= 28)
    {
      ...
      ...
    }
    else if (currentcycleTime - startcycleTime > 300000)
    {
      ...
      ...
    }
  }

Hope you understand what I'm pointing at; if not, ask.

The program compiles well but as I have said the reading I want is for every 5 seconds and it takes at every moment... My relays are jumping in and out of state change...

Wow... how simple was that :o thank you very much sterretje it has me flying thru my coding... a lot of understanding for a construction guy xD so do I still use the millis for all my other function?

The only thing that needs timing on your list is #4 "When all parameters are within threshold, every hour start aux fan for 5 minutes, then stops". It is the only one that mentions time.

One of the most difficult things for people to learn to do when programming is to come up with a complete and unambiguous specification for the behavior they want. If you've even been frustrated by a hyper-literal person doing what you said but not what you wanted, computers are 100x worse than that. a person can be coerced into compliance with rewards and punishments, but a machine cannot be entreated in such a way.

For those that only see the end result of programmers' efforts, computer controlled systems seem "smart". Those of us behind the scenes (like you are now) come to know just how insanely dimwitted computers truly are.

If I haven't yet scared you off, please keep reading. :slight_smile:

The most important part of coding is often the planning stage. You need to lay everything out on paper. I don't mean type it up, literally write it on paper and draw diagrams and tables if you need to. Thought needs to go into this, and you will most likely be revising some of these things. This is a skill that needs to be developed with practice, so don't be too discouraged if it's difficult at first.

The first part is to explicitly list out the inputs that your logic will be considering, and the outputs they will be controlling. You don't need pin numbers or any specific hardware considerations at this point, just the concepts.

Inputs
Greenhouse air temp: TooHigh, TooLow, JustRight
Humidity: TooLow, JustRight
Time

Outputs
Humidifier On/Off
Heater On/Off
Two-speed fan Off/Low/High
Aux fan On/Off

Now with that listed out, defining your logic comes next. You have a decent start with the list in post #4, but it is not finished. You need to look it over to see if there are any conflicts. For example, what speed should the two-speed be set at if the temperature and humidity are both low at the same time? #1 says it should be low, #2 says it should be high. It's probably impossible for the fan to be both at the same time, so you have a conflict there.

Make a table with each of your outputs as column labels on top (Humidifier, Heater, two-speed fan, Aux fan), and every combination of temp/humidity as the row labels. There are 6 combinations (JR = just right):

  1. Temp Low / Hum Low
  2. Temp JR / Hum Low
  3. Temp High / Hum Low
  4. Temp Low / Hum JR
  5. Temp JR / Hum JR
  6. Temp High / Hum JR

You'll have a 6x4 table drawn out now. For every single one of those combinations, fill out explicitly what every output should be.

The next thing to look for is ambiguity. A human might be able to use their own reasoning and be able to run a fan properly by being told "When all parameters are within threshold, every hour start aux fan for 5 minutes, then stops", but computers are dumb. Suppose the following happens:

  1. All parameters normal, so the aux fan runs at 2:00 pm.
  2. At 2:55 pm, humidity drops below level, and takes 10 minutes to go back to acceptable range. Time is now 3:05pm, after the time the aux fan was scheduled to be on.

What do you want to happen here? Turn the aux fan on immediately, or wait another hour until the next scheduled time? If you're ok with waiting, it's possible that the right humidity cycle could keep the aux fan off indefinitely.

Since you only have ON/OFF control of your various devices, you will probably need significant hysteresis built into the thresholds. You don't want to turn the heater on when the temp goes below 28, then immediately off again when it goes above 28. You will want a gap in between those thresholds so that you aren't cycling your heater too much.

The most difficult part of this project has nothing to do with code, it is planning out exactly what is supposed to happen in exactly what circumstances. After that the code is pretty trivial.

  relay1State = digitalRead(relay1Pin);
  relay2State = digitalRead(relay2Pin);
 
  if (t<=28) {
    digitalWrite(relay1State, HIGH);
    digitalWrite(relay2State, HIGH);
  }

What's even going on here? Why are you reading the output state of the relay pins? Why are you using those states as the pin numbers for digitalRead?

The most important part of coding is often the planning stage. You need to lay everything out on paper. I don't mean type it up, literally write it on paper and draw diagrams and tables if you need to. Thought needs to go into this, and you will most likely be revising some of these things. This is a skill that needs to be developed with practice, so don't be too discouraged if it's difficult at first.

Sounds to me like I forgot that part xD I will start right away

The only thing that needs timing on your list is #4 "When all parameters are within threshold, every hour start aux fan for 5 minutes, then stops". It is the only one that mentions time.

At the moment the relay turrns on at every reading it takes, which is right now at an alarming rate. That is the timing part I was talking about... Maybe the wrong function is used?

What's even going on here? Why are you reading the output state of the relay pins? Why are you using those states as the pin numbers for digitalRead?

Mmm... I wonder myself xD see I have been having hard time just turning the damned thing on...

But here is a question... does order matter in this optic? I mean could I put a delay later in the loop and not have the delay affect the upper area of a sketch?

At the moment the relay turns on at every reading it takes, which is right now at an alarming rate. That is the timing part I was talking about... Maybe the wrong function is used?

You were pointed in the right direction but have not followed up.

I also think that you have the 'else if' in the wrong place (both for the dht reading and the relay control). Currently the 'else if' is not the alternative for 'if(startTime == 0)'.

It will help to use auto format under tools or (ctrl +t) to organize your code and track the opening and closing brackets {}. The ide will highlight both brackets in a pair if you position the cursor after the bracket.

The "else if" applies to the "if" directly preceding it in the code.

Try to organize your code, follow the brackets, apply sterretje's suggestions , and post your latest.