ESP8266 Solenoid Water Valve With Leak Sensor

Introduction: ESP8266 Solenoid Water Valve With Leak Sensor

This is a small project for home assistant that can monitorize the home water supply if a leak is detected. Also, the valve can be turned on/off by sending a mqtt payload.

Supplies

Supplies used:

WEMOS D1 Mini

HK19F DPDT Relay

Bicolor led R/G (common anode)

2 1K resistors

3 NPN Transistors (BC549C)

1 PNP Transistor

Jump wires

Breadboard

Solenoid pulse valve

Step 1: Upload Sketch to Esp8266

For all my esp8266 cips i use the stored WIFI credentials for a faster connection to network. This is realy good when your esp is using the sleep mode so you need to be awake at lowest times as possible. For this project i did not remove the RTC saving credentials for network, not very usefull, but let them there.

How the code works:

Its use 2 mqtt topics.

topic_state : to send and receive the valve status.

topic_alert: on this topic the leak sensor send alert if there is any leakage

On topic_alert, the valve send every second the sensor condition state, dry or wet. The leak sensor can be created from two wires that are close enough.

The sketch:

#include <ESP8266WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>


#define WLAN_SSID "******"
#define WLAN_PASSWD "*********"
#define mqtt_client "ESP8266-valve-1"
#define mqtt_server "*******"
#define mqtt_user "******"
#define mqtt_password "********"

#define topic_state "esp/leak_bathroom/state"
#define topic_alert "esp/leak_bathroom/alert"



#define SensorPin 0 // D3
int ledPinOn = 14; //---D5
int ledPinOff = 12; //---D6
int pulsePin = 13; // d7

WiFiClient espClient;
PubSubClient client(espClient);


struct {
uint32_t crc32; // 4 bytes
uint8_t channel; // 1 byte, 5 in total
uint8_t bssid[6]; // 6 bytes, 11 in total
} rtcData;

long lastMsg = 0;
String state = "off";
String alert = "off";

uint32_t calculateCRC32(const uint8_t* data, size_t length)
{
uint32_t crc = 0xffffffff;
while (length--) {
uint8_t c = *data++;
for (uint32_t i = 0x80; i > 0; i >>= 1) {
bool bit = crc & 0x80000000;
if (c & i) {
bit = !bit;
}

crc <<= 1;
if (bit) {
crc ^= 0x04c11db7;
}
}
}
return crc;
}

void setup() {
Serial.begin(9600);
Serial.setTimeout(2000);
while (!Serial) {
}
Serial.println();
// pinMode(A0, INPUT);
pinMode(ledPinOn, OUTPUT);
pinMode(ledPinOff, OUTPUT);
pinMode(pulsePin, OUTPUT);
digitalWrite(ledPinOn, LOW);
digitalWrite(ledPinOff, HIGH);
digitalWrite(pulsePin, LOW);


// Try to read WiFi settings from RTC memory
bool rtcValid = false;
if (ESP.rtcUserMemoryRead(0, (uint32_t*)&rtcData, sizeof(rtcData))) {
// Calculate the CRC of what we just read from RTC memory, but skip the first 4 bytes as that's the checksum itself.
uint32_t crc = calculateCRC32(((uint8_t*)&rtcData) + 4, sizeof(rtcData) - 4);
if (crc == rtcData.crc32) {
rtcValid = true;
}
}
if (rtcValid) {
Serial.println("The RTC data is good, make a quick connection");
WiFi.begin(WLAN_SSID, WLAN_PASSWD, rtcData.channel, rtcData.bssid, true);
}
else {
Serial.println("The RTC data was not valid, so make a regular connection");
WiFi.begin(WLAN_SSID, WLAN_PASSWD);
}
Serial.print("Connecting to ");
Serial.println(WLAN_SSID);
int retries = 0;
int wifiStatus = WiFi.status();
while (wifiStatus != WL_CONNECTED) {
retries++;
if (retries == 100) {
Serial.println("Quick connect is not working, reset WiFi and try regular connection");
WiFi.disconnect();
delay(10);
WiFi.forceSleepBegin();
delay(10);
WiFi.forceSleepWake();
delay(10);
WiFi.begin(WLAN_SSID, WLAN_PASSWD);
}
if (retries == 600) {
Serial.println("Giving up after 30 seconds and going back to sleep");
// Giving up after 30 seconds and going back to sleep
WiFi.disconnect(true);
delay(1);
WiFi.mode(WIFI_OFF);
ESP.deepSleep(0);
return; // Not expecting this to be called, the previous call will never return.
}
delay(50);
wifiStatus = WiFi.status();
}
Serial.print("Connecting to: ");
Serial.println(WLAN_SSID);
Serial.print(" Connected ");
Serial.println(WiFi.localIP());
Serial.print("Connecting to MQTT...");
client.setServer(mqtt_server, 1883);
if (client.connect(mqtt_client, mqtt_user, mqtt_password)) {
Serial.println("connected");
delay(50);
client.publish(topic_state, "reset");
client.subscribe(topic_state);
client.setCallback(callback);
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println("will try again in 5 seconds");
delay(5000);
}

// Write current connection info back to RTC
rtcData.channel = WiFi.channel();
memcpy(rtcData.bssid, WiFi.BSSID(), 6);
rtcData.crc32 = calculateCRC32(((uint8_t*)&rtcData) + 4, sizeof(rtcData) - 4);
ESP.rtcUserMemoryWrite(0, (uint32_t*)&rtcData, sizeof(rtcData));
}


void loop() {
if (!client.connected()) {
reconnect();
}

client.loop();
long now = millis();
if (now - lastMsg > 3000) {
lastMsg = now;
if (digitalRead(SensorPin) == 1) {
Serial.println("dry");
client.publish(topic_alert, "off");
} else {
Serial.println("wet");
client.publish(topic_alert, "on");
}
}
}

void callback(char* topic, byte* payload, unsigned int length) {
//Serial.print("Message arrived in topic: ");
//Serial.println(topic_state);
String messageTemp;
for (int i = 0; i < length; i++) {
messageTemp = messageTemp + String((char)payload[i]);
}
//Serial.println(messageTemp); // Print out messages.
Serial.print("Valva de apa este:");
if (messageTemp == "on") {
Serial.println(" pornita");
digitalWrite(ledPinOn, HIGH);
digitalWrite(ledPinOff, LOW);
//////////////// send 2 second pulse to relay
digitalWrite(pulsePin, HIGH);
delay(1000);
digitalWrite(pulsePin, LOW);
}
else if (messageTemp == "off") {
Serial.println(" oprita");
digitalWrite(ledPinOn, LOW);
digitalWrite(ledPinOff, HIGH);
//////////////// send 2 second pulse to relay
digitalWrite(pulsePin, HIGH);
delay(1000);
digitalWrite(pulsePin, LOW);
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Restore MQTT connection...");
if (client.connect(mqtt_client, mqtt_user, mqtt_password)) {
Serial.println("reconnected");
client.subscribe(topic_state);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5e+6);
}
}
}

Step 2: Creating the Circuit

The circuit is not that complicated. Carefully follow the parts and wires positions. Or download the Fritzing file for a better view of connections.

For close or open solenoid valve, we must reverse the polarity, so, we have the DPDP relay. Also, the valve must receive only a pulse so we add a NPN transitor to act as a relay for cutting the voltage on DPDT relay.

The other NPN transistor are used for bicolor led, in order to show the valve status, On/Off. Also, we have a PNP transistor used for leak sensor.


I think in the future we also need to add a battery for this valve, in case of power outage and leak.

have fun creating

Be the First to Share

    Recommendations

    • Summer Fun: Student Design Challenge

      Summer Fun: Student Design Challenge
    • DIY Summer Camp Contest

      DIY Summer Camp Contest
    • Stone, Concrete, Cement Challenge

      Stone, Concrete, Cement Challenge

    3 Comments

    0
    ahbrown41
    ahbrown41

    Question 6 months ago on Step 2

    This is a great project, exactly what I was looking for. But I am not clear on some things. Looking simply at the water detector, I see the PNP Transistor (BTW could not find this exact one, but have a few generic PNP transistors), when I hook this up, it requires the wires to touch directly vs touch via water with for instance loose wires. I am not an electronics expert so I may be missing something. Are you using a special detector that requires this configuration?

    0
    mascka
    mascka

    Answer 6 months ago

    The water detection is made wehen the two wires are connected thru the water or directly.

    0
    ahbrown41
    ahbrown41

    Reply 6 months ago

    Thanks for your response! I cannot appear to get this working with the PNP transistors I have. Can you provide a link to the exact version you may gave used? Thanks again