Marcos Y
Published © GPL3+

Automatic Water Source Switching And Monitoring System

In this project, we monitor cistern water level and switch to water well when needed.

AdvancedFull instructions provided8 hours3,247

Things used in this project

Hardware components

SparkFun ESP8266 Thing - Dev Board
SparkFun ESP8266 Thing - Dev Board
I´ve used one SparkFun and one NodeMCU board.
×2
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
2 channel relay module (optocoupler)
×1
Solenoid valve 220v
×1
Transistor BC548B
Or equivalent
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Resistor 221 ohm
Resistor 221 ohm
×2
Jumper wires (generic)
Jumper wires (generic)
×1
5v power supply
×2

Software apps and online services

Cayenne
myDevices Cayenne
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Multimeter
Optional, but very useful

Story

Read more

Schematics

Cistern Level Monitoring connections

How to connect ESP8266 and HC-SR04 sensor

SparkFun Thing Dev connections

The main connections.

Low water sensor

How to make a sensor that will indicate when there is a low water condition in the upper tank.

Code

Cistern Level Monitoring

C/C++
This is the code to monitor water level in cistern.
#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

#define LED     2
#define TRIGGER 5
#define ECHO    4

// NodeMCU Pin D1 > TRIGGER | Pin D2 > ECHO


// WiFi network info.
char ssid[] = "";
char wifiPassword[] = "";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "";
char password[] = "";
char clientID[] = "";

unsigned long lastMillis = 0;
boolean led_st = false;

void setup() {
  Serial.begin(9600);
  Cayenne.begin(username, password, clientID, ssid, wifiPassword);
  pinMode(LED, OUTPUT);
  pinMode(TRIGGER, OUTPUT);
  pinMode(ECHO, INPUT);
  digitalWrite(LED, led_st);
}

void loop() {
  Cayenne.loop();
  
  //Publish data every 10 seconds (10000 milliseconds). Change this value to publish at a different interval.
  if (millis() - lastMillis > 3000) {
    long rssi = WiFi.RSSI();
    long duration, distance;
    digitalWrite(TRIGGER, LOW);  
    delayMicroseconds(2); 
  
    digitalWrite(TRIGGER, HIGH);
    delayMicroseconds(10); 
  
    digitalWrite(TRIGGER, LOW);
    duration = pulseIn(ECHO, HIGH);
    distance = (duration/2) / 29.1;  // distance to water level
  
    lastMillis = millis();
    
    Cayenne.virtualWrite(0, rssi);
    Cayenne.virtualWrite(1, distance, TYPE_PROXIMITY, UNIT_CENTIMETER);
    led_st = !led_st;
    digitalWrite(LED, led_st); // LED will blink each measurement
    delay(300);
  }
}

//Default function for processing actuator commands from the Cayenne Dashboard.
//You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
  CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}

Backup water source automation

C/C++
This is the main code. It implements backup water source system automation, integrated with Cayenne.
//#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

//Channels used
#define WiFi_RSSI 0
#define TANK_ST 4
#define PUMP_DISABLE 12
#define BACKUP_SRC 13
#define CIST_ALARM 14
#define WATER_AUTO 15
#define TIMER_VIN 16

// WiFi network info.
char ssid[] = "";
char wifiPassword[] = "";

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "";
char password[] = "";
char clientID[] = "";

// define & init variables
unsigned long lastMillis = 0;
unsigned long afterTime = 0;

int tankSt = 0;
int rssi = 0;
int cist_low = 0;
int water_automation = 1;
int bkp_timer = 5;

boolean backup_active = false;

// initalize
void setup() {
  Serial.begin(9600);
  pinMode(TANK_ST, INPUT);
  pinMode(PUMP_DISABLE, OUTPUT);
  pinMode(BACKUP_SRC, OUTPUT);
  digitalWrite(PUMP_DISABLE, HIGH);
  digitalWrite(BACKUP_SRC, HIGH);
    
  Cayenne.begin(username, password, clientID, ssid, wifiPassword);
  Cayenne.virtualWrite(PUMP_DISABLE, 0);
  Cayenne.virtualWrite(BACKUP_SRC, 0);
  Cayenne.virtualWrite(CIST_ALARM, 0);
  Cayenne.virtualWrite(WATER_AUTO, 1);
}

// process
void loop() {
  Cayenne.loop();

  // process each interval
  if (millis() - lastMillis > 3000) {
    lastMillis = millis();

    rssi = WiFi.RSSI();
    Cayenne.virtualWrite(WiFi_RSSI, rssi);  
    tankSt = digitalRead(TANK_ST);
    Cayenne.virtualWrite(TANK_ST, tankSt);
    Cayenne.virtualWrite(WATER_AUTO, water_automation);
    Cayenne.virtualWrite(TIMER_VIN, bkp_timer);
    
    if (cist_low == 1 && tankSt == 0 && water_automation == 1 && !backup_active) {
      Serial.println("** Backup Water source activated **");
      digitalWrite(BACKUP_SRC, LOW);
      digitalWrite(PUMP_DISABLE, LOW);
      Cayenne.virtualWrite(PUMP_DISABLE, 1);
      Cayenne.virtualWrite(BACKUP_SRC, 1);
      Cayenne.virtualWrite(CIST_ALARM, 1);
      
      backup_active = true;
      afterTime = lastMillis + (bkp_timer * 60 * 1000); 
      if (lastMillis > afterTime) { // if the sum caused overflows
        afterTime = bkp_timer * 60 * 1000;
        lastMillis = 0;
      }
    } else {
      if (backup_active && (lastMillis > afterTime) && (cist_low == 0 || tankSt == 1)) {
        Serial.println("** Backup Water source deactivated **");
        digitalWrite(BACKUP_SRC, HIGH);
        digitalWrite(PUMP_DISABLE, HIGH);
        Cayenne.virtualWrite(PUMP_DISABLE, 0);
        Cayenne.virtualWrite(BACKUP_SRC, 0);
        if (cist_low == 0) {
          Cayenne.virtualWrite(CIST_ALARM, 0);
        }
        backup_active = false;
       }
      }
  }
    delay(300);
}

// Control cistern pump
CAYENNE_IN(12)
{
  int pump_st = getValue.asInt();
  Serial.print("** pump_st=");
  Serial.println(pump_st);
  if (pump_st == 0) {
    digitalWrite(PUMP_DISABLE, HIGH);
  } else {
    digitalWrite(PUMP_DISABLE, LOW);
  }
}

// Control backup source water solenoid
CAYENNE_IN(13)
{
  int bksrc_st = getValue.asInt();
  Serial.print("** bksrc_st=");
  Serial.println(bksrc_st);
  if (bksrc_st == 0) {
    digitalWrite(BACKUP_SRC, HIGH);
  } else {
    digitalWrite(BACKUP_SRC, LOW);
  }
}

// Cistern alarm received 
CAYENNE_IN(14)
{
  cist_low = getValue.asInt();
  Serial.print("** cist_low=");
  Serial.println(cist_low);
  Cayenne.virtualWrite(CIST_ALARM, cist_low);
}

// Water automation button
CAYENNE_IN(15)
{
  water_automation = getValue.asInt();
  Serial.print("** water_automation button=");
  Serial.println(water_automation);
  Cayenne.virtualWrite(WATER_AUTO, water_automation);
  afterTime = 0; // reset timer if active
}

// slider widget: timer value in minutes
CAYENNE_IN(16)
{
  bkp_timer = getValue.asInt();
  Serial.print("** bkp_timer=");
  Serial.println(bkp_timer);
  Cayenne.virtualWrite(TIMER_VIN, bkp_timer);
}

//Default function for processing actuator commands from the Cayenne Dashboard.
//You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
 CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}

Credits

Marcos Y

Marcos Y

0 projects • 0 followers
Maker and IT professional.

Comments