I am new to Python and hope that someone can point me in the right direction about the logical issues I have with my code.
The program works fine running and behaves, as far as I can understand, as I expect. But now when the killswitch button is being pressed (and I can see in the output that the button is pressed and that the killswitch variable is set to True) the threads continue their work without stopping. When the button is pressed I want the kill_processes-method to run. Why doesnt the threads die gracefully and the program end as I wish? What have I missed here?
# Source: rplcd.readthedocs.io/en/stable/getting_started.html
# https://www.circuitbasics.com/raspberry-pi-lcd-set-up-and-programming-in-python/
# 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)
# Imports--- [ToDo: How to import settings from a configuration file?]
import adafruit_dht, board, digitalio, threading
import adafruit_character_lcd.character_lcd as characterlcd
import RPi.GPIO as GPIO
from time import sleep, perf_counter
from gpiozero import CPUTemperature
from datetime import datetime
# 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
# Initialise 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 sensors---
dhtDevice_nutrient_mist = adafruit_dht.DHT11(board.D24, use_pulseio=False)
#dhtDevice_xx = adafruit_dht.DHT22(board.Dxx, use_pulseio=False)
#dhtDevice = adafruit_dht.DHT11(board.D24, use_pulseio=False)
# 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?
# Init relays---
GPIO.setwarnings(False)
GPIO.setup(relay_fogger, GPIO.OUT)
GPIO.setup(relay_oxygen, GPIO.OUT)
GPIO.setup(relay_led, GPIO.OUT)
# Define liquid nutrient temperature probe
liquid_nutrients_probe = 16 #digitalio.DigitalInOut(board.D16) - Why does this not work?
# Define the killswitch push button
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# Global variables---
killswitch = False
# Fogger bucket vars
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 vars
temp_roots = 0
humidity_roots = 0
# Oxygen bucket vars
sleep_oxygen = False
# Rapsberry Pi internal temperature
rpi_internal_temp = 0
# Methods---
def get_temp_nutrient_solution(killswitch): # Temp för näringsväska. KLAR för TEST
"""Measure the temperature of the nutrient solution where the ultrasonic fogger is."""
while not killswitch:
global temp_nutrient_solution
temp_nutrient_solution = 22
#lcd.message = datetime.now().strftime('%b %d %H:%M:%S\n')
#lcd.message = "Dummy temp liquid solution:/n {:.1f}C".format(temp_nutrient_solution)
# For development process
print(
"T: {:.1f} C / {:.1f} F".format(
temp_nutrient_solution, c2f(temp_nutrient_mist)
)
)
sleep(1)
def get_temp_humidity_nutrient_mist(killswitch): # Temp och fuktighet för ånga. KLAR för TEST
"""Measure the tmeperature and humidity of the nutrient mist where the ultrasonic fogger is."""
while not killswitch:
try:
# Update global temp value and humidity once per second
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(
"T: {:.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(error.args[0])
sleep(1) # sleep(1) for DHT11 and sleep(2) for DHT22
pass
except Exception as error:
dhtDevice.exit()
kill_processes() # Förbättra denna här så att den ska visa vilken DHT-enhet som har fått error
raise error
sleep(1)
def relay_fogger_control(killswitch, sleep_fogger): # Fogger av eller på
"""Fogger on for 45 min and off for 15. Perpetual mode unless kill_processes() is activated"""
while not killswitch or sleep_fogger:
GPIO.output(relay_fogger, GPIO.HIGH)
sleep(1)
#sleep(fogger_on_seconds)
GPIO.output(relay_fogger, GPIO.LOW)
sleep(1)
#sleep(fogger_off_seconds)
def relay_heatLED_control(killswitch): # Värmelampa LED av eller på
"""Heat LED controller. When is it too hot for the crops? Sleep interval? Perpetual mode unless kill_processes() is activated"""
while not killswitch:
GPIO.output(relay_led, GPIO.HIGH)
sleep(3)
#sleep(fogger_on_seconds)
GPIO.output(relay_led, GPIO.LOW)
sleep(3)
#sleep(fogger_off_seconds)
def relay_oxygen_control(killswitch, sleep_oxygen): # Syremaskin av eller på
"""Oxygen maker. Perpetual mode unless kill_processes() is activated"""
while not killswitch or sleep_oxygen:
GPIO.output(relay_oxygen, GPIO.HIGH)
sleep(5)
#sleep(fogger_on_seconds)
GPIO.output(relay_oxygen, GPIO.LOW)
#sleep(fogger_off_seconds)
sleep(5)
def kill_processes(): # Döda alla processer
"""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
t1.join()
t2.join()
t3.join()
t4.join()
t5.join()
#t6.join()
reset_clear_lcd()
# Stop message and GPIO clearing
lcd.message = 'Full stop.\r\nSafe to remove.'
GPIO.cleanup()
def reset_clear_lcd(): # Reset och rensa LCD
"""Move cursor to (0,0) and clear the screen"""
lcd.home()
lcd.clear()
def get_rpi_temp(): # Reset och rensa LCD
"""Move cursor to (0,0) and clear the screen"""
global rpi_internal_temp
cpu = CPUTemperature()
rpi_internal_temp = cpu.temperature
def c2f(temperature_c):
"""Convert Celsius to Fahrenheit"""
return temperature_c * (9 / 5) + 32
def lcd_display_data_controller(killswitch): # LCD display data controller
"""Display various measurments and data on the small LCD. Switch every four seconds."""
while not killswitch:
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(5)
reset_clear_lcd()
# Nutrient liquid temperature
lcd.message = (
"F1: {:.1f}C/{:.1f}F ".format(
temp_nutrient_solution, c2f(temp_nutrient_solution)
)
)
sleep(5)
reset_clear_lcd()
# Nutrient mist temperature and humidity
lcd.message = (
"F2: {:.1f}C/{:.1f}F \nHumidity: {}% ".format(
temp_nutrient_mist, c2f(temp_nutrient_mist), humidity_nutrient_mist
)
)
sleep(5)
reset_clear_lcd()
# Root temperature and humidity
lcd.message = (
"R1: {:.1f}C/{:.1f}F \nHumidity: {}% ".format(
temp_roots, c2f(temp_roots), humidity_roots
)
)
sleep(5)
reset_clear_lcd()
def button_callback(channel):
global killswitch
print("Button was pushed!")
killswitch = True
# Init the button
GPIO.add_event_detect(12, GPIO.RISING, callback=button_callback)
# Create the threads
#tx = threading.Thread(target=xx, args=(killswitch,sleep_fogger,))
t1 = threading.Thread(target=get_temp_nutrient_solution, args=(killswitch,))
t2 = threading.Thread(target=get_temp_humidity_nutrient_mist, args=(killswitch,))
t3 = threading.Thread(target=relay_fogger_control, args=(killswitch,sleep_fogger,))
t4 = threading.Thread(target=lcd_display_data_controller, args=(killswitch,))
t5 = threading.Thread(target=get_rpi_temp)
#t6 = threading.Thread(target=killswitch_button)
# Start the threads
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
#t6.start()
# Code main process---
while not killswitch:
sleep(1)
# Graceful exit
kill_processes()