Greenhouse Controls

From STEAMwiki
Jump to: navigation, search

Code and design for autonomous planter.

Autonomous greenhouse.jpg

Fusion 360 link: http://a360.co/2gzSERa

Web code: https://thimbleprojects.org/makalotai/126697

IFTTT instructions on logging the sensor data from the particle chip: http://wiki.steamlabs.ca/wiki/index.php/Send_Data_to_IFTTT_to_Spreadsheet_Example

Particle Arduino Code:

// This #include statement was automatically added by the Particle IDE.
#include "neopixel/neopixel.h"

// This #include statement was automatically added by the Particle IDE.
#include "spark-dallas-temperature/spark-dallas-temperature.h"

// This #include statement was automatically added by the Particle IDE.
#include "OneWire/OneWire.h"

int servoPin = D0;  // Servo pin location.
Servo servo;  
int angle = 0;      // Servo position in degrees. 

OneWire ds = OneWire(A2);  // 1-wire signal on pin A2 for temperature
unsigned long lastUpdate = 0;
float lastTemp;

int moisture = 0;       
int temperature = 0;    
int ambientlight = 0;   

String LightMode = "Automatic";            
String VentMode = "Automatic";             

#define PIXEL_PIN D2        //Set LED pixel PIN.
#define PIXEL_COUNT 8       //Set LED pixel COUNT.
#define PIXEL_TYPE WS2812   //Set LED pixel TYPE.

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

void setup() {
    Serial.begin(9600);
    
    servo.attach(servoPin);   
    
    Particle.function("LMode", LMode);    //Register cloud functions for web light and vent buttons.
    Particle.function("VMode", VMode);    

    Particle.variable("moisture", &moisture, INT);          //For web readout
    Particle.variable("temperature", &temperature, INT);    
    Particle.variable("ambientlight", &ambientlight, INT);  
    
    pinMode(A0, INPUT);     //moisture                                     
    pinMode(A2, INPUT);     //temperature readout on Arduino destop app serial monitor                              
    pinMode(A4, INPUT);     //ambient light                               
    pinMode(D2, OUTPUT);    //LED mini lightstrip  
    pinMode(D3, OUTPUT);    //LED Grow lights  
  
    Time.zone(-5);          //Time zone. Update this for DST changes.
}

void loop() { 
/*  Serial.print("Moisture level: "); //For IDE readouts  
    Serial.println(analogRead(A0));  
    delay(1000);                   
    
    Serial.print ("Ambient Light: ");  
    Serial.println(analogRead(A4));   
    
    Serial.print("Temperature (Celcius): ");   
    Serial.println(temp);                   
    delay(1000);    */    
    moisture = analogRead(A0);      //For web readout
    ambientlight = analogRead(A4);  
    temperature = lastTemp;
    delay(1000);                   
    
    String moisture = String(analogRead(A0)); 
    Particle.publish("moisture", moisture, PRIVATE); 
    delay(5000);
    
    String ambientlight = String(analogRead(A4));
    Particle.publish("ambientlight", ambientlight, PRIVATE); 
    delay(5000);
    
    if (LightMode == "Automatic") {          
        
        if (Time.hour() >=6 && Time.hour() <20 ) {  //Run LEDs from 06:00-20:00 (16hrs).
            TurnOnLED("");      
        }    
        if (Time.hour() >=20 || Time.hour() <6) {   //Turn off LEDs from 20:00-06:00 (8hrs).
            TurnOffLED("");    
        }
    }    
    else if (LightMode == "On") {                   //For manual selection of LEDs
        TurnOnLED("");
    }
    else if (LightMode == "Off") {
        TurnOffLED("");
    }     

    byte i;                 //This is the start of the OneWire temperature code
    byte present = 0;
    byte type_s;
    byte data[12];
    byte addr[8];
    float celsius, fahrenheit;

    if ( !ds.search(addr)) {                    // The order is changed a bit in this example. First the returned address is printed
        Serial.println("No more addresses.");
        Serial.println();
        ds.reset_search();
        delay(250);
        return;
    }

    Serial.print("ROM =");
        for( i = 0; i < 8; i++) {
        Serial.write(' ');
        Serial.print(addr[i], HEX);
        }

    if (OneWire::crc8(addr, 7) != addr[7]) {    // second the CRC is checked, on fail, print error and just return to try again
        Serial.println("CRC is not valid!");
        return;
    }
    
    Serial.println();   // we have a good address at this point. What kind of chip do we have? We will set a type_s value for known types or just return

    switch (addr[0]) {    // the first ROM byte indicates which chip
        case 0x10:
            Serial.println("  Chip = DS1820/DS18S20");
            type_s = 1;
            break;
        case 0x28:                              //This is the correct chip: DS18B20
            Serial.println("  Chip = DS18B20"); 
            type_s = 0;                         
            break;                              
        case 0x22:
            Serial.println("  Chip = DS1822");
            type_s = 0;
            break;
        case 0x26:
            Serial.println("  Chip = DS2438");
            type_s = 2;
            break;
        default:
            Serial.println("Unknown device type.");
            return;
    }

    ds.reset();               // this device has temp so let's read it. First clear the 1-wire bus
    ds.select(addr);          // now select the device we just found
    // ds.write(0x44, 1);     // tell it to start a conversion, with parasite power on at the end
    ds.write(0x44, 0);        // or start conversion in powered mode (bus finishes low)
    // just wait a second while the conversion takes place. Different chips have different conversion times, check the specs, 1 sec is worse case + 250ms.
    //You could also communicate with other devices if you like but you would need to already know their address to select them.

    delay(1000);     // maybe 750ms is enough, maybe not, wait 1 sec for conversion. We might do a ds.depower() (parasite) here, but the reset will take care of it.

    present = ds.reset();   // first make sure current values are in the scratch pad 
    ds.select(addr);
    ds.write(0xB8,0);       // Recall Memory 0
    ds.write(0x00,0);       // Recall Memory 0

    present = ds.reset();   // now read the scratch pad
    ds.select(addr);
    ds.write(0xBE,0);       // Read Scratchpad
    if (type_s == 2) {
    ds.write(0x00,0);       // The DS2438 needs a page# to read
    }

    Serial.print("  Data = ");    // transfer and print the values
    Serial.print(present, HEX);
    Serial.print(" ");
    for ( i = 0; i < 9; i++) {    // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
    }
    Serial.print(" CRC=");
    Serial.print(OneWire::crc8(data, 8), HEX);
    Serial.println();

    int16_t raw = (data[1] << 8) | data[0];          // Convert the data to actual temperature because the result is a 16 bit signed integer, it should 
    if (type_s == 2) raw = (data[2] << 8) | data[1]; // be stored to an "int16_t" type, which is always 16 bits even when compiled on a 32 bit processor.
    byte cfg = (data[4] & 0x60);

    switch (type_s) {
        case 1:
            raw = raw << 3; // 9 bit resolution default
            if (data[7] == 0x10) {
            raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution
            }
            
            celsius = (float)raw * 0.0625;
            break;

        case 0:     // at lower res, the low bits are undefined, so let's zero them
            if (cfg == 0x00) raw = raw & ~7;    // 9 bit resolution, 93.75 ms
            if (cfg == 0x20) raw = raw & ~3;    // 10 bit res, 187.5 ms
            if (cfg == 0x40) raw = raw & ~1;    // 11 bit res, 375 ms
     
            celsius = (float)raw * 0.0625;      // default is 12 bit resolution, 750 ms conversion time
            break;

        case 2:
            data[1] = (data[1] >> 3) & 0x1f;
            if (data[2] > 127) {
                celsius = (float)data[2] - ((float)data[1] * .03125);
            }else{
        
            celsius = (float)data[2] + ((float)data[1] * .03125);
            }
    }

    if((((celsius <= 0 && celsius > -1) && lastTemp > 5)) || celsius > 125) { // remove random errors
        celsius = lastTemp;
    }

    fahrenheit = celsius * 1.8 + 32.0;
    lastTemp = celsius;
    Serial.print("  Temperature = ");
    Serial.print(celsius);
    Serial.print(" Celsius, ");
    Serial.print(fahrenheit);
    Serial.println(" Fahrenheit");

    String temperature = String(celsius); // Now that we have the readings, we can publish them to the cloud. Store temp in "temperature" string
    Particle.publish("temperature", temperature, PRIVATE); // publish to cloud
    delay(5000); // 5 second delay. This is the end of the OneWire temperature code.

    if (VentMode == "Automatic") {                      
        
       
        if (celsius <=27) {             //Goldilocks Zone.      
            CloseVent("");
        }

        else if (celsius >27) {         //Temperature is too hot, open Vent.    
            OpenVent("");
        }

    }    
    else if (VentMode == "Closed") {    //For manual selection of Vent.    
        CloseVent("");
    }
    else if (VentMode == "Open") {
        OpenVent("");
    } 

}

int OpenVent (String command) {
    if (angle == 0) {
        for (angle = 0; angle < 90; angle += 1) {   //sweep from 0 to 90 degrees and hold OPEN
            servo.write(angle);                     //angle of hold   
            delay(50);                              //Time (take .05 seconds for each degree)
        }
    }
}

int CloseVent (String command) {
    if (angle==90) {
        for (angle = 90; angle > 0; angle -= 1) {   //sweep from 90 to 0 degrees and hold CLOSED
        servo.write(angle);                                 
        delay(50);
        } 
    }
}
    
int TurnOnLED (String command) {    //Grow Light LED strip is turned ON.
    digitalWrite(D3, HIGH);
   
    strip.setPixelColor(0, 0,0,0);  //Turn off mini LED strip.
    strip.setPixelColor(1, 0,0,0);
    strip.setPixelColor(2, 0,0,0);
    strip.setPixelColor(3, 0,0,0);
    strip.setPixelColor(4, 0,0,0);
    strip.setPixelColor(5, 0,0,0);
    strip.setPixelColor(6, 0,0,0);
    strip.setPixelColor(7, 0,0,0);
    strip.show(); 
}  

/*int TurnOffLED (String command) {   //Grow Light LED strip is turned OFF. 
    digitalWrite(D3, LOW);
    
    strip.setPixelColor(0, 0,10,0); //Turn on mini LED strip as a night light. Each pixel is a different muted colour (0-255).
    strip.setPixelColor(1, 15,10,0);
    strip.setPixelColor(2, 15,0,0);
    strip.setPixelColor(3, 0,10,20);
    strip.setPixelColor(4, 15,15,20);
    strip.setPixelColor(5, 0,0,25);
    strip.setPixelColor(6, 0,25,0);
    strip.setPixelColor(7, 15,0,15);
    strip.show();
*/

int TurnOffLED (String command) {   //Grow Light LED strip is turned OFF. 
    digitalWrite(D3, LOW);
    
    strip.setPixelColor(0, 255,255,255); //Turn on mini LED strip as a night light. Each pixel is a brightest white(255).
    strip.setPixelColor(1, 255,255,255);
    strip.setPixelColor(2, 255,255,255);
    strip.setPixelColor(3, 255,255,255);
    strip.setPixelColor(4, 255,255,255);
    strip.setPixelColor(5, 255,255,255);
    strip.setPixelColor(6, 255,255,255);
    strip.setPixelColor(7, 255,255,255);
    strip.show();
}

int LMode (String command) {
    LightMode = command;
}    
int VMode (String command) {
    VentMode = command;
}