#!/usr/bin/env python #coding=utf-8 ############################################ ########### GENERAL INFORMATION ############ ############################################ # Pinout for assembly: # # 3V3 (1) (2) 5V # - (3) (4) 5V # - (5) (6) GND # - (7) (8) - # GND (9) (10) - # - (11) (12) - # - (13) (14) GND # - (15) (16) pin_blue # 3V3 (17) (18) pin_green # - (19) (20) GND # - (21) (22) pin_red # - (23) (24) - # GND (25) (26) - # - (27) (28) - # - (29) (30) GND # - (31) (32) pin_start # pin_error (33) (34) GND # - (35) (36) pin_shutdown # - (37) (38) - # GND (39) (40) - # default state of pins: # # 32: pull up # 33: pull up # 36: pull up # # make sure leds are on pull-up default pins # so when their state is undefined, the led # strips are switched off! # # 22: pull up # 18: pull up # 16: pull up ############################################ ############# START OF IMPORTS ############# ############################################ import pygame, signal, sys, time, os, RPi.GPIO as GPIO from PIL import Image from pygame.locals import * ############################################ ############## END OF IMPORTS ############## ############################################ ############################################ ########### START OF DEFINITIONS ########### ############################################ # base pygame settings pygame_fps = 30 #frames per second setting pygame_clock = pygame.time.Clock() # GPIO for Buttons pin_start = 32 pin_error = 33 pin_shutdown = 36 # GPIO for LED pin_red = 22 pin_green = 18 pin_blue = 16 # other constants # every time the wire is touched, some time is added as penalty. time_per_error = 5 # screen settings screen_size_x = 1920 screen_size_y = 1080 # name length for highscores max_name_length = 10 # preset highscores hs1_name = "Fritz" hs2_name = "Bernd" hs3_name = "Max" hs1_time = 100 hs2_time = 200 hs3_time = 300 ############################################ ############ END OF DEFINITIONS ############ ############################################ ############################################ ####### START OF PRE-INITIALISATION ######## ############################################ # here goes stuff that is requires by functions # so they can be properly defined # initialise the game pygame.init() # fonts pygame_font_1 = pygame.font.Font('freesansbold.ttf', 90) pygame_font_2 = pygame.font.Font('freesansbold.ttf', 65) pygame_font_3 = pygame.font.Font('freesansbold.ttf', 45) # colors pygame_color_green = pygame.Color(42, 217, 13) pygame_color_black = pygame.Color(0, 0, 0) pygame_color_white = pygame.Color(255, 255, 255) pygame_color_yellow = pygame.Color(255, 215, 0) pygame_color_grey = pygame.Color(196, 202, 206) pygame_color_brown = pygame.Color(177, 86, 15) # font colors pygame_font_main_color = pygame_color_black # initialise gpio GPIO.setmode(GPIO.BOARD) GPIO.setup(pin_start, GPIO.IN) GPIO.setup(pin_error, GPIO.IN) GPIO.setup(pin_shutdown, GPIO.IN) # predefinition of led variables, for use in functions led_red = 0 led_green = 0 led_blue = 0 # RPi.GPIO does not provide getter methods for PWM, therefor # defining my own here led_red_brightness = 0 led_green_brightness = 0 led_blue_brightness = 0 # leds are turned on and can be switched off by default leds_off_toggle = False # toggle for the led update to happen every *other* evaluation # of the timer equation timer_led_updated = 0 ############################################ ######## END OF PRE-INITIALISATION ######### ############################################ ############################################ ###### START OF FUNCTION DEFINITIONS ####### ############################################ def get_image_width(filepath): return ((Image.open(filepath).size)[0]) def get_image_height(filepath): return ((Image.open(filepath).size)[1]) def signal_handler(sig, frame): # signal handler, later called to interpret # ctrl+c as "quit" rather than "abort" print('Programm exiting') exit_application() def led_init(): # initialises the LED pins # mind that the pins for the LEDs are pullup by default, # which is on purpose, since this causes the LED strip to # be turned off when the program isnt running or their state # is undefined # set the led pins to be outputs # careful: these pins dont drive the # led strip themselves, but rather 3 # n-channel mosfets, type IRLZ34N global led_red,led_green,led_blue GPIO.setup(pin_red, GPIO.OUT) GPIO.setup(pin_green, GPIO.OUT) GPIO.setup(pin_blue, GPIO.OUT) # set the pwm frequency to 100 # if you experience strobing, try changing # the number led_red = GPIO.PWM(pin_red, 100) led_blue = GPIO.PWM(pin_blue, 100) led_green = GPIO.PWM(pin_green, 100) # start pwm generation with a duty cycle of 0 led_green.start(0) led_blue.start(0) led_red.start(0) def change_red(amount): # takes an input from 0 to 255 and converts it to Duty Cycle # valid duty cycle values are between 0 and 100 # stores the new value in led_brightness_COLOUR # this value is later read by the led_handler to set the # corresponding duty cycles to allow fast interactive updating # # to be used by change_led_colour global led_red_brightness factor = 100/255 led_red_brightness = amount*factor def change_green(amount): # takes an input from 0 to 255 and converts it to Duty Cycle # valid duty cycle values are between 0 and 100 # stores the new value in led_brightness_COLOUR # this value is later read by the led_handler to set the # corresponding duty cycles to allow fast interactive updating # # to be used by change_led_colour global led_green_brightness factor = 100/255 led_green_brightness = amount*factor def change_blue(amount): # takes an input from 0 to 255 and converts it to Duty Cycle # valid duty cycle values are between 0 and 100 # stores the new value in led_brightness_COLOUR # this value is later read by the led_handler to set the # corresponding duty cycles to allow fast interactive updating # # to be used by change_led_colour global led_blue_brightness factor = 100/255 led_blue_brightness = amount*factor def change_led_colour(red_amount,green_amount,blue_amount): # sets an RGB value for the led strip. if red_amount < 0 or red_amount > 255 or green_amount < 0 or green_amount > 255 or blue_amount < 0 or blue_amount > 255: # valid rgb values should be between 0 and 255 print('invalid rgb value received: ' + str(red_amount) + ' ,' + str(green_amount) + ' ,' + str(blue_amount)) return false change_red(red_amount) change_blue(blue_amount) change_green(green_amount) if leds_constant_update = False: led_red.ChangeDutyCycle(led_red_brightness) led_green.ChangeDutyCycle(led_green_brightness) led_blue.ChangeDutyCycle(led_blue_brightness) def change_led_speed(colour, speed): # changes the speed of the LED pwm # # !!!!OBSOLETE!!!! # global led_red,led_green,led_blue if colour == "red": led_red.ChangeDutyCycle(speed) elif colour == "green": led_green.ChangeDutyCycle(speed) elif colour == "blue": led_blue.ChangeDutyCycle(speed) else: return false def led_handler(): global timer_led_blink_toggle, timer_led_switched, timer_led_updated # led blinking effect. can be toggled on or off by setting led_blink to true or false. # blinks every second- if leds_constant_update == True: if led_blink == True: # this equals to 0 every tick for one second, and 1 every tick for another second # it requires being handled in a one-shot situation since this is called # multiple times per second, and will equal to 0 or 1 multiple times per second # aswell. the real value of get_ticks()/1000 cannot be predetermined. if int((pygame.time.get_ticks()/1000))%2 == 0: if timer_led_switched == 0: timer_led_switched = 1 if timer_led_blink_toggle == 1: print('BLINK: switching leds off') leds_toggle() timer_led_blink_toggle = 0 else: print('BLINK: reverting colours') leds_toggle() timer_led_blink_toggle = 1 else: timer_led_switched = 0 # update the led duty cycle # may cause flickering, not sure :) # TODO: test this global led_red,led_green,led_blue # dont update the pwm for the LEDs when they're supposed to be off # instead, just copy the needed values to the 3 brightness variables! # the values will be correctly put in once the toggle is back if leds_constant_update == True: if leds_off_toggle == False: if int((pygame.time.get_ticks()/100))%20 == 0: if timer_led_updated == 0: timer_led_updated = 1 led_red.ChangeDutyCycle(led_red_brightness) led_green.ChangeDutyCycle(led_green_brightness) led_blue.ChangeDutyCycle(led_blue_brightness) else: timer_led_updated = 0 def leds_toggle(): global led_red,led_green,led_blue,leds_off_toggle if leds_off_toggle == False: led_red.ChangeDutyCycle(0) led_green.ChangeDutyCycle(0) led_blue.ChangeDutyCycle(0) leds_off_toggle = True else: leds_off_toggle = False led_red.ChangeDutyCycle(led_red_brightness) led_green.ChangeDutyCycle(led_green_brightness) led_blue.ChangeDutyCycle(led_blue_brightness) def toggle_fullscreen(): screen = pygame.display.get_surface() tmp = screen.convert() caption = pygame.display.get_caption() cursor = pygame.mouse.get_cursor() # Duoas 16-04-2007 w,h = screen.get_width(),screen.get_height() flags = screen.get_flags() bits = screen.get_bitsize() pygame.display.quit() pygame.display.init() screen = pygame.display.set_mode((w,h),flags^FULLSCREEN,bits) screen.blit(tmp,(0,0)) pygame.display.set_caption(*caption) pygame.key.set_mods(0) # HACK: work-a-round for a SDL bug?? pygame.mouse.set_cursor( *cursor ) # Duoas 16-04-2007 return screen def clear_screen(): #print("filled screen") screen.fill(pygame_color_white) return def handle_events(): for event in pygame.event.get(): if event.type == KEYDOWN and event.key == K_ESCAPE: exit_application() # disabled in favour of the shutdown pin #elif event.type == KEYDOWN and event.key == K_q: # shutdown_raspberry() return # shutdown raspberry if shutdown pin is detected if (GPIO.INPUT(pin_shutdown)): shutdown_raspberry() def exit_application(): pygame.quit() GPIO.cleanup() sys.exit() return def shutdown_raspberry(): pygame.quit() GPIO.cleanup() os.system("sudo shutdown -h now") return def enter_name(): clear_screen() print('text entry started') name = '' while True: clear_screen() highscore_surface = pygame_font_1.render('High Score!', True, pygame_font_main_color) highscore_rectangle = highscore_surface.get_rect() highscore_rectangle.topleft = (700, 250) screen.blit(highscore_surface, highscore_rectangle) textbox_text_surface = pygame_font_2.render('Enter Name:',True, pygame_font_main_color) textbox_text_rectangle = textbox_text_surface.get_rect() textbox_text_rectangle.topleft = (700, 400) textbox_surface = pygame_font_2.render(str(name), True, pygame_font_main_color) textbox_rectangle = textbox_surface.get_rect() textbox_rectangle.topleft = (800, 480) screen.blit(textbox_text_surface, textbox_text_rectangle) screen.blit(textbox_surface, textbox_rectangle) draw_border() draw_logos() pygame.display.flip() for event in pygame.event.get(): if len(name) < max_name_length: if event.type == KEYDOWN: if event.key == pygame.K_BACKSPACE: name = name[:-1] elif event.key == pygame.K_RETURN and len(name) > 0: clear_screen() return name elif event.key == pygame.K_RETURN: # do nothing pass elif event.key == pygame.K_ESCAPE: exit_application() else: name += event.unicode elif event.key == pygame.K_BACKSPACE: name = name[:-1] elif event.type == KEYDOWN and event.key == pygame.K_RETURN: clear_screen() return name clear_screen() name = 'Error' return name def check_highscores(time): global hs1_time, hs2_time, hs3_time, hs1_name, hs2_name, hs3_name if time <= hs1_time: print ('new high score:'+str(time)) # make the second the third hs3_time = hs2_time hs3_name = hs2_name # make the first the second hs2_time = hs1_time hs2_name = hs1_name # new high score hs1_time = time hs1_name = enter_name() return elif time <= hs2_time: print ('new second:'+str(time)) # make the second the third hs3_time = hs2_time hs3_name = hs2_name # new second time hs2_time = time hs2_name = enter_name() return elif time <= hs3_time: print ('new third:'+str(time)) hs3_time = time hs3_name = enter_name() return else: return def draw_border(): # draw logos screen.blit(img_itlablogo_image, (screen_size_x-img_itlablogo_imagex-20, screen_size_y-img_itlablogo_imagey-20)) # TODO: Metallic Logo def draw_logos(): # draw rectangle border around everything pygame.draw.rect(screen,pygame_color_black, (500, 200, (screen_size_x-500*2), (screen_size_y-200*2)), 5) def show_debug(): print('#############') print('Game running: ' + str(game_running)) print('Game ending: ' + str(game_ending)) print('Pin status: ') print('Start pin: ' + str(GPIO.input(pin_start))) print('Error pin: ' + str(GPIO.input(pin_error))) print('Shutdown pin: ' + str(GPIO.input(pin_shutdown))) print('#############') ############################################ ####### END OF FUNCTION DEFINITIONS ######## ############################################ ############################################ ######### START OF INITIALISATION ########## ############################################ # signal handing setup for controlled exit via ctrl+c signal.signal(signal.SIGINT, signal_handler) # hide mouse from screen pygame.mouse.set_visible(False) # start fullscreen mode with defined screen geometrics screen = pygame.display.set_mode((screen_size_x, screen_size_y), pygame.FULLSCREEN) #pygame.display.set_caption('Heisser Draht') # not required for fullscreen application # currently unused because its not needed #screen = toggle_fullscreen() # define image variables img_itlablogo = 'img/itlablogo.png' img_itlablogo_image = pygame.image.load(img_itlablogo) img_itlablogo_imagex = get_image_width(img_itlablogo) img_itlablogo_imagey = get_image_height(img_itlablogo) # TODO: Metallic Logo einfügen #img_metalliclogo = 'img/metalliclogo.png' #img_metalliclogo_image = pygame.image.load(img_metalliclogo) #img_metalliclogo_imagex = get_image_width(img_metalliclogo) #img_metalliclogo_imagey = get_image_height(img_metalliclogo) # initialise led strip led_init() game_running = False game_just_started = True game_ending = False highscore_checked = False pin_start_inhibit = False timer_game_ending_started = False # test: enable led blinking permanently led_blink = True timer_led_blink_toggle = 1 led_red_brightness_temp = 0 led_green_brightness_temp = 0 led_blue_brightness_temp = 0 timer_led_switched = 0 ############################################ ########## END OF INITIALISATION ########### ############################################ ############################################ ######### START OF MAIN GAME LOOP ########## ############################################ while True: # default actions to be done every cycle clear_screen() handle_events() show_debug() led_handler() print(str(pygame.time.get_ticks())) print(str(int((pygame.time.get_ticks()/100))%2)) if game_running == False and game_ending == False: # one shot for changing the led colour if game_just_started == True: change_led_colour(10,140,10) game_just_started = False # reset all the surfaces to be empty time_surface = 0 errors_surface = 0 time_rectangle = 0 errors_rectangle = 0 # fill all surfaces again to contain the proper text header_surface = pygame_font_1.render('Dr' + u'ü' + 'cken Sie Start!', True, pygame_font_main_color) header_rectangle = header_surface.get_rect() header_rectangle.topleft = (560, 250) highscore_header_surface = pygame_font_2.render('Highscores:', True, pygame_font_main_color) highscore_header_rectangle = highscore_header_surface.get_rect() highscore_header_rectangle = (770, 410) score1_surface = pygame_font_3.render(hs1_name + ": " + str(hs1_time) + 's', True, pygame_color_yellow) score1_rectangle = score1_surface.get_rect() score1_rectangle.topleft = (820, 540) score2_surface = pygame_font_3.render(hs2_name + ": " + str(hs2_time) + 's', True, pygame_color_grey) score2_rectangle = score2_surface.get_rect() score2_rectangle.topleft = (820, 630) score3_surface = pygame_font_3.render(hs3_name + ": " + str(hs3_time) + 's', True, pygame_color_brown) score3_rectangle = score3_surface.get_rect() score3_rectangle.topleft = (820, 720) # draw headline, highscore headline, the 3 scores, as well as the logos to the screen screen.blit(header_surface, header_rectangle) screen.blit(highscore_header_surface, highscore_header_rectangle) screen.blit(score1_surface, score1_rectangle) screen.blit(score2_surface, score2_rectangle) screen.blit(score3_surface, score3_rectangle) # display everything #pygame.display.flip() # event handling for if the start button is pushed if GPIO.input(pin_start) == True and pin_start_inhibit == False: #wait for the user to press start button # change game state to running and not ending game_running = True game_ending = False # set an inhibitor bit to stop the contact from possibly vibrating and # causing a stop of the game immediately after start pin_start_inhibit = True timer_pin_start_inhibit_starttime = pygame.time.get_ticks() # reset errors errors = 0 error_added = False # for the 2 tick error counter system, see blow # change the headline header_surface = pygame_font_1.render('Spiel l' + u'ä' + 'uft!', True, pygame_font_main_color) # start the timer start_time = int(time.time()) # change led colour to blue change_led_colour(10,10,100) if game_running == True and game_ending == False: # if the start pin is inhibited and at least 3 seconds have passed, # one shot: stop blocking the pin if pin_start_inhibit == True: if ((pygame.time.get_ticks()-timer_pin_start_inhibit_starttime)/1000) > 3: pin_start_inhibit = False # add errors if error pin is detected, only once per 2 ticks # TODO: change system to be time based (only 1 error per 500ms or something) if GPIO.input(pin_error) == True: if error_added == False: errors += 1 int_time = int(time.time()) - start_time + errors * time_per_error error_added = True # for the 2 tick system else: error_added = False # for the 2 tick system # calculate the current time int_time = int(time.time()) - start_time # fill the surface with the current time and errors time_surface = pygame_font_2.render('Zeit: ' + str(int_time) + 's', True, pygame_font_main_color) time_rectangle = time_surface.get_rect() time_rectangle.topleft = (640, 480) errors_surface = pygame_font_2.render('Fehler: ' + str(errors), True, pygame_font_main_color) errors_rectangle = errors_surface.get_rect() errors_rectangle.topleft = (640, 560) clear_screen() # draw errors, time and headline to the screen screen.blit(errors_surface, errors_rectangle) #errors screen.blit(time_surface, time_rectangle) #time screen.blit(header_surface, header_rectangle) #header # display everything #pygame.display.flip() # if another push of start is detected (i.e. the game is ending!) if GPIO.input(pin_start) == True and pin_start_inhibit == False: # change led colour to red change_led_colour(100,10,10) # reset the one shot latch for the end of game timer timer_game_ending_started = False # change the state to not running and ending game_running = False game_ending = True # set the start inhibitor to prevent an instant restart # TODO: maybe unneeded due to the strict game state pin_start_inhibit = True if game_running == False and game_ending == True: # check highscores only once when game ended if highscore_checked == False: check_highscores(int_time) highscore_checked = True # timer for ending the game after 5 seconds if timer_game_ending_started == False: timer_game_ending_timout = pygame.time.get_ticks() timer_game_ending_started = True clear_screen() # change the headline to game over header_surface = pygame_font_1.render('Game over!', True, pygame_font_main_color) # draw errors, time, and new headline to the screen screen.blit(errors_surface, errors_rectangle) #errors screen.blit(time_surface, time_rectangle) #time screen.blit(header_surface, header_rectangle) #header # display everything #pygame.display.flip() # unneeded timer, replaced by tick based system #time.sleep(5) # if 5 seconds have passed since the game state changed to ending (see: timer_game_ending_timeout) if ((pygame.time.get_ticks()-timer_game_ending_timout)/1000) > 5: # reset game_just_started flag to enable the one shot led change in the first state game_just_started = True # change state game_running = False game_ending = False # reset highscore one shot highscore_checked = False # reset timer started one shot timer_game_ending_started = False # unblock the start pin pin_start_inhibit = False if game_running == True and game_ending == True: # something went horribly wrong... # this state is never supposed to be reached pass # overlaying structure - this is to be drawn no matter what's below draw_border() draw_logos() pygame.display.flip() pygame.display.update() pygame_clock.tick(pygame_fps) ############################################ ########## END OF MAIN GAME LOOP ########### ############################################