Chicken Coop - LCD + 2 Fans + 2 Lights + RTC + DHT11 + Stepper

Basically I'm looking to have my Arduino Uno control 2 fans based on temperature readings from DHT11 (i.e. go on above a certain temperature) and turn on 2 different sets of lights based on the time of day (DS1307 RTC).

Both the DS1307 RTC and the DHT11 are communicating with the Arduino and displaying on the LCD.

The relays are currently wired with the Com to 12V and the NC to the various device (i.e. fans or lights). The relays seem to cycle. I can hear them clicking, but my code doesn't control them as I intend. Please assist...

I have yet to connect or code for the stepper motor, but essentially it will open and close a door with a screw drive based on the time of day.

#include <LiquidCrystal.h>
#include <DFR_Key.h>
#include <Wire.h>
#include "RTClib.h"
#include "DHT.h"


  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

  DFR_Key keypad;

  int localKey = 0;
  String keyString = "";

  RTC_DS1307 RTC;

    int StartHrRelay_1 = 21;
    int StartMinRelay_1 = 00;

    int StartHrRelay_2 = 21;
    int StartMinRelay_2 = 00;

    int StartHrRelay_3 = 7;
    int StartMinRelay_3 = 44;

    int StartHrRelay_4 = 7;
    int StartMinRelay_4 = 44;

  long Sleep = 1L;

  long MultiMinute = 60000L;

  boolean LightOn = false;

  DateTime future;
  DateTime DelayFuture;
  DateTime Start;

  int DurDay1 = 0;
  int DurHour1 = 10;
  int DurMinute1 = 0;
  int DurSecond1 = 0;

  int DurDay2 = 0;
  int DurHour2 = 8;
  int DurMinute2 = 0;
  int DurSecond2 = 0;

  int DurDay3 = 0;
  int DurHour3 = 1;
  int DurMinute3 = 0;
  int DurSecond3 = 0;

  int DurDay4 = 0;
  int DurHour4 = 1;
  int DurMinute4 = 30;
  int DurSecond4 = 0;

#define RELAY_ON 0
#define RELAY_OFF 1

int Relay_1 = 3;
int Relay_2 = 4;
int Relay_3 = 5;
int Relay_4 = 6;

  long readVcc() {
    long result;
    // Read 1.1V reference against AVcc
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    delay(2); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Convert
    while (bit_is_set(ADCSRA,ADSC));
    result = ADCL;
    result |= ADCH<<8;
    result = 1126400L / result; // Back-calculate AVcc in mV
    return result;
  }

  #define DHTPIN 2

  #define DHTTYPE DHT11

  DHT dht(DHTPIN, DHTTYPE);

  double dewPoint(double celsius, double humidity)
  {
        double A0= 373.15/(273.15 + celsius);
        double SUM = -7.90298 * (A0-1);
        SUM += 5.02808 * log10(A0);
        SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
        SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
        SUM += log10(1013.246);
        double VP = pow(10, SUM-3) * humidity;
        double T = log(VP/0.61078);   // temp var
        return (241.88 * T) / (17.558-T);
  }

void setup() {
  Serial.begin(57600);
  lcd.begin (16,2);
  lcd.print("  Chicken Coop  ");
  Wire.begin();
  RTC.begin();

  RTC.adjust(DateTime(__DATE__, __TIME__));

  if (! RTC.isrunning()) {
    lcd.setCursor (0,0);
    lcd.print(" *-- RTC's --*");
    lcd.setCursor (0,1);
    lcd.print("  NOT running!");
    Serial.println(" *-- RTC's NOT running! --*");
    delay (5000) ;
    lcd.clear();
    RTC.adjust(DateTime(__DATE__, __TIME__));
    }

  DateTime now = RTC.now();
  DateTime SetStart(now.year(),now.month(),now.day(),StartHrRelay_1,StartMinRelay_1,now.second());

  Start = SetStart;

    digitalWrite(Relay_1, RELAY_OFF);
    pinMode(Relay_1, OUTPUT);

    digitalWrite(Relay_2, RELAY_OFF);
    pinMode(Relay_2, OUTPUT);

    digitalWrite(Relay_3, RELAY_OFF);
    pinMode(Relay_3, OUTPUT);

    digitalWrite(Relay_4, RELAY_OFF);
    pinMode(Relay_4, OUTPUT);
    
    delay(4000);

  dht.begin();

}

void loop()
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(t) || isnan(h)) {
    lcd.setCursor (0,0);
    lcd.print("*-- Failed to");
    lcd.setCursor (0,1);
    lcd.print("    read T&H --*");
    Serial.println("*-- Failed to read T&H --*");
    delay (5000) ;
    lcd.clear();
  }
  else {
    lcd.setCursor (0,0);
    lcd.print("Temp (F): ");
    lcd.print(t*9/5 + 32);
    lcd.print(" *F");
    lcd.setCursor (0,1);
    lcd.print("Humidity: ");
    lcd.print(h);
    lcd.print("% ");

    delay (5000) ;
    lcd.clear();
    
    lcd.setCursor (0,0);
    lcd.print("Temp (C): ");
    lcd.print(t);
    lcd.print(" *C");
    lcd.setCursor (0,1);
    lcd.print("Dew Point: ");
    lcd.print(dewPoint(t, h));
    lcd.print(" *C");

    delay (5000) ;
    lcd.clear();

    Serial.print("Temp (F): ");
    Serial.print(t*9/5 + 32);
    Serial.println(" *F");
    Serial.print("Humidity: ");
    Serial.print(h);
    Serial.println("% ");
    
    Serial.print("Temp (C): ");
    Serial.print(t);
    Serial.println(" *C");
    Serial.print("Dew Point: ");
    Serial.print(dewPoint(t, h));
    Serial.println(" *C");
  }

  if (dht.readTemperature() >= 20) {
    digitalWrite(Relay_1, RELAY_OFF);
  }
  else if (dht.readTemperature() < 18) {
    digitalWrite(Relay_1, RELAY_ON);
  }
  
  if (dht.readTemperature() >= 25) {
    digitalWrite(Relay_2, RELAY_OFF);
  }
  else if (dht.readTemperature() < 23) {
    digitalWrite(Relay_2, RELAY_ON);
  }

  lcd.setCursor (0,0);
  lcd.print("Volts (mV): ");
  lcd.print( readVcc(), DEC );

  delay(5000);
  lcd.clear();

  DateTime now = RTC.now();

  lcd.setCursor (0,0);
  lcd.print("   ");
  lcd.print(now.year(), DEC);
  lcd.print('/');
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.day(), DEC);

  lcd.setCursor (0,1);
  lcd.print("     ");
  lcd.print(now.hour(), DEC);
  lcd.print(':');
  lcd.print(now.minute(), DEC);

  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.println(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();

  delay(5000);
  lcd.clear();

    if (LightOn == false) {

      DelayFuture = CalcFuture(Start,0L,0L,Sleep,0L);

    if ((int)now.hour() >= StartHrRelay_3 && (int)now.hour() <= DelayFuture.hour() && (int)now.minute() >= StartMinRelay_3 && (int)now.minute() <= DelayFuture.minute()) {

      future = CalcFuture(now,DurDay3,DurHour3,DurMinute3,DurSecond3);

      LightOn = true;
      digitalWrite(Relay_3, RELAY_ON);

      Serial.println("\r\nLight On\r\n");

    }
    }

  else {

    if ((int)now.day() >= (int)future.day() && (int)now.hour() >= (int)future.hour() && (int)now.minute() >= (int)future.minute()) {

      LightOn = false;
      digitalWrite(Relay_3, RELAY_OFF);

      Serial.print("\r\nLight Off\r\n");

    }
  }
    delay((Sleep*MultiMinute));

    if (LightOn == false) {

      DelayFuture = CalcFuture(Start,0L,0L,Sleep,0L);

    if ((int)now.hour() >= StartHrRelay_4 && (int)now.hour() <= DelayFuture.hour() && (int)now.minute() >= StartMinRelay_4 && (int)now.minute() <= DelayFuture.minute()) {

      future = CalcFuture(now,DurDay4,DurHour4,DurMinute4,DurSecond4);

      LightOn = true;
      digitalWrite(Relay_4, RELAY_ON);

      Serial.println("\r\nLight On\r\n");

    }
    }

  else {

    if ((int)now.day() >= (int)future.day() && (int)now.hour() >= (int)future.hour() && (int)now.minute() >= (int)future.minute()) {

       LightOn = false;
      digitalWrite(Relay_4, RELAY_OFF);

      Serial.print("\r\nLight Off\r\n");

    }
  }
    delay((Sleep*MultiMinute));
}

   DateTime CalcFuture (DateTime now, int Days, int Hours, int Minutes, int Seconds){

  DateTime future;
    long DaySeconds = 86400L;
    long HourSeconds = 3600L;
    long MinuteSeconds = 60L;

    future = (now.unixtime() + (Days * DaySeconds) + (Hours * HourSeconds) + (Minutes * MinuteSeconds) + (Seconds));
    return future;
    }

//

I can hear them clicking, but my code doesn't control them as I intend. Please assist.

That's a real shame.

Now, perhaps if you told us how you intended that code to work, and how it actually works, we could help you.

All those delay()s aren't.

9500 character post limit, you were lucky to get what you got. I had to delete all of my comments out of the code.

I intend the code to work such that the relays are turned on or off based on information supplied by the RTC and DHT. Relays 1 & 2 are intended to control the fans. Relays 3 & 4 are intended to control the lights.

The arduino is receiving data from the RTC and DHT and displays as the data intended on the LCD. What doesn't work, or doesn't work as intended, is my code to use that data to control the relays. What does that part of the code do? Well, from what I can tell, pretty much nothing. I've search all over for code that I could use to turn relays on or off based off data values from the RTC and DHT compared to pre-assigned arguments and haven't been able to put together anything that works. What I have here is the closest I've been able to come.

Maybe my relays are connected incorrectly, but I don't think so. Since I wired them as normally closed I am able to tell that they are at least getting the 12V power. The lights and fans come on when plugged in.

Most likely it is a problem with my code.

Digital pin 3 is connected to line 1 on the relay board, pin 4 to line 2, pin 5 to line 3 and pin 6 to line 4. The 5V feed from the Arduino is connected to the VCC on the relay board as is the ground. On the relay side, the Coms are daisy chained together and supplied with 12V DC from a separate power source. The normally closeds are then connected to the positive terminal on a 4 separate RCA jacks. The jacks are used to connect the fans and lights. The negative to the jacks comes straight from the power source.

9500 character post limit, you were lucky to get what you got.

Somehow I don't feel lucky. 8)

The 9500 character limit does not apply to attached code.

9500 characters is more than plenty to write a sketch that demonstrates that a relay can be toggled on and off every second.

9500 characters is more than enough to verify that the temperature sensor can be read and the value printed to the serial monitor.

9500 characters is more than enough to verify that you can write to the LCD.

Writing one huge program that does everything, and is full of delay()s, is not the way to approach programming.

Develop a series of little sketches that demonstrate that the individual pieces all work. When you know that that happens, start combining them, two at a time. If, or when, stuff doesn't work together, it is so much simpler to detect why.

I can only infer you are using some sort of shield from some dfr seller. Are you sure you don't have a pin conflict between this hardware and your relays on pins 3,4,5, and 6?

To trigger event at certain time of RTC, I would look into how to do that in my alarm clock code that I wrote for my phi-2 shield.

There is a link to the alarm clock source code download. To be honest, this shield, which I designed, is much better than what you're using. There is an on board RTC and well-written menu and interaction libraries for user interface.

Maybe I have not been clear about what I have done. Let me simplify...

I wrote code for the LCD with just the LCD attached. Tested it and it worked, so I moved on.

I then wrote code for the DHT. Tested it and it worked, so I moved on.

I then wrote code for the RTC. Tested it and it worked, so I moved on.

As the RTC and DHT were successfully communicating with the Arduino which was successfully displaying their data on the LCD, and both the RTC and DHT are needed in the code I am writing for the relays, it was time for me to write code for the last step, using the RTC and DHT data to control the relay board. Before I did that, I took one step sideways and I tested that the Arduino communicates successfully with the relay board using a simple loop on/off code and that worked.

PaulS your suggesting that I should "develop a series of little sketches that demonstrate that the individual pieces all work" and then, "when (I) know that that happens, start combining them, two at a time" is, as you can now see, exactly what I did. But good advice none-the-less and maybe it will help someone else reading this post on one of their projects.

I apologize if this was not clear in my initial post but like I said, we are limited to 9500 characters. And to PaulS suggestion that that does not include attached code, yes PaulS the 9500 character limit does include attached code. Deleting the comment lines out of my code is what got me under 9500 characters.

@liudr, I am using an LCD shield, everything else is connected through that shield to the Arduino. Your shields look do look great though and I may look to use them for another project, but for this project, I already have all the pieces I need and just need help with the code. If you think the libraries or code you have written for them will assist me with using what I already have, please send my way. Thanks

Here is a list of what I am using:
Tiny I2C RTC DS1307 AT24C32 Real Time Clock Module For Arduino AVR ARM PIC
L298N Dual H Bridge DC Stepper Motor-Treiber Controller Board
5V 4-Channel Relay Module Board for Arduino PIC MSP W/ Optocouple SRD-05VDC-SL-C
DHT11 Arduino Compatible Digital Temperature Humidity Sensor Module+Dupond
1602 LCD Board Keypad Shield Blue Backlight For Arduino Duemilanove Robot

http://code.google.com/p/phi-prompt-user-interface-library/downloads/detail?name=Phi_2_project_alarm_clock_v6.zip&can=2&q=

Code above is an alarm clock. You can modify the alarm function to check whether it is time to do certain thing according to RTC.

I apologize if this was not clear in my initial post but like I said, we are limited to 9500 characters. And to PaulS suggestion that that does not include attached code, yes PaulS the 9500 character limit does include attached code. Deleting the comment lines out of my code is what got me under 9500 characters.

The 9500 character limit refers to posted code. That is code in this box. And text. But, see down there? Below this window? The Additional Options link. Well, OK, so it does not look a lot like a link. But, it is. Select that. You can attach much larger than 9500 character files.

I am happy to hear that you are developing the code in pieces. That wasn't clear from your post.

I still think that those delays have got to go. The millis() function and a state machine (what do I need to do on this pass through loop, if is is time to do anything) is much better than sitting on your hands for relatively long periods of time.

Got it. Was just following instructions. The forum says to post code between `` using the #.

Anyways... Which delays do you suggest I delete? All of them? Are you suggesting the delays are causing the issue? If I don't have any delays, won't that cause a display issue on the LCD (i.e. the data won't be displayed long enough for anyone to read it)?

What is the millis() function?

Anyways... Which delays do you suggest I delete? All of them?

If I ask you to make me breakfast, consisting of eggs, bacon, toast, and coffee, can you figure out how to make all the food get done at the same time? Or, am I going to gets eggs, then, some time later, bacon, then, some time later, toast, and finally, after I've finished eating, the coffee will be ready?

Yes, all of the delays.

Are you suggesting the delays are causing the issue?

I don't understand what the issue is. I don't see any serial out put that says "Time to turn the relay on" and "Time to turn the relay off", annotated to note whether that happened, or not. Calls to delay() do not belong in a sketch that is doing more than blinking an LED.

If I don't have any delays, won't that cause a display issue on the LCD (i.e. the data won't be displayed long enough for anyone to read it)?

Not if you don't immediately overwrite the data. See the comments above.

What is the millis() function?

Is it necessary to point out that up there at the top of the page there is a link that says Main Site, and that on that page there is one that says Reference, and that on that page all the Arduino-specific functions are documented?

And to think I questioned whether posting here would be of any help or if I would just receive smart ass comments...

Code that I meant to turn relays on and off:

    if (LightOn == false) {

      //Calculate current time + delay minuts (Sleep variable time) to give a wide window making sure we can hit the start time.
      //If the program starts after this window then the relay will not start until StartHr & StartMin the following day.

      DelayFuture = CalcFuture(Start,0L,0L,Sleep,0L);

    if ((int)now.hour() >= StartHrRelay_3 && (int)now.hour() <= DelayFuture.hour() && (int)now.minute() >= StartMinRelay_3 && (int)now.minute() <= DelayFuture.minute()) {

      //Set future DateTime used to determine the duration of light on time

      future = CalcFuture(now,DurDay3,DurHour3,DurMinute3,DurSecond3);

    //Turn on light
      LightOn = true;
      digitalWrite(Relay_3, RELAY_ON);

      Serial.println("\r\nLight On\r\n");

    }
    }

  else {

  //Check current time - turn off light when conditions are met

    if ((int)now.day() >= (int)future.day() && (int)now.hour() >= (int)future.hour() && (int)now.minute() >= (int)future.minute()) {

    //Turn off light
      LightOn = false;
      digitalWrite(Relay_3, RELAY_OFF);

      Serial.print("\r\nLight Off\r\n");

    }
  }
    delay((Sleep*MultiMinute));

  //Check status of light

    if (LightOn == false) {

      //Calculate current time + delay minuts (Sleep variable time) to give a wide window making sure we can hit the start time.
      //If the program starts after this window then the relay will not start until StartHr & StartMin the following day.

      DelayFuture = CalcFuture(Start,0L,0L,Sleep,0L);

    if ((int)now.hour() >= StartHrRelay_4 && (int)now.hour() <= DelayFuture.hour() && (int)now.minute() >= StartMinRelay_4 && (int)now.minute() <= DelayFuture.minute()) {

      //Set future DateTime used to determine the duration of light on time

      future = CalcFuture(now,DurDay4,DurHour4,DurMinute4,DurSecond4);

    //Turn on light
      LightOn = true;
      digitalWrite(Relay_4, RELAY_ON);

      Serial.println("\r\nLight On\r\n");

    }
    }

  else {

  //Check current time - turn off light when conditions are met

    if ((int)now.day() >= (int)future.day() && (int)now.hour() >= (int)future.hour() && (int)now.minute() >= (int)future.minute()) {

    //Turn off light
      LightOn = false;
      digitalWrite(Relay_4, RELAY_OFF);

      Serial.print("\r\nLight Off\r\n");

    }
  }
    delay((Sleep*MultiMinute));
  //Chimney fan (Relay_1) on-off temperature
  if (dht.readTemperature() >= 20) {
    digitalWrite(Relay_1, RELAY_OFF);
  }
  else if (dht.readTemperature() < 18) {
    digitalWrite(Relay_1, RELAY_ON);
  }
  
  //Window fan (Relay_2) on-off temperature
  if (dht.readTemperature() >= 25) {
    digitalWrite(Relay_2, RELAY_OFF);
  }
  else if (dht.readTemperature() < 23) {
    digitalWrite(Relay_2, RELAY_ON);
  }
    if ((int)now.hour() >= StartHrRelay_3 && (int)now.hour() <= DelayFuture.hour() && (int)now.minute() >= StartMinRelay_3 && (int)now.minute() <= DelayFuture.minute()) {

Doesn't now.hour() return an int? Why is it necessary to cast to int?

Nested ifs are far easier to understand and debug, in my opinion, than compound ifs.

Please, do yourself, and us, a favor. Put each { on a new line, use the return key more often, and use Tools + Auto Format.

You have Serial.print() statements in your code. You are not sharing what you see, bogart.

Doesn't now.hour() return an int? Why is it necessary to cast to int?

No idea, I didn't write that code. I borrowed it from someone else's project. If it doesn't need to be there, or if it should be changed to something different, what do you suggest?

I've now used Tools + Auto Format. Didn't even know that feature existed. Other than making my code look pretty, I don't think it will solve my issue. But then again, neither will removing the delays.

Other than making my code look pretty, I don't think it will solve my issue.

No, but it makes it easier to see the structure.

But then again, neither will removing the delays.

Maybe not. But you aren't being clear on what the problem is, or where in the code/when the problem occurs.

If it doesn't need to be there, or if it should be changed to something different, what do you suggest?

I suggest that you look at the documentation for the class that now is an instance of, and determine what type the hour(), minute(), and second() functions return, and determine whether it is necessary to cast that value to a different type.

Thanks for your help PaulS.

Anyone else??? Paul doesn't seem to understand the issue.

Attached is my code. The delays are still in but I have Auto Formatted it. Seems to me that if I delete the delays the loop will cycle through too fast for the information to be displayed on the LCD.

Test.ino (9.88 KB)

Paul doesn't seem to understand the issue.

Perhaps because you have not stated the problem clearly enough.

Something like this. I see this in the serial monitor:

Time to turn the relay.

My code looks like this:

Serial.print("Time to turn the relay on");
digitalWrite(relayPin, HIGH);

The relay does not come on.

Now, something like that would be easy to troubleshoot.

Read up ^. The relays cycle when using a test code (i.e. one that cycles the relay and does nothing else) but don't with the attached code. The test code I used was essentially the same code as you listed in your preceding post.

I think its time for you to move on PaulS. This issue seem to be above your level of competency. Thanks for your help.

One last post, then I'll leave you alone.
Why are you reading the temperature so many times?

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

  if (dht.readTemperature() >= 20)
  {
    digitalWrite(Relay_1, RELAY_OFF);
  }
  else if (dht.readTemperature() < 18)
  {
    digitalWrite(Relay_1, RELAY_ON);
  }

  //Window fan (Relay_2) on-off temperature
  if (dht.readTemperature() >= 25)
  {
    digitalWrite(Relay_2, RELAY_OFF);
  }
  else if (dht.readTemperature() < 23)
  {
    digitalWrite(Relay_2, RELAY_ON);
  }

Where are the serial print statements to show whether the relay is to be turned on or off? What actually happens?

I know that it is frustrating when code doesn't work as you want, but, you need to remember that we can't see what you are seeing. If you don't show serial output, we can't see it. If you don't say what the relays are actually doing, we don't know.

Anyway, I wish you luck in solving your problem.

Maybe that is what is missing, the serial print statements. Maybe serial print statements should replace dht.readTemperature().

I don't know what the relays are doing with that code. As I said, they don't seem to be doing anything.

Attached is my revised code using PaulS's suggestions. I haven't been able to test it, because the USB chip on my Arduino burned up, but I've ordered a replacement.

Test.ino (10.7 KB)