/*
* 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;
}
Comments