Jay Elder
Published

Fishtones Fountain Smart Watering System

Smart Houseplant watering system designed after the Fishtones from Eureekas Castle

AdvancedWork in progress34
Fishtones Fountain Smart Watering System

Things used in this project

Hardware components

Grove - Air quality sensor v1.3
Seeed Studio Grove - Air quality sensor v1.3
×1
Photon 2
Particle Photon 2
×1
Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
DFRobot Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
×1
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Atmospheric Sensor Breakout - BME280
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
12V DC Water Pump
Seeed Studio 12V DC Water Pump
×1
General Purpose Transistor PNP
General Purpose Transistor PNP
×1
Resistor 220 ohm
Resistor 220 ohm
×1
Resistor 2.21k ohm
Resistor 2.21k ohm
×1
Grove - Optocoupler Relay (M281)
Seeed Studio Grove - Optocoupler Relay (M281)
×1

Software apps and online services

VS Code
Microsoft VS Code

Hand tools and fabrication machines

Laser cutter (generic)
Laser cutter (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Watering system fritzing

Code

Water system c++ code

C/C++
/* 
 * Project Smart Houseplant Watering System
 * Author: Jay Elder
 * Date: 03/19/2026
 */


#include "Particle.h"
#include "credentials.h"
#include "Adafruit_BME280.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_GFX.h"
#include "IotClassroom_CNM.h"
#include "Grove_Air_quality_Sensor.h"
#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"

 TCPClient TheClient; 
 Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY); 

Adafruit_MQTT_Subscribe pumpFeed = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/Pump"); 
Adafruit_MQTT_Publish sensorFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Temperature, Moisture, Air quality");
Adafruit_MQTT_Publish pubAQ = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Air Quality");  
Adafruit_MQTT_Publish pubTemp = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Temperature"); 
Adafruit_MQTT_Publish pubMoist = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Moisture"); 
Adafruit_MQTT_Publish pubHumid = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Humidity");

SYSTEM_MODE(AUTOMATIC);


SYSTEM_THREAD(ENABLED);

const int OLED_RESET=-1;
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_BME280 bme;
AirQualitySensor airSensor(A5);

const int soilSensor = A0;
const int pump = D9;
bool status;
bool bmeStatus = false;
float tempC = 0.0;
float tempF = 0.0;
float pressurePa = 0.0;
float pressureInHg = 0.0;
float humid = 0.0;
int soilRead = 0;
int airQuality = 0;
float pumpCommand = 0.0;
float tempToFah(float measurement);
float pressureToInHg(float measurementPa);
void MQTT_connect();
bool MQTT_ping();
unsigned long lastDisplayMs = 0;
unsigned long lastReadMs = 0;
unsigned long lastPublishMs = 0;
unsigned long pumpUntilMs = 0;


void setup() {
  
  Serial.begin(9600);
  waitFor(Serial.isConnected, 10000);
  pinMode(pump,OUTPUT);
  Serial.begin(9600);
    waitFor(Serial.isConnected, 10000);
    
    pinMode(pump, OUTPUT);
    digitalWrite(pump, LOW);

    pinMode(soilSensor, INPUT);

    mqtt.subscribe(&pumpFeed);
  
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.clearDisplay();
    display.setTextColor(WHITE);
    display.display();
    
    bmeStatus = bme.begin(0x76);
    if (bmeStatus) {
        Serial.println("BME280 ready.");
    } else {
        Serial.println("BME280 not found.");
    Serial.println("Initializing air quality sensor...");
    delay(2000);
    if (airSensor.init()) {
        Serial.println("Air quality sensor ready.");
    } else {
        Serial.println("Air quality sensor ERROR.");
    }
 }
}

void loop() {
    MQTT_connect();
    MQTT_ping();

    Adafruit_MQTT_Subscribe *subscription;
    while ((subscription = mqtt.readSubscription(10))) {
        if (subscription == &pumpFeed) {
            pumpCommand = atof((char *)pumpFeed.lastread);
            Serial.printf("Pump feed value: %.1f\n", pumpCommand);

            if (pumpCommand > 0.5) {
                pumpUntilMs = millis() + 2000;
            }
        }
    }

    if (millis() < pumpUntilMs) {
        digitalWrite(pump, HIGH);
    } else {
        digitalWrite(pump, LOW);
    }

    if (millis() - lastReadMs >= 1000) {
        lastReadMs = millis();

        if (bmeStatus) {
            tempC = bme.readTemperature();
            pressurePa = bme.readPressure();
            humid = bme.readHumidity();

            tempF = tempToFah(tempC);
            pressureInHg = pressureToInHg(pressurePa);
        }

        soilRead = analogRead(soilSensor);
        airQuality = airSensor.slope();

        Serial.printf("TempF: %.1f  Press: %.2f inHg  Humid: %.1f  Soil: %d  AQ raw: %d\n",
                      tempF, pressureInHg, humid, soilRead, airSensor.getValue());

        if (airQuality == AirQualitySensor::FORCE_SIGNAL) {
            Serial.println("Air quality: High pollution! Force signal active.");
        } else if (airQuality == AirQualitySensor::HIGH_POLLUTION) {
            Serial.println("Air quality: High pollution.");
        } else if (airQuality == AirQualitySensor::LOW_POLLUTION) {
            Serial.println("Air quality: Low pollution.");
        } else if (airQuality == AirQualitySensor::FRESH_AIR) {
            Serial.println("Air quality: Fresh air.");
        }
    }

    if (millis() - lastDisplayMs >= 1000) {
        lastDisplayMs = millis();

        display.clearDisplay();
        display.setTextSize(1);
        display.setCursor(0, 0);
        display.printf("Temp: %.1f F\n", tempF);
        display.printf("Pres: %.2f inHg\n", pressureInHg);
        display.printf("Hum : %.1f %%\n", humid);
        display.printf("Soil: %d\n", soilRead);
        display.printf("Air : %d\n", airQuality);
        display.display();
    }

    if (millis() - lastPublishMs >= 10000) {
        lastPublishMs = millis();

        if (mqtt.connected()) {
            pubTemp.publish(tempF);
            pubMoist.publish(soilRead);
            pubHumid.publish(humid);
            pubAQ.publish(airQuality);

            Serial.println("Published sensor data to Adafruit IO.");
        }
    }
}

float tempToFah(float measurement) {
    return (9.0 / 5.0) * measurement + 32.0;
}

float pressureToInHg(float measurementPa) {
    return measurementPa * 0.0002953;
}

void MQTT_connect() {
    if (mqtt.connected()) {
        return;
    }

    Serial.print("Connecting to MQTT... ");

    int8_t ret;
    while ((ret = mqtt.connect()) != 0) {
        Serial.printf("MQTT connect failed: %s\n", mqtt.connectErrorString(ret));
        Serial.println("Retrying MQTT connection in 5 seconds...");
        mqtt.disconnect();
        delay(5000);
    }

    Serial.println("MQTT connected.");
}

bool MQTT_ping() {
    static unsigned long lastPing = 0;

    if (millis() - lastPing > 120000) {
        lastPing = millis();
        if (!mqtt.ping()) {
            mqtt.disconnect();
            return false;
        }
    }
    return true;
}

Credits

Jay Elder
1 project • 1 follower

Comments