From c0014aa7d9d9a2f73e0198219ef05ca6a7c7cf4a Mon Sep 17 00:00:00 2001 From: jerick Date: Tue, 12 Aug 2025 08:31:38 -0400 Subject: [PATCH] functioning start stop button --- random_timer.py | 238 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 169 insertions(+), 69 deletions(-) diff --git a/random_timer.py b/random_timer.py index a3c3a63..72e9bb2 100755 --- a/random_timer.py +++ b/random_timer.py @@ -1,87 +1,187 @@ #!/usr/bin/env python3 -import time -import threading import RPi.GPIO as GPIO +import time +import random +import threading from RPLCD.i2c import CharLCD -# GPIO pin setup for buttons -BUTTON_START_STOP = 17 +# --- CONFIG --- +WORDS_FILE = "words.txt" +TIMER_SECONDS = 30 +I2C_ADDRESS = 0x27 +I2C_CHIP = 'PCF8574' + +# --- GPIO SETUP --- +GPIO.setmode(GPIO.BCM) + +# Buttons mapped to pins +BUTTON_START_STOP = 23 BUTTON_NEXT = 27 BUTTON_TEAM1 = 22 +BUTTON_TEAM2 = 17 -GPIO.setmode(GPIO.BCM) -GPIO.setup(BUTTON_START_STOP, GPIO.IN, pull_up_down=GPIO.PUD_UP) -GPIO.setup(BUTTON_NEXT, GPIO.IN, pull_up_down=GPIO.PUD_UP) -GPIO.setup(BUTTON_TEAM1, GPIO.IN, pull_up_down=GPIO.PUD_UP) +for btn_pin in (BUTTON_START_STOP, BUTTON_NEXT, BUTTON_TEAM1, BUTTON_TEAM2): + GPIO.setup(btn_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) -# LCD setup (adjust address and cols/rows if needed) -lcd = CharLCD('PCF8574', 0x27) +# --- LCD SETUP --- +lcd = CharLCD(I2C_CHIP, I2C_ADDRESS) +lcd.clear() -# Game variables -team1_score = 0 -team2_score = 0 -timer_running = False +# --- GLOBALS --- +words = [] timer_thread = None -seconds = 0 -winner_displayed = False +timer_running = False +time_left = TIMER_SECONDS +score_t1 = 0 +score_t2 = 0 +current_word = "" -# Timer function -def timer(): - global seconds, timer_running - while timer_running: - seconds += 1 - time.sleep(1) +lock = threading.Lock() # Protect shared state -def update_lcd(): - global winner_displayed - lcd.clear() - if winner_displayed: - if team1_score >= 7: - lcd.write_string("Team 1 Wins!") - elif team2_score >= 7: - lcd.write_string("Team 2 Wins!") - else: - lcd.write_string(f"T1:{team1_score} T2:{team2_score}") - lcd.crlf() - lcd.write_string(f"Time: {seconds}s") +# --- LOAD WORDS --- +def load_words(path=WORDS_FILE): + try: + with open(path, "r", encoding="utf-8") as f: + lines = [line.strip() for line in f if line.strip()] + if not lines: + raise ValueError("Word file is empty.") + return lines + except Exception as e: + print(f"❌ Error loading words: {e}") + exit(1) -def start_stop_timer(channel): - global timer_running, timer_thread - if not timer_running: - timer_running = True - threading.Thread(target=timer, daemon=True).start() - else: - timer_running = False +# --- LCD HELPERS --- +def lcd_print_lines(line1="", line2=""): + lcd.clear() + lcd.cursor_pos = (0, 0) + lcd.write_string(line1.ljust(16)[:16]) + lcd.cursor_pos = (1, 0) + lcd.write_string(line2.ljust(16)[:16]) -def next_pressed(channel): - global team1_score, team2_score, winner_displayed - if not winner_displayed: - team2_score += 1 - check_winner() - update_lcd() +def lcd_show_word_and_timer(word, seconds): + lcd.clear() + lcd.cursor_pos = (0, 0) + lcd.write_string(f"Time:{seconds:02d} ") + lcd.cursor_pos = (1, 0) + lcd.write_string(word.center(16)) -def team1_pressed(channel): - global team1_score, winner_displayed - if not winner_displayed: - team1_score += 1 - check_winner() - update_lcd() +def lcd_show_scores(): + lcd.clear() + lcd.write_string(f"T1:{score_t1} T2:{score_t2}".center(16)) -def check_winner(): - global winner_displayed, timer_running - if team1_score >= 7 or team2_score >= 7: - winner_displayed = True - timer_running = False +def lcd_show_winner(winner_team): + lcd.clear() + lcd.write_string(f"🏆 Team {winner_team} Wins!".center(16)) -# Attach button event listeners -GPIO.add_event_detect(BUTTON_START_STOP, GPIO.FALLING, callback=start_stop_timer, bouncetime=300) -GPIO.add_event_detect(BUTTON_NEXT, GPIO.FALLING, callback=next_pressed, bouncetime=300) -GPIO.add_event_detect(BUTTON_TEAM1, GPIO.FALLING, callback=team1_pressed, bouncetime=300) +# --- TIMER THREAD --- +class CountdownTimer(threading.Thread): + def __init__(self, seconds): + super().__init__() + self.seconds = seconds + self._running = threading.Event() + self._running.set() -try: - while True: - update_lcd() - time.sleep(0.5) -except KeyboardInterrupt: - GPIO.cleanup() - lcd.clear() + def run(self): + global time_left, timer_running + time_left = self.seconds + while time_left > 0 and self._running.is_set(): + with lock: + lcd_show_word_and_timer(current_word, time_left) + time.sleep(1) + time_left -= 1 + with lock: + timer_running = False + lcd_show_scores() + + def stop(self): + self._running.clear() + +# --- GAME LOGIC --- +def pick_random_word(): + global current_word + current_word = random.choice(words) + +def start_stop_button_callback(channel): + print("Start Button Pressed") + global timer_running, timer_thread + with lock: + if timer_running: + # Stop timer + if timer_thread: + timer_thread.stop() + timer_thread.join() + timer_running = False + lcd_show_scores() + else: + # Start timer & show first word + pick_random_word() + timer_thread = CountdownTimer(TIMER_SECONDS) + timer_thread.daemon = True + timer_running = True + timer_thread.start() + +def next_button_callback(channel): + global current_word + with lock: + if timer_running: + pick_random_word() + +def team1_button_callback(channel): + global score_t1 + with lock: + if timer_running: + score_t1 += 1 + if score_t1 >= 7: + timer_running = False + lcd_show_winner(1) + reset_game_after_delay() + else: + lcd_show_scores() + +def team2_button_callback(channel): + global score_t2 + with lock: + if timer_running: + score_t2 += 1 + if score_t2 >= 7: + timer_running = False + lcd_show_winner(2) + reset_game_after_delay() + else: + lcd_show_scores() + +def reset_game_after_delay(delay=5): + def reset(): + global score_t1, score_t2, current_word + time.sleep(delay) + with lock: + score_t1 = 0 + score_t2 = 0 + current_word = "" + lcd_show_scores() + threading.Thread(target=reset, daemon=True).start() + +# --- Setup GPIO event detection --- +GPIO.add_event_detect(BUTTON_START_STOP, GPIO.FALLING, callback=start_stop_button_callback, bouncetime=300) +GPIO.add_event_detect(BUTTON_NEXT, GPIO.FALLING, callback=next_button_callback, bouncetime=300) +GPIO.add_event_detect(BUTTON_TEAM1, GPIO.FALLING, callback=team1_button_callback, bouncetime=300) +GPIO.add_event_detect(BUTTON_TEAM2, GPIO.FALLING, callback=team2_button_callback, bouncetime=300) + +# --- Main program --- +def main(): + global words + words = load_words() + lcd_show_scores() + print("Catchphrase started! Use buttons to play.") + + try: + while True: + time.sleep(0.1) + except KeyboardInterrupt: + pass + finally: + GPIO.cleanup() + lcd.clear() + +if __name__ == "__main__": + main()