Raspberry Pi Code

The code for this project is on GitHub.


After you've set up your pi, log in or ssh in, and get ready to set up some stuff. First, we'll download the kegbot code. You can download all the code by clicking the green button below.

or use:

Download: file
git clone https://github.com/adafruit/Adafruit_Learning_System_Guides/tree/master/Kegomatic/Kegomatic.git
We'll need to install Python's setup tools in order to make it easier to install the pre-requisites for our Twitter code.
Download: file
sudo -i
wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python
Next, we will install a whole bunch of pre-requisites. Execute these one line at a time and look for errors. If you don't see any, you're good to go.
Download: file
sudo easy_install twitter
sudo easy_install simplejson
sudo easy_install httplib2
sudo easy_install python-oauth2
And that's it for setup... now the code should work... let's take a look at what it does!
Download: file
import os
import time
import math
import pygame, sys
from pygame.locals import *
import RPi.GPIO as GPIO
from twitter import *
The above lines are all the imports we need... we need the twitter API for twittering, the GPIO library so we can use the GPIO pins, pygame so we can make this a fun windowed application, math so we can do some math stuff, time so we can do accurate timing, and os so we can run things at the os level.

Next, we'll need to initialize the twitter account:
Download: file

First, go set up a Twitter app here.  You will need to be logged in to Twitter from whatever account you would like to do the kegomatic tweeting.

Then, come back to the code, and replace OAUTH_TOKEN, OAUTH_SECRET, CONSUMER_KEY, and CONSUMER_SECRET with the actual values from your twitter account app page.

After that, we will need to initialize the GPIO pins...

Download: file
boardRevision = GPIO.RPI_REVISION
GPIO.setmode(GPIO.BCM) # use real GPIO numbering
GPIO.setup(22,GPIO.IN, pull_up_down=GPIO.PUD_UP)
The next several lines are there to set up pygame, the windowed context, and various global variables we will need throughout the program...
Download: file
# set up pygame

# set up the window

# set up the flow meter
pouring = False
lastPinState = False
pinState = 0
lastPinChange = int(time.time() * 1000)
pourStart = 0
pinChange = lastPinChange
pinDelta = 0
hertz = 0
flow = 0
litersPoured = 0
pintsPoured = 0
tweet = ''

# set up the colors
BLACK = (0,0,0)
WHITE = (255,255,255)

windowSurface = pygame.display.set_mode((VIEW_WIDTH,VIEW_HEIGHT), FULLSCREEN, 32) 
basicFont = pygame.font.SysFont(None, FONTSIZE)
Next comes the renderThings() function, which draws all of our updated data to the screen...
Download: file
def renderThings(lastPinChange, pinChange, pinDelta, hertz, flow, pintsPoured, pouring, pourStart, tweet, windowSurface, basicFont):
  # Clear the screen
  # Draw LastPinChange
  text = basicFont.render('Last Pin Change: '+time.strftime('%H:%M:%S', time.localtime(lastPinChange/1000)), True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,1*LINEHEIGHT))
  # Draw PinChange
  text = basicFont.render('Pin Change: '+time.strftime('%H:%M:%S', time.localtime(pinChange/1000)), True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,2*LINEHEIGHT))
  # Draw PinDelta
  text = basicFont.render('Pin Delta: '+str(pinDelta) + ' ms', True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,3*LINEHEIGHT))
  # Draw hertz
  text = basicFont.render('Hertz: '+str(hertz) + 'Hz', True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,4*LINEHEIGHT))

  # Draw instantaneous speed
  text = basicFont.render('Flow: '+str(flow) + ' L/sec', True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,5*LINEHEIGHT))

  # Draw Liters Poured
  text = basicFont.render('Pints Poured: '+str(pintsPoured) + ' pints', True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,6*LINEHEIGHT))
  # Draw Pouring
  text = basicFont.render('Pouring: '+str(pouring), True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,7*LINEHEIGHT))

  # Draw Pour Start
  text = basicFont.render('Last Pour Started At: '+time.strftime('%H:%M:%S', time.localtime(pourStart/1000)), True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,8*LINEHEIGHT))
  # Draw Tweet
  text = basicFont.render('Tweet: '+str(tweet), True, WHITE, BLACK)
  textRect = text.get_rect()
  windowSurface.blit(text, (40,9*LINEHEIGHT))

  # Display everything
Now we begin the main loop, which will loop forever (until we quit the program). The first thing we need to do at the beginning of the loop every time is to figure out how much time has passed since the last time we ran the loop. To do that, we get need to get the current time. We also need to know if the pin is set high or low right now, so we can start counting the time between pulses of the flow meter.
Download: file
# main loop
while True:
  currentTime = int(time.time() * 1000)
  if GPIO.input(22):
    pinState = True
    pinState = False
We also have a small amount of keyboard handling code, so that the user can press the escape key to exit the program.
Download: file
# Handle keyboard events
  for event in pygame.event.get():
    if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
Now, this next if statement is a doozie, so bear with me...

If the pin state is high and is not the same as it was last time through the loop (so that the pin staying high doesn't result in a steady stream of clicks as the loop runs over and over) then we enter pouring mode. While in pouring mode, we figure out how much time has passed between this click and the last click of the flow meter, and use that to calculate the flow. Once we have the number of milliseconds between the last click and now, we can figure out the hertz of the meter, divide that by the rate expected of the flow meter to get 1 liter per second of flow, and convert the result from liters to pints. This (very small) number represents the amount of beer that has been poured during this time through the loop.
Download: file
# If we have changed pin states low to high...
  if(pinState != lastPinState and pinState == True):
    if(pouring == False):
      pourStart = currentTime
    pouring = True
    # get the current time
    pinChange = currentTime
    pinDelta = pinChange - lastPinChange
    if (pinDelta < 1000):
      # calculate the instantaneous speed
      hertz = 1000.0000 / pinDelta
      flow = hertz / (60 * 7.5) # L/s
      litersPoured += flow * (pinDelta / 1000.0000)
      pintsPoured = litersPoured * 2.11338
This next if statement is a little shorter... it says that if we are pouring, and we notice that our sensor has been idle for more than 3 seconds, then we can assume that we are no longer pouring, and it's time to calculate how big the pour was and tweet it. After we do that, we should reset the amount poured so the next guy can begin his pour.
Download: file
if (pouring == True and pinState == lastPinState and (currentTime - lastPinChange) > 3000):
    # set pouring back to false, tweet the current amt poured, and reset everything
    pouring = False
    if (pintsPoured > 0.1):
      pourTime = int((currentTime - pourStart)/1000) - 3
      tweet = 'Someone just poured ' + str(round(pintsPoured,2)) + ' pints of root beer in ' + str(pourTime) + ' seconds'
      litersPoured = 0
      pintsPoured = 0
Finally, we must draw everything to the screen, and update the time variables so that we can accurately measure time the next time we go through the loop.
Download: file
  renderThings(lastPinChange, pinChange, pinDelta, hertz, flow, pintsPoured, pouring, pourStart, tweet, windowSurface, basicFont)
  lastPinChange = pinChange
  lastPinState = pinState
To run this code, run the following:
Download: file
sudo python kegbot.py
That should bring up a window with the bot statistics, and then you can start pouring and tweeting!
This guide was first published on Sep 27, 2013. It was last updated on Sep 27, 2013. This page (Raspberry Pi Code) was last updated on Jun 18, 2019.