We've got a pool at the house, and with winter coming to the northern hemisphere, we had two options.

  1. Close the pool
  2. Keep the pool open.

Closing the pool is relatively easy, we call someone up, they put the cover on, drain the pool down a bit, empty the lines and shut off the power. In spring, we call them back up, and they reverse the process. Total cost: ~$500

Keeping the pool open means we run the pump, the booster pump, and the salt cell all winter long. Some napkin math says that's slightly less than $500, but also requires maintenance.

We recently got a quote to replace our tattered pool cover for a staggering $5000. Which is about as much as the liner itself. Hardly worth it.

So keep the pool open.

This comes with a drawback; you absolutely 100% cannot let the water in the pipes, pumps, filter, and salt cell freeze. Ever.

Our pump controller (from the late precambrian), has a lovely knob on it that SHOULD turn on the pumps at the desired air temperature. I don't trust it.

I had a floating pool temperature monitor that kicked out the temperature once a minute over 433MHz to a little display that sat in my kitchen window. It has since died and I did an autopsy; dead mainboard.

I'm going to use Platformio and Arduino (the software) to solve this software problem. Why? Because I'm lazy and I have two kids, so this project's gotta be done before naptime is over. You can either use Platformio (follow any of the excellent Platformio guides online), use the Arduino IDE, or you can figure out some magic emacs that does the job for you (I recommend C-x M-c M-butterfly).

Today, through the magic of amazon and the almighty scrap bin, we return it to life.

Tools:

  • Computer
  • Soldering iron
  • Steady hands
  • Tweezers
  • Hot air rework

Supplies:

  • Adafruit Feather ESP32
  • 56K Ohm Resistor
  • 47K Ohm Resistor
  • 10K Ohm Resistor
  • Capacitor (probably anything above 47uF)
  • Lithium Ion Battery (18650 salvaged from ???)
  • Some wire
  • Two solar cells
  • Too much silicone caulking
  • Some of the smallest heatshrink you've ever seen
  • The existing chassis

Why did I choose the ESP32 from adafruit? Simple. Its already got the lithium charging circuitry onboard and its small enough to fit in the chassis.

Step 0: Program the damn thing. I wanted this to be pretty simple, so it connects over wifi, and posts to my local MQTT broker, then goes to sleep. What happens after that is a problem for future $AUTHOR. If it can't connect to wifi (happens occasionally), it also goes to sleep. The badly written code is posted below.

Step 1: Modify the ESP32 board. I removed the power LED (because that sucked down 20mA constantly). I replaced the 100K resistor (R12 on the schematic) with a 56K and a 47K. The two in series are close enough to 100K to keep the Q3 mosfet in check, while also providing a handy voltage divider I can use to measure the solar panel voltage (which we wire into A2/IO34). I also removed the USB port (DON'T DO THIS UNTIL ITS BEEN PROGRAMMED) and slapped a 100uF (ish, measured on a multimeter and salvaged from a junk bin) between the power LED pad and the ground pad left by the absent USB port.

The Ugly

Step 2: Connect peripherals. I bought these nice mating connector pigtails for a project a good while ago, and as a good tinkerer does, I bought a 50 pack. Since they have 3 wires, I removed one from each connector so I couldn't connect the wrong thing to the wrong place. You could also key it based on connector gender (for a grand total of 6 un-mix-upable connections!). You do you.

Step 3: Remove paint and polish plastic for solar panels. The lid of this thing had a tiny solar panel in it, which was insufficient. I stripped all the paint off with 80 grit sandpaper, then smoothed it out increasing grit until I hit 5000. Then I finished it off with headlight polish (like you get at the car parts store). I flipped it over, tacked the solar panels (link) in place with some hot glue, wired them up in series, and then sealed it all up with silicone caulk.

#include "Arduino.h"
#include "WiFi.h"
#include "MQTT.h"

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

#define BATTERY_PIN 35
#define SOLAR_PIN 34
#define TEMPERATURE_PIN 39

#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ 
#define mS_TO_S_FACTOR 1000 /* Conversion factor for micro seconds to seconds */ 
#define TIME_TO_SLEEP 10 /* Time ESP32 will go to sleep (in seconds) */

//#define DEBUG
//Change the lines below to fit your WiFi Network
const char* ssid     = "SSID";
const char* password = "Password";

WiFiClient net;
MQTTClient client;

int publishDelay = 100;
int wifiTimeout = 2000;
int maxConnectAttempts = 200;

//This survives reboots, so you can tell how bad your wifi is. 
RTC_DATA_ATTR int wifiFailCount = 0;

int solarR1 = 47;
int solarR2 = 56;

void connect() {
  #ifdef DEBUG 
	Serial.println("checking wifi...");
  #endif
  int connectionAttempt = 0;
  while (WiFi.status() != WL_CONNECTED) {
    #ifdef DEBUG 
	Serial.print(".");
    #endif
    delay(wifiTimeout/maxConnectAttempts);
    connectionAttempt++;
    if (connectionAttempt >= maxConnectAttempts){
	  wifiFailCount++;
	  #ifdef DEBUG 
		Serial.println("FAILED");
	  #endif
    	  esp_deep_sleep_start();
    }
  }
  #ifdef DEBUG
  	Serial.println("\nconnecting...");
  #endif
  //Change the line below to fit your MQTT Broker
  while (!client.connect("topic", "user", "password")) {
    #ifdef DEBUG
	    Serial.print(".");
	    delay(300);
    #endif
  }
  #ifdef DEBUG
  	Serial.println("connected!");
  #endif

  client.subscribe("/topic");
}

void setup()
{
  #ifdef DEBUG
  	Serial.begin(115200);
  #endif

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  
  WiFi.begin(ssid, password);
  
//Change the line below to fit your MQTT Broker, use FQDN or IP
  client.begin("mqtt-broker.fqdn.org", net);
  #ifdef DEBUG
	    Serial.println("");
	    Serial.println("WiFi connected");
    	    Serial.println("IP address: ");
          Serial.println(WiFi.localIP());
  #endif
  connect();

  pinMode(LED_BUILTIN, OUTPUT);

}

void loop()
{
  delay(250);
  String json = "{\"BatteryVoltage\":";
  json += (analogRead(BATTERY_PIN)*2*3.3)/4096;
  json += ",\"SolarPanelVoltage\":";
  json += (analogRead(SOLAR_PIN)*(3.3*solarR1+3.3*solarR2))/(solarR2*4096);
  json += ",\"WaterTemperature\":";
  json += analogRead(TEMPERATURE_PIN);
  json += ",\"OnTime\":";
  json += micros()+(publishDelay*mS_TO_S_FACTOR);
  json += ",\"wifiFailCount\":";
  json += wifiFailCount;
  json += "}";
  #ifdef DEBUG
	  Serial.println(json);
  #endif
  client.publish("/poolTempMonitor", json);
  delay(publishDelay);
  wifiFailCount = 0;
  #ifdef DEBUG
	  Serial.println("Night Night");
	  Serial.println(micros());
  #endif
  esp_deep_sleep_start();
}

Running a wireless device sucks. It sucks on battery, it sucks on processing power, it sucks on sleep/wake cycling, and it REALLY sucks on debugging. I've not had any power issues, but my pool is in full sun almost all day every day.

Next post will be about calibrating the temperature and dealing with the MQTT data.