Janel Sanchez
Published

Smart Watering System

I wet my plants! Actually they wet themselves when they are thirsty, how convenient is that?!

BeginnerWork in progress940
Smart Watering System

Things used in this project

Hardware components

Argon
Particle Argon
×1
Automatic Irrigation DIY Kit (Capacitive Soil Moisture Sensor, Water Pump, Vinyl Tubing from kit were used)
×1
BME/BMP280
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
Resistor 2.21k ohm
Resistor 2.21k ohm
×2
General Purpose Transistor PNP
General Purpose Transistor PNP
×1
1 Channel DC 3V Relay High Level Driver Module Optocoupler Relay
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×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
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×1
Solid Single Core Tinned Copper Wire
×1

Software apps and online services

VS Code
Microsoft VS Code
Fusion
Autodesk Fusion

Hand tools and fabrication machines

Wire Stripper & Cutter, 26-16 AWG / 0.4-1.29mm Capacity Wires
Wire Stripper & Cutter, 26-16 AWG / 0.4-1.29mm Capacity Wires
Soldering Station, Hobbyist
Soldering Station, Hobbyist

Story

Read more

Custom parts and enclosures

Plant Stand Top STL

Plant Stand Bottom STL

Plant Pot STL

Schematics

Fritzing

How to wire your project together to use effectively with code provided.

Schematic

Code

Smart Watering System

C/C++
**Use with Particle Argon**
Automatically water your plant when the soil is too dry, publish data (moisture, temperature, humidity, pressure, air quality, dust values) to adafruit.io, subscribe data ("a button") from adafruit.io to run pump, display (date, time, moisture, temperature, humidity, pressure, air quality, dust values) to OLED.
/*
 * Project L14_04_PlantWater
 * Description: Automatically water your plant when the soil is too dry and publish/subscribe
 * Author: Janel Sanchez
 * Date: 12-Nov-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 "credentials.h"

#include "Adafruit_SSD1306.h"
#include "Adafruit_BME280.h"
#include "Air_Quality_Sensor.h"

#define OLED_RESET D4
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

Adafruit_BME280 bme;
bool status;

AirQualitySensor airsensor(A0);

/************ 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 ***************************************/ 
// Setup Feeds to publish or subscribe 
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname> 
Adafruit_MQTT_Subscribe waterButton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/buttonToWater"); 
Adafruit_MQTT_Publish soilMoisture = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/moistureLevels");
Adafruit_MQTT_Publish bmeTemperature = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/temperatureLevels");
Adafruit_MQTT_Publish bmeHumidity = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/humidityLevels");
Adafruit_MQTT_Publish bmePressure = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/pressureLevels");
Adafruit_MQTT_Publish air = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/airQualityLevels");
Adafruit_MQTT_Publish dust = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/dustLevels");

/************Declare Variables*************/
const int moistureSensor = A4;
const int moisturePump = A5;
const int dustSensor = A1;
int airQuality;
int waterButtonValue;

unsigned long last;
unsigned long lastTime;
unsigned long lastDisplayTime;
int moistureValue;

unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;

float tempC;
float tempF = 0;
float pressPA;
float humidRH;

const char degreeSymbol = 247;

void setup() {
  Serial.begin(9600);
  delay(100); //wait for Serial Monitor to startup

  pinMode(moistureSensor, INPUT);
  pinMode(moisturePump, OUTPUT);
  pinMode(dustSensor, INPUT);
  starttime = millis();//get the current time;


  if (airsensor.init()) {
    Serial.println("Air Sensor READY!");
  }
  else {
    Serial.println("Air Sensor ERROR!");
  }

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)

  status = bme.begin();
  if(!status) {
    Serial.println("Initialization Failed");
  }

  // Setup MQTT subscription for onoff feed.
  //mqtt.subscribe(&TempF);
  mqtt.subscribe(&waterButton);

  sync_my_time();
}

void loop() {
  MQTT_connect();
  pingMQTT();

  tempC = bme.readTemperature();
  pressPA = ((bme.readPressure()/100.0)*0.03);
  humidRH = bme.readHumidity();
  tempF = (tempC*1.8)+32;

  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here
 
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(10000))) {
    if (subscription == &waterButton) {
      waterButtonValue = atoi((char *)waterButton.lastread);
    }
    if (waterButtonValue == HIGH) {
      digitalWrite (moisturePump, HIGH);
      Serial.printf("Dashboard Water Button Pressed ON \n");
    }
    else {
      digitalWrite(moisturePump, LOW);
      Serial.printf("Dashboard Water Button Pressed OFF \n");
    }
  }

  moistureValue = analogRead(moistureSensor);
  readAirSensor();
  readDustSensor();

  if((millis()-lastTime > 30000)) {
    if(mqtt.Update()) {
      soilMoisture.publish(moistureValue);
      Serial.printf("Publishing Moisture Value %i \n",moistureValue);

      bmeTemperature.publish(tempF);
      Serial.printf("Publishing Temperatue %0.1f%cF \n",tempF);

      bmeHumidity.publish(humidRH);
      Serial.printf("Publishing Humidity %0.2f \n",humidRH);

      bmePressure.publish(pressPA);
      Serial.printf("Publishing Pressure %0.2f \n",pressPA);
      
      air.publish(airQuality);
      Serial.printf("Publishing Air Quality %i \n",airQuality);
      
      dust.publish(concentration);
      Serial.printf("Publishing Dust %0.2f \n",concentration);
      }

    if(moistureValue >= 2500) {
      activatePump();
    }

    lastTime = millis();
  }

  displayPrint ();
}

// 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.
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 sync_my_time () {
  Time.zone(-7);
  Particle.syncTime();
  waitUntil(Particle.syncTimeDone);
}

void displayPrint () {
  String DateTime, DateOnly, TimeOnly, YearOnly;
  char currentDate[11], currentTime[9], currentYear[5];

  DateTime = Time.timeStr();

  DateOnly = DateTime.substring(0,10);
  DateOnly.toCharArray(currentDate,11);

  YearOnly = DateTime.substring(20,24);
  YearOnly.toCharArray(currentYear,5);

  TimeOnly = DateTime.substring(11,19);
  TimeOnly.toCharArray(currentTime,9);

  if ((millis()-lastDisplayTime)>10000) {
    display.clearDisplay();
    display.display();

    display.setTextSize(1);
    display.setCursor(0,0);
    display.setTextColor(BLACK, WHITE);
    Serial.printf("Display Date: %s %s \n", currentDate,currentYear);
    display.printf("Date: %s %s", currentDate,currentYear);
    Serial.printf("Display Time: %s \n", currentTime);
    display.printf("Time: %s\n", currentTime);

    display.setTextColor(WHITE);
    Serial.printf("Display Moisture: %i \n", moistureValue);
    display.printf("Moisture: %i \n", moistureValue);

    display.setTextColor(WHITE);
    Serial.printf("Display Temperature: %0.1f°F \n", tempF);
    display.printf("Temperature: %0.1f%cF \n", tempF,degreeSymbol);

    display.setTextColor(WHITE);
    Serial.printf("Display Humidity: %0.2f \n", humidRH);
    display.printf("Humidity: %0.2f \n", humidRH);

    display.setTextColor(WHITE);
    Serial.printf("Display Pressure: %0.2finHg \n", pressPA);
    display.printf("Pressure: %0.2finHg \n", pressPA);

    display.setTextColor(WHITE);
    Serial.printf("Display Air Quality: %i \n", airQuality);
    display.printf("Air Quality: %i \n", airQuality);

    display.setTextColor(WHITE);
    Serial.printf("Display Dust Value: %0.2f \n", concentration);
    display.printf("Dust Value: %0.2f \n", concentration);
  
    display.display();
    lastDisplayTime = millis();
  }
}

void activatePump() {
  digitalWrite(moisturePump, HIGH);
  Serial.printf("Pump is ON \n");
  delay(500);
  digitalWrite(moisturePump, LOW);
  Serial.printf("Pump is OFF \n");
}

void readDustSensor() {
  duration = pulseIn(dustSensor, LOW);
  lowpulseoccupancy = lowpulseoccupancy+duration;

  if ((millis()-starttime) > sampletime_ms) {
    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();
  }
}

void readAirSensor() {
  int slope = airsensor.slope();
  airQuality = airsensor.getValue();
}

void pingMQTT() {
  if ((millis()-last)>120000) {
      Serial.printf("Pinging MQTT \n");
      if(! mqtt.ping()) {
        Serial.printf("Disconnecting \n");
        mqtt.disconnect();
      }
      last = millis();
  }
}

Credits

Janel Sanchez

Janel Sanchez

3 projects • 11 followers

Comments