I have a sump pump in my basement that doesn’t run very often. I wanted to be able to get a notification when it did run and a notification in the event of a water leak, which might indicate that the pump isn’t functioning properly. Detecting water was relatively simple, but trying to figure out when the pump runs was a little more challenging. I looked into a vibration sensor since the pipes would vibrate very lightly when it came one, but the sensor just wasn’t sensitive enough. I also considered measuring the amount of sound, but I thought that might cause some false positives when the kids were in the basement playing.

So I decided to use a Split Core Transformer to measure current. This works well for me because the sump pump would be the only thing on that circuit, and any increase in current flow would mean that the pump was running.

Here is a list of things you will need for this project (It may look like a lot, but much of it is very inexpensive):

Software Requirements:

Home Assistant configured with an MQTT broker such as Mosquitto

Arduino IDE

The first thing is to wire up the electronics. When I got the SCT, I cut the 3.5mm connector off it, but later realized that I should have kept it on, and used a female 3.5mm connector. Polarity does matter for the SCT, so if it doesn’t give you a good reading one way, just swap the wires. Polarity doesn’t matter for the water sensor. In addition to the schematic, you can also refer to the gallery of my images for this project.

I used this site along with this site to figure out the schematic for the SCT. I wish I could explain better exactly what it is doing, but unfortunately I don’t understand it well enough to do that. Feel free to comment if you can offer up any knowledge on how this works.

You will want to put the SCT around a single wire on the circuit. I just opened up the outlet that the pump is plugged into, and clamped the SCT around the white wire, and closed it up. It fit well in the electrical box for me, but if you have an outlet further downstream, it may be a little cramped in there.

There are more pictures in the Gallery for this project showing the wiring and the SCT.

Once you have it wired up, you can load the code below onto it. This code includes the web based OTA firmware updater, Wifi Manager, and the RemoteDebug library from my previous post. It also makes use of the EmonLib library, which takes the data from the SCT and converts it into usable ‘energy’ data. In our case, we are just getting the current that was calculated by the SCT. One thing to keep in mind, is that the current measurement that it is outputting is not exact, and not really even close, but it jumps up enough for me to know that the sump pump is definitely on. With that said, I think it might be possible that the I could take the measurement, and multiply it by around 3 to get the exact measurement. I need to test it out using my Kill A Watt meter to determine that, and I will update this post if I ever get around to that.

#include <ESP8266WiFi.h> //ESP8266 Core WiFi Library (you most likely already have this in your sketch) #include <DNSServer.h> //Local DNS Server used for redirecting all requests to the configuration portal #include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic #include <PubSubClient.h> //MQTT #include <ESP8266mDNS.h> #include <ESP8266HTTPUpdateServer.h> #include <EmonLib.h> #include <ArduinoJson.h> #include <RemoteDebug.h> ////**********START CUSTOM PARAMS******************// //Define parameters for the http firmware update const char* host = "SumpESP"; const char* update_path = "/WebFirmwareUpgrade"; const char* update_username = "admin"; const char* update_password = "espP@$s"; //Define the pins #define CURRENT_PIN A0 #define WATER_PIN D1 //Define MQTT Params #define mqtt_server "IP_OF_MQTT_BROKER" #define sensorTopic "basement/sump" const char* mqtt_user = "mqtt_user"; const char* mqtt_pass = "mqtt_pass"; bool RemoteSerial = true; //True = Remote Serial, False = local serial //************END CUSTOM PARAMS********************// RemoteDebug RSerial; const char compile_date[] = __DATE__ " " __TIME__; bool lastWaterState; EnergyMonitor emon1; ESP8266WebServer httpServer(80); ESP8266HTTPUpdateServer httpUpdater; WiFiClient espClient; PubSubClient client(espClient); WiFiManager wifiManager; long lastMsg = 0; void setup() { //Set Relay(output) and Door(input) pins pinMode(CURRENT_PIN, INPUT); pinMode(WATER_PIN, INPUT); RSerial.begin(host); RSerial.setSerialEnabled(true); emon1.current(CURRENT_PIN, 30); emon1.calcIrms(15000); Serial.begin(115200); //Set the wifi config portal to only show for 3 minutes, then continue. wifiManager.setConfigPortalTimeout(180); wifiManager.autoConnect(host); client.setServer(mqtt_server, 1883); client.setCallback(callback); //callback is the function that gets called for a topic sub //setup http firmware update page. MDNS.begin(host); httpUpdater.setup(&httpServer, update_path, update_username, update_password); httpServer.begin(); MDNS.addService("http", "tcp", 80); RSerial.printf("HTTPUpdateServer ready! Open http://%s.local%s in your browser and login with username '%s' and your password

", host, update_path, update_username); } void loop() { //If MQTT client can't connect to broker, then reconnect if (!client.connected()) { reconnect(); } client.loop(); //the mqtt function that processes MQTT messages httpServer.handleClient(); //handles requests for the firmware update page if (RemoteSerial) RSerial.handle(); checkSensors(); delay(750); } void callback(char* topic, byte* payload, unsigned int length) { } void checkSensors(){ bool waterDetect = !digitalRead(WATER_PIN); float current = emon1.calcIrms(15000); RSerial.print("Current: "); RSerial.println(current); RSerial.print("Water: "); RSerial.println(waterDetect); if ( current > 1 || waterDetect || waterDetect != lastWaterState ){ lastWaterState = waterDetect; publishData(current, waterDetect); delay(750); } long now = millis(); if (now - lastMsg > 30000) { lastMsg = now; publishData(current, waterDetect); } } void publishData(float p_current, bool p_water) { StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); // INFO: the data must be converted into a string; a problem occurs when using floats... root["current"] = (String)p_current; root["water"] = (String)p_water; char data[200]; root.printTo(data, root.measureLength() + 1); client.publish(sensorTopic, data, true); } void reconnect() { //Reconnect to Wifi and to MQTT. If Wifi is already connected, then autoconnect doesn't do anything. wifiManager.autoConnect(host); RSerial.print("Attempting MQTT connection..."); if (client.connect(host, mqtt_user, mqtt_pass)) { RSerial.println("connected"); } else { RSerial.print("failed, rc="); RSerial.print(client.state()); RSerial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } }

Once the ESP/Wemos is loaded with the code, it will send an MQTT message anytime the state of water sensor changes or the current measurement is greater than 1 (It’s typically around 0, and shoots up to around 2.7 and stays there for a few minutes). It also sends an MQTT message regardless of any state changes.

Once you have done that, you can configure Home Assistant using the code below. Of course, you can feel free to use whatever you are used to in order to grab the data from your MQTT broker. You will need to edit the ‘service: notify.telegramBot’ lines to point to whichever notification service you are using.

sensor: - platform: mqtt state_topic: "basement/sump" unit_of_measurement: "A" name: "Sump Current" value_template: '{{ value_json.current }}' binary_sensor: - platform: threshold name: "Sump Status" sensor_class: power threshold: 2 type: upper entity_id: sensor.sump_current - platform: mqtt state_topic: "basement/sump" name: Sump Water Detect sensor_class: moisture payload_on: "1" payload_off: "0" value_template: '{{ value_json.water }}' automation: - alias: 'Sump Running' trigger: platform: state entity_id: binary_sensor.sump_status state: 'on' for: seconds: 1 action: - service: notify.telegramBot data: title: 'HASS - Sump Pump ran at "{{now().strftime("%H:%M:%S %m-%d")}}"' message: 'HASS - The Sump Pump just ran at "{{now().strftime("%H:%M:%S %Y-%m-%d")}}". The current is {{ states.sensor.sump_current.state }} Amps' - service: homeassistant.turn_on entity_id: script.sump_notify_rate_limit - alias: 'Sump Water Detected' trigger: platform: state entity_id: binary_sensor.sump_water_detect state: 'on' for: seconds: 15 action: - service: notify.telegramBot data: title: 'HASS - DANGER: Sump Pump Detected Water @ "{{now().strftime("%H:%M:%S %m-%d")}}"' message: 'HASS - Water was detected in the sump pump room at "{{now().strftime("%H:%M:%S %Y-%m-%d")}}". The current is {{ states.sensor.sump_water_detect.state }} Amps' - service: homeassistant.turn_on entity_id: script.sump_water_notify_rate_limit script: sump_notify_rate_limit: alias: Rate Limit Sump Pump Notifications sequence: - service: homeassistant.turn_off entity_id: automation.sump_running - delay: '00:01:00' - service: homeassistant.turn_on entity_id: automation.sump_running sump_water_notify_rate_limit: alias: Rate Limit Sump Pump Water Detect Notifications sequence: - service: homeassistant.turn_off entity_id: automation.sump_water_detected - delay: '00:15:00' - service: homeassistant.turn_on entity_id: automation.sump_water_detected

What this does is send you a notification any time water is detected or the sump pump runs. It limits the notifications for the pump to just one a minute since the pump won’t run more often than that. It also limits the water notifications to once every 15 minutes. It will keep notifying you until water is no longer detected.

This was my toughest project with the Wemos so far so I’m hoping it’s able to help others. If you have any questions or any advice to offer feel free to post a comment.