Jeren Arellano Reeder
Published © MIT

SmartGrow Tower

SmartGrow gives your plants the exact care they need when they need it! Displaying live environmental data in real time.

BeginnerWork in progressOver 1 day106
SmartGrow Tower

Things used in this project

Hardware components

Photon 2
Particle Photon 2
×1
Gravity: I2C BME280 Environmental Sensor
DFRobot Gravity: I2C BME280 Environmental Sensor
×1
Breadboard (generic)
Breadboard (generic)
×1
Grove - Air quality sensor v1.3
Seeed Studio Grove - Air quality sensor v1.3
×1
Adafruit Water Pump
×1
2N3906 Emitter follower
×1
Grove - Capacitive Soil Moisture Sensor
Seeed Studio Grove - Capacitive Soil Moisture Sensor
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×2

Software apps and online services

Adafruit.IO Dashboard

Hand tools and fabrication machines

Bambu 3D Printer
Laser cutter (generic)
Laser cutter (generic)

Story

Read more

Custom parts and enclosures

SolidWork Files for tower

Tower Assembly

Code

SmartGrow Tower

C/C++
A self monitoring plant that can water itself as much as it needs when it needs! The SmartGrow Tower is able to show live environmental readings and display them onto OLED displays. Using Adafruit Dashboard you can monitor the plant and are able to manually water the plant from your device.
// Include Particle Device OS APIs
// Project: SmartGrow Tower
// Author: Jeren Arellano Reeder
#include "Particle.h"
#include "Air_Quality_Sensor.h"
#include "Grove_Air_quality_Sensor.h"
#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "credentials.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_GFX.h"
#include "Adafruit_BME280.h"
//classes
AirQualitySensor sensor (A0);
TCPClient TheClient; 
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY); 
Adafruit_MQTT_Publish pubFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/plantAQ");
Adafruit_MQTT_Publish pubFeed2 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/plantmoisture");
Adafruit_MQTT_Publish pubFeed3 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/planttemp");
Adafruit_MQTT_Publish pubFeed4 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/planthumid");
Adafruit_MQTT_Publish pubFeed5 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/plantpressure");
Adafruit_MQTT_Subscribe subFeed = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/WaterButton");
//bme
char number = 164;
Adafruit_BME280 bme;
int hexAddress = 0x76;
const int OLED_RESET=-1;
Adafruit_SSD1306 display1(OLED_RESET);
Adafruit_SSD1306 display2(OLED_RESET);

//relay
const int relayPin = D6;
int relayState;
unsigned int lastRelay;
unsigned int lastRelay2;
unsigned int lastPublish;
//stats
float temp, pres, humid, Fahrenheit, inHg;
bool status;
//strings floats ints
float subValue,pubValue,pubValue2,pubValue3,pubValue4,pubValue5,pubValue6,pubValue7;
String dateTime;
String timeOnly;
unsigned int last, lastTime;
const int moisturePin = A1;
float moistureValue;
//functions
void MQTT_connect();
bool MQTT_ping();
void OLED(void);

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);


void setup() {

pinMode(relayPin,OUTPUT);

  Time.zone(-6);
  Particle.syncTime();
  Serial.begin(9600);
  waitFor(Serial.isConnected,10000);
  //wifi
  WiFi.on();
  WiFi.connect();
  while(WiFi.connecting()) {
    Serial.printf(".");
  }
  Serial.printf("\n\n");

  mqtt.subscribe(&subFeed);
  while(!Serial);
  //air sensor
  Serial.println("Starting Sensor");
  delay(10000);
  
  if(sensor.init()){
    Serial.println("Sensor is ready");
  }
  else{
    Serial.println("Sensor Error");
  }

  status = bme.begin(hexAddress);
  if (status == TRUE) {
    Serial.printf("BME280 at address 0x%02X started successfully!\n",hexAddress);
//OLED 
display1.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display2.begin(SSD1306_SWITCHCAPVCC, 0x3D); 
display1.clearDisplay();
display2.clearDisplay();
display1.display();
display2.display();
delay(2000);
  }
}

void loop() {

  OLED();
//BME280
  temp = bme.readTemperature();
  pres = bme.readPressure();
  humid = bme.readHumidity();
  Fahrenheit = (temp * 9/5) + 32;
  inHg = (pres * 0.00029529983071445);
  Serial.printf("Pressure %f\nHumidity %f\nFahrenheit %f\n",inHg,humid,Fahrenheit);
  delay(1000);
//AIR SENSOR
 int quality = sensor.slope();
 
 Serial.print("Air Quality: ");
 Serial.println(sensor.getValue());

 if (quality == AirQualitySensor::FORCE_SIGNAL){
  Serial.println("VERY HIGH POLLUTION WARNING");
 }
 else if(quality == AirQualitySensor::HIGH_POLLUTION){
  Serial.println("HIGH POLLUTION");
 }
 else if(quality == AirQualitySensor::LOW_POLLUTION){
  Serial.println("LOW POLLUTION");
 }
 else if(quality == AirQualitySensor::FRESH_AIR){
  Serial.println("FRESH AIR");
 }

 delay(1000);

{
    lastTime = millis();
//MOISTURE SENSOR AND TIME
  moistureValue = analogRead(moisturePin);
  dateTime = Time.timeStr();
  timeOnly = dateTime.substring(11,19);
  if( millis () - lastTime >10000) {
  lastTime = millis ();

 Serial.printf ("Date/Time %s\n", dateTime.c_str () );
 Serial.printf ("Time is %s\n", timeOnly.c_str () );
 Serial.printf("Moisture Level is %0.2f",moistureValue);
  }
} 

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(100))) {
    if (subscription == &subFeed) {
      subValue = atof((char *)subFeed.lastread);
    }
  }
//RELAY EVERY 2 MINS
  if (millis() - lastRelay > 120000) {
  lastRelay = millis();

  if (moistureValue > 2670) {
    digitalWrite(relayPin, HIGH);
    Serial.println("RELAY ON");
    delay(500); 
    digitalWrite(relayPin, LOW);
    Serial.println("RELAY OFF");
  }
}
//ADAFRUIT WATERBUTTON
if (subValue == 1) {
  digitalWrite(relayPin, HIGH);
  Serial.println(" relayState =  ON");
  delay(500); 
  digitalWrite(relayPin, LOW);
  Serial.println(" relayState =  OFF");
}

if (subValue == 0) {
  digitalWrite(relayPin, LOW);
  Serial.println(" relayState =  OFF");
}

   if((millis()-lastPublish > 15000)) {
    if(mqtt.Update()) {
      pubValue = sensor.getValue();
      pubValue2 = moistureValue;
      pubValue3 = Fahrenheit;
      pubValue4 = humid;
      pubValue5 = inHg;
      pubFeed.publish(pubValue);
      pubFeed2.publish(pubValue2);
      pubFeed3.publish(pubValue3);
      pubFeed4.publish(pubValue4);
      pubFeed5.publish(pubValue5);
      Serial.printf("Publishing %0.2f \n",pubValue);
      Serial.printf("Publishing %0.2f \n",pubValue2); 
      Serial.printf("Publishing %0.1f \n",pubValue3); 
      Serial.printf("Publishing %0.2f \n",pubValue4);
      Serial.printf("Publishing %0.2f \n",pubValue5);   
      } 
    lastPublish = millis();
}
}
//CONNECTING
void MQTT_connect() {
  int8_t ret;

  if (mqtt.connected()) {
    return;
  }
 
  Serial.print("Connecting to MQTT... ");
 
  while ((ret = mqtt.connect()) != 0) { 
       Serial.printf("Error Code %s\n",mqtt.connectErrorString(ret));
       Serial.printf("Retrying MQTT connection in 5 seconds...\n");
       mqtt.disconnect();
       delay(5000);
  }
  Serial.printf("MQTT Connected!\n");
}

bool MQTT_ping() {
  static unsigned int last;
  bool pingStatus;

  if ((millis()-last)>120000) {
      Serial.printf("Pinging MQTT \n");
      pingStatus = mqtt.ping();
      if(!pingStatus) {
        Serial.printf("Disconnecting \n");
        mqtt.disconnect();
      }
      last = millis();
  }
  return pingStatus;
}
//OLEDS
void OLED(void){
   int quality = sensor.slope();
  dateTime = Time.timeStr();
  timeOnly = dateTime.substring(11,19);

   //DISPLAYS
  display1.display();
  display1.clearDisplay();
  display1.setTextSize(1);
  display1.setTextColor(WHITE);
  display1.setCursor(1,0);
  display1.printf("MoistureMeter %0.2f\n %s\n",moistureValue, dateTime.c_str ());

  display1.setTextSize(1);
  display1.setTextColor(WHITE);
  display1.setCursor(1,38);
  display1.printf("Fahrenheit %0.1fF\nHumidity %0.1f\nPressure %0.1f\n",Fahrenheit,humid,inHg);;

  display1.display();
   display1.clearDisplay();

  display2.display();
  display2.clearDisplay();
  display2.setTextSize(1);
  display2.setTextColor(WHITE);
  display2.setCursor(1,0);
  display2.print("Sensor value:\n");
  display2.println(sensor.getValue());
 if (quality == AirQualitySensor::FORCE_SIGNAL){
  display2.println("VERY HIGH POLLUTION WARNING\n");
 }
 else if(quality == AirQualitySensor::HIGH_POLLUTION){
  display2.println("HIGH POLLUTION\n");
 }
 else if(quality == AirQualitySensor::LOW_POLLUTION){
  display2.println("LOW POLLUTION\n");
 }
 else if(quality == AirQualitySensor::FRESH_AIR){
  display2.println("FRESH AIR\n");
 }

display2.display();
display2.clearDisplay();
   delay(1000);
}

Credits

Jeren Arellano Reeder
3 projects • 7 followers
Hardware engineer specializing in solidworks, embedded systems, circuit design, PCB layout, and integration for IoT.

Comments