Phylicia Bediako
Published © MIT

HG Smart Plant Care System

The Happy Growing Smart Plant Care System uses sensors and an OLED display to help your plant tell you what it needs.

BeginnerWork in progress167
HG Smart Plant Care System

Things used in this project

Story

Read more

Custom parts and enclosures

3D Printed Console Lid

3D Printed Console Body

3D Printed Flower Pot Platform & Reservoir

3D Printed Flower Pot w Drain Holes

Schematics

Midterm2Fritzing

HG Drawn Schematic

Code

Happy Growing Code

C/C++
/* 
 * Project myProject
 * Author: Your Name
 * Date: 
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */

// Include Particle Device OS APIs
#include "Particle.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_BME280.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "Air_Quality_Sensor.h" //Library name: Grove_Air_quality_Sensor
#include "Button.h"
#include "Colors.h"
#include "IoTTimer.h"
#include "neopixel.h"
#include "credentials.h"
#include "JsonParserGeneratorRK.h"
// #include "../lib/Adafruit_MQTT/src/Adafruit_MQTT.h"
// #include "../lib/Adafruit_MQTT/src/Adafruit_MQTT_SPARK.h"
// #include "../lib/Grove_Air_quality_Sensor/src/Air_Quality_Sensor.h"
// #include "../lib/Adafruit_BME280/src/Adafruit_BME280.h"
// #include "../lib/Adafruit_SSD1306/src/Adafruit_SSD1306.h"
// #include "../lib/neopixel/src/neopixel.h"

//Subscribe & Publish
TCPClient TheClient;
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY);  
Adafruit_MQTT_Publish hgSoilFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/HGSoilFeed");
Adafruit_MQTT_Publish hgAirFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/HGAirQualFeed");
Adafruit_MQTT_Publish hgTempFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/HGTempFeed");
Adafruit_MQTT_Publish hgHumidFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/HGHumidityFeed");
Adafruit_MQTT_Publish hgPressFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/HGAirPressureFeed");
//Adafruit_MQTT_Subscribe subFeed = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/buttononoff");
Adafruit_MQTT_Subscribe hgButtonSub = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/hgbuttonsub");

float pubValue;
int hgButtonPress; //replaced subValue
void MQTT_connect();
bool MQTT_ping();
unsigned int lastPubTime;

//Soil Moisture Sensor Variables
int soilDryness;
const int SOILPIN=A0;

//SEEED Air Quality Sensor Variables
const int AIRQUALPIN=A1;
int airQuality;
AirQualitySensor sensor(AIRQUALPIN);

//BME Variables
float tempC, tempF, humidRH, pressPA, pressInHg;
const char DEGREE=0xF8;
const char PCT=0x25;
const int BME280=0x76;
bool status;
Adafruit_BME280 bme;
bool tempProblem, humidProblem, soilProblem;

//OLED Variables
const int OLEDSCRN=0x3c;
const int OLED_RESET = -1;
Adafruit_SSD1306 display(OLED_RESET);
int displayMode;
unsigned int lastDisplayTime;

//Neopixel variables
const int PIXELCOUNT=12;
int pix;
int startPixel, endPixel, color;
Adafruit_NeoPixel pixel (PIXELCOUNT,SPI1,WS2812B);
void pixelFill(int startPixel, int endPixel, int color);

//Water Pump
const int WATERPIN=D16;
int waterTimer, lastWaterTimer;

//Timestamp variables
String dateTime;
String timeHHMM; //formatted time var
String dateMMDD; //formatted date var
const int MDTTIME = -6;
const int MSTTIME = -7;
unsigned int lastTime;

SYSTEM_MODE(AUTOMATIC);

void displayMessage(int textSize, char message[]);


void setup() {
  Wire.begin();
  Serial.begin(9600);
  waitFor(Serial.isConnected,10000);
  
  WiFi.on();
  WiFi.connect();
  while(WiFi.connecting()) {
    Serial.printf(".");
  }
  Serial.printf("\n\n");

  mqtt.subscribe(&hgButtonSub);
  
  Time.zone(MDTTIME);
  Particle.syncTime();

   status = bme.begin(BME280);
   if (status == false){
     Serial.printf("BME280 at address 0x%02x failed to start",BME280);
   }

   display.begin(SSD1306_SWITCHCAPVCC,OLEDSCRN);
   display.setTextColor(WHITE);
   display.display();
   display.clearDisplay();


   pixel.begin();
   pixel.setBrightness(35);
   pixel.show();

   Serial.printf("Waiting for sensor to intitialize...");
   delay(20000);

   if(sensor.init()){
     Serial.printf("Sensor ready.");
   }
   else{
     Serial.printf("Sensor ERROR");
   }

  pinMode(WATERPIN,OUTPUT);
}

/***************************************************************/
void loop() {
  MQTT_connect();
  MQTT_ping();

  dateTime= Time.timeStr(); //current date and time
  timeHHMM = dateTime.substring(11,16); //for time that displays only hours and mins (no seconds)
  dateMMDD= dateTime.substring(4,9); //for date that displays only month and day (no day of week or year)
  soilDryness = analogRead(SOILPIN);
  airQuality=sensor.slope();

   tempC=bme.readTemperature();
   tempF=(tempC*1.8)+32;
   humidRH=bme.readHumidity();
   pressPA= bme.readPressure();
   pressInHg=(pressPA * 0.0002952998751);

   if((soilDryness>=1700)&&(soilDryness<=2500)){
    soilProblem=0;
   }
    if((soilDryness>2500)){
      soilProblem=1;
      }
    if((soilDryness<1700)){
      soilProblem=1;
    }

  if((humidRH>=50)&&(humidRH<=70)){
    humidProblem=0;
  }
  if ((humidRH <50)){
    humidProblem=1;
  }
  if ((humidRH >70)){
    humidProblem=1;
  }

  if((tempF>=65) &&(tempF<=85)){
    tempProblem=0;
  }
  if ((tempF>85)){     
    tempProblem=1;
  }
  if ((tempF<65)){ 
    tempProblem=1;
  }
  Serial.printf("T %i, H %i, S %i", tempProblem, humidProblem, soilProblem);

  
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(100))) {
    if (subscription == &hgButtonSub) {
      hgButtonPress = atoi((char *)hgButtonSub.lastread);

       if(hgButtonPress==1){
        digitalWrite(WATERPIN,HIGH);
       }
       if(hgButtonPress==0){
        digitalWrite(WATERPIN,LOW);
       }
     }
      Serial.printf("HG Water Button %i\n",hgButtonPress);
  }

  if((millis()-lastPubTime)>30000){
    if(mqtt.Update()){
      hgSoilFeed.publish(soilDryness,1);
      hgAirFeed.publish(airQuality,1);
      hgTempFeed.publish(tempF,1);
      hgHumidFeed.publish(humidRH,1);
      hgPressFeed.publish(pressInHg,1);
    }
    lastPubTime = millis();
  }

  


//Displays
 if ((millis()-lastDisplayTime)>4000){
  displayMode++;
  if(displayMode==6){
    displayMode=1;
  }
   lastDisplayTime = millis();
 }

//Screen 1: Intro
  if ((displayMode==1)){
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(1);
    display.printf("Hi! I'm a\nRED BANANA CROTON\n \nI like\nTemp: 65-85%cF\nHumidity: 50-70%cRH\nWater every 3-5 days",DEGREE,PCT);
    display.display();
  }

//Screen 2: Current Conditions
  if((displayMode==2)){
    display.setTextSize(1);
    display.setCursor(0,0);
    display.clearDisplay();
    display.printf("%s at %s\nTemp: %0.1f %cF\nHumidity: %0.1f %cRH\nSoil Dryness: %i\nAirQuality: %i\nPressure: %0.1f inHG",dateMMDD.c_str(), timeHHMM.c_str(), tempF, DEGREE, humidRH, PCT, soilDryness,airQuality, pressInHg);
    display.display();
  }

//Screen 3: Temperature Problems
  if((displayMode==3)){ 
    if ((tempF>85)){     
      displayMessage(2,"I'm hot!");
    }
    if ((tempF<65)){ 
      displayMessage(2,"I'm cold!");
    }
  }

//Screen 4: Humidity Problems
  if((displayMode==4)){
    if ((humidRH <50)){
      displayMessage(2, "The air is\ntoo dry!");
    }
    if ((humidRH >70)){
      displayMessage(2, "It's too\nhumid!");
    }
  }

//Screen 5: Soil Watering Problems
  if((displayMode==5)){
    if((soilDryness>2500)){
      displayMessage(2, "The soil\nis too\ndry!");
      }

    if((soilDryness<1700)){
      displayMessage(2, "I'm\ndrowning!");
    }
  }  

  //Watering the Soil
  waterTimer=millis();

  if((soilDryness>2500)){
    if((waterTimer-lastWaterTimer)>60000){
      digitalWrite(WATERPIN,HIGH);
    }
    if((waterTimer-lastWaterTimer)>61000){
      lastWaterTimer=waterTimer;
      digitalWrite(WATERPIN,LOW);
    }
  }

//NeoPixels
  if ((tempProblem == 0) && (humidProblem ==0) && (soilProblem == 0)){
    startPixel=0;
    endPixel= 5;
    pixelFill(startPixel, endPixel, green);

    startPixel=6;
    endPixel= 11;
    pixelFill(startPixel, endPixel, 0x000000);
  }
  if ((tempProblem == 1) || (humidProblem ==1) || (soilProblem == 1)){
    startPixel=6;
    endPixel= 11;
    pixelFill(startPixel, endPixel, red);

    startPixel=0;
    endPixel= 5;
    pixelFill(startPixel, endPixel, 0x000000);
  }

  //Air Quality
  Serial.printf("(Sensor value: %i)\n",airQuality);
  if (airQuality == AirQualitySensor::FORCE_SIGNAL){
    Serial.printf("Warning! Excessive pollution!");
    }
  if (airQuality == AirQualitySensor::HIGH_POLLUTION) {
    Serial.printf("High pollution ");
  }
  if (airQuality == AirQualitySensor::LOW_POLLUTION) {
    Serial.printf("Low pollution ");
  }
  if (airQuality == AirQualitySensor::FRESH_AIR) {
    Serial.printf("Fresh air ");  
  }

}

/************************************************************/
void displayMessage(int textSize, char message[]){
  display.setTextSize(textSize);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.clearDisplay();
  display.printf(message);
  display.display();
}

/************************************************************/
void pixelFill(int startPixel, int endPixel, int color){
  for(pix=startPixel; pix<=endPixel; pix=pix+1){
    pixel.setPixelColor(pix, color);
  }
  pixel.show();
  delay(100);
}

/************************************************************/
void MQTT_connect() {
  int8_t ret;
 
  // Return if already connected.
  if (mqtt.connected()) {
    return;
  }
 
  Serial.print("Connecting to MQTT... ");
 
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.printf("Error Code %s\n",mqtt.connectErrorString(ret));
       Serial.printf("Retrying MQTT connection in 5 seconds...\n");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds and try again
  }
  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;
}

Credits

Phylicia Bediako
3 projects • 5 followers

Comments