Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 8621

General programming discussion • Re: Need help with using a scheduler to activate buttons on a GUI.

$
0
0
Well, I'm pretty satisfied with what I got for now, I added a section on the GUI to show Temperature and Humidity values coming from a AM2302/DHT22 Digital Temperature and Humidity Sensor.

It now shows time in AM an PM values instead of 0 - 24.

Here is the actual GUI:
GUI8.png
And here is the possible final code:

Code:

import tkinter as tkimport RPi.GPIO as GPIOimport threadingimport timefrom datetime import datetimeimport queuefrom apscheduler.schedulers.background import BackgroundSchedulerimport loggingimport boardimport adafruit_dht# Enable detailed logginglogging.basicConfig(level=logging.DEBUG, format='%(message)s')# GPIO SetupGPIO.setmode(GPIO.BCM)GPIO.setwarnings(False)# Define GPIO pinspins = {    "LED Shelf #1 & 2": 17,    "LED Shelf #3 & 4": 18,    "Water Sprayer": 25,    "Fans": 5,    "Water Shelf 1": 27,    "Water Shelf 2": 22,    "Water Shelf 3": 23,    "Water Shelf 4": 24}# DHT22 Sensor SetupDHT_SENSOR = adafruit_dht.DHT22(board.D4)  # Replace with actual GPIO pin# Set up GPIO as outputfor pin in pins.values():    GPIO.setup(pin, GPIO.OUT)    GPIO.output(pin, GPIO.HIGH)  # OFF state for relays# Initialize GUIwin = tk.Tk()win.title("Microgreens Farm Controller")win.geometry("800x480")# Default colors for OFF stateDEFAULT_COLORS = {    17: "#ffeb3b",    18: "#ffeb3b",    5: "#FFA500",    25: "#87CEEB",}# Checkbox control variablesshelf_enabled_vars = {pins[f"Water Shelf {i}"]: tk.IntVar() for i in range(1, 5)}# Timer valuestimer_values = {pins[f"Water Shelf {i}"]: tk.StringVar() for i in range(1, 5)}def start_timer(pin, button, duration, label):    time.sleep(duration)    GPIO.output(pin, GPIO.HIGH)    button.config(text=f"{label} OFF", bg="#87CEFA")def toggle_water_shelf(pin, button, label):    print(f"Toggling water shelf {label} with pin {pin}")    if shelf_enabled_vars[pin].get():        if GPIO.input(pin):            GPIO.output(pin, GPIO.LOW)            button.config(text=f"{label} ON", bg="#2ecc71")            print(f"Water shelf {label} turned ON")            try:                duration = int(timer_values[pin].get())            except ValueError:                duration = 10            threading.Thread(target=start_timer, args=(pin, button, duration, label), daemon=True).start()            logging.debug(f"Water Shelf {label} turned ON for {duration} seconds.")        else:            GPIO.output(pin, GPIO.HIGH)            button.config(text=f"{label} OFF", bg="#87CEFA")            logging.debug(f"Water Shelf {label} turned OFF.")    else:        logging.debug(f"Watering not enabled for {label}.")# Scheduler variablesday_vars = {day: tk.IntVar() for day in ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}hour_vars = {hour: tk.IntVar() for hour in range(24)}schedule_shelf_vars = {pins[f"Water Shelf {i}"]: tk.IntVar() for i in range(1, 5)}# Light cycle settingsled_on_durations = {pin: tk.StringVar(value="16") for pin in [17, 18]}led_off_durations = {pin: tk.StringVar(value="8") for pin in [17, 18]}# Scheduler setupscheduler = BackgroundScheduler()scheduler.start()def apply_led_schedule(pin, button, label):    """Applies the light schedule for the given LED."""    try:        on_duration = int(led_on_durations[pin].get())        off_duration = int(led_off_durations[pin].get())    except ValueError:        logging.error("Invalid duration input. Using default 12 ON / 6 OFF.")        on_duration, off_duration = 12, 6    def turn_on():        if GPIO.input(pin) == 0:  # Only turn ON if it was previously ON            GPIO.output(pin, GPIO.LOW)            button.config(text=f"{label} ON", bg="#2ecc71")            logging.info(f"{label} turned ON")            scheduler.add_job(turn_off, 'interval', hours=on_duration, id=f"led_off_{pin}")        def turn_off():        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS[pin])        logging.info(f"{label} turned OFF")        scheduler.add_job(turn_on, 'interval', hours=off_duration, id=f"led_on_{pin}")        turn_on()def toggle_device(pin, button, label):    """Manually toggles LED state and cancels the schedule if turned OFF manually."""    if GPIO.input(pin):        GPIO.output(pin, GPIO.LOW)        button.config(text=f"{label} ON", bg="#2ecc71")    else:        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"))        scheduler.remove_job(f"led_off_{pin}", jobstore=None)        scheduler.remove_job(f"led_on_{pin}", jobstore=None)# Main layoutframe = tk.Frame(win)frame.pack(expand=True, fill="both", pady=5)left_frame = tk.Frame(frame)left_frame.grid(row=0, column=0, padx=18, sticky="n")water_shelf_frame = tk.Frame(frame)water_shelf_frame.grid(row=0, column=1, padx=5, sticky="n")def create_toggle_button(label, pin, row):    button = tk.Button(left_frame, text=label + " OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"), width=16, height=2)    button.config(command=lambda: toggle_device(pin, button, label))    button.grid(row=row, column=0, padx=2, pady=2, sticky="w")    return buttoncontrols = [    ("LED Shelf #1 & 2", 17),    ("LED Shelf #3 & 4", 18),    ("Water Sprayer", 25),    ("Fans", 5),]for i, (label, pin) in enumerate(controls):    create_toggle_button(label, pin, i)def create_water_shelf_button(label, pin, row):    button = tk.Button(water_shelf_frame, text=label + " OFF", bg="#87CEFA", width=16, height=2)    button.config(command=lambda: toggle_water_shelf(pin, button, label))    button.grid(row=row, column=0, padx=2, pady=2, sticky="w")        shelf_checkbox = tk.Checkbutton(water_shelf_frame, text="Enable Watering", variable=shelf_enabled_vars[pin])    shelf_checkbox.grid(row=row, column=1, padx=2, pady=5, sticky="w")        timer_entry = tk.Entry(water_shelf_frame, textvariable=timer_values[pin], width=5)    timer_entry.grid(row=row, column=2, padx=2, pady=5, sticky="e")    timer_values[pin].set("10")    timer_label = tk.Label(water_shelf_frame, text="Seconds")    timer_label.grid(row=row, column=3, padx=2, pady=2, sticky="e")    return buttonwater_shelf_buttons = {}for i in range(1, 5):    water_shelf_buttons[pins[f"Water Shelf {i}"]] = create_water_shelf_button(f"Water Shelf {i}", pins[f"Water Shelf {i}"], i)# Scheduler Framescheduler_frame = tk.Frame(win)scheduler_frame.pack(side="top", pady=1)# Scheduler (Shelves Section)shelves_frame = tk.Frame(win)shelves_frame.pack(side="top", pady=0)# Scheduler (Days Section)days_frame = tk.Frame(win)days_frame.pack(side="top", pady=0)# Scheduler (Hours Section)hours_frame = tk.Frame(win)hours_frame.pack(side="top", pady=0)# Scheduler (Set Schedule Button)setButton_frame = tk.Frame(win)setButton_frame.pack(side="top", pady=0)# Function to toggle devicesdef toggle_device(pin, button, label):    if GPIO.input(pin):        GPIO.output(pin, GPIO.LOW)        button.config(text=f"{label} ON", bg="#2ecc71")    else:        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"))                def schedule_watering():    selected_days = [day for day, var in day_vars.items() if var.get() == 1]    selected_hours = [hour for hour, var in hour_vars.items() if var.get() == 1]    selected_shelves = [shelf for shelf, var in schedule_shelf_vars.items() if var.get() == 1]    # Create popup window to confirm schedule    schedule_popup = tk.Toplevel(win, padx=20, pady=10)    schedule_popup.title("Schedule Confirmation")    tk.Label(schedule_popup, text="Scheduled Confirmation", font=("Arial", 14), padx=30, pady=5).pack()    tk.Label(    schedule_popup,     text=f"Scheduled: {', '.join([f'Water Shelf {list(pins.keys())[list(pins.values()).index(s)].split()[-1]} ({timer_values[s].get()}s)' for s in selected_shelves])}").pack()    tk.Label(schedule_popup, text=f"Scheduled Days: {', '.join(selected_days)}").pack()    tk.Label(schedule_popup, text=f"Scheduled Hours: {', '.join(map(str, selected_hours))}").pack()    for day in selected_days:        for hour in selected_hours:            for shelf in selected_shelves:                shelf_label = f"Water Shelf {list(pins.values()).index(shelf)}"  # Get correct label                                scheduler.add_job(                    lambda s=shelf, l=shelf_label: toggle_water_shelf(s, water_shelf_buttons[s], l),                    'cron', day_of_week=day.lower(), hour=hour, minute=0                )tk.Label(scheduler_frame, text="Watering Schedule", font=("Arial", 12, "bold")).grid(row=0, column=2, columnspan=8)# Days selectiontk.Label(days_frame, text="Days:").grid(row=1, column=0, sticky="n")for i, (day, var) in enumerate(day_vars.items()):    tk.Checkbutton(days_frame, text=day, variable=var).grid(row=1, column=i+1, sticky="w")# Hours selection with AM/PM labelstk.Label(hours_frame, text="Hours:").grid(row=2, column=0, sticky="w")hour_vars = {hour: tk.IntVar() for hour in range(24)}for i, (hour, var) in enumerate(hour_vars.items()):    am_pm_label = f"{hour % 12 if hour % 12 else 12}{'am' if hour < 12 else 'pm'}"    tk.Checkbutton(hours_frame, text=am_pm_label, variable=var).grid(row=2 + (i // 12), column=(i % 12) + 1, sticky="w")# Shelves selectiontk.Label(shelves_frame, text="Shelves:").grid(row=3, column=0, sticky="w")for i, (shelf, var) in enumerate(schedule_shelf_vars.items()):    tk.Checkbutton(shelves_frame, text=f"Shelf {i+1}", variable=var).grid(row=3, column=i+1, sticky="w")# Schedule Buttontk.Button(setButton_frame, text="Set Schedule", bg="green", fg="white", command=schedule_watering).grid(row=1, column=2, columnspan=8, pady=0)def enqueue_event(shelf):    logging.debug(f"Enqueueing event for {shelf}")    print(f"Watering {shelf} for the scheduled duration.")        # LED Controlsfor i, (label, pin) in enumerate([("LED Shelf #1 & 2", 17), ("LED Shelf #3 & 4", 18)]):    button = tk.Button(left_frame, text=label + " OFF", bg=DEFAULT_COLORS[pin], width=16, height=2)    button.config(command=lambda p=pin, b=button, l=label: toggle_device(p, b, l))    button.grid(row=i, column=0, padx=2, pady=2, sticky="w")        tk.Label(left_frame, text="ON").grid(row=i, column=1)    tk.Entry(left_frame, textvariable=led_on_durations[pin], width=2).grid(row=i, column=2)        tk.Label(left_frame, text="OFF").grid(row=i, column=3)    tk.Entry(left_frame, textvariable=led_off_durations[pin], width=2).grid(row=i, column=4)    tk.Label(left_frame, text="Hrs").grid(row=i, column=7)        tk.Button(left_frame, text="Set", command=lambda p=pin, b=button, l=label: apply_led_schedule(p, b, l)).grid(row=i, column=5, padx=2)"""Reads temperature and humidity from DHT22 and updates the GUI."""def update_temp_humidity():    try:        temperature = DHT_SENSOR.temperature        humidity = DHT_SENSOR.humidity    except RuntimeError as error:        logging.error(f"Sensor error: {error}")        temperature, humidity = None, None    if humidity is not None and temperature is not None:        temp_label.config(text=f"Temp: {temperature:.1f}\u00B0C")        humidity_label.config(text=f"Humidity: {humidity:.1f}%")    else:        temp_label.config(text="Temp: --")        humidity_label.config(text="Humidity: --")        win.after(5000, update_temp_humidity)  # Refresh every 5 seconds# Temperature & Humidity Sensor Frametemp_frame = tk.Frame(win)temp_frame.pack(side="top", pady=0)temp_label = tk.Label(temp_frame, text="Temp: --", font=("Arial", 12))temp_label.pack(side="left", padx=5)humidity_label = tk.Label(temp_frame, text="Humidity: --", font=("Arial", 12))humidity_label.pack(side="right", padx=5)update_temp_humidity()        # Exit program functiondef stopProgram():    scheduler.shutdown()    GPIO.cleanup()    win.quit()tk.Button(win, text="Exit", bg="#c0392b", fg="white", width=15, height=2, command=stopProgram).pack(side="bottom", pady=0)win.mainloop()
Pretty cool project...

Cheers,

Statistics: Posted by papagino — Tue Apr 01, 2025 11:34 pm



Viewing all articles
Browse latest Browse all 8621

Trending Articles