Brian Rashap
Published

House Plant Watering System

Demonstration of the second midterm project for the Central New Mexico Community College IoT Bootcamp (deepdivecoding. com/iot)

IntermediateFull instructions providedOver 1 day442
House Plant Watering System

Things used in this project

Hardware components

Argon
Particle Argon
×1
Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
DFRobot Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
×1
Gravity: I2C BME280 Environmental Sensor
DFRobot Gravity: I2C BME280 Environmental Sensor
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
Grove - Air quality sensor v1.3
Seeed Studio Grove - Air quality sensor v1.3
×1
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×1

Software apps and online services

Visual Studio Code - Particle Workbench Extension
Solidworks
Adafruit.io
zapier

Hand tools and fabrication machines

Epilog Laser Cutter
Ultimaker 3

Story

Read more

Custom parts and enclosures

Flower Pot .stl file

.stl file for 3D print of flower pot and BME280 holder

Watering Tube Holder

Enclosure .svg file for laser cutting

Schematics

Schematic

Component Layout

PCB Layout

Code

Smart Plant Watering Code

C/C++
Particle Argon code for the IoT Bootcamp smart houseplant watering system.
/*
 * Project PlantWater2
 * Description: Revised IoT Class House Plant Watering System
 * Author: Brian Rashap
 * Date: 3-DEC-2020
 */

#include "Credentials.h"
#include <Adafruit_MQTT.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_BME280.h"
#include "Air_Quality_Sensor.h"

// Setup SSD_1306 Display
#define SSD1306_128_64
Adafruit_SSD1306 display(-1);

// Declare Air Quality Sensor Object
AirQualitySensor aq(A0);

// THese #include statement is for MQTT to Adafruit.io
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h" 
#include "Adafruit_MQTT/Adafruit_MQTT.h" 

/************ Global State (you don't need to change this!) ***   ***************/ 
TCPClient TheClient; 

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

/****************************** Feeds ***************************************/ 
Adafruit_MQTT_Publish Htemp = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Temperature");
Adafruit_MQTT_Publish Hmoist = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Moisture");
Adafruit_MQTT_Publish Hwater = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Water"); 
Adafruit_MQTT_Publish Hpres = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Pressure"); 
Adafruit_MQTT_Publish Hhum = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Humidity"); 
Adafruit_MQTT_Publish Hdust = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_Dust"); 
Adafruit_MQTT_Publish Hqual = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Plant_AQ"); 

Adafruit_MQTT_Subscribe Hpump = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/Plant_Pump"); 

// Declare Constants
const byte BME_ADDR = 0x76;
const byte OLED_ADDR = 0x3C;
const byte BLUEPIN = D12;
const byte YELLOWPIN = D11;
const byte RELAYPIN = D10;
const byte MOISTPIN = A4;
const byte AQPIN = A2;
const byte DUSTPIN = A1;
const int threshold = 2400;

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; 

/************Declare Variables*************/
float tempC,prs,hum,conc;
int moist, quality, watered;
int lastPublish, lastSoil;
unsigned int soilDelay = 600000;
unsigned int publishDelay = 120000;
bool status,blueState,yellowState;

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

void setup() {
  pinMode(RELAYPIN,OUTPUT);
  digitalWrite(RELAYPIN,LOW);
  Serial.begin(9600);
  waitFor(Serial.isConnected, 15000);        //give time for Serial Monitor to initalize

  // initialize and clear display
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.clearDisplay();
  display.display();
  printhello();

  //Connect to WiFi without going to Particle Cloud
  WiFi.connect();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,10);
  display.clearDisplay();
  display.printf("  Waiting\n  for Wifi\n");
  display.display();
  while(WiFi.connecting()) {
    Serial.printf(".");
    delay(50);
  }
  Serial.printf("\n\n\n");

  // Initialize Pins
  pinMode(BLUEPIN,INPUT_PULLDOWN);
  pinMode(YELLOWPIN,INPUT_PULLDOWN);
  
  pinMode(MOISTPIN,INPUT); 
  pinMode(DUSTPIN,INPUT);
  pinMode(D7,OUTPUT);

  status = bme.begin(BME_ADDR);
  if (status) {
    Serial.printf("BME280 Online\n");
  }
  else{
    Serial.printf("Error Initializing BME280\n");
  }

  if (aq.init()) {
    Serial.println("AQ Sensor ready.");
  }
  else {
    Serial.println("AQ Sensor ERROR!");
  }

  mqtt.subscribe(&Hpump);

  // Set timers so that moisture checked and data published at startup.
  lastPublish=-999999;
  lastSoil=-999999;
}


void loop() {

  MQTT_connect();

  moist = analogRead(A4);

  //Right now both buttons manually water the plant. They are there for future expandability.
  blueState = digitalRead(BLUEPIN);
  yellowState = digitalRead(YELLOWPIN);

  if(blueState | yellowState){
    waterPlant(750);
    watered = 1;
    oledprint(tempC,prs,hum,conc,moist,quality);
  }

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {
    if (subscription == &Hpump) {
      watered = atoi((char *)Hpump.lastread);
      Serial.printf("Received %i from Adafruit.io feed Plant_Pump \n",watered);
      if(watered){
        waterPlant(1250);
        oledprint(tempC,prs,hum,conc,moist,quality);
      }
    }
  }

  if(millis()-lastSoil > soilDelay){
    if(moist > threshold) {
      watered = 1;
      waterPlant(1000);
      oledprint(tempC,prs,hum,conc,moist,quality);
    }
    else {
      watered = 0;
    }
    lastSoil=millis();
  }

  if(millis()-lastPublish > publishDelay) {
    tempC = bme.readTemperature();
    prs = (bme.readPressure()*0.0002953)+5;
    hum = bme.readHumidity();
    conc = getDust();
    quality = aq.getValue();
    oledprint(tempC,prs,hum,conc,moist,quality);
    if(mqtt.Update()) {
      Serial.printf("Publishing to MQTT\n");
      Htemp.publish(tempC);
      Hmoist.publish(moist);
      Hhum.publish(hum);
      Hpres.publish(prs);
      Hdust.publish(conc);
      Hqual.publish(quality);
      Hwater.publish(watered);
    }
    else {
      Serial.printf("Publish failed\n");
    } 
    lastPublish = millis();
    watered = 0;
  }
}

void waterPlant(int waterTime) {
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  display.clearDisplay();
  display.printf(".Watering.");
  display.display();
  delay(500);
  digitalWrite(RELAYPIN,HIGH);
  delay(waterTime);
  digitalWrite(RELAYPIN,LOW);
}

void printhello() {
  display.clearDisplay();
  // display a pixel in each corner of the screen
  display.drawPixel(0, 0, WHITE);
  display.drawPixel(127, 0, WHITE);
  display.drawPixel(0, 63, WHITE);
  display.drawPixel(127, 63, WHITE);
  // display a line of text
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,10);
  display.print("   Hello\n   World!");
  display.display();
  delay(2000);
}

void oledprint(float Otemp, float Opres, float Ohum, float Odust, int Omoist, int Oaq) {

   // initialize and clear display
  display.clearDisplay();
  display.display();

  // display a pixel in each corner of the screen
  display.drawPixel(0, 0, WHITE);
  display.drawPixel(127, 0, WHITE);
  display.drawPixel(0, 63, WHITE);
  display.drawPixel(127, 63, WHITE);

  // display a line of text
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,5);
  display.printf("Environmental Reading");
  display.setCursor(0,18);
  display.printf("Temperature(F) %0.2f\n",Otemp);
  display.printf("Pressure(inHg) %0.2f\n",Opres);
  display.printf("Humidity (%rH)  %0.2f\n",Ohum);
  display.printf("Moisture:      %04i \n",Omoist);
  display.printf("Dust: %05.0f  AQ: %i \n",Odust,Oaq);

  display.display();
}

float getDust() {
  unsigned long duration;
  unsigned long starttime;
  unsigned long sampletime_ms = 30000;//sampe 30s ;
  unsigned long lowpulseoccupancy = 0;
  float ratio = 0;
  float concentration = -1;

  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  display.clearDisplay();
  display.printf(".Getting.\nDustData\n");
  display.display();

  starttime = millis();//get the current time;

  while(concentration == -1) {
    duration = pulseIn(DUSTPIN, LOW);
    lowpulseoccupancy = lowpulseoccupancy+duration;

    if ((millis()-starttime) > sampletime_ms)//if the sampel time == 30s
    {
        ratio = lowpulseoccupancy/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
        Serial.print(lowpulseoccupancy);
        Serial.print(",");
        Serial.print(ratio);
        Serial.print(",");
        Serial.println(concentration);
        lowpulseoccupancy = 0;
        starttime = millis();
    }
  }
return concentration;
}



// Function to connect and reconnect as necessary to the MQTT server.
void MQTT_connect() {
  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.printf("%s\n",(char *)mqtt.connectErrorString(ret));
       Serial.printf("Retrying MQTT connection in 5 seconds..\n");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
  }
  Serial.printf("MQTT Connected!\n");
}

Credits

Brian Rashap

Brian Rashap

12 projects • 107 followers
Former General Manager of US Facilities Operations at Intel Corporation. Currently loving my encore career as a teacher focused on IoT.

Comments