Published © MIT

Automated Growth Chamber

Bring NASA to your greenhouse!

IntermediateShowcase (no instructions)Over 1 day401
Automated Growth Chamber

Things used in this project

Hardware components

Argon
Particle Argon
×1
0.96 in OLED Display Module 128 x 64
×1
Onyehn BME280 Temperature Humidity Barometric Pressure Sensor Module with IIC I2C
×1
Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
DFRobot Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
×1
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×1
Grove - Air quality sensor v1.3
Seeed Studio Grove - Air quality sensor v1.3
×1
Flymmy 5v Relay Board Relay Module 1 Channel Opto-Isolated High or Low Level Trigger
×1
Gikfun DC 3V 5V Micro Submersible Mini Water Pump
×1
Flora RGB Neopixel LEDs- Pack of 4
Adafruit Flora RGB Neopixel LEDs- Pack of 4
×1
General Purpose Transistor PNP
General Purpose Transistor PNP
×1
Resistor 220 ohm
Resistor 220 ohm
×1
Through Hole Resistor, 2.2 kohm
Through Hole Resistor, 2.2 kohm
×1

Software apps and online services

Adafruit IO
CorelDRAW
Fritzing
Visual Studio Code

Hand tools and fabrication machines

Epilog Laser

Story

Read more

Custom parts and enclosures

Laser Stencil

Schematics

Fritzing Diagram

Code

Main

Arduino
/*
 * Project: Automated Growth Chamber
 * Description: This program is designed to collect data from several sensors,
 *              display the data to an OLED display, publish the data to the Adafruit IO
 *              dashboard, and automatically water a plant based upon moisture readings.
 * Author: Saige Martinez
 * Date: November 12, 2020
 */

#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h" 
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "config.h"

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Particle.h"
#include "neopixel.h"
#include "Air_Quality_Sensor.h"

// Create an BME280 class. 
Adafruit_BME280 bme;

// // Setup the SSD1306 display class by passing in the reset pin.
const int OLED_RESET = D4;
Adafruit_SSD1306 display(OLED_RESET);

// Set the Adafruit_Neopixel class by passing it the pixel count, pin, and type.
const int PIXEL_PIN = D8;
const int PIXEL_COUNT = 4;
#define PIXEL_TYPE WS2812B
Adafruit_NeoPixel pixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

// Set the AirQualitySensor class by passing it pin.
const int AIR_PIN  = A0;
AirQualitySensor air(AIR_PIN);

// Create an TCPClient class to connect to the MQTT server. 
TCPClient client; 

// Setup the MQTT client class by passing in the TCP client, MQTT server, and login details. 
Adafruit_MQTT_SPARK mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); 

// Feeds
Adafruit_MQTT_Publish temperature = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/temperature");
Adafruit_MQTT_Publish humidity = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/humidity");
Adafruit_MQTT_Publish pressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/pressure");
Adafruit_MQTT_Publish moisture = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/soil moisture");
Adafruit_MQTT_Publish particulate = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/particulate");
Adafruit_MQTT_Publish airQuality = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/air quality");
Adafruit_MQTT_Subscribe pumpSwitch = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/pump");
Adafruit_MQTT_Subscribe lightSwitch = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/pixels");

// Declare variables here.
const int PUMP_PIN = D9;
const int MOISTURE_PIN = A5;
int moistureReading;
int moistureMapping;
int last = 0;
const int DUST_PIN = D10;
unsigned long duration;
unsigned long startTime;
unsigned long sampleTime = 30000;
unsigned long lowPulseOccupancy = 0;
float ratio = 0;
float concentration = 0;
int airQualityReading;
int airQualityMapping;

void setup() {
  Serial.begin(9600);

  bme.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  pixel.begin();
  pixel.show();
  // pixel.setBrightness(65);

  pinMode(PUMP_PIN, OUTPUT);
  pinMode(MOISTURE_PIN, INPUT);
  pinMode(DUST_PIN, INPUT);
  pinMode(AIR_PIN, INPUT);
  air.init();
  
  startTime = millis();

  mqtt.subscribe(&pumpSwitch);
  mqtt.subscribe(&lightSwitch);
}

void loop() {
  connectMQTT();
  moistureReading = analogRead(MOISTURE_PIN);
  moistureMapping = map(moistureReading, 0, 4095, 100, 0);
  airQualityReading = air.getValue();
  airQualityMapping = map(airQualityReading, 0, 1000, 0, 500);
  getParticulates();
  displayData();
  publishData();
  subscribeData();
  turnOnPump();
}

void connectMQTT() {
  /*
   * Function to connect and reconnect as necessary to the MQTT server.
   * Should be called in the loop function and it will take care if connecting.
   */

  int8_t ret;
 
  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }
 
  Serial.print("Connecting to MQTT... ");
 
  while ((ret = mqtt.connect()) != 0) { // Connect will return 0 for connected.
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // Wait 5 seconds.
  }
  Serial.println("MQTT Connected!");
}

void getParticulates() {
  duration = pulseIn(DUST_PIN, LOW);
  lowPulseOccupancy = lowPulseOccupancy + duration;

  if((millis()-startTime) > sampleTime) {
    ratio = lowPulseOccupancy / (sampleTime * 10.0);
    concentration = 1.1 * pow(ratio, 3) - 3.8 * pow(ratio, 2) + 520 * ratio + 0.62;
    lowPulseOccupancy = 0;
    startTime = millis();
  }
}

void displayData() {
  /*
   * Function to print data to the OLED display.
   */

  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextColor(WHITE);

  display.println("Environmental Reading");
  display.printlnf("Temperature: %0.1f %cC", bme.readTemperature(), (char)247);
  display.printlnf("Humidity:    %0.1f%%", bme.readHumidity());
  display.printlnf("Pressure:    %0.0f hPa", bme.readPressure()/100.0);
  display.printlnf("Moisture:    %i%%", moistureMapping);
  display.printlnf("PM Level:    %0.0f p/F", concentration);
  display.printlnf("Air Quality: %i AQI", airQualityMapping);

  display.display();
}

void publishData() {
  /*
   * Function to publish data to Adafruit IO.
   */

  if(millis()-last > 30000) {
      if(mqtt.Update()) {
        temperature.publish(bme.readTemperature());
        humidity.publish(bme.readHumidity());
        pressure.publish(bme.readPressure()/100.0);
        moisture.publish(moistureMapping);
        particulate.publish(concentration);
        airQuality.publish(airQualityMapping);
      }
      last = millis();
    }
}

void subscribeData() {
  /*
   * Function to subscribe to Adafruit IO.
   * This is our 'wait for incoming subscription packets' busy subloop,
   * try to spend your time here.
   */

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {
    // Check if it is the pump feed.
    if(subscription == &pumpSwitch) {
      if (strcmp((char *)pumpSwitch.lastread, "OFF") == 0) {
        digitalWrite(PUMP_PIN, LOW); 
      }
      if (strcmp((char *)pumpSwitch.lastread, "ON") == 0) {
        digitalWrite(PUMP_PIN, HIGH);
      }
    }
    // Check if it is the light feed.
    if(subscription == &lightSwitch) {
      if (strcmp((char *)lightSwitch.lastread, "OFF") == 0) {
        for(int i=0; i<4; i++) {
          pixel.setPixelColor(i, 0, 0, 0);
          pixel.show();
        }
      }
      if (strcmp((char *)lightSwitch.lastread, "ON") == 0) {
        for(int i=0; i<4; i++) {
          pixel.setPixelColor(i, 255, 0, 51);
          pixel.show();
        }
      }
    }
  }
}

void turnOnPump() {
  /*
   * Function to turn on water pump when mositure reading
   * is less than 30%.
   */

  if(moistureMapping < 30) {
    digitalWrite(PUMP_PIN, HIGH);
    delay(1000);
    digitalWrite(PUMP_PIN, LOW);
  }
  else {
    digitalWrite(PUMP_PIN, LOW);
  }
}

Credits

Comments