MEGR 3171 Lake Data Project

Team 19 (Daniel Shirilla, Dylan McClintock, River Issacsson-Savela)

projectImage

Things used in this project

 

Hardware components

HARDWARE LIST
1 DFRobot I2C 16x2 Arduino LCD Display Module
1 DFRobot Gravity: Digital 5A Relay Module
3 Particle Argon
1 Rotary Encoder with Push-Button
1 Adafruit Waterproof DS18B20 Digital temperature sensor
1 Hall Effect Position Sensor, 4.5 to 24 Vdc
1 Pushbutton Switch, Momentary

Story

 

This IOT Project uses three Particle Argons to monitor and control systems based on the current lake conditions.

projectImage

Using one of the two of the Argons on-site, three conditions of the lake are recorded: temperature, water level, and if the boat lift is raised or lowered. These conditions, namely water level, are important to observe because of systems reliant on these variables. In this case, there is a sprinkler system that uses the lake water, via a water pump. If this pump begins working with the water level too low, it will intake air, which can severely damage the pump and sprinkler system.

 

Using information gathered by the Argon with sensors, the second Argon on-site can control the on/off state of the sprinkler.

 

Argon 2 Irrigation Control Relay

 

projectImage

A third Argon is also used in this project to have the data reported anywhere without having to access the Particle console or site.

projectImage

Using this setup of a Sensor- dedicated Argon, a Manager Argon, and a Reporter Argon, any other system or variable could be added to the lake, and managing property much more convenient.

 

Argon 1 is attached to a

 

-Rotary encoder,

 

-Temperature sensor

 

-Hall effect sensor

 

As seen below.

projectImage

The rotary encoder corresponds to a water level reading of the lake. To accomplish this, two weights are attached to a cord wrapped around a spool which is being observed by the encoder. As the dock that the system is attached to rises or sinks with the level, the heavier of the two weights remains on the bottom of the lake, while the other weight will be moved due to the tension in the rope. This causes the rotary encoder to change reading and report a new level instantaneously.

projectImage

The temperature sensor is placed slightly below the surface of the water and reports the lake temperature every 5 minutes.

 

The hall effect sensor monitors the boat lift and its status: whether it is raised or is it is lowered.

projectImage

These three data points are displayed on the Argon 2 setup, which is attached to an LCD screen. Argon 1 publishes the current level reading, temperature reading, and hall effect status, to which Argon 2 subscribes the data and displays the data on the LCD screen.

projectImage

Argon 1 also sends the lake level data to Argon 3, which is attached to a relay circuit. This circuit either allows the irrigation system to run or will tell it to cancel watering. Argon 3 only uses the relay to complete the circuit and activate the pump if Argon 1 reports a satisfactory level reading, otherwise, the relay will remain off. Should Argon 3 have to restart or is reset, it will ensure the relay is currently off by prompting Argon 1 for a report of the water level.

projectImage

Another failsafe to prevent damages to the sprinkler system is the manual irrigation override that is attached to Argon 2. This is a simple button that toggles the status of the relay. This communicates to Argon 1 that a toggle is needed, which then sends the command to Argon 3. This allows for the sprinklers to be shut off even if the water level is satisfactory, or for the pump to be activated without the preferred water level. Having this switch also helps protect the sprinkler system should the sensors report wrong data and no one is on-site to repair or disable the system.

 

All data is then sent to ThingSpeak for graphing, which can be seen here: https://thingspeak.com/channels/1569523

Schematics

Sensor Argon Schematic

projectImage

Irrigation System Argon Schematic

projectImage

LCD Argon Schematic

projectImage

Code

 

Sensor Argon Code

C/C++

CODE
#include <JsonParserGeneratorRK.h>
#include <math.h>
#include <OneWire.h>
#include <DS18.h>
#include <iomanip>
#include <string>

// Variables for temperature reading
DS18 sensor(D2);
char temp[32];
float current_temp = 0;
float previous_temp = 0;
int tempcounter = 0;

//Variables for boat lift position
int BL = D4;
int lift_state = 2;//Set to arbitrary numbers to begin if statement in void boatlift
int prev_lift_state = 3;//Same as lift_state

// Variables for lake level reading
int CLK = D0;
int DT = D1;
int PrevCLKState;
int CLKState;
int counter = 0;
float Lake_level;
float Wheel_Dia = 0.5;
float lake_level_offset=97.4;
int irrigation_cutoff = 96;
int k;
float rounded_lake;
String string_lake = "";
String rounded_string_lake = "";
        
void setup()
{
    pinMode(BL, INPUT);
    pinMode(CLK, INPUT);
    pinMode(DT, INPUT);
    PrevCLKState = digitalRead(CLK);
    
    //This If else ststement checks the initial lake level offset and publishes if the lake lavel is OK for irrigation or below the irrigation cutoff
    Lake_level = lake_level_offset;  //Converts rotary encoder position into lake level (ft)
    string_lake = String(Lake_level);
   
    if(lake_level_offset >= irrigation_cutoff)
    {
        Particle.publish("Lake Level OK", PRIVATE);
        k = 0;
    }
     else
    {
        Particle.publish("Lake Level Too Low", PRIVATE);
        k = 1;
    }

    //Initializing some particle variables (not really used)
    Particle.variable("Temperature", temp);
    Particle.variable("LakeLevel", string_lake);
    Particle.subscribe("Is lake level OK?", lake_ok, "e00fce68d8d3966b6d563d65");
}



void loop()
{
    if(tempcounter != 3000000) //300000 Fixes problem with encoder loop not being fast enought to determine directionn of rotation
    {
         encoder(); //Calls encoder sub program to run
    }
    else  //Once 300000 is reahced the temperature will be recorded and irrigstion switch code will run as well
    {
        boatlift();
        tempSensor(); //Calls Temperature sensor program to run
        irrigation(); //Calls Irrigation decision program to run
        Particle.publish("Lake Level", string_lake, PRIVATE); //Publishes new lake level as an event
        tempcounter = 0; //resets cycle counter to 0
    }
    tempcounter ++;
}



//Subprogram to read and publish an event if the boat lift has changed positions
void boatlift()
{
    lift_state = digitalRead(BL);
    if(lift_state == LOW && lift_state != prev_lift_state)
    {
        Particle.publish("Boat Lift Raised", PRIVATE);
        prev_lift_state = lift_state;
    }
    else if(lift_state == HIGH && lift_state != prev_lift_state)
    {
        Particle.publish("Boat Lift Lowered", PRIVATE);
        prev_lift_state = lift_state;
    }
}



//Subprogram for lake level (encoder)
void encoder()
{
    CLKState = digitalRead(CLK); // Reads current state of CLK pin

    if(CLKState != PrevCLKState)//Looks for a change of state
    {  
        // CW rotation
        if(digitalRead(DT) != CLKState)//If DT pin lags CLK pin (DT!=CLK) it then must be a CW rotation
        { 
            counter --;
        }
        //CCW Rotation
        else //If DT pin leads CLK pin (DT=CLK) it then must be a CCW rotation
        {
            counter ++;  
    }

    PrevCLKState = CLKState;
    Lake_level = (counter * 3.14159 * Wheel_Dia / (20 * 12)) + lake_level_offset;  //Converts rotary encoder position into lake level (ft)
    //string_lake = String(round(Lake_level*10)/10.0); //Converts the lake level floating point value into a string so that it can be used in a particle variable
   
   // char buffer[4];
    string_lake = String(Lake_level);
    rounded_lake = trunc(Lake_level*100)*.01;
    rounded_string_lake = String(rounded_lake);
    //rounded_string_lake = sprintf("%1.2f", Lake_level);
    //Particle.publish("Rounded Lake Level", rounded_string_lake, PRIVATE); //Publishes new lake level as an event
    //Particle.publish("Lake Level", string_lake, PRIVATE);
    }
}



void irrigation(){
    
       //Sends a particle publish if the water is too low or OK
       
    if(Lake_level < irrigation_cutoff && k == 0){
        Particle.publish("Lake Level Too Low",  PRIVATE);
        k = 1;
    }
    if(Lake_level >= irrigation_cutoff && k == 1){
        Particle.publish("Lake Level OK", PRIVATE);
        k = 0;
    }
}



//Subprogram for temperature reading
void tempSensor()
{
    
    if (sensor.read())
    {
        current_temp = sensor.fahrenheit();
    
        if (abs(current_temp - previous_temp) >=0.05)
        {
            //snprintf(temp, sizeof(temp), "Temperature %0.2f", current_temp);
            Particle.publish("Temperature", String(sensor.fahrenheit()),PRIVATE);
            previous_temp = current_temp;
        }
    }
upload_data(Lake_level, current_temp, lift_state);
}



void lake_ok(const char *event, const char *data)//IDk if it is the const char event line
{
       //Sends a particle publish if the water is too low or OK
    if(Lake_level < irrigation_cutoff && k == 1)
    {
        Particle.publish("Lake Level Too Low", PRIVATE);
    }
    else if(Lake_level >= irrigation_cutoff && k == 0)
    {
        Particle.publish("Lake Level OK", PRIVATE);
    }
}



void upload_data(float Lake_level, float current_temp, int lift_state)
{
    JsonWriterStatic<256> jw;
    {
        JsonWriterAutoObject obj(&jw);
        
        jw.insertKeyValue("Lift", lift_state);
        jw.insertKeyValue("Temp", current_temp);
        jw.insertKeyValue("Lake", Lake_level);
        
    }
    
    Particle.publish("Sensor Values", jw.getBuffer(), PRIVATE);
}

LCD Argon Code

C/C++

CODE
#include <LiquidCrystal_I2C_Spark.h>

LiquidCrystal_I2C *lcd;

int button = D2;
int button_state;
int  k = 0;

int i = 0;
const char *llevel;
const char *temp;


void setup() 
{

    pinMode(D2, INPUT);

    Particle.subscribe("Lake Level", level , "e00fce681dc977cc0d02e49d");
    Particle.subscribe("Temperature", temperature , "e00fce681dc977cc0d02e49d");

    lcd = new LiquidCrystal_I2C(0x27, 16, 2);

    lcd->init();
    lcd->backlight();

    lcd->setCursor(0 ,0 );
    lcd->print("Level");
    lcd->setCursor(12 ,0 );
    lcd->print("FT");
    
    lcd->setCursor(0 ,1 );
    lcd->print("Temp");
    lcd->setCursor(12 ,1 );
    lcd->print("DegF");
}

void loop() 
{

    button_state = digitalRead(D2);

    if(button_state == LOW && k == 0)
        {
            Particle.publish("Manual Irrigation Cancelation", PRIVATE);
            k = 1;
            lcd->setCursor(15 ,0 );
            lcd->print("I");
            delay(400);
        }

    else if(button_state == LOW && k == 1)
        {
            Particle.publish("Manual Irrigation Resume", PRIVATE);
            k = 0;
            lcd->setCursor(15 ,0 );
            lcd->print("A");
            delay(400);
        }
    
}


void level(const char *event, const char *data)
{

    lcd->setCursor(0 ,0 );
   // lcd->print("Lake");
    lcd->setCursor(6 ,0);
    for (int i = 0; i < 5; i++) {
        lcd->print(data[i]);
    }
    
}

void temperature(const char *event2, const char *data)
{

    lcd->setCursor(6 ,1 );
    for (int i = 0; i < 5; i++) {
        lcd->print(data[i]);
    }
}

Sprinkler Relay Argon Code

C/C++

CODE
void setup() {
    Particle.subscribe("Lake Level Too Low", irrigationoff , "e00fce681dc977cc0d02e49d");
    Particle.subscribe("Lake Level OK", irrigationon , "e00fce681dc977cc0d02e49d");
    pinMode(D0, OUTPUT);
    Particle.publish("Is lake level OK?");
}

void irrigationoff(const char *event, const char *data)
{
    digitalWrite(D0,HIGH);
}


void irrigationon(const char *event, const char *data)
{
    digitalWrite(D0,LOW);
}


void loop() {

}

The article was first published in hackster, November 23, 2021

cr: https://www.hackster.io/dshirill/megr-3171-lake-data-project-a09395

author: Daniel Shirilla

License
All Rights
Reserved
licensBg
0