Eann LawingKathleen
Published © GPL3+

Automatic Plant Watering System Using Particle Photons

Have a plant that needs watering? Going on vacation soon and have nobody to water it? Then this is the project for you.

IntermediateFull instructions provided4 hours1,262
Automatic Plant Watering System Using Particle Photons

Things used in this project

Hardware components

Photon
Particle Photon
×2
Servo Module (Generic)
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×2
Male/Male Jumper Wires
×1
Moisture sensor (capacitive)
×1
Light
×1
Water container with spigot
×1

Software apps and online services

Particle Console
Google Sheets
Google Sheets

Hand tools and fabrication machines

3D Printer

Story

Read more

Custom parts and enclosures

Motor Arm

This motor arm was designed to be long enough to press down the tap.

Water container servo mount

Put the servo motor into the rectangular hole, and push the circular parts into place on the water container.

Schematics

Watering System Photon Circuit Diagrams

The top diagram is of the first particle photon which records moisture sensor values, the bottom diagram is of the second photon which powers the servo motor.

Code

Moisture Sensor Photon Code

C/C++
This is the code to the particle photon that reads moisture sensor values.
#define THRESHOLD           3000 
#define SENSOR_PIN          A0 
#define LIGHT_PIN           D7
#define PUBLISH_INTERVAL    1000 //1 second
#define GRAPH_INTERVAL      60000 //1 minute
#define BOARDLED            D1
int thresholdMet = false; 
unsigned long lastpublishtime = 0;
unsigned long lastgraphtime = 0;
int moisture49ers = 0;
void setup() { 
   Particle.variable("moisture49", moisture49ers);
   Particle.subscribe("MotorControl49ers", myHandler);
   pinMode(SENSOR_PIN,INPUT); 
   pinMode(LIGHT_PIN,OUTPUT); 
   pinMode(BOARDLED,OUTPUT);
} 
void loop() { 
    unsigned long now = millis();
   if (analogRead(SENSOR_PIN) > THRESHOLD) { 
       thresholdMet = true; 
       digitalWrite(LIGHT_PIN,HIGH); 
       if (now - lastpublishtime >= PUBLISH_INTERVAL) {
       Particle.publish("heyimdry49ers","dry");
       lastpublishtime = lastpublishtime + PUBLISH_INTERVAL;
       }
       if (now - lastgraphtime >= GRAPH_INTERVAL) {
       moisture49ers = analogRead(SENSOR_PIN);
       lastgraphtime = lastgraphtime + GRAPH_INTERVAL;
       }
   } 
   else { 
       thresholdMet = false;
       digitalWrite(LIGHT_PIN, LOW);
       if (now - lastpublishtime >= PUBLISH_INTERVAL) {
       Particle.publish("heyimdry49ers","wet"); 
       lastpublishtime = lastpublishtime + PUBLISH_INTERVAL;
       }
       if (now - lastgraphtime >= GRAPH_INTERVAL) {
       moisture49ers = analogRead(SENSOR_PIN);
       lastgraphtime = lastgraphtime + GRAPH_INTERVAL;
       }
   }
}  
void myHandler(const char *event, const char *data)
{
  if (strcmp(data,"off")==0) {
    digitalWrite(BOARDLED,LOW);
  }
  else if (strcmp(data,"on")==0) {
    digitalWrite(BOARDLED,HIGH);
  }
  else {
  }
}

Motor Control Photon Code

C/C++
This is the code to the particle photon that controls the servo motor.
//This code was created for a project for UNCC's Instrumentation and Measurement class.

//The code is one part of a two Particle system that waters a plant based on data from a moisture sensor.

//Define Servo Name and Servo Pin.
Servo Servo1;
#define motor A4

//Motor States
    //ON: The motor depresses the tap of a tank, allowing water to drip onto the plant.
    //OFF: The tap of the tank is release, so no water drips onto the plant.
    
int ONStatePos = 50; //Position of the ON state
int OFFStatePos = 0; //Position of the OFF state

//Intialize Activation Time, or the time (in seconds) since the program started, to "activate" the motor (turn the motor from the OFF state to the ON state)
unsigned long delayActivate = 5; //Amount of time to delay the system when the signal changes to "wet" to "dry", this is to ensure the values are not effected by noise.
unsigned long ActivationTime = delayActivate; //The initial value of the Activation Time is set to delayActivate, this is to ensure the particle recieves accurate signals.

//Constant Timer Control Variables
int OnDuration = 2; //The amount of time (in seconds) the motor remain in the ON state
int waitDuration = 30; //The amount of time (in seconds) the motor waits when in the OFF state to turn back to the ON state

void setup() {
    Servo1.attach(A4); //Attach the Servo to Analog Output Pin
    
    //Subscribe takes an event name from another particle (in this case:"heyimdry49ers") and searches for that event name in the cloud.
    //It then uses the myHandler function to obtain any data that is contained within the event.
    Particle.subscribe("heyimdry49ers", myHandler); //When recreating this, change the name in the quotations for an unique event name.
}

void loop() {
//N/A

}



void myHandler(const char *event, const char *data)
{
    //The data from "heyimdry49ers" is one of two states "dry" or "wet" (see other program)
    
    //strcmp compares the data to a string and returns 0 if they are the same.
    
    //The event "MotorControls49ers" contains the state of the motor, which is sent to the other Particle through the cloud and controls an LED on the other particle. 
    
    //Timer variables to be updated every time data is collected from the event.
    unsigned long CurrentTime = millis()/1000; //The current time (in seconds) since the program started
    unsigned long DeactivationTime = ActivationTime + OnDuration; //The time in seconds since the program started to "deactivate" the motor (turn the motor from the ON state to the OFF state)
    unsigned long NextActivationTime = DeactivationTime + waitDuration; //The value of the activation time
    unsigned long NextUpdateTime = NextActivationTime - 1; //The time since the program started to update ActivationTime from its old value to NextActivationTime
    
    //Control of the motor if the soil is dry
    if (strcmp(data,"dry")==0) {
            if (CurrentTime >= ActivationTime && CurrentTime < DeactivationTime){
                //If the current time is between the activation time and the deactivation time, turn the motor to the "ON" state and publish the current state
                Servo1.write(ONStatePos);
                Particle.publish("MotorControl49ers", "on"); 
            }
            else if (CurrentTime >= DeactivationTime && CurrentTime < NextUpdateTime){
                //If the current time is between the deactivation time and the next update time, turn the motor to the "OFF" state and publish the current state
                Servo1.write(OFFStatePos);
                Particle.publish("MotorControl49ers", "off");
            }
            else if(CurrentTime >= NextUpdateTime){
                //If the current time is greater than or equal to the next update time, publish the current state ("OFF"), and update the activation time to the next value.
                Particle.publish("MotorControl49ers", "off");
                ActivationTime = NextActivationTime;
            }
            //Else (if the current time is less than activation time), do nothing
            else{}
        }
    //If the soil is wet, turn the motor to the "OFF" state and publish the current state
    else if (strcmp(data,"wet")==0) {
            Servo1.write(OFFStatePos);
            Particle.publish("MotorControl49ers", "off");
            ActivationTime = CurrentTime + delayActivate;
      }
    //No other data states are published by the event, so there is nothing else to check. If something else does occur, do nothing.
    else {}
    
}

Credits

Eann Lawing

Eann Lawing

1 project • 1 follower
Kathleen

Kathleen

1 project • 0 followers

Comments