Greenhouse Controls
Code and design for autonomous planter.
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; }