Environment controller - Temperature, Humidity, Light

Just sharing my code. This is used to automate control of a chicken coop that is powered via a solar array\battery bank. It is used to read light and adjust as needed (chickens lay more eggs with 12 hours of light). It turns on a fan if it gets over the heat threshold (based on the heat index, not temperature). Code ouputs in degrees Fahrenheit (I am in the US, which still lags on adopting the Metric system) . Arduino writes slider values to EEProm, in case of power outage the values are read back at void setup(), to make sure the settings reflect the values before loss of power.

setup runs currently on an Arduino Mega 2560 with an adafruit Huzzah module for wifi. Also 2 relays, DHT11 temp and humidity sensor, GY-30 light sensor.

I will add a fritzing image someday when I get time, but the code tells you which pins are required.

Future plans to add door control, solar panel tracking mount and explore using the Huzzah module stand alone, without the Arduino. This is my first time using arduino, so I am learning as I go, and as my knowledge changes, my approach\coding\hardware changes too. :slightly_smiling:

This could be used for greenhouses, aquariums, basements, etc… anything you want to control light and temp…Share your uses if you implement something on this code base!

Code seems to work pretty well, I am using the android OS, but feel free to suggest changes, as I am a beginner at this and still learning.

Also worth noting, a lot of this code is taken from other projects and example sketches, and I failed to keep track of who should get credit for the bits of code. Some is original code, some isn’t.



I’ve made :rooster:The ChickenShack​:rooster: app. You are welcome to try it out!

To start using it:

  1. Download Blynk App: http://j.mp/blynk_Android or http://j.mp/blynk_iOS
  2. Touch the QR icon and point the camera to the code below or open the link below - http://tinyurl.com/j58wh9j
  3. Enjoy my app!
        /* Used to automate a chicken coop.
         * Monitors Heat, Humidity, Heat Index and Light Saturation
         * when the temp gets too hot based on heat index, then turn on a relay connected to an exhaust fan
         * when the light drops below a threshold, turn on a relay connected to an LED array of lights (chickens like 12 hour days, they lay more eggs :)  )
         * when the light drops below a different and lower threshold, turn the LEDs off. This allow me to add some light at dusk, but turn off at night time
         * basically adding light at the end of the day during DST 
         * Future expansion for watering needs and automatic\RFID coop door open and close 
         */
        //#define BLYNK_DEBUG // Optional, this enables lots of prints
        //#define BLYNK_PRINT Serial //this causes buffer overflow as it competes with the console serial out

        #include <BlynkSimpleShieldEsp8266_HardSer.h>
        #include <DHT.h>
        #define DHTTYPE DHT11
        #define DHTPIN 11
        #include <SimpleTimer.h>
        #include <EEPROM.h>
        //#include <ESP8266_HardSer.h>
        #include <SPI.h>
        #include <Wire.h>
        float hum;
        float t;
        float f;
        float hif;
        int photocellPin = 1;
        int FanRelayPin = 9;
        int LightRelayPin = 8;
        int photocellReading;
        int luxdifference;
        /** the current address in the EEPROM (i.e. which byte we're going to write to next) **/
        int addressTempHum = 0;
        int addressLightThresh = 1;
        int addressLightOff = 2;
        long LightThresholdValue; //byte allows to be written to eeprom, but max is 255
        long LightOffValue;
        long HeatThresholdValue;
        int HeatDifference;
        int LightOffDifference;
        int ManualMode;
        int ManualLights;
        int ManualFan;
        int LiteLEDStatus;
        int FanLEDStatus;
        int ManualModeStatus; //init with manual mode off in case of power loss, it comes up with last settings
        int lastButtonStateLiteLed;
        int lastButtonStateFanLed;
        int lastButtonStateManualMode;

        const char* ssid = "YOURSSID";
        const char* password = "YOURPASSWORD";
        WidgetLED led1(30); //LightLED in blynk app
        WidgetLED led2(31);//FanLED in blynk app
        WidgetLED led3(10);//wifi connection light
        WidgetLED led4(28);//manual led for light side
        //WidgetLCD lcd(9);
        unsigned long previousMillis; //for blynk slider debounce
        unsigned long waitTime = 3000; //for blynk slider debounce

        DHT dht(DHTPIN, DHTTYPE); 
        //float humidity, temp_f;  // Values read from sensor
        int BH1750_address = 0x23; // i2c Addresse//for GY-30 light sensor
        byte buff[2];// i2c Addresse//for GY-30 light sensor

        // You should get Auth Token in the Blynk App.
        // Go to the Project Settings (nut icon).
        char auth[] = "YOURBLYNKAUTHCODE";
        // Set ESP8266 Serial object
        #define EspSerial Serial
        ESP8266 wifi(EspSerial);
        SimpleTimer timer;

        void setup()
        {
        timer.setInterval(2000, LiteLed);
        timer.setInterval(2000, FanLed); 
        timer.setInterval (2000, SendTempHumidity); 
        timer.setInterval (2000, SendDataLight); //get light sensor data
        timer.setInterval (2000, LightDelta); //get delta between current and threshold settings
        timer.setInterval (2000, HeatDelta); //get delta between heat index and threshold settings
        timer.setInterval (2000, LightOffDelta);
        timer.setInterval (2000, ManualModeLed);
        //timer.setInterval (30000, ResetArduino);
        //timer.setInterval (2000, DebugData);
        //timer.setInterval (2000, ManualModeLed2);
        ManualModeStatus = 0; //init with manual mode off in case of power loss, it comes up with last settings
        lastButtonStateLiteLed = 0;
        lastButtonStateFanLed = 0;
        lastButtonStateManualMode = 0;

        // Set ESP8266 baud rate
        EspSerial.begin(115200);
        //EspSerial.begin(38400);
        Serial.begin(115200);
        //Serial.begin(38400);
        Wire.begin();//for GY-30 light sensor
        //EEPROM.read(0); //read data stored in EEProm for parameters
        Serial.print("LightThresholdValueFromEEPROM: ");
        //Readind and sending first long.
        LightThresholdValue = EEPROMReadlong(0);
        Serial.println(LightThresholdValue);

        //EEPROM.read(1); //written as byte, but 255 is the max value
        Serial.print("LightOffValueFromEEPROM: ");
        LightOffValue = EEPROMReadlong(4);
        //Readind and sending first long.
        Serial.println(LightOffValue);
              
        //EEPROM.read(2); //written as byte, but 255 is the max value
        Serial.print("HeatThresholdValueFromEEPROM: ");
        HeatThresholdValue = EEPROMReadlong(8);
        //Reading and sending first long.
        Serial.println(HeatThresholdValue);
        dht.begin();// initialize temperature sensor
              
        Blynk.begin(auth, wifi, ssid, password);//This works, just testing other wifi libraries
        //wait for blynk connection
        while (Blynk.connect() == false) {
        // Wait until connected
        }
        pinMode(8, OUTPUT);      // sets the digital pin as output
        digitalWrite(8,HIGH); //turn off light pin on init
        pinMode(9, OUTPUT);      // sets the digital pin as output
        digitalWrite(9, HIGH); //turn off fan pin on init
        pinMode(13, OUTPUT);      // sets the digital pin as output
        digitalWrite(13, LOW); //turn off manual mode pin on init this is backwards compared to the relay
        led1.off();
        led2.off();
        led3.off();
        led4.off();
        }//end setup()

        //int eeprom_address = 0; //We will start writing at the first memory address
        long address=0;//Starting at the first byte on the eeprom.

        void SendTempHumidity() { 
         //delay(1000); //see if this help with wifi drops 
        hum = dht.readHumidity(); //Read humidity
        t = dht.readTemperature(); //Read temperature
        f = dht.readTemperature(true); //Read temperature as Fahrenheit
        hif = dht.computeHeatIndex(f, hum);
        int value1=hum*10;
        String str1;
        char result1[5];
        result1[0]=(value1/100)+'0'; 
        result1[1]=((value1/10)%10)+'0';
        result1[2]='.';
        result1[3]=(value1%10)+'0';
        result1[4]='\0';
        str1 +=result1;
        str1 +="%";
        //char buf[str.length()+1];
        char buf1[8];
        str1.toCharArray(buf1,sizeof(buf1));

        Blynk.virtualWrite(1, buf1); //write humidity with percent sign to virtual pin 1 
        Blynk.virtualWrite(11, hum); //write humidity witouth percent sign to virtual pin 11
        int value2=f*10;
        String str2;
        char result2[5];
        result2[0]=(value2/100)+'0'; 
        result2[1]=((value2/10)%10)+'0';
        result2[2]='.';
        result2[3]=(value2%10)+'0';
        result2[4]='\0';
        str2 +=result2;
        //str2 +="°";
        //char buf[str.length()+1];
        char buf2[8];
        str2.toCharArray(buf2,sizeof(buf2));
        Blynk.virtualWrite(3, buf2); //write temp with percent sign to virtual pin 3
        Blynk.virtualWrite(13, f); //write temp without percent sign to virtual pin 13
        int value3=hif*10;
        String str3;
        char result3[5];
        result3[0]=(value3/100)+'0'; 
        result3[1]=((value3/10)%10)+'0';
        result3[2]='.';
        result3[3]=(value3%10)+'0';
        result3[4]='\0';
        str3 +=result3;
        //str3 +="°";
        //char buf[str.length()+1];
        char buf3[8];
        str3.toCharArray(buf3,sizeof(buf3));

        Blynk.virtualWrite(4, buf3); //write fahrenheit heat index with percent sign to virtual pin 4
        Blynk.virtualWrite(14, hif); //write fahrenheit heat index without percent sign to virutal pin 14

        if (hif >=HeatThresholdValue && ManualModeStatus != 1){
        digitalWrite(FanRelayPin, LOW); //latch the fan relay to activate the fan
        }else if (ManualModeStatus != 1) {
        digitalWrite (FanRelayPin, HIGH); //turn off relay
        }
        }
        void SendDataLight(){
        photocellReading = analogRead(photocellPin); 
        Blynk.virtualWrite(2, photocellReading); 
        Blynk.virtualWrite(12, photocellReading);

        if (photocellReading <= LightThresholdValue && photocellReading >= LightOffValue  && ManualModeStatus != 1){
        digitalWrite(LightRelayPin, LOW); //latch the light relay to activate the supplemental light
        }else if (ManualModeStatus != 1){
        digitalWrite(LightRelayPin,HIGH); //unlatch relay to turn off lights  
        }
        }
        BLYNK_WRITE(V5) { //working 2/23/16
        LightThresholdValue = param.asInt(); //    / 4.02
        address=0;
        EEPROMWritelong(address, LightThresholdValue);//Writing first long.
        Blynk.virtualWrite (16, EEPROMReadlong(0)); //write temp without percent sign to virtual pin 13
        }


        BLYNK_WRITE(V6) {
        LightOffValue = param.asInt();// + 10;
        address=4;
        EEPROMWritelong(address, LightOffValue);//Writing first long.
        Blynk.virtualWrite (17, EEPROMReadlong(4)); //write temp without percent sign to virtual pin 17
        }

        BLYNK_WRITE(V7) {
        HeatThresholdValue = param.asInt();// + 10;
        address=8;
        EEPROMWritelong(address, HeatThresholdValue);//Writing first long.
        Blynk.virtualWrite(15, EEPROMReadlong(8)); //write temp without percent sign to virtual pin 15
        }

        void loop()
        {
        Blynk.run();
        timer.run();

        }//end loop

        void ManualModeLed(){
        ManualModeStatus = digitalRead(13);
        //Serial.println(" ");
        //Serial.print("ManualMode: ");
        //Serial.println(ManualModeStatus);
        if (ManualModeStatus != lastButtonStateManualMode) {
        if (ManualModeStatus == 1){
        led3.on();
        //led4.on();
        } else
        led3.off();
        //led4.off();
        }
        lastButtonStateManualMode = ManualModeStatus;
        }
        /*void ManualModeLed2(){
        ManualModeStatus = digitalRead(13);
        Serial.println(" ");
        Serial.print("ManualMode: ");
        Serial.println(ManualModeStatus);
        if (ManualModeStatus != lastButtonStateManualMode) {
        if (ManualModeStatus == 1){
        led4.on();
        } else
        led4.off();
        }
        lastButtonStateManualMode = ManualModeStatus;
        }
        */
        //this is to work around the sainsmart relay, it is backwards compared to most...
        BLYNK_WRITE(8) //light
        {
          if (param.asInt()) {
            digitalWrite(8, LOW);
          } else {
            digitalWrite(8, HIGH);
          }
        }
        //this is to work around the sainsmart relay, it is backwards compared to most...
        BLYNK_WRITE(9) //fan
        {
          if (param.asInt()) {
            digitalWrite(9, LOW);
          } else {
            digitalWrite(9, HIGH);
          }
        }



        void FanLed(){
        FanLEDStatus = digitalRead(9);
        if (FanLEDStatus != lastButtonStateFanLed) {
        if (FanLEDStatus == 1){
        led2.off();
        }  else
        led2.on();
        }
        lastButtonStateFanLed = FanLEDStatus;
        }
        void LiteLed(){
        LiteLEDStatus = digitalRead(8);
        if (LiteLEDStatus != lastButtonStateLiteLed) {
        if (LiteLEDStatus == 1){
        led1.off();
        }  else
        led1.on();  
        }
        lastButtonStateLiteLed = LiteLEDStatus;
        }

        //This function will write a 4 byte (32bit) long to the eeprom at 
        //the specified address to address + 3....no decimals
        void EEPROMWritelong(int address, long value)
              {
              //Decomposition from a long to 4 bytes by using bitshift.
              //One = Most significant -> Four = Least significant byte
              byte four = (value & 0xFF);
              byte three = ((value >> 8) & 0xFF);
              byte two = ((value >> 16) & 0xFF);
              byte one = ((value >> 24) & 0xFF);

              //Write the 4 bytes into the eeprom memory.
              EEPROM.write(address, four);
              EEPROM.write(address + 1, three);
              EEPROM.write(address + 2, two);
              EEPROM.write(address + 3, one);
              }

        //This function will return a 4 byte (32bit) long from the eeprom
        //at the specified address to address + 3.
        long EEPROMReadlong(long address)
              {
              //Read the 4 bytes from the eeprom memory.
              long four = EEPROM.read(address);
              long three = EEPROM.read(address + 1);
              long two = EEPROM.read(address + 2);
              long one = EEPROM.read(address + 3);

              //Return the recomposed long by using bitshift.
              return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
              }
        void LightDelta(){
        if (LightThresholdValue <= photocellReading){
        luxdifference = photocellReading - LightThresholdValue;
        }else {
        luxdifference = LightThresholdValue - photocellReading;  
        }
        Blynk.virtualWrite (19, luxdifference);
        }

        void HeatDelta(){
        if (hif>=HeatThresholdValue){
          HeatDifference = hif - HeatThresholdValue;
        }else{
          HeatDifference = HeatThresholdValue - hif;
        }
        Blynk.virtualWrite (18, HeatDifference);
        }

        void LightOffDelta(){
        if (photocellReading>=LightOffValue){
        LightOffDifference = photocellReading - LightOffValue;
        }else{
        LightOffDifference = LightOffValue - photocellReading;  
        }
        Blynk.virtualWrite (20, LightOffDifference);
        }
        //void ResetArduino(){
        //delay(3000); //this delay is longer than the timers, so if nothing happens in this time something is wrong
        //if (Blynk.connect() == false) {
        //Serial.println();
        //Serial.print("No connection to Blynk, resetting Arduino");
        //delay(1000);
        //pinMode(12, OUTPUT);  
        //digitalWrite(12,LOW); //pin 12 is connected to reset pin via a 1K resistor
        //}
        //}

        BLYNK_CONNECTED() //synch button states when connected
        {
        //Blynk.syncAll(); //this crashes the connection
        }
4 Likes

@bpthomas very impressive.

I haven’t looked through your code but I just wondered about the EEPROM read / write.
As you will know there is a limit of about 35000 writes to a single EEPROM address. Under the right (wrong) conditions this limit can be reached in a few minutes.
Is your sketch continuously writing to EEPROM in case you have a loss of power and is it just writing if a variable changes?
You might want to perhaps look at Blynk’s sync feature as this should do what you require without the risk of blowing the EEPROM on the Arduino.

hello, thanks for commenting… the EEProm is only updated when the sliders are changed, which in my use case is a couple times a year… for example i would change the light threshold when we observer DST in the US. so the eeprom address would last decades…and if they wear out I can just shift to different addresses. The thing I did notice is that when using the slider widget, and say I slide from 1 to 50, it writes all the values, not just the 50… So i did a sort of “debounce” to let the values settle, then write the data to eeprom… I am looking at the syncall feature, in the code above it is commented out, because it crashes my arduino when it is enabled. I haven’t had a chance to dig in yet. I just updated to the latest blynk library to get this functionality… :smirk:

my previous version used an SDcard to hold the data, but i believe they have the same “lifetime” issue, and are limited to some large number of writes, but you could just swap it out for a new one… I may go back to that, i was just excited to be able to read and write from EEPROM!

Yes the slider is a bit of a pain and personally I think Blynk should be doing the debounce in the server software as it crashes the system for many users.

We will implement this on app side. So you would decide how to send value based on widget properties. For some project it is required to send all values, so you’ll have a choice :wink:.

3 Likes

Sounds like a good move @Dmitriy

@bpthomas I am working on a chicken coop project very similar to yours! While I’m comfortable with web app programming and hardware design, the IOT and Blynk side of things is a bit of a challenge. Anyway, my project involves a “warm” LED which can be PWM’d, and a GPS* for time and location from which sunrise/sunset can be calculated daily. From there you just determine the length of day and how many more minutes you need to get to your target amount. My plan is to increase/decrease the brightness of the LED by 10%/min to not shock the birds with a hard on or off. Also, I want to have a door opener based on time/daylight as well. I’ve found a few things online but I’m leaning towards a “linear actuator” since my door is a slide up/down style. I plan to put in temperature monitors too.

*You could use the internet instead of GPS but I was originally thinking this could run autonomously.

I’d love to discuss further with you and share ideas. I think as a new user I’m not allowed to send a PM yet?

Nick (Sutton,MA)

hello, i also wanted to use a gps for time, i have a couple versions of my control box (which is for my brother, he raises chickens, i just provide the tech!) i skipped the gps module, as it was too expensive for what it provided, and we wanted cell phone control, so we could flip to manual mode if required. if you google aquarium arduino, you should find people that do the fade in LED thing with their salt water aquariums, pretty cool. I actually wanted to do that too. I am pretty timed constrained lately, so sometimes it is just best effort! I am constantly revising the setup for my brother, so it may come into play at some point. He also wants the auto door open\close. I want to mess with RFID to make sure the chickens are in the coop before the door closes, as we have a coyote issue up this way. Linear actuator would be fine, or stepper motor, whatever works. I am working on 12 volts (solar) so that plays into what i use. I also like to make due with what I have laying around, even though it may not be “best”… next I am building a solar tracking mount so the panels can follow the sun.

yeah man, i am happy to help… I will be adding door control and water bowl purge \ top off at some point. I built a separate heating element control for the water(keeps it from freezing in the winter), so may pull that in too… I am happy to help you where I can, may not be immediate, but i can help as needed… hope this helps, let me know how you get along, I am happy to share any of my code.

brian

Yes I am pretty time constrained myself. I’ll be a father for the second time later this month! I was able to get temperature monitoring and remote control of 2 heat lamps in my coop last night. I put my 5wk old pullets out there over the weekend and we still have some pretty chilly weather here 25F (-4C) overnight. I gutted an old electronic timer which was in a NEMA enclosure. I rewired the board, added an additional relay and some regulation, then created a new board with a 5V regulator and some FETs for relay drivers and hooked up to the Sparkfun ESP Thing dev PCBA. It actually worked the first time! I was rushing to throw it together in about an hour and a half window!

I need to do more work with mechanical hardware as I think I’m in decent shape with the code. The water purge/fill sounds great but a mechanical challenge for sure! I have a non uC controlled water heater already (it’s just a cheap 0C switch in series with the AC power). Keep me posted and I’ll do the same. Also if anyone else reading this has any comments or questions please let me know.

One thing I have trouble with is WifiManager for Arduino by @tzapulica. I’ve been reading a lot about it but the problems I have don’t seem to be complaints of other users. I have ordered a couple more ESP8266 dev boards to try to rule out any hardware issues. The issue I have is basically that it’s unstable. Once an SSID is set it seems fine, but when in AP mode I have trouble connecting to the AP, then once connected loading the config page often locks up or resets the ESP. I observe the same behavior even with the minimal sample code from the examples. Perhaps I should voice this elsewhere on the forum.

1 Like

@njc I had similar problems with the module… when in AP mode I would get connection refused or something like that. I really want that piece to work, as it makes my unit serviceable if the SSID or password changes, etc. I am terribly swamped this week at work, just fresh off of vacation. Hopefully I can get back at this soon.

Congrats on the new addition to your family!

Is esp8266’s power supply sufficient?

This is not related to power supply. AP mode is non native - it’s “emulated” and that’s why it is causing some issues. Dedicate as much RAM as you can when in AP mode and yeld() as much as you can so ESP can handle connections. I’m running live telemetry with web sockets in AP mode with ~ 50Hz rate, so it works well when you handle it carefully.

I’ve looked at the code, he’s got a yield() in the blocking loop. I don’t remember about the ram. Are you talking about a malloc ? I’ll have to look again. Do you have a code snippet for us that you found to work?

Also, I’ve started a thread for this issue here: WiFi Manager problems
Let’s continue there. Hopefully @tzapulica will chime in.

One idea: I think the particular issue here is actually in a way ESP handles softAP, first of all it might be good idea to switch to B/G mode only I’ve read somewhere long time ago that in STA_AP mode ESP doesn’t support N mode and it might cause some issues maybe that’s the problem. To be honest i force ESP in either STA or AP mode rather than both. There are some low level functions in ESP8266WiFi libraries that can help you a lot in debugging. However I would strongly suggest getting JTAG and prosper ESP dev board so you can see whats going on in real time.

As for ESP lockups this is usually cause by some watchdog crashes. When you have a look at esp8266 rtos programming guide its mentioned there that:

If an array whose length is over 60 bytes is used in a task, it is suggested that users use os_malloc and os_free rather than local variable to allocate array. Large local variables could lead to task stack overflow.

This maybe has been taken care of in Arduino IDE version or not.

Secondly: Another thing I know there are lockups caused by synchronous code execution but I always tried to write asynchronous code so everything can be executed in parallel. Basically you are trying to send something without actually handling connection, or something like that. Perhaps try to use Scheduler for this.

I will try to test scheduler later with this library and will let you know how it performs.

I posted this in the other thread too, but there seems to be more people looking at this one:

I have tried it on brand new modules. I’m experiencing the same problems right away: