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:And here is the possible final code:
Pretty cool project...
Cheers,
It now shows time in AM an PM values instead of 0 - 24.
Here is the actual GUI: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()Cheers,
Statistics: Posted by papagino — Tue Apr 01, 2025 11:34 pm