class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display self.led = ssd1306(I2CBUS) # Change rows & cols values depending on your display dimensions. logo = Image.open('pi_logo.png') self.led.canvas.bitmap((32, 0), logo, fill=0) self.led.display() # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # print "Done initializing" @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): self.running = True GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) time.sleep(1) self.running = False def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: drink_opts.append(MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p]["value"] else "" config.addOption(MenuItem('pump_selection', opt["name"], {"key": p, "value": opt["value"], "name": opt["name"]})) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif(menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]]["value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif(menuItem.type == "clean"): self.clean() return True return False def clean(self): pins = [] for pump in self.pump_configuration.keys(): pins.append(self.pump_configuration[pump]["pin"]) self.startProgressBar() GPIO.output(pins, GPIO.LOW) self.sleepAndProgress(time.time(),20,20) GPIO.output(pins, GPIO.HIGH) # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2); def displayMenuItem(self, menuItem): print (menuItem.name) self.led.cls() self.led.canvas.text((0,20),menuItem.name, font=FONT, fill=1) self.led.display() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def startProgressBar(self,x=15,y=20): start_time = time.time() self.led.cls() self.led.canvas.text((10,20),"Dispensing...", font=FONT, fill=1) def sleepAndProgress(self, startTime, waitTime, totalTime, x=15, y=35): localStartTime = time.time() height = 10 width = self.screen_width-2*x while time.time() - localStartTime < waitTime: progress = (time.time() - startTime)/totalTime p_loc = int(progress*width) self.led.canvas.rectangle((x,y,x+width,y+height), outline=255, fill=0) self.led.canvas.rectangle((x+1,y+1,x+p_loc,y+height-1), outline=255, fill=1) try: self.led.display() except IOError: print("Failed to talk to screen") time.sleep(0.2) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made self.running = True # Parse the drink ingredients and create pouring data pumpTimes = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] * FLOW_RATE pumpTimes.append([self.pump_configuration[pump]["pin"], waitTime]) # Put the drinkjs in the order they'll stop pouring pumpTimes.sort(key=lambda x: x[1]) # Note the total time required to pour the drink totalTime = pumpTimes[-1][1] # Change the times to be relative to the previous not absolute for i in range(1,len(pumpTimes)): pumpTimes[i][1] -= pumpTimes[i-1][1] print(pumpTimes) self.startProgressBar() startTime = time.time() print("starting all") GPIO.output([p[0] for p in pumpTimes], GPIO.LOW) for p in pumpTimes: pin, delay = p if delay > 0: self.sleepAndProgress(startTime, delay, totalTime) GPIO.output(pin, GPIO.HIGH) print("stopping {}".format(pin)) # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) def left_btn(self, ctx): print("LEFT_BTN pressed") if not self.running: self.running = True self.menuContext.advance() print("Finished processing button press") self.running = False def right_btn(self, ctx): print("RIGHT_BTN pressed") if not self.running: self.running = True self.menuContext.select() print("Finished processing button press") self.running = 2 print("Starting button timeout") def run(self): self.startInterrupts() # main loop try: try: while True: letter = raw_input(">") if letter == "l": self.left_btn(False) if letter == "r": self.right_btn(False) except EOFError: while True: time.sleep(0.1) if self.running not in (True,False): self.running -= 0.1 if self.running == 0: self.running = False print("Finished button timeout") except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit finally: GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure screen # spi_bus = 0 # spi_device = 0 # gpio = gaugette.gpio.GPIO() # spi = gaugette.spi.SPI(spi_bus, spi_device) serial = i2c(port=1, address=0x3C) device = sh1106(serial) # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display # self.led = gaugette.ssd1306.SSD1306(gpio, spi, reset_pin=OLED_RESET_PIN, dc_pin=OLED_DC_PIN, rows=self.screen_height, cols=self.screen_width) # Change rows & cols values depending on your display dimensions. self.led = device self.oled_font = ImageFont.truetype('FreeSans.ttf', 12) # self.led.begin() # self.led.clear_display() # self.led.display() # self.led.invert_display() # time.sleep(0.5) # self.led.normal_display() time.sleep(0.5) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN #self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin) #self.strip.begin() # Initialize pins for output # Limit brightness to ~1/4 duty cycle #self.strip.setBrightness(NEOPIXEL_BRIGHTNESS) # turn everything off # for i in range(0, self.numpixels): # self.strip.setPixelColor(i, 0) # self.strip.show() print("Done initializing") @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) def stopInterrupts(self): GPIO.remove_event_detect(self.btn1Pin) GPIO.remove_event_detect(self.btn2Pin) def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): self.clean() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def displayMenuItem(self, menuItem): print(menuItem.name) # self.led.clear_display() # self.led.draw_text2(0,20,menuItem.name,2) # self.led.display() with canvas(self.led) as draw: draw.rectangle(self.led.bounding_box, outline="white", fill="black") draw.text((10, 10), menuItem.name, font=self.oled_font, fill="white") def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = 0xFF0000 # 'On' color (starts red) # while getattr(t, "do_run", True): # self.strip.setPixelColor(head, color) # Turn on 'head' pixel # self.strip.setPixelColor(tail, 0) # Turn off 'tail' # self.strip.show() # Refresh strip # time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) # head += 1 # Advance head position # if(head >= self.numpixels): # Off end of strip? # head = 0 # Reset to start # color >>= 8 # Red->green->blue->black # if(color == 0): # color = 0xFF0000 # If black, reset to red # tail += 1 # Advance tail position # if(tail >= self.numpixels): # tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights green # for i in range(0, self.numpixels): # self.strip.setPixelColor(i, 0xFF0000) # self.strip.show() time.sleep(5) # turn lights off # for i in range(0, self.numpixels): # self.strip.setPixelColor(i, 0) # self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def progressBar(self, waitTime): interval = waitTime / 100.0 # for x in range(1, 101): # self.led.clear_display() # self.updateProgressBar(x, y=35) # self.led.display() # time.sleep(interval) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True # launch a thread to control lighting # lightsThread = threading.Thread(target=self.cycleLights) # lightsThread.start() # Parse the drink ingredients and spawn threads for pumps maxTime = 0 pumpThreads = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] / float( self.pump_configuration[pump]["mlpersec"]) if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # stop the light thread #lightsThread.do_run = False #lightsThread.join() # show the ending sequence lights # self.lightsEndingSequence() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def left_btn(self, ctx): if not self.running: self.menuContext.advance() def right_btn(self, ctx): if not self.running: self.menuContext.select() def updateProgressBar(self, percent, x=15, y=15): height = 10 width = self.screen_width - 2 * x # for w in range(0, width): # self.led.draw_pixel(w + x, y) # self.led.draw_pixel(w + x, y + height) # for h in range(0, height): # self.led.draw_pixel(x, h + y) # self.led.draw_pixel(self.screen_width-x, h + y) # for p in range(0, percent): # p_loc = int(p/100.0*width) # self.led.draw_pixel(x + p_loc, h + y) def run(self): self.startInterrupts() # main loop try: while True: time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure screen spi_bus = 0 spi_device = 0 #Load the display driver. Attention: 128_64 is the display size. Needed to be changed if different in your setup self.led = disp = Adafruit_SSD1306.SSD1306_128_64( rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000) ) # Change rows & cols values depending on your display dimensions. # Initialize library. self.led.begin() # Clear display. self.led.clear() self.led.display() # Create image buffer. # Make sure to create image with mode '1' for 1-bit color. self.image = Image.new('1', (self.screen_width, self.screen_height)) # Load default font. #self.font = ImageFont.load_default() self.font = ImageFont.truetype(FONTFILE, FONTSIZE) # Create drawing object. self.draw = ImageDraw.Draw(self.image) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin) #Auskommentiert solange noch kein LED Strip angebracht. #self.strip.begin() # Initialize pins for output self.strip.setBrightness( NEOPIXEL_BRIGHTNESS) # Limit brightness to ~1/4 duty cycle # turn everything off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() print "Done initializing" @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) # adds an option that shuts down the rpi configuration_menu.addOption(MenuItem('shutdown', 'Shutdown')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): self.clean() return True elif (menuItem.type == "shutdown"): self.shutdown() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) def shutdown(self): shutdowntext = "Shutdown takes 10 seconds. Bye!" self.led.clear() self.draw.rectangle((0, 0, self.screen_width, self.screen_height), outline=0, fill=0) words_list = WRAPPER.wrap(text=shutdowntext) TextNew = '' for ii in words_list[:-1]: TextNew = TextNew + ii + "\n" TextNew += words_list[-1] self.draw.text((0, 10), str(TextNew), font=self.font, fill=255) self.led.image(self.image) self.led.display() time.sleep(5) #Clean shutdown device subprocess.Popen(['shutdown', '-h', 'now']) def displayMenuItem(self, menuItem): print menuItem.name self.led.clear() self.draw.rectangle((0, 0, self.screen_width, self.screen_height), outline=0, fill=0) words_list = WRAPPER.wrap(text=menuItem.name) MenuItemNew = '' for ii in words_list[:-1]: MenuItemNew = MenuItemNew + ii + "\n" MenuItemNew += words_list[-1] self.draw.text((0, 10), str(MenuItemNew), font=self.font, fill=255) self.led.image(self.image) self.led.display() def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = 0xFF0000 # 'On' color (starts red) while getattr(t, "do_run", True): self.strip.setPixelColor(head, color) # Turn on 'head' pixel self.strip.setPixelColor(tail, 0) # Turn off 'tail' self.strip.show() # Refresh strip time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) head += 1 # Advance head position if (head >= self.numpixels): # Off end of strip? head = 0 # Reset to start color >>= 8 # Red->green->blue->black if (color == 0): color = 0xFF0000 # If black, reset to red tail += 1 # Advance tail position if (tail >= self.numpixels): tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights green for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0xFF0000) self.strip.show() time.sleep(5) # turn lights off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def progressBar(self, waitTime): interval = waitTime / 100.0 for x in range(1, 101): self.led.clear() self.draw.rectangle((0, 0, self.screen_width, self.screen_height), outline=0, fill=0) self.updateProgressBar(x, y=35) self.led.image(self.image) self.led.display() time.sleep(interval) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True # launch a thread to control lighting lightsThread = threading.Thread(target=self.cycleLights) lightsThread.start() # Parse the drink ingredients and spawn threads for pumps maxTime = 0 pumpThreads = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] * FLOW_RATE if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # stop the light thread lightsThread.do_run = False lightsThread.join() # show the ending sequence lights self.lightsEndingSequence() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def left_btn(self, ctx): print("LEFT_BTN pressed") if not self.running: self.running = True self.menuContext.advance() print("Finished processing button press") self.running = False def right_btn(self, ctx): print("RIGHT_BTN pressed") if not self.running: self.running = True self.menuContext.select() print("Finished processing button press") self.running = 2 print("Starting button timeout") def updateProgressBar(self, percent, x=15, y=15): height = 10 width = self.screen_width - 2 * x for w in range(0, width): self.draw.point((w + x, y), fill=255) self.draw.point((w + x, y + height), fill=255) for h in range(0, height): self.draw.point((x, h + y), fill=255) self.draw.point((self.screen_width - x, h + y), fill=255) for p in range(0, percent): p_loc = int(p / 100.0 * width) self.draw.point((x + p_loc, h + y), fill=255) def run(self): self.startInterrupts() # main loop try: try: while True: letter = raw_input(">") if letter == "l": self.left_btn(False) if letter == "r": self.right_btn(False) except EOFError: while True: time.sleep(0.1) if self.running not in (True, False): self.running -= 0.1 if self.running == 0: self.running = False print("Finished button timeout") except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): # initialize drink attributes self.drink_attributes = {} # initialize main menu self.mainMenu = None # allow button press self.running = False # initialize the LCD self.lcdLayer = LCD.LCDLayer() self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN self.btn3Pin = UP_BTN_PIN self.btn4Pin = DOWN_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn3Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn4Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup( self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH ) print "Done initializing" @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): GPIO.add_event_detect( self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE ) GPIO.add_event_detect( self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE ) GPIO.add_event_detect( self.btn3Pin, GPIO.FALLING, callback=self.up_btn, bouncetime=UP_PIN_BOUNCE ) GPIO.add_event_detect( self.btn4Pin, GPIO.FALLING, callback=self.down_btn, bouncetime=DOWN_PIN_BOUNCE ) def stopInterrupts(self): GPIO.remove_event_detect(self.btn1Pin) GPIO.remove_event_detect(self.btn2Pin) GPIO.remove_event_detect(self.btn3Pin) GPIO.remove_event_detect(self.btn4Pin) def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # create a drinks menu drinks_menu = Menu("Drinks") # add drink options drink_opts = [] for o in drink_options: drink_opts.append( MenuItem( 'drink', o["name"], { "ingredients": {o["value"]: 1}, "strong": o["alcohol"], "ice": 0 } ) ) for d in drink_list: try: drink_opts.append( MenuItem( 'drink', d["name"], { "ingredients": d["ingredients"], "strong": d["strong"], "add": d["add"], "ice": d["ice"] } ) ) except KeyError: drink_opts.append( MenuItem( 'drink', d["name"], { "ingredients": d["ingredients"], "strong": d["strong"], "ice": d["ice"] } ) ) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: config.addOption( MenuItem( 'pump_selection', opt["name"], {"key": p, "value": opt["value"], "name": opt["name"]} ) ) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) drinks_menu.addOptions(drink_opts) drinks_menu.setParent(m) self.filterDrinks(drinks_menu) m.addOption(drinks_menu) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self, self.lcdLayer) def setMainMenu(self, menu): self.mainMenu = menu def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem) return True elif(menuItem.type == "pump_selection"): value = menuItem.attributes["value"] self.pump_configuration[ menuItem.attributes["key"]]["value"] = value Bartender.writePumpConfiguration(self.pump_configuration) self.menuContext.retreat() return True elif(menuItem.type == "pour"): self.pourDrink(menuItem.attributes) return True elif(menuItem.type == "clean"): self.clean() return True elif(menuItem.type == "menu_link"): if (menuItem.child is not None): try: self.drink_attributes.update(menuItem.attributes) print(self.drink_attributes) except TypeError: print("menu item has no attributes") self.menuContext.currentMenu = menuItem.child self.menuContext.showMenu() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime) ) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts # don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) print( "Starting pour on pin " + str(pin) + " for " + str(waitTime) + " seconds") time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) print("Stopping pour on pin " + str(pin)) def progressBar(self, waitTime): self.lcdLayer.lcd_blank() self.lcdLayer.lcd_string("Pouring", LCD.LCD_LINE_1, LCD.CENTERED) self.lcdLayer.lcd_byte( LCD.LCD_LINE_3 | LCD.LCD_SET_DDRAM, LCD.LCD_RS_CMD) block = 0xff for i in range(20): self.lcdLayer.lcd_byte(block, LCD.LCD_RS_CHR) time.sleep(waitTime / 20) return True def makeDrink(self, drink): # Check for strength if (drink.attributes["strong"] == 1): strengthCheck = Menu("This drink is strong") strengthCheck.addOption(MenuLink( "Continue", strengthCheck, None, {"strength": "normal"})) strengthCheck.setParent(self.menuContext.currentMenu) self.menuContext.setMenu(strengthCheck) else: # Select strength strengthSelect = Menu("Select strength") strengthSelect.addOption(MenuLink( "Weak", strengthSelect, None, {"strength": "weak"})) strengthSelect.addOption(MenuLink( "Normal", strengthSelect, None, {"strength": "normal"})) strengthSelect.addOption(MenuLink( "Strong", strengthSelect, None, {"strength": "strong"})) strengthSelect.setParent(self.menuContext.currentMenu) self.menuContext.setMenu(strengthSelect) # Select size # create a size menu and point it back to strengthCheck or # strengthSelect sizeSelect = Menu("Select size") sizeSelect.setParent(self.menuContext.currentMenu) for links in self.menuContext.currentMenu.options: if links.type == "menu_link": links.setChild(sizeSelect) # add size options menus to strengthCheck and strengthSelect sizeSelect.addOption(MenuLink( "Shot", sizeSelect, None, {"size": "shot"})) sizeSelect.addOption(MenuLink( "4 oz", sizeSelect, None, {"size": "4 oz"})) sizeSelect.addOption(MenuLink( "6 oz", sizeSelect, None, {"size": "6 oz"})) sizeSelect.addOption(MenuLink( "8 oz", sizeSelect, None, {"size": "8 oz"})) sizeSelect.addOption(MenuLink( "10 oz", sizeSelect, None, {"size": "10 oz"})) sizeSelect.addOption(MenuLink( "12 oz", sizeSelect, None, {"size": "12 oz"})) # pour menu pour = Menu("Ready to pour?") pour.addOption(MenuItem("pour", "Continue", drink.attributes)) # add ice if (drink.attributes["ice"] == 1): addIce = Menu("Add Ice") addIce.setParent(sizeSelect) pour.setParent(addIce) addIce.addOption(MenuLink( "Continue", addIce, pour)) for option in sizeSelect.options: if option.type == "menu_link": option.setChild(addIce) else: pour.setParent(sizeSelect) for option in sizeSelect.options: if option.type == "menu_link": option.setChild(pour) # finally show the menu structure self.menuContext.showMenu() def pourDrink(self, drinkAttributes): self.running = True ingredients = drinkAttributes["ingredients"].copy() print("Ingredients: " + str(ingredients.keys())) size = self.drink_attributes["size"].split()[0] if size == "shot": size = 1.25 else: size = float(size) print("Size = " + str(size) + " oz") strength = self.drink_attributes["strength"] print("Strength: " + str(strength)) alcModifier = 1.0 if (strength == "strong"): alcModifier = 1.3 elif (strength == "weak"): alcModifier = 0.7 # strength calculations totalIngredients = 0.0 for ing in ingredients: totalIngredients += ingredients[ing] for opts in drink_options: if (ing == opts["value"] and opts["alcohol"] == 1): ingredients.update( {ing: ingredients[ing] * 1.00 * alcModifier}) print("Total parts: " + str(totalIngredients)) # size calculations for ing in ingredients: ingredients.update( {ing: ingredients[ing] * size / totalIngredients}) print(str(ingredients[ing]) + " oz of " + str(ing)) maxTime = 0.00 pumpThreads = [] for ing in ingredients.keys(): print("Looking for pump for ingredient: " + str(ing)) found = False for pump in self.pump_configuration.keys(): print("Checking pump " + str(pump)) print( "This pump is for: " + str(self.pump_configuration[pump]["value"])) if ing == self.pump_configuration[pump]["value"]: waitTime = (ingredients[ing] * 1.00) / FLOW_RATE # oz/(oz/sec)=sec if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime) ) print( "Found pump for " + str(ing) + ". Setting up pour for " + str(waitTime) + " sec") pumpThreads.append(pump_t) found = True if (not found): print("Could not find pump for ingredient: " + str(ing)) print("Total pour time: " + str(maxTime)) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # sleep for a couple seconds to make sure the interrupts # don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False # check for additions try: addMenu = Menu("Just add " + self.drink_attributes["add"]) addMenu.addOption(MenuLink("Done", None, self.mainMenu)) addMenu.setParent(self.mainMenu) self.menuContext.setMenu(addMenu) except KeyError: self.menuContext.setMenu(self.mainMenu) self.drink_attributes = {} self.menuContext.showMenu() def down_btn(self, ctx): if not self.running: self.menuContext.scroll_down() def up_btn(self, ctx): if not self.running: self.menuContext.scroll_up() def left_btn(self, ctx): if not self.running: self.menuContext.retreat() def right_btn(self, ctx): if not self.running: self.menuContext.select() def run(self): self.menuContext.showMenu() self.startInterrupts() # main loop try: while True: time.sleep(0.1) except KeyboardInterrupt: self.lcdLayer.lcd_cleanup() # clean up GPIO on CTRL+C exit self.lcdLayer.lcd_cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.make_drink = MAKE_DRINK self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN self.clk = CLK_PIN self.dt = DT_PIN # configure interrupts for buttons #GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure interrupts for encoder GPIO.setup(self.clk, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(self.dt, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #Pi config RST = 21 DC = 20 SPI_PORT = 0 SPI_DEVICE = 0 # configure screen self.disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST) self.disp.begin() self.disp.clear() self.disp.display() #draw self.image = Image.new('1', (SCREEN_WIDTH, SCREEN_HEIGHT)) self.draw = ImageDraw.Draw(self.image) self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), outline=0, fill=0) self.font = ImageFont.load_default() # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display # Change rows & cols values depending on your display dimensions. # load the pump configuration from file self.pump_configuration = Bartender.readJson( '/home/pi/Documents/PiDrink/static/json/pump_config.json') for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # load the current drink list self.drink_list = Bartender.readJson( '/home/pi/Documents/PiDrink/static/json/drink_list.json') self.drink_list = self.drink_list["drink_list"] # load the current drink options self.drink_options = Bartender.readJson( '/home/pi/Documents/PiDrink/static/json/drink_options.json') self.drink_options = self.drink_options["drink_options"] # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin) self.strip.begin() # Initialize pins for output self.strip.setBrightness( NEOPIXEL_BRIGHTNESS) # Limit brightness to ~1/4 duty cycle # turn everything off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() print('Done initializing') # def check_email(self): # status, email_ids = self.imap_server.search(None, '(UNSEEN)') # if email_ids == ['']: # mail_list = [] # else: # mail_list = self.get_subjects(email_ids) #FYI when calling the get_subjects function, the email is marked as 'read' # self.imap_server.close() # return mail_list # def get_subjects(self, email_ids): # subjects_list = [] # for e_id in email_ids[0].split(): # resp, data = self.imap_server.fetch(e_id, '(RFC822)') # perf = HeaderParser().parsestr(data[0][1]) # subjects_list.append(perf['Subject']) # return subjects_list def voice_command(self, mail_list): for mail in mail_list: found = False while (found == False): currmen = self.menuContext.currentMenu.getSelection() if (currmen.name == mail): self.makeDrink(currmen.name, currmen.attributes["ingredients"]) self.make_drink = False found = True else: self.menuContext.currentMenu.nextSelection() found = False @staticmethod def readJson(file): return json.load(open(file)) @staticmethod def writePumpConfiguration(configuration): with open("/home/pi/Documents/PiDrink/static/json/pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): #GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) GPIO.add_event_detect(self.clk, GPIO.BOTH, callback=self.rotary_on_change, bouncetime=1000) def stopInterrupts(self): #GPIO.remove_event_detect(self.btn1Pin) GPIO.remove_event_detect(self.btn2Pin) GPIO.remove_event_detect(self.clk) def buildMenu(self): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in self.drink_list: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in self.drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): if self.make_drink: self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) self.make_drink = False return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): if self.make_drink: self.clean() self.make_drink = False return True return False def changeConfiguration(self, pumps): # Change configuration of every pump for i in range(1, 7): self.pump_configuration["pump_" + str(i)]["value"] = pumps[i - 1] Bartender.writePumpConfiguration(self.pump_configuration) def clean(self): waitTime = 30 pumpProcesses = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_p = threading.Thread(target=self.pour, args=( self.pump_configuration[pump]["pin"], waitTime, )) pumpProcesses.append(pump_p) disp_process = threading.Thread(target=self.progressBar, args=(waitTime, )) pumpProcesses.append(disp_process) # start the pump threads for process in pumpProcesses: process.start() # wait for threads to finish for process in pumpProcesses: process.join() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False self.make_drink = True self.menuContext.showMenu() def displayMenuItem(self, menuItem): #Clear display self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), outline=0, fill=0) self.draw.text((20, 10), menuItem.name, font=self.font, fill=255) self.disp.image(self.image) self.disp.display() def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = 0xFF0000 # 'On' color (starts red) while getattr(t, "do_run", True): self.strip.setPixelColor(head, color) # Turn on 'head' pixel self.strip.setPixelColor(tail, 0) # Turn off 'tail' self.strip.show() # Refresh strip time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) head += 1 # Advance head position if (head >= self.numpixels): # Off end of strip? head = 0 # Reset to start color >>= 8 # Red->green->blue->black if (color == 0): color = 0xFF0000 # If black, reset to red tail += 1 # Advance tail position if (tail >= self.numpixels): tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights green for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0xFF0000) self.strip.show() time.sleep(5) # turn lights off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def progressBar(self, waitTime): interval = waitTime / 116.3 for x in range(1, 101): # Clear display self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), outline=0, fill=0) #self.draw.text((55,20), str(x) + '%', font = self.font, fill=255) self.updateProgressBar(x, y=10) self.disp.image(self.image) self.disp.display() time.sleep(interval) # # self.disp.clear() # # self.disp.display() # self.image = Image.open('happycat_oled_32.ppm').convert('1') # self.disp.image(self.image) # self.disp.display() def makeDrink(self, drink, ingredients): if self.running: return # cancel any button presses while the drink is being made # self.stopInterrupts() print('Making a ' + drink) self.running = True # launch a thread to control lighting lightsThread = threading.Thread(target=self.cycleLights) lightsThread.start() # Make a list for each potential time maxTime = 0 pumpProcesses = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] * FLOW_RATE if (waitTime > maxTime): maxTime = waitTime pump_p = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpProcesses.append(pump_p) disp_process = threading.Thread(target=self.progressBar, args=(maxTime, )) pumpProcesses.append(disp_process) # start the pump threads for process in pumpProcesses: process.start() # wait for threads to finish for process in pumpProcesses: process.join() # stop the light thread lightsThread.do_run = False lightsThread.join() # show the ending sequence lights self.lightsEndingSequence() # reenable interrupts # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # self.startInterrupts() self.running = False print('Finished making ' + drink) self.make_drink = True # show the main menu self.menuContext.showMenu() def left_btn(self, ctx): if not self.running: self.menuContext.advance() def right_btn(self, ctx): if not self.running: self.menuContext.select() def rotary_on_change(self, ctx): if not self.running: if GPIO.input(self.dt): self.menuContext.retreat() else: self.menuContext.advance() def get_ingredients_time(self, drink): # Get the ingredients for the specified drink found = False while (found == False): currmen = self.menuContext.currentMenu.getSelection() if (currmen.name == drink): ingredients = currmen.attributes["ingredients"] self.make_drink = False found = True else: self.menuContext.currentMenu.nextSelection() # Get the drink making time maxTime = 0 for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] * FLOW_RATE if (waitTime > maxTime): maxTime = waitTime # Return making time and ingredients for drink return ingredients, maxTime def updateProgressBar(self, percent, x=15, y=10): height = 10 width = self.screen_width - 2 * x for w in range(0, width): self.draw.point((w + x, y), fill=255) self.draw.point((w + x, y + height), fill=255) for h in range(0, height): self.draw.point((x, h + y), fill=255) self.draw.point((self.screen_width - x, h + y), fill=255) for p in range(0, percent): p_loc = int(p / 100.0 * width) self.draw.point((x + p_loc, h + y), fill=255) self.draw.text((55, 20), str(percent) + '%', font=self.font, fill=255) #self.disp.image(self.image) #self.disp.display() def endprogram(self): global stopprogram stopprogram = True # Goodbye message self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), outline=0, fill=0) self.draw.text((25, 10), "Goodbye...", font=self.font, fill=255) self.disp.image(self.image) self.disp.display() time.sleep(3) self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), outline=0, fill=0) self.draw.text((15, 10), "Have a great day!", font=self.font, fill=255) self.disp.image(self.image) self.disp.display() time.sleep(3) self.disp.clear() self.disp.display() def run(self): self.startInterrupts() # main loop try: while True: if stopprogram: return # self.imap_server = imaplib.IMAP4_SSL("imap.gmail.com",993) # self.imap_server.login(USERNAME, PASSWORD) # self.imap_server.select('INBOX') # mail_list = self.check_email() # if mail_list and not self.running: # self.voice_command(mail_list) time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit sys.exit(0) GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure screen spi_bus = 0 spi_device = 0 # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display self.led = disp = Adafruit_SSD1306.SSD1306_128_64( rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000) ) # Change rows & cols values depending on your display dimensions. # Initialize library. self.led.begin() # Clear display. self.led.clear() self.led.display() # Create image buffer. # Make sure to create image with mode '1' for 1-bit color. self.image = Image.new('1', (self.screen_width, self.screen_height)) # Load default font. self.font = ImageFont.truetype("FreeMono.ttf", 15) # Create drawing object. self.draw = ImageDraw.Draw(self.image) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin) self.strip.begin() # Initialize pins for output self.strip.setBrightness( NEOPIXEL_BRIGHTNESS) # Limit brightness to ~1/4 duty cycle # Set the Default or "StandBy Light" to Blue in this case for i in range(0, self.numpixels): self.strip.setPixelColor(i, COLOR_BLUE) self.strip.show() print "Done initializing" @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): self.running = True GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) time.sleep(1) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) time.sleep(1) self.running = False def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): self.clean() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) def displayMenuItem(self, menuItem): print menuItem.name self.led.clear() self.draw.rectangle((0, 0, self.screen_width, self.screen_height), outline=0, fill=0) self.draw.text((0, 20), str(menuItem.name), font=self.font, fill=255) self.led.image(self.image) self.led.display() def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = COLOR_RED # 'On' color (starts red) while getattr(t, "do_run", True): self.strip.setPixelColor(head, color) # Turn on 'head' pixel self.strip.setPixelColor(tail, 0) # Turn off 'tail' self.strip.show() # Refresh strip time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) head += 1 # Advance head position if (head >= self.numpixels): # Off end of strip? head = 0 # Reset to start if (color == COLOR_RED): color = COLOR_YELLOW # if red,set to yellow elif (color == COLOR_YELLOW): color = COLOR_BLUE # if yellow,set to blue elif (color == COLOR_BLUE): color = COLOR_RED # if blue,set back to red tail += 1 # Advance tail position if (tail >= self.numpixels): tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights yellow for i in range(0, self.numpixels): self.strip.setPixelColor(i, COLOR_YELLOW) self.strip.show() os.system("mpg123 " + DONESOUND) # time.sleep(5) # set them back to blue "StandBy Light" for i in range(0, self.numpixels): self.strip.setPixelColor(i, COLOR_BLUE) self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) # other way of dealing with Display delay, Thanks Yogesh def progressBar(self, waitTime): #-with the outcommented version, it updates faster, but there is a limit with the delay, you have to figure out-# #mWaitTime = waitTime - 7 #interval = mWaitTime/ 100.0 #if interval < 0.07: # interval = 0 #for x in range(1, 101): interval = waitTime / 10.0 for x in range(1, 11): self.led.clear() self.draw.rectangle((0, 0, self.screen_width, self.screen_height), outline=0, fill=0) # self.updateProgressBar(x, y=35) self.updateProgressBar(x * 10, y=35) self.led.image(self.image) self.led.display() time.sleep(interval) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made # self.stopInterrupts() os.system("mpg123 " + DRINKME) self.running = True # launch a thread to control lighting lightsThread = threading.Thread(target=self.cycleLights) lightsThread.start() # Parse the drink ingredients and spawn threads for pumps maxTime = 0 pumpThreads = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: vWaitTime = self.pump_configuration[pump]["flowrate"] waitTime = ingredients[ing] * vWaitTime if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() #Show in Console how long the Pumps running print("The pumps run for", maxTime, "seconds") # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # stop the light thread lightsThread.do_run = False lightsThread.join() # show the ending sequence lights self.lightsEndingSequence() # sleep for a couple seconds to make sure the interrupts don't get triggered # time.sleep(2); # reenable interrupts # self.startInterrupts() self.running = False def left_btn(self, ctx): print("LEFT_BTN pressed") if not self.running: self.running = True self.menuContext.advance() print("Finished processing button press") self.running = False def right_btn(self, ctx): print("RIGHT_BTN pressed") if not self.running: self.running = True self.menuContext.select() print("Finished processing button press") self.running = 2 print("Starting button timeout") def updateProgressBar(self, percent, x=15, y=15): height = 25 width = self.screen_width - 2 * x for w in range(0, width): self.draw.point((w + x, y), fill=255) self.draw.point((w + x, y + height), fill=255) for h in range(0, height): self.draw.point((x, h + y), fill=255) self.draw.point((self.screen_width - x, h + y), fill=255) for p in range(0, percent): p_loc = int(p / 100.0 * width) self.draw.point((x + p_loc, h + y), fill=255) def run(self): self.startInterrupts() # main loop try: try: while True: letter = raw_input(">") if letter == "l": self.left_btn(False) if letter == "r": self.right_btn(False) except EOFError: while True: time.sleep(0.1) if self.running not in (True, False): self.running -= 0.1 if self.running == 0: self.running = False print("Finished button timeout") except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btn1Pin = LEFT_BTN_PIN self.btn2Pin = RIGHT_BTN_PIN # configure interrups for buttons GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure screen spi_bus = 0 spi_device = 0 gpio = gaugette.gpio.GPIO() spi = gaugette.spi.SPI(spi_bus, spi_device) self.led = ssd1306( I2CBUS ) # Change rows & cols values depending on your display dimensions. logo = Image.open('logo.png') self.led.canvas.bitmap((0, 0), logo, fill=1) self.led.canvas.rectangle( (0, 0, self.led.width - 1, self.led.height - 1), outline=1) self.led.display() # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN time.sleep(2) print "Done initializing" @staticmethod def readPumpConfiguration(): return json.load(open('pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE) GPIO.add_event_detect(self.btn2Pin, GPIO.FALLING, callback=self.right_btn, bouncetime=RIGHT_PIN_BOUNCE) def stopInterrupts(self): GPIO.remove_event_detect(self.btn1Pin) GPIO.remove_event_detect(self.btn2Pin) def buildMenu(self, drink_list, drink_options): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) m.addOptions(drink_opts) m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) self.led.cls() return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): self.clean() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def displayMenuTitle(self, title): self.led.canvas.text((0, 0), title, font=TITLE_FONT, fill=1) def displayMenuItem(self, menuItem): print menuItem.name self.led.canvas.text((0, 20), menuItem.name, font=ITEM_FONT, fill=1) self.led.display() def displayRender(self): self.led.display() def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = 0xFF0000 # 'On' color (starts red) while getattr(t, "do_run", True): #self.strip.setPixelColor(head, color) # Turn on 'head' pixel #self.strip.setPixelColor(tail, 0) # Turn off 'tail' #self.strip.show() # Refresh strip self.strip.show() # Refresh strip time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) head += 1 # Advance head position if (head >= self.numpixels): # Off end of strip? head = 0 # Reset to start color >>= 8 # Red->green->blue->black if (color == 0): color = 0xFF0000 # If black, reset to red tail += 1 # Advance tail position if (tail >= self.numpixels): tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights green #for i in range(0, self.numpixels): # self.strip.setPixelColor(i, 0xFF0000) #self.strip.show() time.sleep(5) # turn lights off #for i in range(0, self.numpixels): # self.strip.setPixelColor(i, 0) #self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def progressBar(self, waitTime): start_time = time.time() self.led.cls() self.led.canvas.text((30, 20), "Dispensing...", font=FONT, fill=1) while time.time() - start_time < waitTime: prog = (time.time() - start_time) / waitTime self.updateProgressBar(prog, y=35) self.led.display() time.sleep(0.5) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True # launch a thread to control lighting lightsThread = threading.Thread(target=self.cycleLights) lightsThread.start() # Parse the drink ingredients and spawn threads for pumps maxTime = 0 pumpThreads = [] for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ ing] * 60.0 / self.pump_configuration[pump]["flow_rate"] if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # stop the light thread lightsThread.do_run = False lightsThread.join() # show the ending sequence lights self.lightsEndingSequence() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # reenable interrupts # self.startInterrupts() self.running = False def left_btn(self, ctx): if not self.running: self.menuContext.advance() def right_btn(self, ctx): if not self.running: self.menuContext.select() def updateProgressBar(self, percent, x=15, y=15): h = 10 w = self.screen_width - 2 * x p_loc = int(percent * w) self.led.canvas.rectangle((x, y, x + w, y + h), outline=255, fill=0) self.led.canvas.rectangle((x + 1, y + 1, x + p_loc, y + h - 1), outline=255, fill=1) def run(self): self.startInterrupts() # main loop try: while True: time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup() # clean up GPIO on CTRL+C exit GPIO.cleanup() # clean up GPIO on normal exit traceback.print_exc()
class Bartender(MenuDelegate): def __init__(self): GPIO.setmode(GPIO.BCM) self.running = False # set the oled screen height self.screen_width = SCREEN_WIDTH self.screen_height = SCREEN_HEIGHT self.btnMenuPlusPin = MENU_PLUS_BTN_PIN self.btnMenuMinPin = MENU_MIN_BTN_PIN self.btnSelectPin = SELECT_BTN_PIN self.btnAdminPin = ADMIN_BTN_PIN self.btnAlcoholPin = ALCOHOL_BTN_PIN # vars for toggle self.alcohol_enabled = False self.admin_enabled = False self.weborders = False # configure interrups for buttons GPIO.setup(self.btnMenuPlusPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btnMenuMinPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btnSelectPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btnAdminPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.btnAlcoholPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # configure screen spi_bus = 0 spi_device = 0 gpio = gaugette.gpio.GPIO() spi = gaugette.spi.SPI(spi_bus, spi_device) # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display self.led = gaugette.ssd1306.SSD1306( gpio, spi, reset_pin=OLED_RESET_PIN, dc_pin=OLED_DC_PIN, rows=self.screen_height, cols=self.screen_width ) # Change rows & cols values depending on your display dimensions. self.led.begin() self.led.clear_display() self.led.display() self.led.invert_display() time.sleep(0.5) self.led.normal_display() time.sleep(0.5) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() for pump in self.pump_configuration.keys(): GPIO.setup(self.pump_configuration[pump]["pin"], GPIO.OUT, initial=GPIO.HIGH) # setup pixels: self.numpixels = NUMBER_NEOPIXELS # Number of LEDs in strip # Here's how to control the strip from any two GPIO pins: datapin = NEOPIXEL_DATA_PIN clockpin = NEOPIXEL_CLOCK_PIN self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin) self.strip.begin() # Initialize pins for output self.strip.setBrightness( NEOPIXEL_BRIGHTNESS) # Limit brightness to ~1/4 duty cycle # turn everything off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() # add var so the system can go back self.lastpagecommand = "" print("Done initializing") @staticmethod def readPumpConfiguration(): return json.load(open('hardware/pump_config.json')) @staticmethod def writePumpConfiguration(configuration): with open("hardware/pump_config.json", "w") as jsonFile: json.dump(configuration, jsonFile) def startInterrupts(self): GPIO.add_event_detect(self.btnMenuPlusPin, GPIO.FALLING, callback=self.menu_plus_btn, bouncetime=MENU_PLUS_BTN_BOUNCE) GPIO.add_event_detect(self.btnMenuMinPin, GPIO.FALLING, callback=self.menu_min_btn, bouncetime=MENU_MIN_BTN_BOUNCE) GPIO.add_event_detect(self.btnSelectPin, GPIO.FALLING, callback=self.select_btn, bouncetime=SELECT_PIN_BOUNCE) GPIO.add_event_detect(self.btnAlcoholPin, GPIO.FALLING, callback=self.alcohol_btn, bouncetime=ALCOHOL_PIN_BOUNCE) GPIO.add_event_detect(self.btnAdminPin, GPIO.FALLING, callback=self.admin_btn, bouncetime=ADMIN_PIN_BOUNCE) def stopInterrupts(self): GPIO.remove_event_detect(self.btnAlcoholPin) GPIO.remove_event_detect(self.btnAdminPin) GPIO.remove_event_detect(self.btnSelectPin) GPIO.remove_event_detect(self.btnMenuMinPin) GPIO.remove_event_detect(self.btnMenuPlusPin) def buildMenu(self, drink_list, drink_options, alcoholic_drinks_enabled=False, admin_options_enabled=False): # create a new main menu m = Menu("Main Menu") # add drink options drink_opts = [] for d in drink_list: # check if allowed by admin button if alcoholic_drinks_enabled == False: # check if the drink has alcohol if d["alcoholic"] == False: # -----!!!!----- drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"] })) # -----!!!!----- else: drink_opts.append( MenuItem('drink', d["name"], {"ingredients": d["ingredients"]})) configuration_menu = Menu("Configure") # add pump configuration options pump_opts = [] for p in sorted(self.pump_configuration.keys()): config = Menu(self.pump_configuration[p]["name"]) # add fluid options for each pump for opt in drink_options: # star the selected option selected = "*" if opt["value"] == self.pump_configuration[p][ "value"] else "" config.addOption( MenuItem('pump_selection', opt["name"], { "key": p, "value": opt["value"], "name": opt["name"] })) # add a back button so the user can return without modifying config.addOption(Back("Back")) config.setParent(configuration_menu) pump_opts.append(config) # add pump menus to the configuration menu configuration_menu.addOptions(pump_opts) # add a back button to the configuration menu configuration_menu.addOption(Back("Back")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) # adds the option to power off configuration_menu.addOption(MenuItem('poweroff', 'Power off')) configuration_menu.setParent(m) # add drinks to options m.addOptions(drink_opts) # check if admin is enabled if admin_options_enabled == True: m.addOption(configuration_menu) # create a menu context self.menuContext = MenuContext(m, self) def filterDrinks(self, menu): """ Removes any drinks that can't be handled by the pump configuration """ for i in menu.options: if (i.type == "drink"): i.visible = False ingredients = i.attributes["ingredients"] presentIng = 0 for ing in ingredients.keys(): for p in self.pump_configuration.keys(): if (ing == self.pump_configuration[p]["value"]): presentIng += 1 if (presentIng == len(ingredients.keys())): i.visible = True elif (i.type == "menu"): self.filterDrinks(i) def selectConfigurations(self, menu): """ Adds a selection star to the pump configuration option """ for i in menu.options: if (i.type == "pump_selection"): key = i.attributes["key"] if (self.pump_configuration[key]["value"] == i.attributes["value"]): i.name = "%s %s" % (i.attributes["name"], "*") else: i.name = i.attributes["name"] elif (i.type == "menu"): self.selectConfigurations(i) def prepareForRender(self, menu): self.filterDrinks(menu) self.selectConfigurations(menu) return True def menuItemClicked(self, menuItem): if (menuItem.type == "drink"): self.makeDrink(menuItem.name, menuItem.attributes["ingredients"]) return True elif (menuItem.type == "pump_selection"): self.pump_configuration[menuItem.attributes["key"]][ "value"] = menuItem.attributes["value"] Bartender.writePumpConfiguration(self.pump_configuration) return True elif (menuItem.type == "clean"): self.clean() return True elif (menuItem.type == "poweroff"): self.shutdown() return True return False def clean(self): waitTime = 20 pumpThreads = [] # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True for pump in self.pump_configuration.keys(): pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(waitTime) # wait for threads to finish for thread in pumpThreads: thread.join() # show the main menu self.menuContext.showMenu() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(2) # ; # reenable interrupts # self.startInterrupts() self.running = False def shutdown(self): print("system will go off") os.system("sudo shutdown -h now") def displayMenuItem(self, menuItem): print menuItem.name self.led.clear_display() self.led.draw_text2(0, 20, menuItem.name, 2) self.led.display() print("set on oled") print(menuItem.type) print(menuItem.name) self.displayInBrowser(menuItem) def displayInBrowser(self, menuItem): # check the type and open the matching page if (menuItem.type == "drink"): path = "/drink?drink={0}".format(base64.encodestring( menuItem.name)) self.create_exectute_display_command(path) else: path = "/basic-menu?item={0}&type={1}".format( base64.encodestring(menuItem.name), base64.encodestring(menuItem.type)) self.create_exectute_display_command(path) def create_exectute_display_command(self, webpath): # makes the command with the given parms command = "DISPLAY=:0 firefox '{0}:{1}{2}' &".format( server_host, server_port, webpath) # execute os.system(command) def cycleLights(self): t = threading.currentThread() head = 0 # Index of first 'on' pixel tail = -10 # Index of last 'off' pixel color = 0xFF0000 # 'On' color (starts red) while getattr(t, "do_run", True): self.strip.setPixelColor(head, color) # Turn on 'head' pixel self.strip.setPixelColor(tail, 0) # Turn off 'tail' self.strip.show() # Refresh strip time.sleep(1.0 / 50) # Pause 20 milliseconds (~50 fps) head += 1 # Advance head position if (head >= self.numpixels): # Off end of strip? head = 0 # Reset to start color >>= 8 # Red->green->blue->black if (color == 0): color = 0xFF0000 # If black, reset to red tail += 1 # Advance tail position if (tail >= self.numpixels): tail = 0 # Off end? Reset def lightsEndingSequence(self): # make lights green for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0xFF0000) self.strip.show() time.sleep(5) # turn lights off for i in range(0, self.numpixels): self.strip.setPixelColor(i, 0) self.strip.show() def pour(self, pin, waitTime): GPIO.output(pin, GPIO.LOW) time.sleep(waitTime) GPIO.output(pin, GPIO.HIGH) def progressBar(self, waitTime): interval = waitTime / 100.0 for x in range(1, 101): self.led.clear_display() self.updateProgressBar(x, y=35) self.led.display() time.sleep(interval) def makeDrink(self, drink, ingredients): # cancel any button presses while the drink is being made # self.stopInterrupts() self.running = True # show a page so the user knows it's buzzy self.create_exectute_display_command("/making?drink={0}".format( base64.encodestring(drink))) self.served_drink = drink # launch a thread to control lighting lightsThread = threading.Thread(target=self.cycleLights) lightsThread.start() # Parse the drink ingredients and spawn threads for pumps maxTime = 0 pumpThreads = [] # store the longes wait time to sleep later longestWaitTime = 0 for ing in ingredients.keys(): for pump in self.pump_configuration.keys(): if ing == self.pump_configuration[pump]["value"]: waitTime = ingredients[ing] * FLOW_RATE if (waitTime > maxTime): maxTime = waitTime pump_t = threading.Thread( target=self.pour, args=(self.pump_configuration[pump]["pin"], waitTime)) pumpThreads.append(pump_t) # chek waiting time if longestWaitTime < waitTime: longestWaitTime = waitTime # start the pump threads for thread in pumpThreads: thread.start() # start the progress bar self.progressBar(maxTime) # wait for threads to finish for thread in pumpThreads: thread.join() # sleep(longestWaitTime) # show the main menu self.menuContext.showMenu() # stop the light thread lightsThread.do_run = False lightsThread.join() # show the ending sequence lights self.lightsEndingSequence() # sleep for a couple seconds to make sure the interrupts don't get triggered time.sleep(4) # ; # reenable interrupts # self.startInterrupts() self.running = False def menu_plus_btn(self, ctx): print("plus hit") if not self.running: self.menuContext.advance() def menu_min_btn(self, ctx): print("min hit") if not self.running: self.menuContext.previous() def select_btn(self, ctx): if not self.running: print("cocktail selected") self.menuContext.select() else: #could be better but due the fact: # thread can't be stopped # error ignored #no better solution print("Cancel selected") self.create_exectute_display_command("/cancel?drink={0}".format( base64.encodestring(self.served_drink))) for pump in self.pump_configuration.keys(): GPIO.output(self.pump_configuration[pump]["pin"], GPIO.HIGH) print("cancel done") def alcohol_btn(self, ctx): print("alcohol hit") # rebuild the menu self.alcohol_enabled = self.alcohol_enabled ^ 1 self.buildMenu(drink_list, drink_options, self.alcohol_enabled, self.admin_enabled) # -----!!!!----- def admin_btn(self, ctx): print("admin hit") # rebuild the menu self.admin_enabled = self.admin_enabled ^ 1 self.buildMenu(drink_list, drink_options, self.alcohol_enabled, self.admin_enabled) # -----!!!!----- def updateProgressBar(self, percent, x=15, y=15): height = 10 width = self.screen_width - 2 * x for w in range(0, width): self.led.draw_pixel(w + x, y) self.led.draw_pixel(w + x, y + height) for h in range(0, height): self.led.draw_pixel(x, h + y) self.led.draw_pixel(self.screen_width - x, h + y) for p in range(0, percent): p_loc = int(p / 100.0 * width) self.led.draw_pixel(x + p_loc, h + y) def run(self): self.startInterrupts() def start_operation(self): self.buildMenu(drink_list, drink_options) self.run() @staticmethod def set_gpio(): GPIO.setmode(GPIO.BCM) def clean_gpio(self): self.stopInterrupts() GPIO.cleanup()