Mario Cisneros
Published © MIT

Smart Houseplant Watering System

This project uses IOT to automate plant watering so the plant will be watered when needed and continue to stay vibrant and healthy.

IntermediateWork in progress7 hours63
Smart Houseplant Watering System

Things used in this project

Hardware components

Gravity: I2C BME280 Environmental Sensor
DFRobot Gravity: I2C BME280 Environmental Sensor
×1
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×1
Grove - Capacitive Soil Moisture Sensor
Seeed Studio Grove - Capacitive Soil Moisture Sensor
×1
water pump
×1
Kamoer NKP low flow peristaltic pump 12V dc
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
Photon 2
Particle Photon 2
×1
Repurposed toy fan
×1
Mister (generic)
×1

Software apps and online services

Zapier

Hand tools and fabrication machines

Drill / Driver, Cordless
Drill / Driver, Cordless
3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Capacitive moisture sensor

Soil moisture values

Air quality and dust sensor

Sense particulates in the air and air quality

Load cell

Not used yet but to be used to determine reservoir water level

Code

Smart Plant Watering System

C/C++
Automatic plant watering system
/* 
 * Project Smart Houseplant Watering System
 * Author: Mario Cisneros
 * Date: 3/22/2026
 */

// Include Particle Device OS APIs
#include "Particle.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Air_Quality_Sensor.h"
#include "Adafruit_BME280.h"
#include "IoTClassroom_CNM.h"

// Let Device OS manage the connection to the Particle Cloud
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

const int MOISTURE = A1;
AirQualitySensor sensor(A2);
int quality;
const int WPUMP = D16;
const int Mist = D15;
const int FAN = D2;
const int DUSTPIN = D19;
unsigned int lowPulseOccupy, duration, lastTime, updateTime;
float ratio, concentration;
bool status;

void getConc();

const int OLED_RESET=-1;
// Define BME280 object
Adafruit_BME280 bme;
float tempC, humidRH;
float celsToF(float tempC);
float feren;
int moistRead;
String dateTime, timeOnly;
Adafruit_SSD1306 display(OLED_RESET);

IoTTimer tempTimer, humTimer;

void setup() {
  Time.zone(-6);                              // MST = -7, MDT = -6
  Particle.syncTime();                        // Sync time with Particle Cloud
  pinMode(MOISTURE, INPUT);
  pinMode(DUSTPIN, INPUT);
  pinMode(WPUMP, OUTPUT);
  pinMode(FAN, OUTPUT);
  status = bme.begin(0x76); // Initialize BME280
  if (status == false){
    Serial.printf("BME280 at address0x%02X failed to start\n\n", 0x76);
  }
  tempTimer.startTimer(100); 
  Serial.begin(9600);
  waitFor(Serial.isConnected, 1000);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // Initialize w/the I2C address 0x3c for the OLED
  display.display();
  delay(2000);
  display.clearDisplay();
  display.setTextSize(2);
  display.setRotation(0);
  display.setTextColor(WHITE);
  display.printf("OLED is awake!!!\n");
  display.display();
  delay(500);
  while(!Serial);
  Serial.printf("\nWaiting on sensor to initiate...");
  delay(20000);
  if(sensor.init()){
    Serial.printf("Sensor ready.");
  }
  else{
    Serial.printf("Sensor ERROR");

  quality = 0;
  }
  updateTime = 30000;
  new Thread("concThread", getConc); // Initiate thread
    // Connect to Particle Cloud, not relying on System Mode to setup my connection!!!
  while(!Particle.connected()) {
    Particle.connect();
    delay(100);           //Small delay needed
    Serial.printf("x");
    }
  Serial.printf("\n\n");
  delay(3000);
}

void loop() {
    display.clearDisplay();
    display.setCursor(0,0);
    dateTime = Time.timeStr();            // Current Date & Time from Particle Time class
    timeOnly = dateTime.substring(11,19); // Extract Time from DateTime String
    
    if((millis()-lastTime) > updateTime){
    Serial.printf("\nTime: %0.2f, CONC: %0.2f",millis()/1000.0,concentration);
    lastTime = millis();
  }
    moistRead = analogRead(MOISTURE);
    display.printf("Moisture level %i Time = %s\n", moistRead, timeOnly.c_str());
    display.display();
    Serial.printf("Moisture level is %i, %s\n", moistRead, timeOnly.c_str());
    if(moistRead > 3135){
      digitalWrite(WPUMP, HIGH);
      delay(500);
      digitalWrite(WPUMP, LOW);
    }
    quality = sensor.slope();

  Serial.printf("Sensor value: %i - ", sensor.getValue());
  
  if(quality == AirQualitySensor::FORCE_SIGNAL){
    Serial.printf("High pollution! Force signal active.\n");
    digitalWrite(FAN, HIGH);
    if((millis()-lastTime) > 5000){
      digitalWrite(FAN, LOW);
    }
  }
  else if(quality == AirQualitySensor::HIGH_POLLUTION){
    Serial.printf("High Pollution!\n");
    digitalWrite(FAN, HIGH);
    if((millis()-lastTime) > 3000){
      digitalWrite(FAN, LOW);
    }
  }
  else if(quality == AirQualitySensor::LOW_POLLUTION){
    Serial.printf("Low Pollution!\n");
  }
  else if(quality == AirQualitySensor::FRESH_AIR){
    Serial.printf("Fresh air.\n");
  }
  delay(1000);
  if(tempTimer.isTimerReady()){
    tempC = bme.readTemperature();
    humidRH = bme.readHumidity();
    feren = celsToF(tempC);
    Serial.printf("The temperature in Ferenheit is %0.2fF degrees\n", feren);
    if(feren > 80){
      digitalWrite(FAN, HIGH);
      if((millis()-lastTime) > 5000){
        digitalWrite(FAN, LOW);
      }
    }
    humidRH = bme.readHumidity();
    Serial.printf("The humidity is %0.2f Relative Humidity\n\n", humidRH);
    display.setRotation(0);
    display.setCursor(0,0);
    display.printf("Temp is %0.2f\n", feren);
    display.printf("Humidity is %0.2f\n", humidRH);
    tempTimer.startTimer(500);
  }
}

void getConc(){
  const int sampleTime = 30000;
  unsigned int duration, startTime;
  startTime = 0;
  lowPulseOccupy = 0;
  while(true){
    duration = pulseIn(DUSTPIN, LOW);
    lowPulseOccupy = lowPulseOccupy+duration;
    if((millis() - startTime > sampleTime)){
      ratio = lowPulseOccupy/(sampleTime*10.0);
      concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62;
      startTime = millis();
      lowPulseOccupy = 0;
    }
  }
}

float celsToF(float tempC){
  return (180.0/100.0)*tempC + 32;
}

Credits

Mario Cisneros
2 projects • 0 followers

Comments