Chad
Published

Reverse Osmosis Controller

Monitor and control a reverse osmosis system for your reef or house water purification.

IntermediateFull instructions providedOver 1 day7,536
Reverse Osmosis Controller

Things used in this project

Hardware components

Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
×1
Raspberry Pi 4 Display Touchscreen 7 Inch HDMI 1024×600
Switched out for larger screen old iPad 2 screen with converter
×1
1/4” 0-100 PSI Pressure Transducer
×3
Gravity TDS sensor
×3
1/4" Flow Sensor
×2
1/4" Solenoid Valve 12VDC
×2
DS18B20 Temperature Sensor Digital Thermal Probe Sensor Waterproof
×2
Grove - 2-Channel SPDT Relay
Seeed Studio Grove - 2-Channel SPDT Relay
×1
ADS1115 16 Bit 12C PGA Converter with Programmable Gain
×2
3.3V-5V 4 Channels Logic Level Converter Bi-Directional Shifter
×1
Prototying Board
×1
3 pin waterproof electrical connector
×8
20 Gauge Solid Wire-Solid Wire Kit-6
×1
Waterproof Dustproof IP65 ABS Plastic Junction Box
×1
Waterproof Cable Gland Connector Black Plastic Adjustable
×1
22AWG UL2464 Power Cable LED Red & Black & Yellow 3 Conductors
×1
Heat Shrink Tubing
×1
VL53L1X Time of Flight (ToF) Sensor Breakout
Pimoroni VL53L1X Time of Flight (ToF) Sensor Breakout
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
Limit Switch, 5 A
Limit Switch, 5 A
×4

Software apps and online services

Raspbian
Raspberry Pi Raspbian
Cloud4RPi
Cloud4RPi
No longer supported moved to influxDB 2.0 & Grafana
influxDB
Grafana

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Heat Gun
Crimp Tool, Heavy-Duty
Crimp Tool, Heavy-Duty

Story

Read more

Custom parts and enclosures

Background Image

Background image

Arrow Image 2

Arrow Image

Arrow Image

Alarm Image

Alarm Image

Schematics

Wiring Diagram

Wiring Diagram Fritzing File

System Connections & Layout

How I connected & mounted everything together

Code

WATERTREATMENT.py

Python
import influxdb_client, os, time
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.exceptions import InfluxDBError
from influxdb_client.client.write_api import SYNCHRONOUS
import pygame
from pygame.locals import *
import time, sys
from pantry_wrapper import *
from time import sleep, time
from datetime import datetime
from arrow import *
import threading
from threading import Timer
import board
import busio
import subprocess
import re
import qwiic_vl53l1x
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
import RPi.GPIO as GPIO

token = "your token here"
org = "your email"
url = "https://eastus-1.azure.cloud2.influxdata.com"
client = InfluxDBClient(url="https://eastus-1.azure.cloud2.influxdata.com", token=token, org=org)

bucket="RODI"

write_api = client.write_api(write_options=SYNCHRONOUS)

ToF = qwiic_vl53l1x.QwiicVL53L1X()
if (ToF.sensor_init() == None):# Begin returns 0 on a good init
    print("Sensor online!\n")

# Setup inputs/outputs
output1 = 7 # Flush Valve
output2 = 8 # Rinse Valve
output3 = 18 # Cooling Fan
input1 = 23 # Concentrate Flow Sensor
input2 = 24 # Permeate Flow Sensor
input3 = 17 # Softener Vessel 1 Flow Sensor
input4 = 26 # Softener Vessel 2 Flow Sensor
input5 = 13 # Softener Vessel 1 Lmit Switch
input6 = 19 # Softener Vessel 1 Lmit Switch
input7 = 27 # Softener Vessel 2 Lmit Switch
input8 = 22 # Softener Vessel 2 Lmit Switch

# Setup GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(input1, GPIO.IN, pull_up_down = GPIO.PUD_UP) # concentrate flow sensor
GPIO.setup(input2, GPIO.IN, pull_up_down = GPIO.PUD_UP) # permeate flow sensor
GPIO.setup(input3, GPIO.IN, pull_up_down = GPIO.PUD_UP) # vessel 1 flow sensor
GPIO.setup(input4, GPIO.IN, pull_up_down = GPIO.PUD_UP) # vessel 2 flow sensor
GPIO.setup(input5, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(input6, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(input7, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(input8, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(output1, GPIO.OUT) # Flush Valve
GPIO.setup(output2, GPIO.OUT) # Rinse Valve
GPIO.setup(output3, GPIO.OUT)
GPIO.output(output2,1)

startTime = int(time())   


# Setup setpoints
flush = 0.1
Tank_H = 80 #Tank height in cm
Tank1_H = 42.5 #Sump Depth in cm

DATA_SAVING_INTERVAL = 10 # secs
POLL_INTERVAL = 0.5 # sec

# Create the I2C bus
i2c = busio.I2C(board.SCL, board.SDA)
GAIN=2
# Create the ADC object using the I2C bus
ads1 = ADS.ADS1115(i2c, address=0x48,mode=0x0000)
ads2 = ADS.ADS1115(i2c, address=0x49,gain=GAIN,mode=0x0000)

ADS1x15_CONFIG_GAIN = {
    2/3: 0x0000,
    1:   0x0200,
    2:   0x0400,
    4:   0x0600,
    8:   0x0800,
    16:  0x0A00
}

# Create single-ended input on channel 0
chan0 = AnalogIn(ads1, ADS.P0)
chan1 = AnalogIn(ads1, ADS.P1)
chan2 = AnalogIn(ads1, ADS.P2)
chan3 = AnalogIn(ads1, ADS.P3)
chan4 = AnalogIn(ads2, ADS.P0)
chan5 = AnalogIn(ads2, ADS.P1)
chan6 = AnalogIn(ads2, ADS.P2)
chan7 = AnalogIn(ads2, ADS.P3)

#####################################################################################

# pygame
pygame.init()

# setup screen
screenDimentions = (2048, 1450)
screen = pygame.display.set_mode((screenDimentions))

pygame.display.set_caption('REVERSE OSMOSIS')
view_mode = 'Normal'

done = False

# setup font size
FONTSIZE = 45
LINEHEIGHT = 32
basicFont = pygame.font.SysFont(None, FONTSIZE)
Alarm = ''

# setup font colors
BLACK = (255,255,255)
WHITE = (0,0,0)
GREY = (200,200,200)
DKGREY = (169,169,169)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GRAY = (0, 0, 55)

# Background
bg = pygame.image.load('SOFT-bg4(3).png')
alarm_bg = pygame.image.load('Alarm1.png')

# setup the flow arrows
in_arrow = arrow(675, 1020, 700, 1020)
di_in_arrow = arrow(1015, 550, 1035, 550)
di_out_arrow = arrow(1650, 670, 1650, 670)
soft_in_arrow = arrow(185, 340, 210, 340)
soft_out_arrow = arrow2(390, 975, 415, 975)

class UltraSonic():
    # Ultrasonic sensor class 
    
    def __init__(self, TRIG, ECHO, offset = 0.5):
        # Create a new sensor instance
        self.TRIG = TRIG
        self.ECHO = ECHO
        self.offset = offset                             # Sensor calibration factor
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.TRIG, GPIO.OUT)                  # Set pin as GPIO output
        GPIO.setup(self.ECHO, GPIO.IN)                   # Set pin as GPIO input

    def __str__(self):
        # Return string representation of sensor
        return "Ultrasonic Sensor: TRIG - {0}, ECHO - {1}, Offset: {2} cm".format(self.TRIG, self.ECHO, self.offset)

    def ping(self):
        maxTime = 0.04
        # Get distance measurement
        GPIO.output(self.TRIG, GPIO.LOW)                 # Set TRIG LOW
        sleep(0.2)                                       # Min gap between measurements        
        # Create 10 us pulse on TRIG
        GPIO.output(self.TRIG, GPIO.HIGH)                # Set TRIG HIGH
        sleep(0.00001)                                   # Delay 10 us
        GPIO.output(self.TRIG, GPIO.LOW)                 # Set TRIG LOW
        # Measure return echo pulse duration

        pulse_start = time()
        timeout = pulse_start + maxTime

        while GPIO.input(self.ECHO) == GPIO.LOW and pulse_start < timeout:          # Wait until ECHO is LOW
            pulse_start = time()                         # Save pulse start time

        pulse_end = time()
        timeout = pulse_end + maxTime
        while GPIO.input(self.ECHO) == GPIO.HIGH and pulse_end < timeout:        # Wait until ECHO is HIGH
            pulse_end = time()                           # Save pulse end time

        pulse_duration = pulse_end - pulse_start 
        # Distance = 17160.5 * Time (unit cm) at sea level and 20C
        distance1 = pulse_duration * 17160.5              # Calculate distance
        distance = round(distance1, 2)                    # Round to two decimal points

        if distance > 2 and distance < 400:              # Check distance is in sensor range
            distance = distance + self.offset
            #print("Distance: ", distance," cm")
        else:
            distance = 0
            #print("No obstacle")                         # Nothing detected by sensor
        return distance

    def calibrate(self):
        # Calibrate sensor distance measurement
        while True:
            self.ping()
            response = input("Enter Offset (q = quit): ")
            if response == __QUIT:
                break;
            sensor.offset = float(response)
            print(sensor)
            
    @staticmethod
    def low_pass_filter(value, previous_value, beta):
        # Simple infinite-impulse-response (IIR) single-pole low-pass filter.
        # ß = discrete-time smoothing parameter (determines smoothness). 0 < ß < 1
        # LPF: Y(n) = (1-ß)*Y(n-1) + (ß*X(n))) = Y(n-1) - (ß*(Y(n-1)-X(n)))
        smooth_value = previous_value - (beta * (previous_value - value))
        return smooth_value
        
sensor = UltraSonic(10, 9)       # create a new sensor instance on GPIO pins 9 & 10
print(sensor)


# Flow Meter Class
class FlowMeter():
    gallons_per_liter = 0.264172
    seconds_per_minute = 60
    MS_per_second = 1000
    displayFormat = 'metric'
    enabled = True
    clicks = 0
    lastClick = 0
    clickDelta = 0
    hertz = 0.0
    flow = 0
    flow1 = 0# in Liters per second
    thisflow = 0.0
    thisflow1 = 0# in Liters
    total = 0.0
    total1 = 0.0# in Liters
    constant = 0.5
    constant1 = .0037854

    def __init__(self, displayFormat, enabled):
        self.displayFormat = displayFormat
        self.clicks = 0
        self.lastClick = int(time() * FlowMeter.MS_per_second)
        self.clickDelta = 0
        self.hertz = 0.0
        self.flow = 0.0
        self.thisflow = 0.0
        self.total = 0.0
        self.flow1 = 0.0
        self.thisflow1 = 0.0
        self.total1 = 0.0
        self.enabled = True

    def update(self, currentTime):
        self.clicks += 1
        # get the time delta
        self.clickDelta = max((currentTime - self.lastClick), 1)
        # calculate the instantaneous speed
        if (self.enabled == True): # and self.clickDelta < 1000)
            self.hertz = FlowMeter.MS_per_second / self.clickDelta
            self.flow = self.hertz / (FlowMeter.seconds_per_minute * FlowMeter.constant)
            self.flow1 = self.hertz / (FlowMeter.seconds_per_minute * FlowMeter.constant1)# In Liters per second
            instflow = self.flow * (self.clickDelta / FlowMeter.MS_per_second)
            instflow1 = self.flow1 * (self.clickDelta / FlowMeter.MS_per_second)
            self.thisflow += instflow
            self.thisflow1 += instflow1
            self.total += instflow
            self.total1 += instflow1
            # Update the last click
            self.lastClick = currentTime

    def getFormattedClickDelta(self):
        return str(self.clickDelta) + ' ms'
  
    def getFormattedHertz(self):
        return str(round(self.hertz,3)) + ' Hz'
  
    def getFormattedFlow(self):
        if(self.displayFormat == 'metric'):
            return str(round(self.flow,3)) + ' L/s'
        else:
            return str(round(self.flow * FlowMeter.gallons_per_liter, 3)) + ' gallons/s'
  
    def getFormattedThisflow(self):
        if(self.displayFormat == 'metric'):
            return str(round(self.thisflow,3)) + ' L'
        else:
            return str(round(self.thisflow * FlowMeter.gallons_per_liter, 3)) + ' gallons'
  
    def getFormattedTotalflow(self):
        if(self.displayFormat == 'metric'):
            return str(round(self.total,3)) + ' L'
        else:
            return str(round(self.total * FlowMeter.gallons_per_liter, 3)) + ' gallons'

    def clear(self):
        self.thisflow = 0;
        self.total = 0;


# Flow, on Pin 24.
def doAClick1(channel):
    currentTime = int(time() * FlowMeter.MS_per_second) 
    if fm.enabled == True:
        fm.update(currentTime)

# Flow, on Pin 23.
def doAClick2(channel):
    currentTime = int(time() * FlowMeter.MS_per_second)
    if fm2.enabled == True:
        fm2.update(currentTime)

# Flow, on Pin 17.
def doAClick3(channel):
    currentTime = int(time() * FlowMeter.MS_per_second) 
    if fm3.enabled == True:
        fm3.update(currentTime)

# Flow, on Pin 27.
def doAClick4(channel):
    currentTime = int(time() * FlowMeter.MS_per_second)
    if fm4.enabled == True:
        fm4.update(currentTime)

GPIO.add_event_detect(24, GPIO.RISING, callback=doAClick1, bouncetime=20) 
GPIO.add_event_detect(23, GPIO.RISING, callback=doAClick2, bouncetime=20)
GPIO.add_event_detect(17, GPIO.RISING, callback=doAClick3, bouncetime=20) 
GPIO.add_event_detect(26, GPIO.RISING, callback=doAClick4, bouncetime=20)

fm = FlowMeter('metric', 'enabled')
fm2 = FlowMeter('metric', 'enabled')
fm3 = FlowMeter('metric', 'enabled') 
fm4 = FlowMeter('metric', 'enabled')

# Button Class
class Button():
    def __init__(self, txt, location, action, bg=DKGREY, fg=WHITE, size=(160, 60), font_name="Segoe Print", font_size=32):
        self.color = bg  # the static (normal) color
        self.bg = bg  # actual background color, can change on mouseover
        self.fg = fg  # text color
        self.size = size 
        self.font = pygame.font.SysFont(font_name, font_size)
        self.txt = txt
        self.txt_surf = self.font.render(self.txt, 1, self.fg)
        self.txt_rect = self.txt_surf.get_rect(center=[s//2 for s in self.size])
        self.surface = pygame.surface.Surface(size)
        self.rect = self.surface.get_rect(center=location)
        self.call_back_ = action

    def draw(self):
        self.mouseover()
        self.surface.fill(self.bg)
        self.surface.blit(self.txt_surf, self.txt_rect)
        screen.blit(self.surface, self.rect)

    def mouseover(self):
        self.bg = self.color
        pos = pygame.mouse.get_pos()
        if self.rect.collidepoint(pos):
            self.bg = GREY  # mouseover color

    def call_back(self):
        self.call_back_()

def DrawBar(pos, size, borderC, barC):
    pygame.draw.rect(screen, borderC, (*pos, *size), 1)
    innerPos  = (pos[0]+3, pos[1]+3)
    innerSize = ((size[0]-6) * size[1]-6)

def mousebuttondown():
    pos = pygame.mouse.get_pos()
    for button in buttons:
        if button.rect.collidepoint(pos):
            button.call_back()


def drawText(surface, text, color, rect, font, aa=False, bkg=None):
    rect = Rect(rect)
    y = rect.bottom
    lineSpacing = -2
    
    # get the height of the font
    fontHeight = font.size("Tg")[1]

    while text:
        i = 1
        
    # determine if the row of text will be outside our area
        if y + fontHeight > rect.bottom:
            break
        
    # determine maximum width of line
        while font.size(text[:i])[0] < rect.width and i < len(text):
            i += 1

    # if we've wrapped the text, then adjust the wrap to the last word
        if i < len(text):
            i = text.rfind(" ", 0, i) + 1

    # render the line and blit it to the surface
        if bkg:
            image = font.render(text[:i], 1, color, bkg)
            image.set_colorkey(bkg)
        else:
            image = font.render(text[:i], aa, color)

        screen.blit(image, (rect.left, y))
        y += fontHeight + lineSpacing

    # remove the text we just blitted
        text = text[i:]

    return text

class Bar():
    def __init__(self, rect, bar = BLUE, outline = GRAY):
        self.rect = pygame.Rect(rect)
        self.bar = bar
        self.outline = outline
        self.value = 0
    def draw(self, surf):
        length = round(self.value * self.rect.height / 100)
        top = self.rect.height - length
        pygame.draw.rect(surf, self.bar, (self.rect.x, self.rect.y + top, self.rect.width, length))
        pygame.draw.rect(surf, self.outline, self.rect, 2) 
        txt =basicFont .render((str(round(self.value, 2)) + ' %'), True, GRAY)
        txt_rect = txt.get_rect(bottomleft = (self.rect.x + 60, self.rect.y + 250))
        screen.blit(txt, txt_rect)
        
    def draw1(self, surf):
        length = round(self.value * self.rect.height / 100)
        top = self.rect.height - length
        pygame.draw.rect(surf, self.bar, (self.rect.x, self.rect.y + top, self.rect.width, length))
        pygame.draw.rect(surf, self.outline, self.rect, 2) 
        txt =basicFont .render((str(round(self.value, 2)) + ' %'), True, GRAY)
        txt_rect = txt.get_rect(bottomleft = (self.rect.x + 60, self.rect.y + 150))
        screen.blit(txt, txt_rect) 


bar = Bar((150, 625, 40, 250))
bar1 = Bar((1284, 248, 40, 150))

# Point Setups

def distance():
    ToF.start_ranging()# Write configuration bytes to initiate measurement
    sleep(.005)
    distance = ToF.get_distance()# Get the result of the measurement from the sensor
    sleep(.005)
    ToF.stop_ranging()
    return(distance)

DEVICESDIR = "/sys/bus/w1/devices/"

#class for holding temperature values
class Temperature():
    def __init__(self, rawData):
        self.rawData = rawData
    @property
    def C(self):
        return float(self.rawData) / 1000
    @property
    def F(self):
        return self.C * 9.0 / 5.0 + 32.0


#class for controlling the temperature sensor
class TempSensorController(threading.Thread):
    def __init__(self, sensorId, timeToSleep, name):
        threading.Thread.__init__(self)
        threading.Thread.name = 'tempcontrol'
               
        #persist the file location
        self.tempSensorFile = DEVICESDIR + sensorId + "/w1_slave"

        #persist properties
        self.sensorId = sensorId
        self.timeToSleep = timeToSleep
        self.name = name
        #update the temperature
        self.updateTemp()
                 
    def run(self):
        #loop until its set to stopped
        self.running = True
        while(self.running):
            #update temperature
            self.updateTemp()
            #sleep
            sleep(self.timeToSleep)
        self.running = False
       
    def stopController(self):
        self.running = False

    def readFile(self):
        sensorFile = open(self.tempSensorFile, "r")
        lines = sensorFile.readlines()
        sensorFile.close()
        return lines

    def updateTemp(self):
        data = self.readFile()
        #the output from the tempsensor looks like this
        #f6 01 4b 46 7f ff 0a 10 eb : crc=eb YES
        #f6 01 4b 46 7f ff 0a 10 eb t=31375
        #has a YES been returned?
        if (len(data) < 1 or data[0].strip()[-3:] == "YES" or "00 00 00 00 00 00 00 00 00" in data[0]):
        #if data[0].strip()[-3:] == "YES" or "00 00 00 00 00 00 00 00 00" in data[0]:
            #can I find a temperature (t=)
            sleep(0.2)
            try:
                equals_pos = data[1].find("t=")

                if equals_pos != -1:
                    tempData = data[1][equals_pos+2:]
                    #update temperature
                    self.temperature = Temperature(tempData)
                    #update success status
                    self.updateSuccess = True
                
                else:
                    print(self.name, 'failed to update.... Trying Again')
                    sleep(5)
                    self.updateTemp()
     
            except IndexError:
                print(self.name, 'failed to update Index.... Trying Again')
                sleep(1)
                self.updateTemp()
                
        else:
            print(self.name, 'failed to update.... Trying Again')
            sleep(5)
            self.updateTemp()

tempcontrol = TempSensorController("28-01144f668faa", 5, 'temp1')
tempcontrol1 = TempSensorController("28-03191180f6aa", 5, 'temp2')
tempcontrol.start()
tempcontrol1.start()

def ROOMTMP():   
    ROOMTMP = round(tempcontrol1.temperature.C, 1)
    return(ROOMTMP)

def WATERTMP():   
    WATERTMP = round(tempcontrol.temperature.C, 1)
    return(WATERTMP)

def FAN_STATUS():
    FAN_STATUS = 'ON'
    if GPIO.input(18) == 0:
        FAN_STATUS = 'OFF'
    return(FAN_STATUS)

def parse_output(pattern, args):
    try:
        out_str = subprocess.check_output(args)
        if isinstance(out_str, bytes):
            out_str = out_str.decode()
    except Exception:
        out_str = ''

    match = re.search(pattern, out_str)
    return match.group(1) if match else None

def cpu_temp():
    t_str = parse_output(r'temp=(\S*)\'C', ['vcgencmd', 'measure_temp'])
    return float(t_str) if t_str else None

def Vessel1():
    vessel1 = 'REGEN'
    if GPIO.input(13) == 1 and GPIO.input(22) == 1:     
        Vessel1 = 'IN SERVICE'        
    if GPIO.input(13) == 0 and GPIO.input(22) == 0:     
        Vessel1 = 'STANDBY'
    if GPIO.input(13) == 1 and GPIO.input(22) == 0:     
        Vessel1 = 'REGEN'
    if GPIO.input(13) == 0 and GPIO.input(22) == 1:     
        Vessel1 = 'REGEN'   
    return(Vessel1)

def Vessel2():
    vessel2 = 'REGEN'
    if GPIO.input(19) == 1 and GPIO.input(27) == 1:     
        Vessel2 = 'IN SERVICE'        
    if GPIO.input(19) == 0 and GPIO.input(27) == 0:     
        Vessel2 = 'STANDBY'
    if GPIO.input(19) == 1 and GPIO.input(27) == 0:     
        Vessel2 = 'REGEN'
    if GPIO.input(19) == 0 and GPIO.input(27) == 1:     
        Vessel2 = 'REGEN'              
    return(Vessel2)

def FLOW1():
    currentTime = int(time() * FlowMeter.MS_per_second)   
    if (currentTime - fm.lastClick > 1000):
        fm.flow = 0.0 
    FLOW1 = round(fm.flow,3)    
    return(FLOW1)

def FLOW1T():   
    currentTime = startTime * FlowMeter.MS_per_second
    FLOW1T = round((fm.total / 60),3)
    return(FLOW1T)

def FLOW2():
    currentTime = int(time() * FlowMeter.MS_per_second)
    if (currentTime - fm2.lastClick > 1000):
        fm2.flow = 0.0  
    FLOW2 = round(fm2.flow,3)
    return(FLOW2)

def FLOW2T():    
    currentTime = int(time() * FlowMeter.MS_per_second)
    FLOW2T = round((fm2.total / 60),3)
    return(FLOW2T)

def FLOW():
    FLOW = round(fm.flow + fm2.flow,3)
    return(FLOW)

def FLOWT():
    FLOWT = round(((fm.total + fm2.total) / 60),3)
    return(FLOWT)

def FLOW3():
    currentTime = int(time() * FlowMeter.MS_per_second)   
    if (currentTime - fm3.lastClick > 1000):
        fm3.flow1 = 0.0 
    FLOW3 = round(fm3.flow1,3)    
    return(FLOW3)

def FLOW3T():
    if Vessel1 == 'STANDBY':
        fm3.total1 = 0.0
    currentTime = startTime * FlowMeter.MS_per_second
    FLOW3T = round((fm3.total1 / 60),3)
    return(FLOW3T)

def FLOW4():
    currentTime = int(time() * FlowMeter.MS_per_second)
    if (currentTime - fm4.lastClick > 1000):
        fm4.flow1 = 0.0  
    FLOW4 = round(fm4.flow1,3)
    return(FLOW4)

def FLOW4T():
    if Vessel2 == 'STANDBY':
        fm4.total1 = 0.0
    currentTime = int(time() * FlowMeter.MS_per_second)
    FLOW4T = round((fm4.total1 / 60),3)
    return(FLOW4T)

def FLOW5():
    FLOW5 = round(fm3.flow1 + fm4.flow1,3)
    return(FLOW5)

def FLOW5T():
    FLOW5T = round(((fm3.total1 + fm4.total1) / 60),3)
    return(FLOW5T)

def filtered_value():
        beta = .75 
        filtered_value = 2
        filtered_value = sensor.low_pass_filter(sensor.ping(), filtered_value, float(beta))
        filtered_value = round(filtered_value, 2)
        #print("Filtered: ", filtered_value, " cm")
        return (filtered_value)

def LEVEL():
    LEVEL = round((1000 - distance())/10/80*100,1) # Salt Level
    return(LEVEL)

def LEVEL1():
    LEVEL1 = round((Tank1_H - filtered_value() - 10)/30*100,1) # Water Level
    return(LEVEL1)

def PRESS1():
    PRESS1 = round(((chan2.voltage - 0.5) * 25),1) # RO Inlet Pressure
    return (PRESS1)

def PRESS2():
    PRESS2 = round(((chan1.voltage - 0.5) * 25),1) # RO System Pressure
    return (PRESS2)

def PRESS3():
    PRESS3 = round(((chan0.voltage - 0.5) * 50),1) # System Inlet Pressure
    return (PRESS3)

def TDSVALUE1():
    TDS1CALC = round(chan6.value * 6.19 / 65536,5)
    TEMPCOMP1 = 1.0 + (0.02 * (WATERTMP - 25))
    COMPVOLT1 = round(TDS1CALC / TEMPCOMP1,5)
    TDSVALUE1 = round((((133.42 * COMPVOLT1 * COMPVOLT1 * COMPVOLT1) - (255.86 * COMPVOLT1 * COMPVOLT1) + (857.39 * COMPVOLT1)) * 0.5),1)
    return (TDSVALUE1)

def TDSVALUE2():
    TDS2CALC = round(chan5.value * 6.19 / 65536,5)
    TEMPCOMP2 = 1.0 + (0.02 * (WATERTMP - 25))
    COMPVOLT2 = round(TDS2CALC / TEMPCOMP2,5)
    TDSVALUE2 = round((((133.42 * COMPVOLT2 * COMPVOLT2 * COMPVOLT2) - (255.86 * COMPVOLT2 * COMPVOLT2) + (857.39 * COMPVOLT2)) * 0.5),1)
    return (TDSVALUE2)

def TDSVALUE3(): # DI Outlet TDS
    TDS3CALC = round(chan4.value * 6.19 / 65536,5)
    TEMPCOMP3 = 1.0 + (0.02 * (WATERTMP - 25))
    COMPVOLT3 = round(TDS3CALC / TEMPCOMP3,5)
    TDSVALUE3 = round(((((133.42 * COMPVOLT3 * COMPVOLT3 * COMPVOLT3) - (255.86 * COMPVOLT3 * COMPVOLT3) + (857.39 * COMPVOLT3)) * 0.5) - 3),1)
    return (TDSVALUE3)


#####################################################################################
                                    # main loop
##################################################################################
while not done:


##################################################################################    
                    # pygame loop
##################################################################################
    try:
        save_timer = 0
        
        while True:
            
            
            ROOMTMP = round(tempcontrol1.temperature.C,1)
            WATERTMP = round(tempcontrol.temperature.C,1)
            CPU_TEMP = cpu_temp
            
            screen.blit(bg,(0,0))

        ##################################################################################    
                                # time
        ##################################################################################

            #print("Time: " + datetime.now().strftime('%Y-%m-%d,%H:%M:%S,'))
                        
        ##################################################################################
                                    # performance
        ##################################################################################
            if FLOW() >= flush:
                recovery = round((FLOW1() / FLOW()) * 100,0)
                
            else:
                recovery = 0

            if FLOW() >= flush and TDSVALUE1() >= 10:
                SLTREJ = round((TDSVALUE1() - TDSVALUE2()) / TDSVALUE1() * 100,1)
            else:
                SLTREJ = 0
            
        ##################################################################################
                                    # Auto Rinse
        ##################################################################################                                   

            def Valve_RI(): 
                GPIO.output(output2, 1)
                RI.cancel()
            
            RI = Timer(15,Valve_RI)
            
            def Valve_AR():
                GPIO.output(output2, 1)
                AR.cancel()
                
            AR = Timer(30,Valve_AR) 

            def manual_rinse_membrane():
                if RI.is_alive() is False:
                    RI.start()
                    
                    GPIO.output(output2, 0)

            if GPIO.input(output2) == False or AR.is_alive() is True or RI.is_alive() is True:
                pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(1524, 1045, 42, 45))
                text = basicFont.render("RINSING", True, WHITE, BLACK)
                textRect = text.get_rect()
                screen.blit(text, (1520, 1200 + (1 * LINEHEIGHT)))
                        
            else:
                if AR.is_alive() or RI.is_alive() is False:
                    pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(1524, 1045, 42, 45))            
       
            
            if FLOW() >= flush and TDSVALUE2() >= 15:
                if AR.is_alive()is False:
                    AR.start()
                    GPIO.output(output2, 0)

        ##################################################################################
                                    # Auto Flush
        ##################################################################################

        #             def Valve_AF(): 
        #                 GPIO.output(output1, 1)
        #                 AF.cancel()
        #                 timer.cancel()
        #                 
        #             def Valve_AFC():
        #                 GPIO.output(output1, 1)
        #                 timer.cancel()
        #                 AF.cancel()
        #                 
        #                 
        #             AF = Timer(10,Valve_AF)
        #             
        #             class RepeatTimer(Timer):
        #                 def run(self):
        #                     while not self.finished.wait(self.interval):
        #                         self.function(*self.args,**self.kwargs)
        #                         GPIO.output(output1, 0)
        #                         
        #             
        #             timer = RepeatTimer(5, Valve_AFC)
        #             
        #             if FLOW() >= flush:
        #                 
        #                 
        #                 timer.start()
        #                 #GPIO.output(output1, 0)
        #                 AF.start()
        #                            
        #             else:
        #                 GPIO.output(output1, 1)
        #                 timer.cancel()
        #                 AF.cancel()
                                   
        ##################################################################################
                             # Manual Flush Button
        ##################################################################################
          
            def Valve_F(): 
                GPIO.output(output1, 1)
                F.cancel()
                
            F = Timer(10,Valve_F)
            
            def manual_flush_membrane():
                if F.is_alive() is False:
                    F.start()
                    GPIO.output(output1, 0)

            if GPIO.input(output1) == False or F.is_alive() is True:
                pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(1372, 1045, 42, 45))
                text = basicFont.render("FLUSHING", True, WHITE, BLACK)
                textRect = text.get_rect()
                screen.blit(text, (1295, 1200 + (1 * LINEHEIGHT)))
                
            else:
                if F.is_alive() is False:
                    pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(1372, 1045, 42, 45))  
            
    #            print(F.is_alive())
    #            print(threading.active_count())
        ##################################################################################
                             # Manual Rinse Button
        ##################################################################################

                    
        ##################################################################################
                             # Totalizer Reset Buttons
        ##################################################################################
                    
            def reset_totalizer():
                fm.total = 0
                fm2.total = 0
                
            def reset_soft_totalizer():
                fm3.total1 = 0
                fm4.total1 = 0
                
            def FAN_ON():
                GPIO.output(output3,1)
                
            def FAN_OFF():
                GPIO.output(output3,0)
                                 
        #####################################################################################
                                # Buttons
        #####################################################################################
            
            button_01 = Button("MAN FLUSH", (1370, 1200), manual_flush_membrane)
            button_02 = Button("RINSE", (1590, 1200), manual_rinse_membrane)
            button_03 = Button("RESET", (800, 50), reset_totalizer)
            button_04 = Button("RESET", (600, 50), reset_soft_totalizer)
            button_05 = Button("FAN ON", (625, 1325), FAN_ON)
            button_06 = Button("FAN OFF", (625, 1400), FAN_OFF)
            buttons = [button_01, button_02, button_03, button_04, button_05, button_06]
            for button in buttons:
                button.draw()
            
            #Draw the flow arrows
            if FLOW() >= flush:
                in_arrow.update()
                screen.blit(in_arrow.image,(in_arrow.x, in_arrow.y))
                di_in_arrow.update()
                screen.blit(di_in_arrow.image,(di_in_arrow.x, di_in_arrow.y))
                di_out_arrow.update()
                screen.blit(di_out_arrow.image,(di_out_arrow.x, di_out_arrow.y))
                                  
            #draw the flow arrows
            if FLOW5() >= 2:
                soft_in_arrow.update()
                screen.blit(soft_in_arrow.image,(soft_in_arrow.x, soft_in_arrow.y))
                soft_out_arrow.update()
                screen.blit(soft_out_arrow.image,(soft_out_arrow.x, soft_out_arrow.y))
                                 
            # Draw Vessel1 Status Box
            if GPIO.input(19) == 1 and GPIO.input(27) == 1:
                pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(418, 353, 70, 22))
            if GPIO.input(19) == 0 and GPIO.input(27) == 0:
                pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(418, 353, 70, 22))
            if GPIO.input(19) == 1 and GPIO.input(27) == 0:
                pygame.draw.rect(screen, (0, 255, 255), pygame.Rect(418, 353, 70, 22))
            if GPIO.input(19) == 0 and GPIO.input(27) == 1:
                pygame.draw.rect(screen, (0, 255, 255), pygame.Rect(418, 353, 70, 22))

            # Draw Vessel2 Status Box
            if GPIO.input(22) == 1 and GPIO.input(13) == 1:
                pygame.draw.rect(screen, (0, 255, 0), pygame.Rect(572, 353, 70, 22))       
            if GPIO.input(22) == 0 and GPIO.input(13) == 0:
                pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(572, 353, 70, 22))
            if GPIO.input(22) == 1 and GPIO.input(13) == 0:
                pygame.draw.rect(screen, (0, 255, 255), pygame.Rect(572, 353, 70, 22))
            if GPIO.input(22) == 0 and GPIO.input(13) == 1:
                pygame.draw.rect(screen, (0, 255, 255), pygame.Rect(572, 353, 70, 22))

            text = basicFont.render("FAN STATUS", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (350, 1265 + ((LINEHEIGHT))))

            if GPIO.input(18) == 0:
                text = basicFont.render("OFF", True, WHITE, BLACK)
                textRect = text.get_rect()
                screen.blit(text, (400, 1265 + (2 * (LINEHEIGHT))))
                
            if GPIO.input(18) == 1:
                text = basicFont.render("ON", True, WHITE, BLACK)
                textRect = text.get_rect()
                screen.blit(text, (400, 1265 + (2 * (LINEHEIGHT))))
                
            if CPU_TEMP() >= 50:
                GPIO.output(output3,1)
            elif CPU_TEMP() <= 45:
                GPIO.output(output3,0)
                
            text = basicFont.render("CPU TEMP", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (350, 1280 + (3 * (LINEHEIGHT))))
            
            text = basicFont.render((str(CPU_TEMP())), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (400, 1280 + (4 * (LINEHEIGHT))))
            
            text = basicFont.render("ACTIVE THREADS", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (1750, 1265 + (1 * (LINEHEIGHT))))
            
            text = basicFont.render(str(threading.active_count()), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (1875, 1265 + (2 * (LINEHEIGHT))))
            
            text = basicFont.render("SALT", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (218, 670 - LINEHEIGHT))
            text = basicFont.render("LEVEL", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (210, 700 - LINEHEIGHT))
                
            #Draw Pressure 1
            text = basicFont.render("SUPPLY", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (145, 250 + LINEHEIGHT))
            #Draw Suppy Pressure
            text = basicFont.render((str(PRESS3()) + ' PSI'), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (140, 250 + (2 * LINEHEIGHT)))
            
            text = basicFont.render("SYSTEM", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (130, 980 + LINEHEIGHT))
            #Draw Total flow
            text = basicFont.render((str(FLOW5()) + ' L/min'), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (120, 980 + (2 *LINEHEIGHT)))
            
            #Draw Vessel1 flow
            text = basicFont.render((str(FLOW3()) + ' L/min'), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (360, 265 + LINEHEIGHT))
            #Draw Vessel2 flow
            text = basicFont.render((str(FLOW4()) + ' L/min'), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (600, 265 + LINEHEIGHT))

            # Draw  Totalizers
            text = basicFont.render("TOTALIZERS", True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (110, 40 - LINEHEIGHT))
            text = basicFont.render('VESSEL 1   ' + (str(FLOW3T()) + ' Liters'), True, WHITE, BLACK)
            textRect = text.get_rect()
            screen.blit(text, (80, 10 + (LINEHEIGHT)))
            text = basicFont.render('VESSEL 2   ' + (str(FLOW4T()) + ' Liters'), True, WHITE, BLACK)
...

This file has been truncated, please download it to see its full contents.

Arrow

Python
Code for moving arrows in display
import pygame, sys
from pygame.locals import *

class arrow():
    image = ''
    x = 0
    y = 0
    ll = 0 # left limit
    rl = 0 # right limit
    direction = 'right'

    def __init__(self, x, y, ll, rl):
        self.image = pygame.image.load('arrow.png')
        self.image = self.image.convert_alpha()
        self.x = x
        self.y = y
        self.ll = ll
        self.rl = rl

    def update(self):
        if (self.direction == 'right'):
            self.x += 5
        else:
            self.x -= 5

        if (self.x > self.rl or self.x < self.ll):
            self.direction = 'right' if self.direction == 'left' else 'left'

class arrow2():
    image = ''
    x = 0
    y = 0
    ll = 0 # left limit
    rl = 0 # right limit
    direction = 'left'

    def __init__(self, x, y, ll, rl):
        self.image = pygame.image.load('arrow2.png')
        self.image = self.image.convert_alpha()
        self.x = x
        self.y = y
        self.ll = ll
        self.rl = rl

    def update(self):
        if (self.direction == 'right'):
            self.x += 5
        else:
            self.x -= 5

        if (self.x > self.rl or self.x < self.ll):
            self.direction = 'right' if self.direction == 'left' else 'left'

Temperature Sensor Thread

Python
import threading
import time

DEVICESDIR = "/sys/bus/w1/devices/"

#class for holding temperature values
class Temperature():
    def __init__(self, rawData):
        self.rawData = rawData
    @property
    def C(self):
        return float(self.rawData) / 1000
    @property
    def F(self):
        return self.C * 9.0 / 5.0 + 32.0

#class for controlling the temperature sensor
class TempSensorController(threading.Thread):
    def __init__(self, sensorId, timeToSleep):
        threading.Thread.__init__(self)
       
        #persist the file location
        self.tempSensorFile = DEVICESDIR + sensorId + "/w1_slave"

        #persist properties
        self.sensorId = sensorId
        self.timeToSleep = timeToSleep

        #update the temperature
        self.updateTemp()
       
        #set to not running
        self.running = False
       
    def run(self):
        #loop until its set to stopped
        self.running = True
        while(self.running):
            #update temperature
            self.updateTemp()
            #sleep
            time.sleep(self.timeToSleep)
        self.running = False
       
    def stopController(self):
        self.running = False

    def readFile(self):
        sensorFile = open(self.tempSensorFile, "r")
        lines = sensorFile.readlines()
        sensorFile.close()
        return lines

    def updateTemp(self):
        data = self.readFile()
        #the output from the tempsensor looks like this
        #f6 01 4b 46 7f ff 0a 10 eb : crc=eb YES
        #f6 01 4b 46 7f ff 0a 10 eb t=31375
        #has a YES been returned?
        if data[0].strip()[-3:] == "YES":
            #can I find a temperature (t=)
            equals_pos = data[1].find("t=")
            if equals_pos != -1:
                tempData = data[1][equals_pos+2:]
                #update temperature
                self.temperature = Temperature(tempData)
                #update success status
                self.updateSuccess = True
            else:
                pass
                #self.updateSuccess = False
        else:
            pass
#             self.updateSuccess = False
       
if __name__ == "__main__":

    #create temp sensor controller, put your controller Id here
    # look in "/sys/bus/w1/devices/" after running
    #  sudo modprobe w1-gpio
    #  sudo modprobe w1-therm
    tempcontrol = TempSensorController("28-01144f668faa", 1)
    tempcontrol1 = TempSensorController("28-03191180f6aa", 1)
    
    try:
        print("Starting temp sensor controller")
        #start up temp sensor controller
        tempcontrol.start()
        tempcontrol1.start()
        #loop forever, wait for Ctrl C
        while(True):
            print(tempcontrol.temperature.C)
            print(tempcontrol1.temperature.C)
            #print tempcontrol.temperature.F
            #time.sleep(5)
    #Ctrl C
    except KeyboardInterrupt:
        print("Cancelled")
   
    #Error
    except:
        print("Unexpected error:", sys.exc_info()[0])
        pass #raise

    #if it finishes or Ctrl C, shut it down
    finally:
        print("Stopping temp sensor controller")
        #stop the controller
        tempcontrol.stopController()
        #wait for the tread to finish if it hasn't already
        tempcontrol.join()
       
    print("Done")

Credits

Chad

Chad

5 projects • 12 followers

Comments