Raspberry Pi Pool Controller

It’s been a while since my last project. I have been working on something a bit more complicated this time. Here is the story…

Once upon a time there were three nerdy friends. Two of these nerdy friends owned homes with pools and over the years accumulated a plethora of information about how they work, how they are controlled, etc. Let’s call them Jorge and Scott for the purposes of this build. These two nerdy homeowners had hacked their own pool controller systems and connected at least some control to Home Assistant. So, when nerdy friend number three (let’s call him Jemal for the purposes of this build) bought a new house with a pool, naturally, the other two nerds were called in to assist. Jemal wanted to be like the cool kids and have his pool controlled by HA as well because the majority of his other electronics were already controlled by HA.

Here is the run-down of Jemal’s current pool. It’s very basic for a saltwater pool. It uses a mechanical timer to control power to the pump and salt system. That’s about it… The salt system is a knock off of a Goldline system that we couldn’t find much documentation on.

After reviewing the current pool and seeing how we could automate it as is (not much) a link for a new controller was sent to Jemal. Upon seeing the price for the new controller, Jemal proclaimed he was faint, and he had a significant amount of pain coming from his backside. Presumably because of the cost of the new system and the lack of lubrication.

several conversations later, Scott (me) interjected, “you know, we could just build a controller that does all of the same things the expensive one does for a fraction of the cost”. Have you ever said something and then instantly asked yourself, “Why did I just suggest that?”. Okay, I am not alone.

So, it had been decided. We were building a pool controller. My mind started swirling with the logic involved and all the questions about how this could be accomplished. Everyone who knows me also knows that I am not a programmer. I do not have the brain for it. I started by concentrating on the electronics involved. Gathering requirements didn’t take long. I compared what we would build to what Jorge and I already had. The majority of a pool controller is just relay circuits. A lot of relay circuits….

I started some research and came across this chip called the ULN2803 Darlington Transistors Eight NPN Array. I knew that a relay circuit required a diode to stop run away when latching the relay. It sounded promising as I didn’t really want to put diodes on the board if I didn’t have to. I do not know why. So, along with some connectors, this chip and some relays, I started designing the circuit board. I added the Raspberry Pi, the UNL2803 and I picked the TE Connectivity Potter & Brumfield Relays / T9AS1D12-12 because they have voltage rating of 277V on the contact side and powered by 12V DC for the coil. I finished all 8 of those circuits and then moved to the pool and air temperature. For that I wanted to allow the air temp sensor to live outside any enclosure since my air temperature often reads over 125 degrees F in the summer when the real air temp is ONLY around 115. I built the circuit using common DS18B20 probes with stainless steel ends. Jorge suggested I add a way to turn on each channel manually, so I added eight toggle switches for the ULN2803. I made sure that the contact side traces were as big as possible as the pump was rated for 10 – 12 amps. This is the only thing I am worried about. If it burns we will go with DIN Rail mounted industrial relays. More on that later when we see if we let the smoke out. I received the ULN2803 chips and I threw together a test using some other spare parts I had laying around. Sure enough, the ULN2803 didn’t fry and the relay didn’t catch on fire. I ordered up the parts and sent the boards to be made.

This is where we stop and talk about how hard it is, for me anyway, to order the correct parts. I think I spend a lot of time researching what’s available to me via several electronics distributors and have become a master of making the new device sketches and footprints in EASYEDA. The fact is that, just like Home Depot, you can never just go once for what you need for a project. Maybe I am not that good at sourcing. I digress.

Here is the circuit and the board(Rev3) see below…Sigh

If you would like the gerber file please email me. I also have a few spare boards for purchase to help a brother out with his project addiction. Shhh, don’t tell my wife.

We decided that we would use a Raspberry Pi as our micro-controller (GPIO) backbone. We chose this because it’s less(meaning none) C coding than using an ESP32 based controller and Python would be more flexible. A choice that later was confirmed to be the right decision.

While the parts were on their way, I started on the software. Since we were using the Raspberry Pi, I started with Python and used the Flask framework to build an API. My thought was that it would be easy to control using a REST API. I used the RPi.GPIO module to control the eight GPIO pins, one for each channel. I create a toggle function and a status function for each pin. I used the Flask jsonify module to return the state of the pin in JSON format. Then I thought, MQTT! It would be sweet to control it via MQTT as well as an option. So I added the Flask_mqtt module and coded the MQTT toggle and status functions. Using a separate Python script (because of the for loop) I created the MQTT temperature script. I used the 1Wire port on the Raspberry Pi to connect both sensors using a 4.7k resistor between 3.3V and the data bus. Then I used the W1ThremSensor module to read the temp every minute and publish to a MQTT topic using the paho mqtt module. After that I added some variables to make it easier to customize things later and added logging.

I found some potential issues with my original code, no surprise there. I also got a code review from a friend that gave me some suggestions for some improvement.

The first issue I had is that the Salt system should NEVER be turned on without the pump running. So I figured it would be simple to add that logic into the code. Well, for some maybe. I started with the MQTT portion as I would have to edit both the REST and MQTT logic. After an hour or so I came up with something that worked. I grabbed the pump state and read the MQTT data provided by the request to the topic and used that in the if else statement. If the pump state was off(0) and the MQTT data was “on” I set the SALT state to OFF(LOW), verified the SALT state and published that back to the Salt State topic. The I sent a warning to the log
Saying that you can’t turn on the salt without the pump running. Else if the pump state was on(1) and the MQTT data was “off” then I set the Salt to LOW (0), verify the SALT state and publish that state to the SALT State topic and finally log that the Salt was turned off. Else if the pump state is on(1) and the MQTT data is “on” then turn the SALT to HIGH(1), verify the SALT State and publish that state to the SALT State topic. WHEW! My brain hurts. Step 1, complete.

I also added code that turns on and off the Salt when the pump is triggered so that when the pump is running, the pool is getting chlorine. I did that for both the REST and MQTT functions.

Now onto the logic for the REST SALT function. Using the same If else logic I used in the MQTT function I came up with this. First I check the state of both the PUMP and the SALT and sent that those states to their respective variable.

salt_status=int((GPIO.input(SALT)))
pump_status=int((GPIO.input(PUMP)))

Then, I started the if else statement. If both of these were HIGH(1), then change the SALT state to LOW(0), verify the SALT state and publish the SALT state to the SALT status MQTT topic. I did this so both the REST and the MQTT would know what the states were so that there could be no confusion about the states if changes were made to any state. Then I logged the SALT state.

Then a few else if statements, if the pump status is on(1) and the salt status is off(0) then turn on the salt, verify the state, publish that state to the MQTT topic and log it. Else if the pump state was off(0) and the salt state if off(0) send a warning and do nothing. The salt can’t be turned on without the pump running and log it. Else if the pump is off(0) and the salt is on(1), turn the SALT off and send a warning that the salt was running without the pump and to check your system for damage. It should never happen, but I thought it was a good idea to put it in there as a fail safe. Then logged that as a warning.

The last piece was the two script issue. One for the pool control and the other for the temperature probes. After reviewing the code, a friend suggested that I use the module apscheduler to schedule a function to check the temperature probes. I installed that module, moved the code from the temperature script to a new function in the pool control program and setup the scheduler. Holy crap it worked!

With my new one script solution I moved on to setting the program to start as a service at system boot and we were off to the races.

Here is the code.

from flask import Flask, request, jsonify
import RPi.GPIO as GPIO
from flask_mqtt import Mqtt
import time
from w1thermsensor import W1ThermSensor
import logging
from apscheduler.schedulers.background import BackgroundScheduler

app = Flask(__name__)

logging.basicConfig(filename='/var/log/Frosty_app.log', level=logging.INFO,
                    format='%(asctime)s    %(levelname)s   %(message)s' ,
                    datefmt='%d-%m-%Y %H:%M:%S') 

temp_sensor1_ID = '3c01d607e0e2' #change this to be an ID from one of you 1-wire devices in /sys/bus/w1/devices 
temp_sensor2_ID = '3c01d6075170' #change this to be an ID from one of you 1-wire devices in /sys/bus/w1/devices
sensor1_offset = 3 # This is where you calibrate the temperature sensor1 to assure accuracy 
sensor2_offset = 3 # This is where you calibrate the temperature sensor2 to assure accuracy 
temp_sched = 3600 # time period between tempeature readings for scheduler


# Setup MQTT broker connection
app.config['MQTT_BROKER_URL'] = '192.168.10.115'
app.config['MQTT_BROKER_PORT'] = 1883
app.config['MQTT_USERNAME'] = ''
app.config['MQTT_PASSWORD'] = ''
app.config['MQTT_REFRESH_TIME'] = 1.0  # refresh time in seconds
mqtt = Mqtt(app)


# Get inital temperature reading before scheduler starts
temp_sensor1 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor1_ID)
temp_sensor2 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor2_ID)
sensor1_temp = int(temp_sensor1.get_temperature(W1ThermSensor.DEGREES_F) + sensor1_offset)
sensor2_temp = int(temp_sensor2.get_temperature(W1ThermSensor.DEGREES_F) + sensor2_offset)
mqtt.publish('temp/sensor1', sensor1_temp)
mqtt.publish('temp/sensor2', sensor2_temp)
log_sensor1 = str(sensor1_temp)
logging.info("sensor1: " + log_sensor1)
log_sensor2 = str(sensor2_temp)
logging.info("sensor2: " + log_sensor2)


# Define the GPIO ports that each channel will use.
PUMP = 26
SALT = 19
LIGHT = 13
AERATOR = 6
AUX1 = 5
AUX2 = 22
AUX3 = 27
AUX4 = 17

#Inital set all MQTT to off
mqtt.publish('state/pump', 0)
mqtt.publish('state/salt', 0)
mqtt.publish('state/light', 0)
mqtt.publish('state/aerator', 0)
mqtt.publish('state/aux1', 0)
mqtt.publish('state/aux2', 0)
mqtt.publish('state/aux3', 0)
mqtt.publish('state/aux4', 0)

# Setup each of the GPIO Ports
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(PUMP, GPIO.OUT, initial=0)
GPIO.setup(SALT, GPIO.OUT, initial=0)
GPIO.setup(LIGHT, GPIO.OUT, initial=0)
GPIO.setup(AERATOR, GPIO.OUT, initial=0)
GPIO.setup(AUX1, GPIO.OUT, initial=0)
GPIO.setup(AUX2, GPIO.OUT, initial=0)
GPIO.setup(AUX3, GPIO.OUT, initial=0)
GPIO.setup(AUX4, GPIO.OUT, initial=0)


# ########################    MQTT   ########################## 


def mqtt_sensor_publish():
     temp_sensor1 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor1_ID)
     temp_sensor2 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor2_ID)
     sensor1_temp = int(temp_sensor1.get_temperature(W1ThermSensor.DEGREES_F) + sensor1_offset)
     sensor2_temp = int(temp_sensor2.get_temperature(W1ThermSensor.DEGREES_F) + sensor2_offset)
     mqtt.publish('temp/sensor1', sensor1_temp)
     mqtt.publish('temp/sensor2', sensor2_temp)
     log_sensor1 = str(sensor1_temp)
     logging.info("sensor1: " + log_sensor1)
     log_sensor2 = str(sensor2_temp)
     logging.info("sensor2: " + log_sensor2)

scheduler = BackgroundScheduler()
scheduler.add_job(func=mqtt_sensor_publish, trigger="interval", seconds=temp_sched)
scheduler.start()

mqtt.subscribe('toggle/pump')
@mqtt.on_topic('toggle/pump')
def handle_mytopic(client, userdata, message):
   app = Flask(__name__)    
   with app.app_context():
    status=int(GPIO.input(PUMP))
    if status == 0 and message.payload.decode() == "on":
        GPIO.output(PUMP, GPIO.HIGH)
        GPIO.output(SALT, GPIO.HIGH)
        verify_pump_state = GPIO.input(SALT)
        verify_salt_state = GPIO.input(PUMP)
        mqtt.publish('state/pump', verify_pump_state)
        mqtt.publish('state/salt', verify_salt_state)
        logging.info("MQTT - Pump turned on")
        logging.info("MQTT - Salt turned on")
    elif status == 1 and message.payload.decode() == "off": 
        GPIO.output(PUMP, GPIO.LOW)
        GPIO.output(SALT, GPIO.LOW)
        verify_pump_state = GPIO.input(PUMP)
        verify_salt_state = GPIO.input(SALT)
        mqtt.publish('state/pump', verify_pump_state)
        mqtt.publish('state/salt', verify_salt_state)
        logging.info("MQTT - Pump turned off")
        logging.info("MQTT - Salt turned off")
    else:
        logging.info("MQTT - Pump - Nah Bruh")

mqtt.subscribe('toggle/salt')
@mqtt.on_topic('toggle/salt')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        salt_status=int(GPIO.input(SALT))
        pump_status=int(GPIO.input(PUMP))
        if pump_status == 0 and message.payload.decode() == "on":
            GPIO.output(SALT, GPIO.LOW)
            verify_state = GPIO.input(SALT)
            mqtt.publish('state/salt', verify_state)
            logging.info("MQTT - WARNING - You can't turn on the Salt system without the pump running") 
        elif pump_status == 1 and message.payload.decode() == "off":
            GPIO.output(SALT, GPIO.LOW)
            verify_state = GPIO.input(SALT)
            logging.info("MQTT - Salt turned off")
            mqtt.publish('state/salt', verify_state)
        elif pump_status == 1 and message.payload.decode() == "on":
            GPIO.output(SALT, GPIO.HIGH)
            verify_state = GPIO.input(SALT)
            logging.info("MQTT - Salt turned on")
            mqtt.publish('state/salt', verify_state)
        else:
            logging.info("MQTT - Salt - Nah Bruh")

mqtt.subscribe('toggle/light')
@mqtt.on_topic('toggle/light')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(LIGHT))
        if status == 1 and message.payload.decode() == "off":
            GPIO.output(LIGHT, GPIO.LOW)
            verify_state = GPIO.input(LIGHT)
            mqtt.publish('state/light', verify_state)
            logging.info("MQTT - Light turned off")
        elif status== 0 and message.payload.decode() == "on":
            GPIO.output(LIGHT, GPIO.HIGH)
            verify_state = GPIO.input(LIGHT)
            mqtt.publish('state/light', verify_state)
            logging.info("MQTT - Light turned on")
        else:
            logging.info("MQTT - Light - Nah Bruh")

# -- status/update is used when Home assistant restarts. 
# -- It re-publishes the current state of all channels and temperature probes

mqtt.subscribe('status/update')
@mqtt.on_topic('status/update')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        light_status=int(GPIO.input(LIGHT))
        pump_status=int(GPIO.input(PUMP))
        salt_status=int(GPIO.input(SALT))
        aerator_status=int(GPIO.input(AERATOR))
        aux1_status=int(GPIO.input(AUX1))
        aux2_status=int(GPIO.input(AUX2))
        aux3_status=int(GPIO.input(AUX3))
        aux4_status=int(GPIO.input(AUX4))

        mqtt.publish('state/light', light_status)
        mqtt.publish('state/pump', pump_status)
        mqtt.publish('state/salt', salt_status)
        mqtt.publish('state/aerator', aerator_status)
        mqtt.publish('state/aux1', aux1_status)
        mqtt.publish('state/aux2', aux2_status)
        mqtt.publish('state/aux3', aux3_status)
        mqtt.publish('state/aux4', aux4_status)

        temp_sensor1 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor1_ID)
        temp_sensor2 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor2_ID)
        sensor1_temp = int(temp_sensor1.get_temperature(W1ThermSensor.DEGREES_F) + sensor1_offset)
        sensor2_temp = int(temp_sensor2.get_temperature(W1ThermSensor.DEGREES_F) + sensor2_offset)
        mqtt.publish('temp/sensor1', sensor1_temp)
        mqtt.publish('temp/sensor2', sensor2_temp)

        logging.info("MQTT Updated state info")

mqtt.subscribe('toggle/aerator')
@mqtt.on_topic('toggle/aerator')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(AERATOR))
        if status == 1 and message.payload.decode() == "off":
            GPIO.output(AERATOR, GPIO.LOW)
            verify_state = GPIO.input(AERATOR)
            logging.info("MQTT - Aerator turned off")
            mqtt.publish('state/aerator', verify_state)
        elif status== 0 and message.payload.decode() == "on":
            GPIO.output(AERATOR, GPIO.HIGH)
            verify_state = GPIO.input(AERATOR)
            logging.info("MQTT - Aerator turned on")
            mqtt.publish('state/aerator', verify_state)
        else:
            logging.info("MQTT - Aerator - Nah Bruh")

mqtt.subscribe('toggle/aux1')
@mqtt.on_topic('toggle/aux1')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(AUX1))
        if status == 1 and message.payload.decode() == "off":
            GPIO.output(AUX1, GPIO.LOW)
            verify_state = GPIO.input(AUX1)
            logging.info("MQTT - Aux1 turned off")
            mqtt.publish('state/aux1', verify_state)
        elif status== 0 and message.payload.decode() == "on":
            GPIO.output(AUX1, GPIO.HIGH)
            verify_state = GPIO.input(AUX1)
            logging.info("MQTT - Aux1 turned on")
            mqtt.publish('state/aux1', verify_state)
        else:
            logging.info("MQTT - Aux1 - Nah Bruh")

mqtt.subscribe('toggle/aux2')
@mqtt.on_topic('toggle/aux2')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(AUX2))
        if status == 1 and message.payload.decode() == "off":
            GPIO.output(AUX2, GPIO.LOW)
            verify_state = GPIO.input(AUX2)
            logging.info("MQTT - Aux2 turned off")
            mqtt.publish('state/aux2', verify_state)
        elif status== 0 and message.payload.decode() == "on":
            GPIO.output(AUX2, GPIO.HIGH)
            verify_state = GPIO.input(AUX2)
            logging.info("MQTT - Aux2 turned on")
            mqtt.publish('state/aux2', verify_state)
        else:
            logging.info("MQTT - Aux2 - Nah Bruh")

mqtt.subscribe('toggle/aux3')
@mqtt.on_topic('toggle/aux3')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(AUX3))
        if status == 1 and message.payload.decode() == "off":
            GPIO.output(AUX3, GPIO.LOW)
            logging.info("MQTT - Aux3 turned off")
            verify_state = GPIO.input(AUX3)
            mqtt.publish('state/aux3', verify_state)
        elif status== 0 and message.payload.decode() == "on":
            GPIO.output(AUX3, GPIO.HIGH)
            verify_state = GPIO.input(AUX3)
            logging.info("MQTT - Aux3 turned on")
            mqtt.publish('state/aux3', verify_state)
        else:
            logging.info("MQTT - Aux - Nah Bruh")

mqtt.subscribe('toggle/aux4')
@mqtt.on_topic('toggle/aux4')
def handle_mytopic(client, userdata, message):
    with app.app_context():
        status=int(GPIO.input(AUX4))
        if status== 1 and message.payload.decode() == "off":
            GPIO.output(AUX4, GPIO.LOW)
            verify_state = GPIO.input(AUX4)
            logging.info("MQTT - Aux4 turned off")
            mqtt.publish('state/aux4', verify_state)
        elif status == 0 and message.payload.decode() == "on":
            GPIO.output(AUX4, GPIO.HIGH)
            verify_state = GPIO.input(AUX4)
            logging.info("MQTT - Aux4 turned on")
            mqtt.publish('state/aux4', verify_state)
        else:
            logging.info("MQTT - Aux4 - Nah Bruh")


#########################    REST TOGGLE   #############################


# PUMP Toggle 
@app.route('/toggle/pump/', methods=['GET'])
def pump_toggle():
    status = GPIO.input(PUMP) 
    if status == 1:
        GPIO.output(PUMP, GPIO.LOW)
        GPIO.output(SALT, GPIO.LOW)
        verify_pump_state = GPIO.input(PUMP)
        verify_salt_state = GPIO.input(SALT)
        mqtt.publish('state/pump', verify_pump_state)
        mqtt.publish('state/salt', verify_salt_state)
        logging.info("REST API - Pump turned off")
        return jsonify({"message": "Pump successfully turned off"})
        return jsonify({"message": "Salt successfully turned off"})
    elif status == 0:
        GPIO.output(PUMP, GPIO.HIGH)
        GPIO.output(SALT, GPIO.HIGH)
        verify_pump_state = GPIO.input(PUMP)
        verify_salt_state = GPIO.input(SALT)
        mqtt.publish('state/pump', verify_pump_state)
        mqtt.publish('state/salt', verify_salt_state)
        logging.info("REST API - Pump turned on")
        return jsonify({"message": "Pump successfully turned on"})
        return jsonify({"message": "Salt successfully turned on"})
    else:
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Pump Toggle")
        return jsonify({"message": "Boom! Something blew up, check your settings and try again"}, {"GPIO state": status})

# SALT Toggle
@app.route('/toggle/salt/', methods=['GET'])
def salt_toggle():
    salt_status=int((GPIO.input(SALT)))
    pump_status=int((GPIO.input(PUMP)))
    if pump_status == 1 and salt_status == 1:
        GPIO.output(SALT, GPIO.LOW)
        salt_state=GPIO.input(SALT)
        logging.info("REST API - Salt turned off")
        mqtt.publish('state/salt', salt_state)
        return jsonify(message="Salt system successfully turned off")
    elif pump_status == 1 and salt_status == 0:
        GPIO.output(SALT, GPIO.HIGH)
        salt_state=GPIO.input(SALT)
        logging.info("REST API - Salt turned on")
        mqtt.publish('state/salt', salt_state)
        return jsonify(message="Salt system successfully turned on")
    elif pump_status == 0 and salt_status == 0:
        GPIO.output(SALT, GPIO.LOW)
        salt_state=GPIO.input(SALT)
        logging.info("REST API - Warning - Can not turn on Salt system without the pump running. Salt system off")
        mqtt.publish('state/salt', salt_state)
        return jsonify(message="Warning - Can not turn on Salt system without the pump running. Salt system off")
    elif pump_status == 0 and salt_status == 1:
        GPIO.output(SALT, GPIO.LOW)
        salt_state=GPIO.input(SALT)
        logging.info("REST API - Warning - Your salt system was found to be on without the pump. Salt off, check you system for possible damage")
        mqtt.publish('state/salt', salt_state)
        return jsonify(message="Warning - Your salt system was found to be on without the pump. Salt off, check you system for possible damage")
    else:
        salt_state=GPIO.input(SALT)
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Salt Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Salt_state=salt_state)

# LIGHT Toggle
@app.route('/toggle/light/', methods=['GET'])
def light_toggle():
    status = GPIO.input(LIGHT)
    if status == 1:
        GPIO.output(LIGHT, GPIO.LOW)
        light_state=GPIO.input(LIGHT)
        logging.info("REST API - Light turned off")
        mqtt.publish('state/light', light_state)
        return jsonify(message="Light successfully turned off")
    elif status == 0:
        GPIO.output(LIGHT, GPIO.HIGH)
        light_state=GPIO.input(LIGHT)
        logging.info("REST API - Light turned on")
        mqtt.publish('state/light', light_state)
        return jsonify(message="Light system successfully turned on")
    else:
        light_state=GPIO.input(LIGHT)
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Light Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Light_status=light_state)

# AERATOR Toggle
@app.route('/toggle/aerator/', methods=['GET'])
def aerator_toggle():
    status = GPIO.input(AERATOR)
    if status == 1:
        GPIO.output(AERATOR, GPIO.LOW)
        aerator_state=GPIO.input(AERATOR)
        logging.info("REST API - Aerator turned off")
        mqtt.publish('state/aerator', aerator_state)
        return jsonify(message="AERATOR successfully turned off")
    elif status == 0:
        GPIO.output(AERATOR, GPIO.HIGH)
        aerator_state=GPIO.input(AERATOR)
        logging.info("REST API - Aerator turned on")
        mqtt.publish('state/aerator', aerator_state)
        return jsonify(message="AERATOR successfully turned on")
    else:
        aerator_state=GPIO.input(AERATOR)
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Aerator Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Aerator_state=aerator_state)

# AUX1 Toggle
@app.route('/toggle/aux1/', methods=['GET'])
def aux1_toggle():
    status = GPIO.input(AUX1)
    if status == 1:
        GPIO.output(AUX1, GPIO.LOW)
        aux1_state=GPIO.input(AUX1)
        logging.info("REST API - Aux1 turned off")
        mqtt.publish('state/aux1', aux1_state)
        return jsonify(message="AUX1 successfully turned off")
    elif status == 0:
        GPIO.output(AUX1, GPIO.HIGH)
        aux1_state=GPIO.input(AUX1)
        logging.info("REST API - Aux1 turned on")
        mqtt.publish('state/aux1', aux1_state)
        return jsonify(message="AUX1 successfully turned on")
    else:
        aux1_state=GPIO.input(AUX1)
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Aux1 Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Aux1_state=aux1_state)

# AUX2 Toggle
@app.route('/toggle/aux2/', methods=['GET'])
def aux2_toggle():
    status = GPIO.input(AUX2)
    if status == 1:
        GPIO.output(AUX2, GPIO.LOW)
        aux2_state=GPIO.input(AUX2)
        logging.info("REST API - Aux2 turned off")
        mqtt.publish('state/aux2', aux2_state)
        return jsonify(message="AUX2 successfully turned off")
    elif status == 0:
        GPIO.output(AUX2, GPIO.HIGH)
        aux2_state=GPIO.input(AUX2)
        logging.info("REST API - Aux2 turned on")
        mqtt.publish('state/aux2', aux2_state)
        return jsonify(message="AUX2 successfully turned on")
    else:
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Aux2 Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Aux2_state=aux2_state)

# AUX3 Toggle
@app.route('/toggle/aux3/', methods=['GET'])
def aux3_toggle():
    status = GPIO.input(AUX3)
    if status == 1:
        GPIO.output(AUX3, GPIO.LOW)
        aux3_state=GPIO.input(AUX3)
        logging.info("REST API - Aux3 turned off")
        mqtt.publish('state/aux3', aux3_state)
        return jsonify(message="AUX3 successfully turned off")
    elif status == 0:
        GPIO.output(AUX3, GPIO.HIGH)
        aux3_state=GPIO.input(AUX3)
        logging.info("REST API - Aux3 turned on")
        mqtt.publish('state/aux3', aux3_state)
        return jsonify(message="AUX3 successfully turned on")
    else:
        aux3_state=GPIO.input(AUX3)
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Aux3 Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Aux3_state=aux3_state)

# AUX4 Toggle
@app.route('/toggle/aux4/', methods=['GET'])
def aux4_toggle():
    status = GPIO.input(AUX4)
    if status == 0:
        GPIO.output(AUX4, GPIO.HIGH)
        aux4_state=GPIO.input(AUX4)
        logging.info("REST API - Aux4 turned on")
        mqtt.publish('state/aux4', aux4_state)
        return jsonify(message="AUX4 successfully turned on")
    elif status == 1:
        GPIO.output(AUX4, GPIO.LOW)
        aux4_state=GPIO.input(AUX4)
        logging.info("REST API - Aux4 turned off")
        mqtt.publish('state/aux4', aux4_state)
        return jsonify(message="AUX4 successfully turned off")
    else:
        logging.info("REST API - Boom! Something blew up, check your settings and try again - Aux4 Toggle")
        return jsonify(message="Boom! Something blew up, check your settings and try again", Aux4_state=aux4_state)


######################    REST STATUS    ########################


# # Temperature Sensor 1
@app.route('/temp/sensor1', methods=['GET'])
def temp_1():
    temp_sensor1 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor1_ID)
    sensor1_temp = int(temp_sensor1.get_temperature(W1ThermSensor.DEGREES_F) + sensor1_offset)
    return jsonify(temp_sensor1=int(sensor1_temp))

# Temperature Sensor 2
@app.route('/temp/sensor2', methods=['GET'])
def temp_2():
    temp_sensor2 = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, temp_sensor2_ID)
    sensor2_temp = int(temp_sensor2.get_temperature(W1ThermSensor.DEGREES_F) + sensor2_offset)
    return jsonify(temp_sensor2=int(sensor2_temp))

# PUMP State
@app.route('/state/pump/', methods=['GET'])
def pump_state():
    status = GPIO.input(PUMP)
    return jsonify(pump_state=status)

# SALT State
@app.route('/state/salt/', methods=['GET'])
def salt_state():
    status = GPIO.input(SALT)
    return jsonify(salt_state=status)

# LIGHT State
@app.route('/state/light/', methods=['GET'])
def light_state():
    status = GPIO.input(LIGHT)
    return jsonify(light_state=status)

# AERATOR State
@app.route('/state/aerator/', methods=['GET'])
def aerator_state():
    status = GPIO.input(AERATOR)
    return jsonify(aerator_state=status)

# AUX1 State
@app.route('/state/aux1/', methods=['GET'])
def aux1_state():
    status = GPIO.input(AUX1)
    return jsonify(aux1_state=status)

# AUX2 State
@app.route('/state/aux2/', methods=['GET'])
def aux2_state():
    status = GPIO.input(AUX2)
    return jsonify(aux2_state=status)

# AUX3 State
@app.route('/state/aux3/', methods=['GET'])
def aux3_state():
    status = GPIO.input(AUX3)
    return jsonify(aux3_state=status)

# AUX4 State
@app.route('/state/aux4/', methods=['GET'])
def aux4_state():
    status = GPIO.input(AUX4)
    return jsonify(aux4_state=status)

My next tale of woe came when the PCB boards were delivered.

My first board arrived with the incorrect footprint for the relays. Problem number 1. I
somehow ordered momentary switches and not toggle switches so that wasn’t going to work. I corrected the relay footprint and sent it off for manufacturing. A few days later they arrived. Dag-gum-it if I didn’t draw the footprint backwards. Ugh. The good news was that the relays fit now, but only on the back of the board. Not cool.. I also somehow removed the common ground between the power supplies, so changes made and off Rev 3 went to production. Finally, it was right.
While we were waiting for the boards to arrive, Jemal and I found a bit of a problem. If the pool controller was on and Home assistant restarted, it didn’t know what the state of the relays were and would report wrong. I added a piece of code that would send out the states to their respective MQTT topics. I used yet another MQTT call to trigger that and we placed that in an automation in Home assistant which said, on start, it would call the MQTT topic to update the states.

With Rev3 in hand, I was able to assemble and test the board. Worked like a champ. Then I realized, the pump motor is 220V and requires 2 connections for hot. Deep down I was afraid of the relays not being able to handle the 220V anyway so the solution was easy. I added a SPDT contactor that had 110V AC coil voltage and was rated for over 220V AC. I wired up relay to complete the 110V circuit, which would in-turn, activate the contactor and boom, the two loads were controlled by a single relay.

Here is what is looked like in it’s enclosure.

Above you can see what it looks like in Home assistant. I didn’t go into how to configure it in Home assistant as that seems like another completely different topic. If you would like me to write up something on HA integration, hit me up on email.

I will post some more pics and update when we install this bad boy.

2 thoughts on “Raspberry Pi Pool Controller

    1. Thank you! Install went great. It’s running my buddies pool now and he is able to control everything from Home Assistant. With the extra channels, he is able too control his backyard light as well.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.