DIY High Temp Pump Shutdown

setsailsoon

Gold Supporter
LifeTime Supporter
TFP Guide
Oct 25, 2015
5,812
Palm City/FL
Pool Size
28000
Surface
Plaster
Chlorine
Salt Water Generator
SWG Type
CircuPool RJ-60 Plus
Folks,

I had this pump problem on my main well pump that would cause it to run dry and overheat then start to soften the pvc components that then required a big re-pipe job. Many of these were eliminated with a pump relay that requires a manual reset but not all would trip on low pressure. So I decided to add a level of protection that would trigger on water temp at the pump. My DIY solution for this uses a cheap ESP 32 chip, expansion board, and a few relays. Plus some configurable code that I've included in the attachment. This problem can plague pool pumps as well so I've added it here. The parts are:
  • ESP 32 Dev Kit - less than $10
  • Intermediate relay $5
  • Pump relay $15
  • 24 v supply (you can tap into this from your irrigation or pool control panel)
  • 10K Thermister $2
  • 24 v (dc or ac depending on your closest source) pump start relay $15
  • Water proof junction box $15
  • D1 1N4007 diode - $1
Total cost is about $60 and much less if you have some spare pool automation parts laying around. Installation should be pretty easy if you know how to safely wire 240 vac. If not get an electrician to help. It's much cheaper than a visit to the emergency room and invaluable compared to losing your life! Both are real risks if you don't know what you're doing.

To program the chip download the free Arduino IDE latest version. Simplified wiring diagram is printed below. Code for programing an ESP32 (D or E) Dev module V4 is attached in a text file. You don't need Just copy and paste into the IDE software. Web page is also displayed below. Please also note, I haven't built and tested this yet. I'll update when I do. You don't need any special libraries loaded to the IDE software. I used ver 2.3.4 for this code and it compiled with no errors. Also please note the temp displayed is with no temp sensor so it's erroneous. If you have a different value resistor than the 10K I've added ability to change the code by inputting your value. This should make calibration a little easier. Install the 10K thermister in the pump discharge as close as possible in the PVC pipe to the pump. It needs to be inches away to be effective and prefferably installed in a Tee that is connected directly to the pump. Some pool pump connection hardware makes this difficult and in that case I'd put it in as close as possible using a standard 10K PVC mount.

I hope this is helpful.

Chris

Simplified Wiring Diagram

┌─────────────────────────────┐
│ ESP32 Board │
│ │
│ +-----------------------+ │
│ | | │
│ | ADC (e.g., GPIO36) |◄─┐│
│ | | ││
│ | GPIO (Control) ─────┼────┼─────────┐
│ | | ││ │
│ +-----------------------+ ││ │
└─────────────┬─────────────┘│ │
│ │ │
+------------┴─────────+ │ │
| 3.3V (Power Supply) | │ │
+------------┬─────────+ │ │
│ │ │
Thermistor Voltage Divider │ │
----------------------------- │ │
│ │ │
+---------------┴----------------─┴---------┴---------+
| |
| [Voltage Divider Circuit] |
| |
| +3.3V ──────> [Thermistor] ─────┐ |
| (e.g., 10K) │ |
| │ |
| Junction ──→ ADC Input |
| │ |
| [Fixed Resistor] |
| (default 10K, adjustable) |
| │ |
| GND |
+----------------------------------------------------+

(Sensor Section)

│ (Measured Temperature used in code)

┌─────────────────────────────┐
│ ESP32 GPIO (Control Signal) │
└────────────┬────────────────┘


┌─────────────────────────────────────┐
│ Low Voltage Relay Module │
│ (Intermediate Relay, driven by ESP32)│
└────────────┬──────────────────────────┘
│ (NO/COM Contacts)

├───────→ +24V DC (24V Supply for coil)


┌─────────────────────────────────────┐
│ 3‑hp Relay Coil (24V DC) │
└────────────┬──────────────────────────┘

[Flyback Diode]


┌─────────────────────────────────────┐
│ 3‑hp Relay Contacts │
│ (Isolated High‐Power Side) │
└────────────┬──────────────────────────┘

┌─────────────┴─────────────┐
│ │
240 VAC Line (L) 240 VAC Neutral (N)
│ │
│ ┌────┴────┐
│ │ 240 VAC│
│ │ Pump │
│ │ (Load) │
│ └─────────┘

└──── (Common AC wiring as required)




Web Page

1740409519833.png
 

Attachments

  • ESPPumpHTSD.txt
    8.2 KB · Views: 1
  • Wow
  • Like
Reactions: JamesW and Newdude
After submitting this I realized that especially for pool pumps I should implement a latching reset to the code so the pump won't keep restarting after it cools down. I'll update the code as soon as I get it done.

Chris
 
  • Like
Reactions: Newdude
OK, here's the revised code. I'll paste it here so you can copy directly into your Arduino IDE software window. Also, Ive added a warning to press the reset button on the web site. This is required for the first start and remains latched until an overheating event occurs and a reset will be required again to restart. To make this easy to install I've used a 3v reset signal that can be battery powered using a generic duel battery power supply. You'll also need to insert your SSID and PW for your local network. Lastly, this sets a static IP Address to 192.168.1.91. You can change that in the code easily if needed by editing the section below the WiFi credentials.

I hope this is useful.

Chris

Pump Overheat Code

#include <WiFi.h>
#include <WebServer.h>
#include <math.h>

// WiFi credentials – replace with your network SSID and password
const char* ssid = "************";
const char* password = "*********(*";

// Static IP configuration – update these values according to your network
IPAddress local_IP(192, 168, 1, 91);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8); // Optional
IPAddress secondaryDNS(8, 8, 4, 4); // Optional

// Define pins and sensor parameters
const int sensorPin = 36; // ADC input pin for the thermistor voltage divider
const int relayPin = 5; // Digital pin controlling the pump relay (via the 3‑hp relay)
const int manualResetPin = 14; // GPIO pin for manual reset input (3V momentary)
const float V_supply = 3.3; // Supply voltage for the ESP32

// Global variables for sensor calibration and circuit parameters
float thresholdTemperature = 25.0; // Default threshold (in Celsius)
float calibrationOffset = 0.0; // Calibration offset (in Celsius)
float voltageDividerResistor = 10000.0; // Voltage divider resistor value (ohms), default = 10K
// Flag for temperature unit: false = Celsius, true = Fahrenheit.
bool useFahrenheit = true; // Default display in °F

// Shutdown latch: if true, the pump remains off until a manual reset is performed.
// Initially, we require a manual reset.
bool shutdownLatch = true;

// Create a web server running on port 80
WebServer server(80);

// Function to read the thermistor and compute temperature in Celsius.
// Uses a simplified Beta parameter equation. Adjust Beta and R0 as needed.
float readTemperature() {
int adcValue = analogRead(sensorPin);
float voltage = (adcValue / 4095.0) * V_supply;
float R_thermistor = voltageDividerResistor * ((V_supply / voltage) - 1.0);

// Example parameters: nominal resistance 10K at 25°C (298.15K) and Beta = 3950.
float beta = 3950.0;
float T0 = 298.15; // 25°C in Kelvin
float R0 = 10000.0;

float tempK = 1.0 / ((1.0/T0) + (1.0/beta) * log(R_thermistor / R0));
float tempC = tempK - 273.15; // Convert Kelvin to Celsius

// Apply calibration offset (in Celsius)
return tempC + calibrationOffset;
}

// Web handler for the root URL: displays current readings and settings.
void handleRoot() {
float tempCelsius = readTemperature();

// Prepare display values (convert to Fahrenheit if needed)
float displayTemp = tempCelsius;
float displayThreshold = thresholdTemperature;
float displayCalOffset = calibrationOffset; // calibration delta
String unitSymbol = "&deg;C";
if (useFahrenheit) {
displayTemp = tempCelsius * 9.0 / 5.0 + 32;
displayThreshold = thresholdTemperature * 9.0 / 5.0 + 32;
displayCalOffset = calibrationOffset * 9.0 / 5.0;
unitSymbol = "&deg;F";
}

// Build HTML page with custom CSS styling.
String html = "<html><head><title>ESP32 Temperature Monitor</title>";
html += "<style>";
html += "body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; }";
html += "h1 { color: #333; font-size: 36px; margin-bottom: 20px; }";
html += "p { font-size: 28px; color: #555; margin: 10px 0; }";
html += "form { margin-top: 20px; }";
html += "input[type=number] { font-size: 24px; padding: 5px; }";
html += "input[type=submit] { font-size: 24px; padding: 5px 10px; }";
html += "a { font-size: 24px; color: #0066cc; text-decoration: none; }";
html += "a:hover { text-decoration: underline; }";
html += "</style></head><body>";
html += "<h1>ESP32 Temperature Monitor</h1>";
html += "<p>Current Temperature: " + String(displayTemp, 2) + " " + unitSymbol + "</p>";

// Display pump status message based on shutdownLatch and temperature condition.
if (shutdownLatch) {
if (tempCelsius >= thresholdTemperature) {
html += "<p style='color:red;'>High Temperature Shutdown Active. Pump is OFF until manual reset.</p>";
} else {
html += "<p style='color:red;'>Pump is OFF. Manual reset required to enable pump.</p>";
}
} else {
html += "<p>Pump Relay is " + String((digitalRead(relayPin) == HIGH ? "ON" : "OFF")) + "</p>";
}

html += "<p>Threshold Temperature: " + String(displayThreshold, 2) + " " + unitSymbol + "</p>";
html += "<p>Calibration Offset: " + String(displayCalOffset, 2) + " " + unitSymbol + "</p>";
html += "<p>Voltage Divider Resistor: " + String(voltageDividerResistor, 0) + " &Omega;</p>";

// Form to update threshold temperature
html += "<form action='/set' method='GET'>";
html += "Set Threshold Temperature (" + unitSymbol + "): <input type='number' step='0.1' name='threshold' value='" + String(displayThreshold, 2) + "'>";
html += "<input type='submit' value='Update'>";
html += "</form>";

// Form to update calibration offset
html += "<form action='/setcal' method='GET'>";
html += "Set Calibration Offset (" + unitSymbol + "): <input type='number' step='0.1' name='offset' value='" + String(displayCalOffset, 2) + "'>";
html += "<input type='submit' value='Update'>";
html += "</form>";

// Form to update voltage divider resistor value
html += "<form action='/setres' method='GET'>";
html += "Set Voltage Divider Resistor (ohm): <input type='number' step='1' name='resistor' value='" + String(voltageDividerResistor, 0) + "'>";
html += "<input type='submit' value='Update'>";
html += "</form>";

// Link to toggle temperature unit
html += "<p>Current Unit: " + unitSymbol + "</p>";
if (useFahrenheit) {
html += "<p><a href='/setunit?unit=C'>Switch to Celsius</a></p>";
} else {
html += "<p><a href='/setunit?unit=F'>Switch to Fahrenheit</a></p>";
}

html += "</body></html>";
server.send(200, "text/html", html);
}

// Handler to update the threshold temperature via a GET parameter.
void handleSetThreshold() {
if (server.hasArg("threshold")) {
float newThreshold = server.arg("threshold").toFloat();
if (useFahrenheit) { // Convert from Fahrenheit to Celsius
newThreshold = (newThreshold - 32.0) * 5.0 / 9.0;
}
thresholdTemperature = newThreshold;
}
server.sendHeader("Location", "/");
server.send(303, "text/plain", "Threshold updated");
}

// Handler to update the calibration offset via a GET parameter.
void handleSetCal() {
if (server.hasArg("offset")) {
float newOffset = server.arg("offset").toFloat();
if (useFahrenheit) {
newOffset = newOffset * 5.0 / 9.0;
}
calibrationOffset = newOffset;
}
server.sendHeader("Location", "/");
server.send(303, "text/plain", "Calibration offset updated");
}

// Handler to update the voltage divider resistor value via a GET parameter.
void handleSetResistor() {
if (server.hasArg("resistor")) {
voltageDividerResistor = server.arg("resistor").toFloat();
}
server.sendHeader("Location", "/");
server.send(303, "text/plain", "Voltage divider resistor updated");
}

// Handler to update the temperature display unit via a GET parameter.
void handleSetUnit() {
if (server.hasArg("unit")) {
String unit = server.arg("unit");
useFahrenheit = (unit == "F");
}
server.sendHeader("Location", "/");
server.send(303, "text/plain", "Unit updated");
}

// In loop, check if the manual reset button (3V momentary input) is pressed.
// If pressed, clear the shutdown latch.
void checkForManualReset() {
if (digitalRead(manualResetPin) == HIGH) {
Serial.println("Manual reset detected. Clearing shutdown latch.");
shutdownLatch = false;
delay(500); // Debounce delay
}
}

// Update the pump relay based on temperature and shutdown latch.
// If the temperature exceeds the threshold, latch shutdown and keep pump off.
// Otherwise, enable pump only if shutdown latch is cleared.
void updatePumpRelay() {
float tempCelsius = readTemperature();

if (shutdownLatch) {
digitalWrite(relayPin, LOW); // Pump remains off if latch is active
} else {
if (tempCelsius >= thresholdTemperature) {
Serial.println("High temperature detected! Activating shutdown latch.");
shutdownLatch = true;
digitalWrite(relayPin, LOW); // Shut pump off
} else {
digitalWrite(relayPin, HIGH); // Enable pump if temperature is safe
}
}
}

void setup() {
Serial.begin(115200);

pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW); // Ensure pump relay is off initially
pinMode(manualResetPin, INPUT_PULLDOWN);

// Connect to WiFi immediately
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());

// Set up URL endpoints for the web server.
server.on("/", HTTP_GET, handleRoot);
server.on("/set", HTTP_GET, handleSetThreshold);
server.on("/setcal", HTTP_GET, handleSetCal);
server.on("/setres", HTTP_GET, handleSetResistor);
server.on("/setunit", HTTP_GET, handleSetUnit);

server.begin();
Serial.println("Web server started");

// Note: The pump remains off (shutdownLatch is true)
// and the web page will instruct that a manual reset is needed.
}

void loop() {
server.handleClient();
updatePumpRelay();
checkForManualReset();
delay(100); // Small delay to ease CPU load
}

New Web Page
1740414702337.png
 
Last edited:
  • Wow
Reactions: JamesW
Thread Status
Hello , This thread has been inactive for over 60 days. New postings here are unlikely to be seen or responded to by other members. For better visibility, consider Starting A New Thread.