Raspberry Pi Python button gives false positives

56 Views Asked by At

Can someone point me in the correct direction of how to fix this issue with the false positives I recieve?

I have tried to do according to this suggestion (https://raspberrypi.stackexchange.com/questions/63512/how-can-i-detect-how-long-a-button-is-pressed) but to no avail. I still get those random false positives that ruin everything I have worked for these past two weeks.

I have added the entire code for clarity (but relevant parts are down).

###################################
# Control program
###################################
# LCD            R Pi                

# RS             GPIO 26 (pin 37)        
# E              GPIO 19 (35)
# DB4            GPIO 13 (33)
# DB5            GPIO 6  (31)
# DB6            GPIO 5  (29)
# DB7            GPIO 11 / SCLK (23)

# WPSE311/DHT11  GPIO 24 (18) 

# Relay Fog      GPIO 21 (40)
# Relay Oxy      GPIO 20 (38)
# Relay LED      GPIO 16 (36)

# Button Killsw. GPIO 12 (32)

# F1 (Therm.)    GPIO 4  (7)   - Gul kabel
# F2 (Fogger)    GPIO 25 (22)  - Blå kabel
# R  (Root)      GPIO 24 (18)  - Lila kabel
# G  (Grow area) GPIO 23 (16)  - Brun kabel
# P  (Bar . SCL) GPIO 3  (5)   - Grön kabel (OBS! Går via I2C)
# P  (Bar . SDA) GPIO 2  (3)   - Grå kabel  (OBS! Går via I2C)




###################################
# Imports--- [ToDo: How to import settings from a configuration file?]
###################################

import adafruit_dht, board, digitalio, threading, os, glob 
import adafruit_character_lcd.character_lcd as characterlcd
import RPi.GPIO as GPIO
import adafruit_bmp280
import logging
import time
from datetime import datetime
from time import sleep, perf_counter
from gpiozero import CPUTemperature
from datetime import datetime




###################################
# Definitions---
###################################

# Compatible with all versions of RPI as of Jan. 2019
# v1 - v3B+
lcd_rs = digitalio.DigitalInOut(board.D26)
lcd_en = digitalio.DigitalInOut(board.D19)
lcd_d4 = digitalio.DigitalInOut(board.D13)
lcd_d5 = digitalio.DigitalInOut(board.D6)
lcd_d6 = digitalio.DigitalInOut(board.D5)
lcd_d7 = digitalio.DigitalInOut(board.D11)

# Define LCD column and row size for 16x2 LCD.
lcd_columns = 16
lcd_rows    = 2

# Define relays
relay_fogger = 21 #digitalio.DigitalInOut(board.D21) #- Why does this not work?
relay_oxygen = 20 #digitalio.DigitalInOut(board.D20) #- Why does this not work?
relay_led    = 16 #digitalio.DigitalInOut(board.D16) #- Why does this not work?

# Define liquid nutrient temperature probe
liquid_nutrients_probe = 23 #digitalio.DigitalInOut(board.D23) - Why does this not work?

# Define the killswitch push button
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)




###################################
# Initializations
###################################

# Init file log
#logging.basicConfig(filename=datetime.now()+'.log', format='%(asctime)s %(levelname)s: %(message)s', encoding='utf-8', level=logging.DEBUG)
#logging.basicConfig(filename='Logging.log', format='%(asctime)s: %(message)s', encoding='utf-8', level=logging.DEBUG)


# Init the lcd class---
lcd = characterlcd.Character_LCD_Mono(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows)

# Init thermometer for liquid nutrients fluid
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'

# Initialise the BMP280
i2c = board.I2C()
# For some strange reason, the default address of the BMP280 is 0x76 when checking all via 'i2cdetect -y 1' whereas default expects 0x77.
# Thus are we forcing the address to correpsond correctly with below line of code."""
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

# Init DHT22 sensors---
dhtDevice_nutrient_mist = adafruit_dht.DHT22(board.D23, use_pulseio=False)
dhtDevice_roots_chamber = adafruit_dht.DHT22(board.D24, use_pulseio=False)
dhtDevice_grow_area = adafruit_dht.DHT22(board.D25, use_pulseio=False)

# Init relays---
GPIO.setwarnings(False)
GPIO.setup(relay_fogger, GPIO.OUT)
GPIO.setup(relay_oxygen, GPIO.OUT)
GPIO.setup(relay_led, GPIO.OUT)




###################################
# Global variables---
###################################
killswitch = False
display_info_delay = 5 # Seconds

# Fogger bucket variables
temp_nutrient_solution = 0
temp_nutrient_mist = 0
humidity_nutrient_mist = 0
fogger_on_seconds = 2700 #45 min
fogger_off_seconds = 900 #15 min
sleep_fogger = False

# Grow bucket variables
temp_roots = 0
humidity_roots = 0
pressure_roots = 0
temp_grow = 0
humidity_grow = 0

# Oxygen bucket variables
sleep_oxygen = False

# Rapsberry Pi internal temperature
rpi_internal_temp = 0

# DHT22 error status variables
dht_fog_chamber_error = False
dht_root_chamber_error = False
dht_grow_area_error = False


###################################
# Methods---
###################################

def button_callback(channel):
    """When the button is pressed."""
    global killswitch
    # Due to serious issues with false positives regarding button being pushed
    start_time = time.time()
    
    while GPIO.input(channel) == 0: # Wait for button up
        pass
    
    buttonTime = time.time() - start_time
    
    if buttonTime >= .1:
        print("###########\nKillswitch button was pressed!\n###########")
        killswitch = True

# Init the button
GPIO.add_event_detect(12, GPIO.RISING, callback=button_callback, bouncetime=150)
#GPIO.add_event_detect(12, GPIO.FALLING, callback=button_callback, bouncetime=500)


def read_temp_raw():    
    """Help function for the thermometer readings."""
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return lines


def get_temp_nutrient_solution():
    """Measure the temperature of the nutrient solution where the ultrasonic fogger is."""
    global killswitch
    while killswitch==False:
        global temp_nutrient_solution
        
        lines = read_temp_raw()
        while lines[0].strip()[-3:] != 'YES':
            time.sleep(0.2)
            lines = read_temp_raw()
        equals_pos = lines[1].find('t=')
        if equals_pos != -1:
            temp_string = lines[1][equals_pos+2:]
            temp_nutrient_solution = float(temp_string) / 1000.0
        
        # For development process
        print(
            "F1 Temp nutrient solution: {:.1f} C / {:.1f} F".format(
                temp_nutrient_solution, c2f(temp_nutrient_solution)
            )
        )
        sleep(1)


def get_temp_humidity_nutrient_mist(): 
    """Measure the temperature and humidity of the nutrient mist where the ultrasonic fogger is."""
    global killswitch
    while killswitch==False:
        try:
            # Update global temp value and humidity
            global temp_nutrient_mist
            global humidity_nutrient_mist            
            temp_nutrient_mist = dhtDevice_nutrient_mist.temperature
            humidity_nutrient_mist = dhtDevice_nutrient_mist.humidity
            
            # For development process
            print(
                "F2: {:.1f} C / {:.1f} F Humidity: {}% ".format(
                    temp_nutrient_mist, c2f(temp_nutrient_mist), humidity_nutrient_mist
                )
            )
 
        except RuntimeError as error:
            # Errors happen fairly often, DHT's are hard to read, just keep going
            print("Warning (DHT fog): " + error.args[0])
            sleep(2) # sleep(1) for DHT11 and sleep(2) for DHT22
            pass
        except Exception as error:
            global dht_fog_chamber_error
            print("DHT fog sensor fatal error!")
            dht_fog_chamber_error = True
            dhtDevice_nutrient_mist.exit()
            raise error
     
        sleep(2)


def get_temp_humidity_grow_area(): 
    """Measure the temperature and humidity of the grow area."""
    global killswitch    
    while killswitch==False:
        try:
            # Update global temp value and humidity
            global temp_grow
            global humidity_grow           
            temp_grow = dhtDevice_grow_area.temperature
            humidity_grow = dhtDevice_grow_area.humidity
            
            # For development process
            print(
                "Grow area: {:.1f} C / {:.1f} F Humidity: {}% ".format(
                    temp_grow, c2f(temp_grow), humidity_grow
                )
            )
 
        except RuntimeError as error:
            # Errors happen fairly often, DHT's are hard to read, just keep going
            print("Warning (DHT grow): " + error.args[0])
            sleep(2) # sleep(1) for DHT11 and sleep(2) for DHT22
            pass
        except Exception as error:
            global dht_grow_area_error
            print("DHT grow sensor fatal error!")
            dht_grow_area_error = True
            dhtDevice_grow_area.exit()
            raise error
     
        sleep(2)


def get_temp_humidity_root_chamber(): 
    """Measure the temperature and humidity of the roots chamber."""
    global killswitch    
    while killswitch==False:
        try:
            # Update global temp value and humidity
            global temp_roots
            global humidity_roots           
            temp_roots = dhtDevice_roots_chamber.temperature
            humidity_roots = dhtDevice_roots_chamber.humidity
            
            # For development process
            print(
                "Root chamber: {:.1f} C / {:.1f} F Humidity: {}% ".format(
                    temp_roots, c2f(temp_roots), humidity_roots
                )
            )
 
        except RuntimeError as error:
            # Errors happen fairly often, DHT's are hard to read, just keep going
            print("Warning (DHT root): " + error.args[0])
            sleep(2) # sleep(1) for DHT11 and sleep(2) for DHT22
            pass
        except Exception as error:
            global dht_root_chamber_error
            print("DHT root sensor fatal error!")
            dht_root_chamber_error = True
            dhtDevice_roots_chamber.exit()
            raise error
     
        sleep(2)


def get_pressure_root_chamber():
    """Gets the pressure from the BMP280 device. This device can also measure temperature (more precise than DHT22) and altitude. """
    global killswitch
    while killswitch==False:
        #temperature = bmp280.temperature()
        global pressure_roots
        pressure_roots = bmp280.pressure
        # For development process
        print(
            "Pressure roots: {:.1f} hPa ".format(
                pressure_roots
            )
        )
        #print('Pressure: {} hPa'.format(pressure_roots))
        sleep(2)


def relay_fogger_control():
    """Fogger on for 45 min and off for 15. Perpetual mode unless kill_processes() is activated"""
    global killswitch
    global fogger_on_seconds, fogger_off_seconds
    
    while killswitch==False:
        GPIO.output(relay_fogger, GPIO.HIGH)
        sleep(10)
        #sleep(fogger_on_seconds)
        GPIO.output(relay_fogger, GPIO.LOW)
        sleep(10)
        #sleep(fogger_off_seconds)


def relay_heatled_control():
    """Heat LED controller. When is it too hot for the crops? Sleep interval? Perpetual mode unless kill_processes() is activated"""
    global killswitch
    global led_on_seconds, led_off_seconds
    
    while killswitch==False:
        GPIO.output(relay_led, GPIO.HIGH)
        sleep(20)
        #sleep(led_on_seconds)
        GPIO.output(relay_led, GPIO.LOW)
        sleep(10)
        #sleep(led_off_seconds)

def relay_oxygen_control():
    """Oxygen maker. Perpetual mode unless kill_processes() is activated or barometric pressure is too high."""
    global killswitch
    while killswitch==False:
        GPIO.output(relay_oxygen, GPIO.HIGH)
        sleep(15)
        #sleep(oxygen_on_seconds)
        GPIO.output(relay_oxygen, GPIO.LOW)        
        #sleep(oxygen_off_seconds)
        sleep(25)
   

def reset_clear_lcd(): 
    """Move cursor to (0,0) and clear the screen"""
    lcd.home()
    lcd.clear()
    

def get_rpi_temp(): 
    """Get Rapsberry Pi internal temperature"""
    global rpi_internal_temp
    rpi_internal_temp = CPUTemperature().temperature


def c2f(temperature_c):
    """Convert Celsius to Fahrenheit"""
    return temperature_c * (9 / 5) + 32


def lcd_display_data_controller():
    """Display various measurments and data on the small LCD. Switch every (display_info_delay) seconds."""
    global killswitch, display_info_delay
    global dht_fog_chamber_error, dht_grow_area_error, dht_root_chamber_error
    global temp_roots, humidity_roots, pressure_roots
    global temp_grow, humidity_grow
    global temp_nutrient_solution, humidity_nutrient_mist
    
    while killswitch==False:
        reset_clear_lcd()
        
        # For testing purpose
        #lcd.message("New round.")
        #sleep(display_info_delay)
        #reset_clear_lcd()

        # Root temperature and humidity
        if dht_root_chamber_error==False:
            lcd.message = (
            "R1: {:.1f}C/{:.1f}F \nHumidity: {}% ".format(
                temp_roots, c2f(temp_roots), humidity_roots
                )
            )
            sleep(display_info_delay)
            reset_clear_lcd()
        else:
            lcd.message = ("ERROR: DHT root\nchamber!")
            sleep(display_info_delay)
            reset_clear_lcd()


        # Root pressure
        lcd.message = (
            "Root pressure:\n{:.1f} hPa".format(
                pressure_roots
            )
        )
        sleep(display_info_delay)
        reset_clear_lcd()

        # Crop grow area temperature and humidity
        if dht_grow_area_error==False:    
            lcd.message = (
                "G:{:.1f}C/{:.1f}F \nHumidity: {}% ".format(
                    temp_grow, c2f(temp_grow), humidity_grow
                )
            )
            sleep(display_info_delay)
            reset_clear_lcd()
        else:
            lcd.message = ("ERROR: DHT grow\narea!")
            sleep(display_info_delay)
            reset_clear_lcd()


        # Nutrient liquid temperature   
        lcd.message = (
            "F nutrient temp.\n{:.1f}C/{:.1f}F ".format(
                temp_nutrient_solution, c2f(temp_nutrient_solution)
            )
        )
        sleep(display_info_delay)
        reset_clear_lcd()      


        # Nutrient mist temperature and humidity
        if dht_fog_chamber_error==False:
            lcd.message = (
            "F: {:.1f}C/{:.1f}F \nHumidity: {}% ".format(
                temp_nutrient_mist, c2f(temp_nutrient_mist), humidity_nutrient_mist
                )
            )
            sleep(display_info_delay)
            reset_clear_lcd()
        else:
            lcd.message = ("ERROR: DHT fog\nchamber!")
            sleep(display_info_delay)
            reset_clear_lcd()

            
        # Raspberry Pi internal temperature  
        lcd.message = (
            "R Pi (int. temp): \n{:.1f}C/{:.1f}F ".format(
                rpi_internal_temp, c2f(rpi_internal_temp)
            )
        )
        sleep(display_info_delay)
        reset_clear_lcd()


def kill_processes(): 
    """ToDo: A button must be pressed which gracefully kills all processes preparing for shutdown."""
    # Power off machines
    GPIO.output(relay_fogger, GPIO.LOW)
    GPIO.output(relay_led, GPIO.LOW)
    GPIO.output(relay_oxygen, GPIO.LOW)
    
    # Joined the threads / stop the threads after killswitch is true - Shutdown order is very important
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    t5.join()
    t6.join()
    t8.join()
    t9.join()
    t10.join()
    t7.join() # display thread last to die
    #tx.join() 
    reset_clear_lcd()
    
    # Stop message
    lcd.message = 'Full stop.\nOk to shut down.'
    
    # GPIO clearing
    GPIO.cleanup()


###################################
# Thread setup - Startup order is important
###################################
#tx = threading.Thread(target=xx, args=(killswitch,sleep_fogger,))
#tx = threading.Thread(target=killswitch_button)
t1 = threading.Thread(target=get_rpi_temp)
t2 = threading.Thread(target=get_temp_nutrient_solution)
t3 = threading.Thread(target=get_temp_humidity_nutrient_mist)
t4 = threading.Thread(target=get_temp_humidity_root_chamber)
t5 = threading.Thread(target=get_pressure_root_chamber)
t6 = threading.Thread(target=get_temp_humidity_grow_area)
t7 = threading.Thread(target=lcd_display_data_controller)
t8 = threading.Thread(target=relay_fogger_control)
t9 = threading.Thread(target=relay_oxygen_control)
t10 = threading.Thread(target=relay_heatled_control)

 
# Start the threads
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t6.start()

sleep(2) # Give everything a bit extra time before the LCD starts displaying data

t7.start()
t8.start()
t9.start()
t10.start()
#tx.start()




###################################
# Code main process---
###################################
while not killswitch:
    sleep(1)




###################################
# Graceful exit
kill_processes()
###################################

Now, the relevant parts are below.

# Define the killswitch push button
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def button_callback(channel):
    """When the button is pressed."""
    global killswitch
    # Due to serious issues with false positives regarding button being pushed
    start_time = time.time()
    
    while GPIO.input(channel) == 0: # Wait for button up
        pass
    
    buttonTime = time.time() - start_time
    
    if buttonTime >= .1:
        print("###########\nKillswitch button was pressed!\n###########")
        killswitch = True

# Init the button
GPIO.add_event_detect(12, GPIO.RISING, callback=button_callback, bouncetime=150)
#GPIO.add_event_detect(12, GPIO.FALLING, callback=button_callback, bouncetime=500)


def kill_processes(): 
    """ToDo: A button must be pressed which gracefully kills all processes preparing for shutdown."""
    # Power off machines
    GPIO.output(relay_fogger, GPIO.LOW)
    GPIO.output(relay_led, GPIO.LOW)
    GPIO.output(relay_oxygen, GPIO.LOW)
    
    # Joined the threads / stop the threads after killswitch is true - Shutdown order is very important
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    t5.join()
    t6.join()
    t8.join()
    t9.join()
    t10.join()
    t7.join() # display thread last to die
    #tx.join() 
    reset_clear_lcd()
    
    # Stop message
    lcd.message = 'Full stop.\nOk to shut down.'
    
    # GPIO clearing
    GPIO.cleanup()


###################################
# Thread setup - Startup order is important
###################################
#tx = threading.Thread(target=xx, args=(killswitch,sleep_fogger,))
#tx = threading.Thread(target=killswitch_button)
t1 = threading.Thread(target=get_rpi_temp)
t2 = threading.Thread(target=get_temp_nutrient_solution)
t3 = threading.Thread(target=get_temp_humidity_nutrient_mist)
t4 = threading.Thread(target=get_temp_humidity_root_chamber)
t5 = threading.Thread(target=get_pressure_root_chamber)
t6 = threading.Thread(target=get_temp_humidity_grow_area)
t7 = threading.Thread(target=lcd_display_data_controller)
t8 = threading.Thread(target=relay_fogger_control)
t9 = threading.Thread(target=relay_oxygen_control)
t10 = threading.Thread(target=relay_heatled_control)

 
# Start the threads
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t6.start()

sleep(2) # Give everything a bit extra time before the LCD starts displaying data

t7.start()
t8.start()
t9.start()
t10.start()
#tx.start()




###################################
# Code main process---
###################################
while not killswitch:
    sleep(1)




###################################
# Graceful exit
kill_processes()
###################################

Can anyone see where the error lies in or what I must change in order to make this work? As it is now, if I push the button nothing happens, no matter if I push it briefly or hold it.

0

There are 0 best solutions below