Peer Review/Questions of RV Automation Script

Thank you for taking the time to view my post. Below is my first Python script I’ve wrote over the last day so pardon any oversights.

I posted this on stackoverflow a few days ago, but have gotten very little input so far. This is basically a copy and paste from the post. With @wendell posting several videos mentioning automation I figured I’d give this forum a try.
https:// stackoverflow .com/questions/62900058/peer-review-questions-of-rv-automation-script

Goal: To run the script on a Raspberry Pi with four relay hats and each hat having four relays to empty and rinse out the tanks on my RV automatically based on parameters entered on a simple webpage. Using a script like this will allow me to save water… I have forgotten about the water being on and the gate open before causing unnecessary water usage. Getting a general review of the code for optimization and/recommendations.

Background: I travel in my RV for work and have recently started making some quality of life changes and fun tweaks to it. I replaced the manual cable gates on my RV with electronic gates after one of the cables started jamming up and, disgustingly, I discovered my black tank was leaking at the tank exit connection. Another plus is that I can stay inside in the winter when needing to rinse and empty the tanks. I currently have a remote control relay board I use to open and close the three tanks on my RV and a switch to kill the ground for the electronic gates. The reason for the switch is because I installed the manual switches that came with the gates as a “just in case”, but the fuse blows when I use the switches with the remote relay board having power. I cannot figure out what I wired wrong. I am going to install aftermarket tank rinsers in all of the tanks with a manifold that has a NC water valve for each rinser and the factory installed black tank rinser for a total of four rinsing options (I probably won’t use the factory one much once the aftermarket one is install, but want the option). The white water will have a ball valve that is controlled by an electric motor.

Current roadblocks:

  1. I cannot figure out a good search term to find how to interface the script with a webpage. Almost all search results show how to write a webpage in python, but not how to pipe things into a python script from a webpage. Links to tutorials for solutions would be appreciated.
  2. How to write a full error catchall so the power is cut in case something goes wrong with the script or Pi. All of the water valves that fill tanks are going to be NC aka the water valves that could cause flooding.
  3. Two timers on the webpage. One for the total remaining time of the script and one for the current task.
  4. A graceful way to be able to activate the White Water valve during the flush and fill process for a set amount of time so I could wash my hands or other things that briefly require water. I prefer to keep the white water closed any time I am flushing the tanks, even though I use two hoses out of sanitary caution.
  5. If possible, a way to name each relay address as a parameter. This will cut back on a lot of the comments and make the code easier to read. Something like PosPow = (board0, relay1)?
  6. Same as 4, but for i2cbus.write_byte_data, an easy parameter to make the code cleaner.
  7. This is probably a bit out of scope for the post, but I might as well mention it. The switches that came with the gates have a blinking light to indicate they are open, I’d like to use that light pulse to put a status of open or closed on the webpage. If a pulse isn’t received within one second the gate is closed. This could also be used to replace the manual Gate Toggle Time parameter. The white water ball valve also has a status light.
  8. I am using Thonny on the Raspberry Pi over VNC to write this code, is there a more intelligent software I could be using? Preferably free. Something like a Notepad++ maybe. The two biggest pet peeves I’m having is how the CTRL &/or Shift works for keyboard editing text and how Tabs are sometimes spaces instead of Tabs.
  9. Probably something else I forgot while writing this and will edit once I remember.

Products referenced above:

Electronic Tank Gate: https://www.amazon.com/gp/product/B003VAYMB4 I have 2x 3" and 1x 1.5". That’s why there is a variance in the Grey2 Gate Toggle Time.

Electric Ball Valve: https://www.amazon.com/gp/product/B06Y11B8VN

NC Valve: https://www.amazon.com/gp/product/B06XW8MSTX

Pi Relay Board: https://www.amazon.com/gp/product/B07Q2P9D7K

Current Remote Control Relay Board: https://www.amazon.com/gp/product/B01D5XWDYO

I believe I have blabbed on enough so here is the code. Thank you again for your time.

#https://wiki.52pi.com/index.php/DockerPi_4_Channel_Relay_SKU:_EP-0099
import time
from smbus import SMBus
import sys

#BEGIN PARAMETERS-----------------------------------------------------------------------

#I2C Reference
i2cbus = SMBus(1)
#I2C Write
#i2cbuswrite = i2cbus.write_byte_data

#White Water Close Time
WWC = 8+1 #Time to be Web Based
#White Water Open Time
WWO = 8+1 #Time to be Web Based
#White Water Override During Script. Using Water During Flushing Process Effects Overall Water Pressure and Will Prevent Proper Tank Filling and Flushing
WWOVER = False  #T/F to be Web Based #???? Wrap Whole Script in an Override
#White Water Override During Script Time
WWOVERT = 2*60 #Time to be Web Based

#Black1 Rinse
B1R = True #T/F to be Web Based
#Black1 Use Factory Fill
B1FF = True #T/F to be Web Based
#Black1 Use Added Fill
B1AF = False #T/F to be Web Based
#Black1 Close/Fill Time
B1CT = 13*60 #Time to be Web Based
#Black1 Open/Drain Time
B1OT = 5*60 #Time to be Web Based
#Black1 Gate Toggle Time
B1GTT = 10 #Time to be Web Based
#Black1 Gate Cycles
B1GC = 5 #Cycles to be Web Based
#Black1 Tank Stir Time
B1TST = 15 #Time to be Web Based
#Black1 Post Fill Tank
B1PF = True #T/F to be Web Based
#Black1 Post Fill Time
B1PFT = 2*60 #Time to be Web Based

#Grey1 Rinse
G1R = False
#Grey1 Close/Fill Time
G1CT = 10*60
#Grey1 Open/Drain Time
G1OT = 5*60
#Grey1 Gate Toggle Time
G1GTT = 10
#Grey1 Gate Cycles
G1GC = 5
#Grey1 Tank Stir Time
G1TST = 15
#Grey1 Post Fill Tank
G1PF = True
#Grey1 Post Fill Time
G1PFT = 2*60

#Grey2 Rinse
G2R = False
#Grey2 Close/Fill Time
G2CT = 10*60
#Grey2 Open/Drain Time
G2OT = 5*60
#Grey2 Gate Toggle Time
G2GTT = 7
#Grey2 Gate Cycles
G2GC = 5
#Grey2 Tank Stir Time
G2TST = 15
#Grey2 Post Fill Tank
G2PF = True
#Grey2 Post Fill Time
G2PFT = 2*60

#Relay Function
ron = 0xFF
roff = 0x00

#Relay Addresses
relay1 = 0x01
relay2 = 0x02
relay3 = 0x03
relay4 = 0x04
relays = [relay1, relay2, relay3, relay4]

#Relay Board Address
board0 = 0x10
board1 = 0x11
board2 = 0x12
board3 = 0x13
boards = [board0, board1, board2, board3]

#Positive Power (board0, relay1
#Negative Power (board0, relay2
#White Water Close (board0, relay3
#White Water Open (board0, relay4

#Black Gate Close (board1, relay1
#Black Gate Open (board1, relay2
#Black Gate Added Water Tank Fill (board1, relay3
#Black Gate Factory Water Tank Fill (board1, relay4

#Grey1 Gate Close (board2, relay1
#Grey1 Gate Open (board2, relay2
#Grey1 Gate Added Water Tank Fill (board2, relay3
# (board2, relay4

#Grey2 Gate Close (board3, relay1
#Grey2 Gate Open (board3, relay2
#Grey2 Gate Added Water Tank Fill (board3, relay3
# (board4, relay4

#END PARAMETERS-----------------------------------------------------------------------

#BEGIN DEBUG RESET RELAYS BEFORE SCRIPT-----------------------------------------------
#Loop to turn off all gates
for x in boards:
    currentboard = x
    for y in relays:
        currentrelay = y
        i2cbus.write_byte_data(x, y, roff)
        time.sleep(.025)
time.sleep(1)
#END DEBUG RESET RELAYS BEFORE SCRIPT--------------------------------------------------

#Turn On Power
#Positive Power On (board0, relay1
i2cbus.write_byte_data(board0, relay1, ron)
#Negative Power On (board0, relay2
i2cbus.write_byte_data(board0, relay2, ron)
time.sleep(.025)

#White Water Close (board0, relay3
i2cbus.write_byte_data(board0, relay3, ron)
time.sleep(WWC)
i2cbus.write_byte_data(board0, relay3, roff)
time.sleep(1)

#Sync All Gates to Off
#Close Black1 (board1, relay1
i2cbus.write_byte_data(board1, relay1, ron)
time.sleep(B1GTT)
i2cbus.write_byte_data(board1, relay1, roff)

#Close Grey1 (board2, relay1
i2cbus.write_byte_data(board2, relay1, ron)
time.sleep(G1GTT)
i2cbus.write_byte_data(board2, relay1, roff)

#Close Grey2 (board3, relay1
i2cbus.write_byte_data(board3, relay1, ron)
time.sleep(G2GTT)
i2cbus.write_byte_data(board3, relay1, roff)

try: #Grey1 & Grey2 Will Be Added Under this Try After Black1 is Fully Configured. 
    
    #Rinse Black1 (board1, relay1 & (board1, relay2
    if B1R is True:
        
        #Black1 Factory Water Tank Fill (board1, relay4 Used as Water Source
        if B1FF is True:
            #Turn On Factory Water Tank Fill
            i2cbus.write_byte_data(board1, relay4, ron)
        
        #Black1 Added Water Tank Fill (board1, relay3 Used as Water Source
        if B1AF is True:
            #Turn On Added Water Tank Fill
            i2cbus.write_byte_data(board1, relay3, ron)
        
        #Time to Stir Up Tank
        time.sleep(B1TST)
        
        #Black1 Open for Intial Drain
        i2cbus.write_byte_data(board1, relay2, ron)
        time.sleep(B1GTT)
        i2cbus.write_byte_data(board1, relay2, roff)
        
        #Black1 Drain
        time.sleep(B1OT)
        
        #Black1 Rinse Cycle
        for x in range(B1GC):
            #Black1 Close
            i2cbus.write_byte_data(board1, relay1, ron)
            time.sleep(B1GTT)
            i2cbus.write_byte_data(board1, relay1, roff)

            #Black1 Fill
            time.sleep(B1CT)
            
            #Black1 Open
            i2cbus.write_byte_data(board1, relay2, ron)
            time.sleep(B1GTT)
            i2cbus.write_byte_data(board1, relay2, roff)
            
            #Black1 Drain
            time.sleep(B1OT)

        #Black1 Close
        i2cbus.write_byte_data(board1, relay1, ron)
        time.sleep(B1GTT)
        i2cbus.write_byte_data(board1, relay1, roff)
        
        #Black1 Water Post Flush Fill
        if B1PF is True:
            time.sleep(B1PFT)
        
        #Black1 Factory Water Tank Fill (board1, relay4 Used as Water Source
        if B1FF is True:
            #Turn Off Factory Water Tank Fill
            i2cbus.write_byte_data(board1, relay4, roff)
        
        #Black1 Added Water Tank Fill (board1, relay3 Used as Water Source
        if B1AF is True:
            #Turn Off Added Water Tank Fill
            i2cbus.write_byte_data(board1, relay3, roff)

except KeyboardInterrupt:
    print("Keyboard Quit the Loop")
    
    for x in boards: #Loop to turn off all gates
        currentboard = x
        for y in relays:
            currentrelay = y
            i2cbus.write_byte_data(x, y, roff)

finally:
    #Loop to turn off all gates
    for x in boards:
        currentboard = x
        for y in relays:
            currentrelay = y
            i2cbus.write_byte_data(x, y, roff)
    
    #White Water Open (board0, relay4
    i2cbus.write_byte_data(board0, relay3, ron)
    time.sleep(WWO)
    i2cbus.write_byte_data(board0, relay3, roff)
    
    #Turn Off Power
    #Positive Power Off (board0, relay1
    i2cbus.write_byte_data(board0, relay1, roff)
    #Negative Power Off (board0, relay2
    i2cbus.write_byte_data(board0, relay2, roff)
    
    sys.exit()
```code
1 Like

For that you’ll need an api that gets info from the webpage and pipe it into your program… how to do that? no idea…

I’d recommend something else instead of a web interface tho, I think python has some GUI libs you could use

if you really wanna do it then you’ll need something to host the webpage and take care of all the routes, write the web interface and then link all the forms into your API, I think your program as a whole could be made to listen to a port and send all the requests into that port from the web interface

basically you’ll need to build an API into your program, so learn to make an API with python, you’ll need that to handle the requests

1 Like

I was thinking a webpage so I could set the Pi up as headless and manage the settings remotely on a local network webpage, no public access for sure. I was going to have the Pi host the webpage to make this an all in one solution.

Another reason is designing a GUI for the Pi locally would be fine, but the relay HATs make the Pi awkward and would be difficult to make a case for that held a screen. I’ve used VBA to design simple GUIs for macros before so I think that would’t be too difficult.

The planned wiring location is in the front basement of the RV so the additional wires i need to run won’t be as difficult. I’ve had to fish three sets of bonded four wire through the wall for the current remote relay board I have and it isn’t the easiest or cleanest. The Pi will be replacing the remote relay board and wiring to fix that. The remote relay board is behind the RV control panel right now.

Do you have any starting points for an API?

The one comment I got on stackoverflow mentioned http.server class(I think that’s what they are called) for python. See: https://docs.python.org/3/library/http.server.html
I threw in a chunk of the sample code and it did allow me to see the Pi’s desktop files/folders. Having the http function built into the script would allow a couple of solutions for me, I believe.
The script could be part of the boot process and always be running; meaning if it wasn’t there was an issue with the Pi, this could also be a solution to #2. If the GUI was always displayed for the script the settings would be the webpage and no need for an API/piping.
I am still looking into this option.

The Pi I am using to write the code on and tinker with the hardware is not the PI or Raspbian I will be using for the final build so I have a few extra abilities in regards to the OS right now for writing the code.
Side note: This is the new 8GB Pi, does anyone else think 8GB is overkill on a PI?

1 Like

Reinvent the wheel, or use something like ESPHome, or Home Assistant.

ESPHome is a smart-home firmware for Arduino compatible ESP8266 / ESP32 microcontrollers. It offers extensive support for various sensors, controls and automation.

Home Assistant runs on PI and has everything you’re looking for in terms of controlling relays, automation, web interface, etc.

1 Like

Update:

I’ve made some changes to my code; nothing too exciting though. Fun fact: a comment only line needs to have a space after the pound sign and a comment on a line with code needs to have two spaces before the pound sign and one after. The code will run fine without the spaces, but that is technically the way it is supposed to be.

It took a while, but i was able to get an apache2 web server to directly run python scripts using CGI.

Now I am trying to get a tkinter GUI to display on the webpage when the script is run. I keep getting errors even though running the script on my desktop works fine. I’m not sure what I am missing. Or maybe I need to use something beside tkintier to make the GUI. IDK.