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.