Wesley Eccles
Published © MIT

Automatic Plant Watering + Time-Lapse

Automatic Plant Watering System with a built-in time-lapse to watch your plant grow!

IntermediateWork in progress558
Automatic Plant Watering + Time-Lapse

Things used in this project

Hardware components

Argon
Particle Argon
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
ESP32 CAM
×1
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Atmospheric Sensor Breakout - BME280
×1
Memory Socket, Micro SD
Memory Socket, Micro SD
×1
Grove - Relay
Seeed Grove - Relay
×1
Grove - Air quality sensor v1.3
Seeed Grove - Air quality sensor v1.3
×1
Grove - Dust Sensor(PPD42NS)
Seeed Grove - Dust Sensor(PPD42NS)
×1
Water Pump
×1

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino
adafruit.io
GitHub

Hand tools and fabrication machines

CNC Router
Laser cutter (generic)
Laser cutter (generic)
Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Breadboard Diagram

Schematic

Code

Particle Argon - Code

C/C++
/*
 * Project L14_04_PlantWater
 * Description: Plant Watering System
 * Author: Wesley Eccles 
 * Date: 4_20_21
 */

#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h" 
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "credentials.h"
#include "neopixel.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Air_Quality_Sensor.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

//TIME
  String DateTime , TimeOnly ;
  char currentDateTime[25] , currentTime[9];

//CAMERA
  const int CameraPIN= A5;
  int PhotosTaken=0;
//Air Quality
  AirQualitySensor sensor(A0);

//DUST SENSOR
  int DUSTPIN = 8;
  unsigned long Dustduration;
  unsigned long Duststarttime;
  unsigned long Dustsampletime_ms = 30000;//sampe 30s ;
  unsigned long lowpulseoccupancy = 0;
  float ratio = 0;
  float concentration = 0;

//SYSTEM
  SYSTEM_MODE(SEMI_AUTOMATIC);


//BME
  #define SEALEVELPRESSURE_HPA (1013.25)
  Adafruit_BME280 bme; // I2C

//ADAFRUIT.IO
  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 ***************************************/ 
// Setup Feeds to publish or subscribe 
  Adafruit_MQTT_Subscribe BUTTON = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/button"); 
  Adafruit_MQTT_Publish TEMP = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/TempF");
  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/Moisture");
  Adafruit_MQTT_Publish AIRQUALITY = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/AirQuality");
  Adafruit_MQTT_Publish DUST = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Dust");
  Adafruit_MQTT_Publish PHOTOS = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Photos");


  int last, lastTime;
  bool buttonState;

  long unsigned int publishFreq= 60000;   //sampe 60s ;

//RELAY
  const int RELAYPIN = D3;

//MOISTURE 
  const int MOISTPIN = A1;
  int moistureVal;

//OLED
  #define OLED_RESET D4
  Adafruit_SSD1306 display(OLED_RESET);
  int OLEDlastTime, i;

void setup() {
 //Camera 
  pinMode(CameraPIN, OUTPUT);
  digitalWrite(CameraPIN,HIGH);

//Serial Print
  Serial.begin(9600);
  delay(100); 

//OLED
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,25);
  display.clearDisplay();
  display.printf("Starting\n    Up");
  display.display();
//TIME
  Time.zone(-6);          // MST = -7, MDT = -6
  Particle.syncTime();    // Sync time with Particle Cloud

//RELAY
  pinMode(RELAYPIN,OUTPUT);

//MOISTURE
  pinMode(MOISTPIN,INPUT);

//BME
  bme.begin();

//ADAFRUIT.IO
 Serial.printf("Connecting to Internet \n");
  WiFi.connect();
  while(WiFi.connecting()) {
    Serial.printf(".");
    delay(100);
  }
  Serial.printf("\n Connected!!!!!! \n");
    mqtt.subscribe(&BUTTON);


//AIRQUALITY
  Serial.println("Waiting sensor to init...");
  //delay(20000);
  if (sensor.init()) {
    Serial.println("Sensor ready.");
  }
  else {
      Serial.println("Sensor ERROR!");
  }


//DUST
  pinMode(DUSTPIN,INPUT);
  Duststarttime = millis();

//Display
  i=-1;
  OLEDlastTime= -3000;
}


void loop() {

  KeepConnection();
  CheckButtonPress();
 

if((millis()-OLEDlastTime > 4000)) { // Change Display Every 3 minutes
  i++;
  if (i==4){
    i=0;
  }
  Serial.println(i);

    switch (i) {
      case 0:
        display.clearDisplay();
        TimeWriteToOLED();
        OLEDTemp();
        break;
      
      case 1:
        display.clearDisplay();
        TimeWriteToOLED();
        OLEDPresMoist();
        break;
      
      case 2:
        display.clearDisplay();
        TimeWriteToOLED();
        OLEDAir();
        break;     
      case 3:
        display.clearDisplay();
        TimeWriteToOLED();
        OLEDPhotos();
        break;    
    }

  OLEDlastTime = millis(); //Reset the time 
}

  if((millis()-lastTime > publishFreq)) { // PUBLISH EVERY 30 min
    
    WaterPlant(3000);
    PublishEnviro();
    TakePhoto();
    DustData();
  
    lastTime = millis(); //Reset the time 
  }
}

float tempF(){
  float _tempF;
  float _tempC;
  _tempC = bme.readTemperature();
  _tempF = (_tempC*1.8)+32;
  if (_tempF<50){
    return 69.69;
  }
  return _tempF;  
}
 
float Pressure(){
  float _pressure;
  _pressure= bme.readPressure() / 100.0F;
  return _pressure;
}

float Humidity(){
  float _humidity;
  _humidity= bme.readHumidity();
  return _humidity;
}

int  MoisturePercent(){
  int _moisturePercent, mapped;
  _moisturePercent= analogRead(MOISTPIN);
  mapped =  map(_moisturePercent, 2000,3500, 100,0);
  return mapped;
}

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.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
  }
  Serial.println("MQTT Connected!");
}

void KeepConnection(){
  // Validate connected to MQTT Broker
  MQTT_connect();
  // Ping MQTT Broker every 2 minutes to keep connection alive
  if ((millis()-last)>120000) {
      Serial.printf("Pinging MQTT \n");
      if(! mqtt.ping()) {
        Serial.printf("Disconnecting \n");
        mqtt.disconnect();
      }
      last = millis();
  }
 }

void WaterPlant(int _thresholdVal){
  
  if (analogRead(MOISTPIN)>_thresholdVal){
      digitalWrite(RELAYPIN,HIGH);
      delay(500);
      digitalWrite(RELAYPIN,LOW);
  }

}

int AirQualityRead(){
  int quality = sensor.slope();

  Serial.print("Sensor value: ");
  Serial.println(sensor.getValue());
  
  if (quality == AirQualitySensor::FORCE_SIGNAL) {
    Serial.println("High pollution! Force signal active.");
    return 3;
  }
  else if (quality == AirQualitySensor::HIGH_POLLUTION) {
    Serial.println("High pollution!");
    return 2;
  }
  else if (quality == AirQualitySensor::LOW_POLLUTION) {
    Serial.println("Low pollution!");
    return 1; 
  }
  else if (quality == AirQualitySensor::FRESH_AIR) {
    Serial.println("Fresh air.");
    return 0;
  }
}

int DustData() {
   while ((millis()-Duststarttime) < Dustsampletime_ms) { //if the sample time == 30
    Dustduration = pulseIn(DUSTPIN, LOW);
    lowpulseoccupancy = lowpulseoccupancy+Dustduration;
   }
    
    ratio = lowpulseoccupancy/(Dustsampletime_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);
    if(mqtt.Update()) {
      DUST.publish (concentration);
    }
    lowpulseoccupancy = 0;
    Duststarttime = millis();
    return concentration;
    
}

void PublishEnviro (){
 if(mqtt.Update()) {
      TEMP.publish (tempF());
      Serial.printf("Publishing TempF %f F\n",tempF()); 
      PRESSURE.publish (Pressure());
      Serial.printf("Publishing Pressure %f hPa\n",Pressure()); 
      HUMIDITY.publish (Humidity());
      Serial.printf("Publishing Humidity %f Percent\n",Humidity()); 
      MOISTURE.publish (MoisturePercent());
      Serial.printf("Publishing Moisture Level: %i Percent %i \n",analogRead(MOISTPIN), MoisturePercent()); 
      AIRQUALITY.publish (AirQualityRead());          // Return 3-0 -- 3 is High Polution _ 0 is Clean Air
      
  }
}

void CheckButtonPress(){
  Adafruit_MQTT_Subscribe *subscription;
    while ((subscription = mqtt.readSubscription(500))) {
      if (subscription == &BUTTON) {
        buttonState = atoi((char *)BUTTON.lastread);
        if (buttonState){
            WaterPlant(0);
            PublishEnviro();
            TakePhoto();
            DustData();
        }
      }
    }
}

void TakePhoto(){
  digitalWrite(CameraPIN,LOW);
  delay(500);
  digitalWrite(CameraPIN,HIGH);
  PhotosTaken++;
  PHOTOS.publish (PhotosTaken);
}

void OLEDText() {
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,0);
  display.clearDisplay();
  display.printf("Temp\n Hum");
  display.display();
}

void TimeWriteToOLED() {
  
  DateTime = Time.timeStr();                // Current Date and Time from Particle Time class
  TimeOnly = DateTime.substring(11 ,16);   // Extract the Time from the DateTime String

  // Convert String to char arrays - this is needed for formatted print
  DateTime.toCharArray(currentDateTime ,25);
  TimeOnly.toCharArray(currentTime ,6);

  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(25,0);
  //display.clearDisplay();
  display.printf("%s",currentTime);
  //display.display();
}

void OLEDTemp() {
  
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  //display.clearDisplay();
  display.printf("Temp F: %.0f",tempF() );
   display.setCursor(0,40);
  display.printf("Humid: %.0f", Humidity());
  display.display();
}

void OLEDPresMoist() {
  
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  //display.clearDisplay();
  display.printf("Moist: %i",MoisturePercent());
   display.setCursor(0,40);
  display.printf("Pres: %.0f", Pressure());
  display.display();
}
void OLEDAir() {
  int _AirQuality;
  _AirQuality= AirQualityRead();

  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(10,20);

  if (_AirQuality==0){
    display.printf("Fresh Air");
  }
  if (_AirQuality==1){
    display.printf("Low \npollution!");
  }
  if ( _AirQuality==2){
    display.printf("High \npollution!");
  }
  if (_AirQuality==3){
    display.printf(" Extreme\npollution!");
  }
  
  display.display();
}

void OLEDPhotos() {
  
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  display.printf("Photo Shot");
  display.setTextSize(2);
  display.setCursor(60,40);
  display.printf("%i",PhotosTaken);
  display.display();
}

Credits

Wesley Eccles

Wesley Eccles

2 projects • 8 followers
Growing up I have always considered myself to be a maker. Over the years, I've built countless inventions from go-karts to a CNC Router.

Comments