diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5923709 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +sudo: false +language: python +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" diff --git a/README.md b/README.md index c2b31c8..acc45cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -# Smart Bartender -Why spend lots of money going out for drinks when you can have your own smart personal bartender at your service right in your home?! This bartender is built from a Raspberry Pi 3 and some common DIY electronics. - -## Prerequisites for the Raspberry Pi -Make sure you can connect a screen and keyboard to your Raspberry Pi. I like to use VNC to connect to the Pi. I created a [tutorial](https://www.youtube.com/watch?v=2iVK8dn-6x4) about how to set that up on a Mac. +# Smart Bartender with Adafruit SSD1306 Make sure the following are installed: * Python 2.7 (should already be installed on most Raspberry Pi) @@ -23,7 +19,7 @@ See this [article](https://www.raspberrypi.org/documentation/hardware/raspberryp Make sure i2c is also configured properly. Type ``` -sudo vim /etc/modules +sudo nano /etc/modules ``` in the terminal @@ -38,23 +34,24 @@ i2c-dev press `esc` then `ZZ` to save and exit. ## OLED Setup -The Raspberry Pi Guy has a nice script to setup the OLED screen on your raspberry pi. Download the following repository on your Pi: - -https://github.com/the-raspberry-pi-guy/OLED -then navigate to the folder with the terminal +You need to install the Adafruit Python SSD1306 library with ``` -cd ~/path/to/directory +sudo python -m pip install --upgrade pip setuptools wheel +sudo pip install Adafruit-SSD1306 ``` -and run the installation script +Or alternatively: ``` -sh OLEDinstall.sh +sudo python -m pip install --upgrade pip setuptools wheel +git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git +cd Adafruit_Python_SSD1306 +sudo python setup.py install ``` -There is also a [guide](https://learn.adafruit.com/adafruit-oled-displays-for-raspberry-pi/setting-up) on the Adafruit website if you get stuck. +There is also a [guide](https://learn.adafruit.com/ssd1306-oled-displays-with-raspberry-pi-and-beaglebone-black/usage) on the Adafruit website if you get stuck. ## Running the Code @@ -135,7 +132,7 @@ from the repository folder. Copy this to your clipboard. Next, type ``` -sudo vim /etc/rc.local +sudo nano /etc/rc.local ``` to open the rc.local file. Next, press `i` to edit. Before the last line, add the following two lines: diff --git a/bartender.py b/bartender.py index 56de81d..e6c3d08 100644 --- a/bartender.py +++ b/bartender.py @@ -1,12 +1,19 @@ -import gaugette.ssd1306 -import gaugette.platform -import gaugette.gpio +#!/usr/bin/env python +# -*- coding: iso-8859-1 -*- + import time import sys import RPi.GPIO as GPIO import json -import threading import traceback +import threading + +import Adafruit_GPIO.SPI as SPI +import Adafruit_SSD1306 + +from PIL import Image +from PIL import ImageFont +from PIL import ImageDraw from dotstar import Adafruit_DotStar from menu import MenuItem, Menu, Back, MenuContext, MenuDelegate @@ -18,10 +25,10 @@ SCREEN_HEIGHT = 64 LEFT_BTN_PIN = 13 -LEFT_PIN_BOUNCE = 1000 +LEFT_PIN_BOUNCE = 200 RIGHT_BTN_PIN = 5 -RIGHT_PIN_BOUNCE = 2000 +RIGHT_PIN_BOUNCE = 200 OLED_RESET_PIN = 15 OLED_DC_PIN = 16 @@ -31,7 +38,15 @@ NEOPIXEL_CLOCK_PIN = 6 NEOPIXEL_BRIGHTNESS = 64 -FLOW_RATE = 60.0/100.0 +FLOW_RATE = 60.0/1500.0 + +# Raspberry Pi pin configuration: +RST = 14 +# Note the following are only used with SPI: +DC = 15 +SPI_PORT = 0 +SPI_DEVICE = 0 + class Bartender(MenuDelegate): def __init__(self): @@ -51,18 +66,27 @@ def __init__(self): # 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 = 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() - self.led.clear_display() + + # Clear display. + self.led.clear() self.led.display() - self.led.invert_display() - time.sleep(0.5) - self.led.normal_display() - time.sleep(0.5) + + + # 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() + + # Create drawing object. + self.draw = ImageDraw.Draw(self.image) # load the pump configuration from file self.pump_configuration = Bartender.readPumpConfiguration() @@ -99,10 +123,6 @@ 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") @@ -131,7 +151,7 @@ def buildMenu(self, drink_list, drink_options): # 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")) + configuration_menu.addOption(Back("Zurück")) # adds an option that cleans all pumps to the configuration menu configuration_menu.addOption(MenuItem('clean', 'Clean')) configuration_menu.setParent(m) @@ -220,14 +240,13 @@ def clean(self): # 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.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): @@ -272,8 +291,10 @@ def pour(self, pin, waitTime): def progressBar(self, waitTime): interval = waitTime / 100.0 for x in range(1, 101): - self.led.clear_display() + 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) @@ -327,32 +348,57 @@ def makeDrink(self, drink, ingredients): 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.led.draw_pixel(w + x, y) - self.led.draw_pixel(w + x, y + height) + self.draw.point((w + x, y), fill=255) + self.draw.point((w + x, y + height), fill=255) for h in range(0, height): - self.led.draw_pixel(x, h + y) - self.led.draw_pixel(self.screen_width-x, h + y) + 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.led.draw_pixel(x + p_loc, h + y) + self.draw.point((x + p_loc, h + y), fill=255) def run(self): self.startInterrupts() # main loop - try: - while True: - time.sleep(0.1) + 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