The Raspberry Pi Hardware
As I mentioned in the first post, I will be using a Raspberry Pi 3Raspberry Pi 3, and the Raspberry Pi 7-inch touchscreenRaspberry Pi 7-inch touchscreen. So before I can get started with developing the program and lighting effects, I need to get the Raspberry Pi 3, and the touchscreen mated together. I won't walk you through the process on how to set this up though as there are many of these tutorials here on Element14 already such as this one from my friend Enrico Miglino (balearicdynamics), plus it’s as simple as connecting the ribbon cable, and then screwing the Raspberry Pi to the touchscreen’s controller board when you are using a Pi 3 and the latest version of Raspbian.
With the Pi and the Touchscreen now assembled, I just needed to get the latest version of Raspbian burnt onto a SD card, and then the screen would automatically work when I boot the Pi up for the first time. I took this as an opportunity to create a video for my YouTube channel on how to burn Raspbian to an SD card. I have wanted to do a series on Raspberry Pi Basics for a while now, and this project gave me the push I needed to get started on that. Check out the video below, and let me know what you think!
The Slot Machine Software
If you have followed any of my projects over the years, you will know that I try to make the software side of things as easy as possible. This usually results in searching out open source code that I can modify to fit my needs. That is no different from this project, and I was lucky enough to find a fully functional slot machine program that is written in Python, and developed for use on low-power linux machines.
The program I am using is called Bfruit and was written by Balázs Nagy and Ferenc Nagy. Bfruit is licensed under the GNU General Public License version 2.0 (GPLv2). I have uploaded a forked version of this program to my Git Hub, so that I as well as others can continue to develop this program for use on the Raspberry Pi platform.
In my initial testing, the Bfruit program ran very sluggish on a Raspberry Pi 2 Model BRaspberry Pi 2 Model B, and I was a little disappointed as it ran flawlessly in a Debian-based VM on my Windows 10 workstation. Thankfully, when I installed it on a Raspberry Pi 3, the program ran just as fast as it did in the VM. So with things running correctly, I jumped into the Bfruit.py program and modified the code to set a GPIO Pin on the Raspberry Pi to High when the system detected a WIN from the slots roll. For reference, I have posted a pinout diagram for the Raspberry Pi 3 above. I have listed my changes to the Bfruit.py program below, I have also uploaded this as a change log to my GitHub Repo.
Line 29: Imported the RPi.GPIO library
import RPi.GPIO as GPIO ## Import GPIO library\
Line 35: Set the GPIO Mode to BOARD
GPIO.setmode(GPIO.BOARD) ## Use board pin numbering
Line 36: Set GPIO Pin 37 as an output
GPIO.setup(37, GPIO.OUT) ## Setup GPIO Pin 37 to OUT
Lines 606-611: Created two functions to set GPIO Pin 37 HIGH and LOW and included some debugging text
def ledOn (self): GPIO.output(37,True) ## Set GPIO pin 7 HIGH print "LedOn" def ledOff (self): GPIO.output(37,False) ## Set GPIO pin 7 LOW print "LedOff"
Line 604: Called the ledON fuction after a win was detected in the Winner function
self.ledOn()
Line 320: Called the ledOFF function in the Main Loop’s “key down” section.
self.ledOff()
To better explain what each of these changes to the code does, a short line by linedescription of what each change does.
- Line 27: GPIO.setup(37, GPIO.OUT) - This line tells Python to include the RPi.GPIO library when executed so that the program can control the Raspberry Pi’s GPIO Pins.
- Line 35: GPIO.setmode(GPIO.BOARD) - This tells Python that the program will be using the BOARD pin numbering schema instead of the BCM schema.
- Line 36: GPIO.setup(37, GPIO.OUT) - This sets GPIO Pin 37 as an output.
- Line 606: This creates a function called ledOn.
- Line 607: This sets GPIO pin 37 to HIGH.
- Line 608: This prints LedOn in the terminal for debugging purposes.
- Line 609: This creates a function called ledOff.
- Line 610: This sets the GPIO pin 37 to LOW.
- Line 611: This prints LedOff in the terminal for debugging purposes.
- Line 604: This calls the ledOn function when a win is detected by the program. This triggers the interrupt on the Arduino, which triggers the winner lighting function.
- Line 320: This calls the ledOff function when the key press that spins the wheels is detected. This allows the Winner LED animation on the Arduino to reset so that it can be triggered again if another win is detected.
The full code is below. It’s over 800 lines, so it may be a little long if you are reading this on a mobile device.
#!/usr/bin/env python # -*- coding: utf-8 -*- # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # Written by Balázs Nagy <nxbalazs@gmail.com> # Design by Ferenc Nagy <nferencfx@gmail.com> # Project web site: http://bfruit.sf.net #This code was modified on Oct 11 2016 by Charles Gantt #For the Raspberry PySlots Halloween Costume. #Find all three parts of the the project build log at http://bit.ly/RPiSlots import pygame from pygame.locals import * from random import randrange import sys from sys import argv from getopt import getopt, GetoptError import RPi.GPIO as GPIO ## Import GPIO library import time import os VERSION = "0.1.2" GPIO.setmode(GPIO.BOARD) ## Use board pin numbering GPIO.setup(37, GPIO.OUT) ## Setup GPIO Pin 37 to OUT # main menu########################### class Menu: def __init__(self): self.screen = screen self.maincolor = [0, 0, 0] self.white = [255, 255, 255] self.bsound = pygame.mixer.Sound("data/sounds/CLICK10A.WAV") self.background = pygame.image.load("data/menubg/menubg.png") self.backgroundadded = pygame.image.load("data/menubg/added.png") self.sav = pygame.image.load("data/menubg/sav.png") self.highscore = (pygame.image.load("data/menubg/highscore.png")) self.menu = [" New Game ", " Settings ", " High score ", " Exit to Linux "] self.menubg = [] self.menubg.append(pygame.image.load("data/menubg/al.png").convert()) self.menubg.append(pygame.image.load("data/menubg/ci.png").convert()) self.menubg.append(pygame.image.load("data/menubg/he.png").convert()) self.menubg.append(pygame.image.load("data/menubg/na.png").convert()) self.menubg.append(pygame.image.load("data/menubg/di.png").convert()) self.menuall = "" self.selectedmenu = 0 self.mid = [] # get menu width self.menuid() # all menu in one: self.listmenuall() # mainloop sz = 0 szam = 0 szamlalo = 0 self.showhs = False # show hs in menu while True: for self.event in pygame.event.get(): if self.selectedmenu == 2: self.showhs = True else: self.showhs = False if self.event.type == pygame.QUIT: exit() if self.event.type == pygame.KEYDOWN: self.bsound.play() if self.event.key == pygame.K_LEFT: if self.selectedmenu == 0: self.selectedmenu = len(self.menu)-1 else: self.selectedmenu = self.selectedmenu-1 elif self.event.key == pygame.K_RIGHT: if self.selectedmenu == len(self.menu)-1: self.selectedmenu = 0 else: self.selectedmenu = self.selectedmenu+1 elif self.event.key == pygame.K_RETURN: if self.selectedmenu == 0: plc = Game() elif self.selectedmenu == 1: plc = Settings() elif self.selectedmenu == 2: self.selectedmenu = 2 # else: exit() if self.event.key == pygame.K_ESCAPE: exit() # 1st layer: background color self.screen.fill(self.maincolor) self.bg = self.menubg[szam] self.bg.set_alpha(sz) self.screen.blit(self.bg, (0, 0)) self.screen.blit(self.backgroundadded, (0, 0)) # 2nd layer: menus self.crt_menu() # 3rd layer: transparent image self.screen.blit(self.background, (0, 0)) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("Balazs Nagy - BFruit - "+VERSION , True, self.white) self.screen.blit(text_surface, (3, 460)) if self.showhs == True: self.screen.blit(self.sav, (0, 60)) self.screen.blit(self.sav, (0, 120)) self.screen.blit(self.highscore, (50, 60)) font=pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render(scr, True, self.white) self.screen.blit(text_surface, (295, 110)) szamlalo = szamlalo + 4 if szamlalo < 245: sz = sz + 4 if szamlalo > 244: sz = sz - 4 if szamlalo > 490: sz = 0 szamlalo = 0 if szam == len(self.menubg)-1: szam = 0 else: szam = szam + 1 pygame.display.update() def menuid(self): for n in self.menu: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render(n, True, self.white) self.mid.append(text_surface.get_width()) def listmenuall(self): for n in self.menu: self.menuall = self.menuall+n def crt_menu(self): nmb = 0 xpos = 0 while nmb <= self.selectedmenu: xpos = xpos-self.mid[nmb] nmb = nmb+1 xpos = xpos+self.mid[self.selectedmenu]/2 # draw menus on screen font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render(self.menuall, True, self.white) self.screen.blit(text_surface, (320+xpos, 15)) # Settings menu################################ class Settings: def __init__(self): self.screen = screen self.maincolor = [0, 0, 0] self.white = [255, 255, 255] self.bsound = pygame.mixer.Sound("data/sounds/CLICK10A.WAV") self.background = pygame.image.load("data/menubg/menubg.png") self.backgroundadded = pygame.image.load("data/menubg/added.png") self.sav = pygame.image.load("data/menubg/sav.png") self.menu = [" Fullscreen ", " Back to main "] self.menubg = [] self.menubg.append(pygame.image.load("data/menubg/al.png").convert()) self.menubg.append(pygame.image.load("data/menubg/ci.png").convert()) self.menubg.append(pygame.image.load("data/menubg/he.png").convert()) self.menubg.append(pygame.image.load("data/menubg/na.png").convert()) self.menubg.append(pygame.image.load("data/menubg/di.png").convert()) self.menuall = "" self.selectedmenu = 0 self.mid = [] # get menu width self.menuid() # all menu in one: self.listmenuall() # mainloop sz = 0 szam = 0 szamlalo = 0 while True: for self.event in pygame.event.get(): if self.event.type == pygame.QUIT: exit() if self.event.type == pygame.KEYDOWN: self.bsound.play() if self.event.key == pygame.K_LEFT: if self.selectedmenu == 0: self.selectedmenu = len(self.menu)-1 else: self.selectedmenu = self.selectedmenu-1 elif self.event.key == pygame.K_RIGHT: if self.selectedmenu == len(self.menu)-1: self.selectedmenu = 0 else: self.selectedmenu = self.selectedmenu+1 elif self.event.key == pygame.K_RETURN: if self.selectedmenu == 0: pygame.display.toggle_fullscreen() else: plc = Menu() # 1st layer: background color self.screen.fill(self.maincolor) self.bg = self.menubg[szam] self.bg.set_alpha(sz) self.screen.blit(self.bg, (0, 0)) self.screen.blit(self.backgroundadded, (0, 0)) # 2nd layer: menus self.crt_menu() # 3rd layer: transparent image self.screen.blit(self.background, (0, 0)) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("Balazs Nagy - BFruit - "+VERSION , True, self.white) self.screen.blit(text_surface, (3, 460)) szamlalo = szamlalo + 4 if szamlalo < 245: sz = sz + 4 if szamlalo > 244: sz = sz - 4 if szamlalo > 490: sz = 0 szamlalo = 0 if szam == len(self.menubg)-1: szam = 0 else: szam = szam + 1 pygame.display.update() def menuid(self): for n in self.menu: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render(n, True, self.white) self.mid.append(text_surface.get_width()) def listmenuall(self): for n in self.menu: self.menuall = self.menuall+n def crt_menu(self): nmb = 0 xpos = 0 while nmb <= self.selectedmenu: xpos = xpos-self.mid[nmb] nmb = nmb+1 xpos = xpos+self.mid[self.selectedmenu]/2 # draw menus on screen font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render(self.menuall, True, self.white) self.screen.blit(text_surface, (320+xpos, 15)) # the game########################### class Game: def __init__(self): self.mut = 0 self.wins = [0, 0, 0, 0, 0] self.keys = 1 self.credit = 20 self.bet = 1 self.lastwin = 0 self.show = [] self.screen = screen self.bsound = pygame.mixer.Sound("data/sounds/CLICK10A.WAV") self.rasound = pygame.mixer.Sound("data/sounds/film_projector.wav") self.rbsound = pygame.mixer.Sound("data/sounds/film_projector.wav") self.rcsound = pygame.mixer.Sound("data/sounds/film_projector.wav") self.bgsound = pygame.mixer.Sound("data/sounds/background001.wav") self.beepsound = pygame.mixer.Sound("data/sounds/beep.wav") self.background = pygame.image.load("data/img/bg.png") self.rlayer = pygame.image.load("data/img/rlayer.png") self.windowlayer = pygame.image.load("data/img/windowlayer.png") self.imgone = pygame.image.load("data/img/1.png") self.imgtwo = pygame.image.load("data/img/2.png") self.imgthree = pygame.image.load("data/img/3.png") self.imgfour = pygame.image.load("data/img/4.png") self.imgfive = pygame.image.load("data/img/5.png") self.imgsix = pygame.image.load("data/img/6.png") self.imgseven = pygame.image.load("data/img/7.png") self.imgeight = pygame.image.load("data/img/8.png") img = [] img.append(self.imgone) img.append(self.imgtwo) img.append(self.imgthree) img.append(self.imgfour) img.append(self.imgfive) img.append(self.imgsix) img.append(self.imgseven) img.append(self.imgeight) self.bgsound.play(loops=-1) # mainloop while True: self.screen.fill([0, 0, 0]) self.screen.blit(self.background, (0, 0)) for event in pygame.event.get(): if event.type == pygame.QUIT: self.bgsound.stop() exit() if event.type == pygame.KEYDOWN: self.ledOff() self.bsound.play() if event.key == pygame.K_LEFT and self.keys == 1: if self.credit > 0: if self.credit - self.bet < 0: self.bet = self.credit self.credit = self.credit - self.bet self.randi() self.check() self.roll(img) self.screen.blit(self.background, (0, 0)) self.drawl() self.winner() elif self.credit == 0 and self.bet == 0: self.bgsound.stop() plc = Menu() if self.credit > 0: if event.key == pygame.K_UP and self.keys == 1: if self.credit - self.bet - 1 >= 0: self.bet = self.bet + 1 else: self.bet = 1 if self.bet == 11: self.bet = 1 else: self.bet = 0 if event.key == pygame.K_F1: if self.keys == 1: self.keys = 0 self.menu = "h" elif self.keys == 0: self.keys = 1 self.menu = "n" if event.key == pygame.K_RETURN: self.keys = 0 self.menu = "e" if event.key == pygame.K_ESCAPE and self.keys == 1: self.bgsound.stop() plc = Menu() self.draw_side() if self.mut == 1: self.drawl() self.check() self.wins = [0, 0, 0, 0, 0] if self.credit == 0 and self.bet == 0: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 55) text_surface = font.render("Game Over", True, [255, 0, 0]) self.screen.blit(text_surface, (70, 190)) self.screen.blit(self.rlayer, (37, 48)) self.screen.blit(self.windowlayer, (0, 0)) if self.keys == 0 and self.menu == "h": self.helpmenu() if self.keys == 0 and self.menu == "e": self.endthegame(scr) pygame.display.update() def roll(self, img): szam = 0 # toll time rolla = randrange(5, 14) rollb = randrange(rolla+1, rolla+5) rollc = randrange(rollb+1, rollb+5) # a column rollaf = [] rollaf.append(img[int(self.show[0])-1]) rollaf.append(img[int(self.show[1])-1]) rollaf.append(img[int(self.show[2])-1]) while szam <= rolla-3: rollaf.append(img[randrange(0, 8)]) szam = szam + 1 self.rasound.play() rollaf.append(img[int(self.showold[0])-1]) rollaf.append(img[int(self.showold[1])-1]) rollaf.append(img[int(self.showold[2])-1]) szam = 0 # b column rollbf = [] rollbf.append(img[int(self.show[3])-1]) rollbf.append(img[int(self.show[4])-1]) rollbf.append(img[int(self.show[5])-1]) while szam <= rollb-3: rollbf.append(img[randrange(0, 8)]) szam = szam +1 self.rbsound.play() rollbf.append(img[int(self.showold[3])-1]) rollbf.append(img[int(self.showold[4])-1]) rollbf.append(img[int(self.showold[5])-1]) szam = 0 # c column rollcf = [] rollcf.append(img[int(self.show[6])-1]) rollcf.append(img[int(self.show[7])-1]) rollcf.append(img[int(self.show[8])-1]) while szam <= rollc-3: rollcf.append(img[randrange(0, 8)]) szam = szam +1 self.rcsound.play() rollcf.append(img[int(self.showold[6])-1]) rollcf.append(img[int(self.showold[7])-1]) rollcf.append(img[int(self.showold[8])-1]) szama = len(rollaf)-1 szamb = len(rollbf)-1 szamc = len(rollcf)-1 while szamc > 2: self.screen.fill([0, 0, 0]) self.screen.blit(self.background, (0, 0)) if szama > 2: self.screen.blit(rollaf[len(rollaf)-3], (36, 46)) self.screen.blit(rollaf[len(rollaf)-2], (36, 174)) self.screen.blit(rollaf[len(rollaf)-1], (36, 302)) szama = szama - 1 del(rollaf[len(rollaf)-1]) else: self.screen.blit(rollaf[len(rollaf)-3], (36, 46)) self.screen.blit(rollaf[len(rollaf)-2], (36, 174)) self.screen.blit(rollaf[len(rollaf)-1], (36, 302)) self.rasound.stop() if szamb > 2: self.screen.blit(rollbf[len(rollbf)-3], (165, 46)) self.screen.blit(rollbf[len(rollbf)-2], (165, 174)) self.screen.blit(rollbf[len(rollbf)-1], (165, 302)) szamb = szamb - 1 del(rollbf[len(rollbf)-1]) else: self.screen.blit(rollbf[len(rollbf)-3], (165, 46)) self.screen.blit(rollbf[len(rollbf)-2], (165, 174)) self.screen.blit(rollbf[len(rollbf)-1], (165, 302)) self.rbsound.stop() if szamc > 2: self.screen.blit(rollcf[len(rollcf)-3], (295, 46)) self.screen.blit(rollcf[len(rollcf)-2], (295, 174)) self.screen.blit(rollcf[len(rollcf)-1], (295, 302)) szamc = szamc - 1 del(rollcf[len(rollcf)-1]) else: self.screen.blit(rollcf[len(rollcf)-3], (295, 46)) self.screen.blit(rollcf[len(rollcf)-2], (295, 174)) self.screen.blit(rollcf[len(rollcf)-1], (295, 302)) self.draw_side() self.screen.blit(self.rlayer, (37, 48)) self.screen.blit(self.windowlayer, (0, 0)) pygame.display.update() rollc = rollc - 1 self.rcsound.stop() def draw_side(self): #animation digifont = pygame.font.Font("data/DIGITAL2.ttf",24) text_surface = digifont.render("88888888888", True, [60, 0, 0]) self.screen.blit(text_surface, (470, 50)) text_surface = digifont.render("F1 FOR HELP", True, [255, 0, 0]) self.screen.blit(text_surface, (470, 50)) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("Bet:", True, [230, 255, 255]) self.screen.blit(text_surface, (500, 185)) # multip digifont = pygame.font.Font("data/DIGITAL2.ttf",24) text_surface = digifont.render("88", True, [60, 0, 0]) self.screen.blit(text_surface, (500, 210)) text_surface = digifont.render(str(self.bet), True, [255, 0, 0]) self.screen.blit(text_surface, (500, 210)) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("Winner Paid:", True, [230, 255, 255]) self.screen.blit(text_surface, (500, 255)) # last win digifont = pygame.font.Font("data/DIGITAL2.ttf",24) text_surface = digifont.render("888", True, [60, 0, 0]) self.screen.blit(text_surface, (500, 280)) text_surface = digifont.render(str(self.lastwin), True, [255, 0, 0]) self.screen.blit(text_surface, (500, 280)) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("Credit:", True, [230, 255, 255]) self.screen.blit(text_surface, (500, 325)) # startsum digifont = pygame.font.Font("data/DIGITAL2.ttf",24) text_surface = digifont.render("888888", True, [60, 0, 0]) self.screen.blit(text_surface, (500, 350)) text_surface = digifont.render(str(self.credit), True, [255, 0, 0]) self.screen.blit(text_surface, (500, 350)) def drawl(self): self.screen.blit(pygame.image.load("data/img/"+str(self.show[0])+".png"), (36, 46)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[1])+".png"), (36, 174)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[2])+".png"), (36, 302)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[3])+".png"), (165, 46)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[4])+".png"), (165, 174)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[5])+".png"), (165, 302)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[6])+".png"), (295, 46)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[7])+".png"), (295, 174)) self.screen.blit(pygame.image.load("data/img/"+str(self.show[8])+".png"), (295, 302)) # random images def randi(self): self.showold = [] if len(self.show) > 1: self.showold = self.show else: self.showold = ["8", "8", "8", "8", "8", "8", "8", "8", "8"] self.mut = 1 ran = {} ran[0] = randrange(1, 335) ran[1] = randrange(1, 335) ran[2] = randrange(1, 335) ran[3] = randrange(1, 335) ran[4] = randrange(1, 335) ran[5] = randrange(1, 335) ran[6] = randrange(1, 335) ran[7] = randrange(1, 335) ran[8] = randrange(1, 335) self.show = [] for n in ran: if 1 <= ran[n] <= 5: self.show.append("8") if 6 <= ran[n] <= 15: self.show.append("7") if 16 <= ran[n] <= 30: self.show.append("6") if 31 <= ran[n] <= 50: self.show.append("5") if 51 <= ran[n] <= 120: self.show.append("4") if 121 <= ran[n] <= 180: self.show.append("3") if 181 <= ran[n] <= 253: self.show.append("2") if 254 <= ran[n] <= 334: self.show.append("1") def check(self): if self.show[0] == self.show[3] == self.show[6]: pygame.draw.line(self.screen, [246, 226, 0], (36, 111), (423, 111), 8) self.wins[0] = self.show[0] if self.show[1] == self.show[4] == self.show[7]: pygame.draw.line(self.screen, [246, 226, 0], (36, 239), (423, 239), 8) self.wins[1] = self.show[1] if self.show[2] == self.show[5] == self.show[8]: pygame.draw.line(self.screen, [246, 226, 0], (36, 367), (423, 367), 8) self.wins[2] = self.show[2] if self.show[0] == self.show[4] == self.show[8]: pygame.draw.line(self.screen, [246, 226, 0], (37, 47), (422, 433), 8) self.wins[3] = self.show[0] if self.show[2] == self.show[4] == self.show[6]: pygame.draw.line(self.screen, [246, 226, 0], (37, 432), (422, 47), 8) self.wins[4] = self.show[2] def winner(self): self.lastwin = 0 for n in self.wins: winsu = self.bet*int(n) winsum = winsu + self.bet if winsum > self.bet: self.credit = self.credit + winsum self.lastwin = self.lastwin + winsum self.beepsound.play() self.ledOn() def ledOn (self): GPIO.output(37,True) ## Turn on GPIO pin 7 print "LedOn" def ledOff (self): GPIO.output(37,False) ## Turn on GPIO pin 7 print "LedOff" def helpmenu(self): pygame.draw.line(self.screen, [176, 176, 176], (50, 250), (590, 250), 400) font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("How to play:", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 60)) text_surface = font.render("New spin: left arrow", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 80)) text_surface = font.render("Raise bet: arrow up", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 100)) text_surface = font.render("To end game to high score press Enter", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 120)) text_surface = font.render("To close this as game over help press F1", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 160)) def endthegame(self, scr): scrb = int(scr) pygame.draw.line(self.screen, [176, 176, 176], (50, 250), (590, 250), 400) if self.credit > scrb: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("You have a new high score!!!", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 60)) text_surface = font.render("Old high score: "+scr, True, [255, 255, 255]) self.screen.blit(text_surface, (60, 80)) text_surface = font.render("New high score: "+str(self.credit), True, [255, 255, 255]) self.screen.blit(text_surface, (60, 100)) self.writehs(myhsfile) else: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 15) text_surface = font.render("You ended the game, but you don't have a new high score...", True, [255, 255, 255]) self.screen.blit(text_surface, (60, 60)) for event in pygame.event.get(): if event.type == pygame.KEYDOWN: self.bgsound.stop() plc = Menu() def writehs(self, myhsfile): writef = open(myhsfile, "w") writef.write(str(self.credit)) writef.close() def help(): print "BFruit help:" print "Options:" print "-h, --help display this help message" print "-v, --version display game version" print "Contact: nxbalazs@gmail.com" if __name__ == "__main__": try: long = ["help", "version"] opts = getopt(argv[1:], "hv", long)[0] except GetoptError: help() exit() for opt, arg in opts: if opt in ("-h", "--help"): help() exit() if opt in ("-v", "--version"): print "BFruit - version: "+ VERSION exit() # .settings: homedir = os.path.expanduser("~") if homedir[0] == "/": mydir = homedir+"/.bfruit" myhsfile = mydir+"/hs" else: mydir = homedir+"\Application Data\.bfruit" myhsfile = mydir+"\hs" if os.path.exists(mydir) == False: os.mkdir(mydir) if os.path.exists(myhsfile) == False: open(myhsfile, "w").close() hsf = open(myhsfile, "r+") scr = hsf.readline() # high score hsf.close() if scr == "": scr = "1" # pygame init, set display pygame.init() screen = pygame.display.set_mode([640, 480], 0, 24) pygame.display.set_caption("BFruit") pygame.mouse.set_visible(False) # intro border = pygame.image.load("data/intro/border.png").convert() point = pygame.image.load("data/intro/point.png").convert() sun = pygame.image.load("data/intro/sun.png").convert() szam = 0 while szam < 256: for event in pygame.event.get(): if event.type == pygame.QUIT: exit() if event.type == pygame.KEYDOWN: plc = Menu() screen.fill([0, 0, 0]) border.set_alpha(szam) point.set_alpha(szam) screen.blit(border, (160, 120)) screen.blit(point, (185, 150)) screen.blit(point, (185, 180)) screen.blit(point, (185, 210)) screen.blit(point, (185, 240)) screen.blit(point, (185, 270)) screen.blit(point, (215, 150)) screen.blit(point, (215, 180)) screen.blit(point, (215, 210)) screen.blit(point, (215, 240)) screen.blit(point, (215, 270)) screen.blit(point, (245, 150)) screen.blit(point, (245, 180)) screen.blit(point, (245, 210)) screen.blit(point, (245, 240)) screen.blit(point, (245, 270)) screen.blit(point, (275, 150)) screen.blit(point, (275, 180)) screen.blit(point, (275, 210)) screen.blit(point, (275, 240)) screen.blit(point, (275, 270)) screen.blit(point, (305, 150)) screen.blit(point, (305, 180)) screen.blit(point, (305, 210)) screen.blit(point, (305, 240)) screen.blit(point, (305, 270)) screen.blit(point, (335, 150)) screen.blit(point, (335, 180)) screen.blit(point, (335, 210)) screen.blit(point, (335, 240)) screen.blit(point, (335, 270)) screen.blit(point, (365, 150)) screen.blit(point, (365, 180)) screen.blit(point, (365, 210)) screen.blit(point, (365, 240)) screen.blit(point, (365, 270)) screen.blit(point, (395, 150)) screen.blit(point, (395, 180)) screen.blit(point, (395, 210)) screen.blit(point, (395, 240)) screen.blit(point, (395, 270)) screen.blit(point, (425, 150)) screen.blit(point, (425, 180)) screen.blit(point, (425, 210)) screen.blit(point, (425, 240)) screen.blit(point, (425, 270)) szam = szam + 4 pygame.display.update() starttime = time.clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: exit() if event.type == pygame.KEYDOWN: plc = Menu() screen.blit(border, (160, 120)) screen.blit(point, (185, 150)) screen.blit(point, (185, 180)) screen.blit(point, (185, 210)) screen.blit(point, (185, 240)) if time.clock() - starttime < 1: screen.blit(point, (185, 270)) if time.clock() - starttime < 1.2: screen.blit(point, (215, 150)) screen.blit(point, (215, 180)) if time.clock() - starttime < 1.15: screen.blit(point, (215, 210)) if time.clock() - starttime < 1.2: screen.blit(point, (215, 240)) if time.clock() - starttime < 1.23: screen.blit(point, (215, 270)) if time.clock() - starttime < 1.18: screen.blit(point, (245, 150)) if time.clock() - starttime < 1.2: screen.blit(point, (245, 180)) screen.blit(point, (245, 210)) if time.clock() - starttime < 1.43: screen.blit(point, (245, 240)) if time.clock() - starttime < 1.5: screen.blit(point, (245, 270)) screen.blit(point, (275, 150)) screen.blit(point, (275, 180)) screen.blit(point, (275, 210)) screen.blit(point, (275, 240)) if time.clock() - starttime < 1.22: screen.blit(point, (275, 270)) if time.clock() - starttime < 1.41: screen.blit(point, (305, 150)) if time.clock() - starttime < 1.34: screen.blit(point, (305, 180)) if time.clock() - starttime < 1.4: screen.blit(point, (305, 210)) if time.clock() - starttime < 1.36: screen.blit(point, (305, 240)) if time.clock() - starttime < 1.1: screen.blit(point, (305, 270)) screen.blit(point, (335, 150)) screen.blit(point, (335, 180)) screen.blit(point, (335, 210)) screen.blit(point, (335, 240)) screen.blit(point, (335, 270)) screen.blit(point, (365, 150)) if time.clock() - starttime < 1.13: screen.blit(point, (365, 180)) screen.blit(point, (365, 210)) if time.clock() - starttime < 1.4: screen.blit(point, (365, 240)) screen.blit(point, (365, 270)) screen.blit(point, (395, 150)) if time.clock() - starttime < 1.31: screen.blit(point, (395, 180)) screen.blit(point, (395, 210)) if time.clock() - starttime < 1.25: screen.blit(point, (395, 240)) screen.blit(point, (395, 270)) if time.clock() - starttime < 1.43: screen.blit(point, (425, 150)) screen.blit(point, (425, 180)) if time.clock() - starttime < 2.0: screen.blit(point, (425, 210)) screen.blit(point, (425, 240)) if time.clock() - starttime < 2.4: screen.blit(point, (425, 270)) if time.clock() - starttime > 3: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render("nXBalazs" , True, [255, 255, 255]) screen.blit(text_surface, (190, 273)) if time.clock() - starttime > 3.5: font = pygame.font.Font("data/LiberationSans-Regular.ttf", 25) text_surface = font.render("games" , True, [255, 255, 255]) screen.blit(text_surface, (280, 310)) if 5 > time.clock() - starttime > 4: szamsun = 0 while szamsun < 100: sun.set_alpha(szamsun) screen.blit(sun, (0, 0)) szamsun = szamsun + 1 pygame.display.update() if time.clock() - starttime > 5: plc = Menu() pygame.display.update()
Lighting Effects Pt. 1
LED Animation Designs
I have always been a self-proclaimed LED junkie. From as early as I can remember in my maker adventures, I have been fascinated with LEDs, and when the first NeoPixelNeoPixel / WS2811 and WS2812 strips hit the market, my life all but changed. No longer did I have to design complex circuitry and coding to manipulate RGB LED’s, I could use a single data line, and two power lines to control as many of these LEDs as I desired. For this project I knew I had to incorporate NeoPixels into the design somewhere, and my solution was to use them for both advertising the game to possible players, and to indicate when a player has won on a spin.
My initial plan was to have a single animation running from a separate controller that would serve as the slot machine costume’s “ambient” lighting animation, and then have a separate strip of NeoPixels run an animation when someone wins, and I developed two separate scripts for these animations. I was able to combine these into a single script after coming up with an entirely new approach to how I control the NeoPixels, more on that in a bit though. Let’s look at both scripts and see how they work.
Ambient Chase Code
I named the first animation Ambient Chase because that is basically what it is. I modified some code I found in an example animation for the FastLED library that let me create a traditional theater chase animation that cycled through a rainbow effect of 256 different colors. If you have worked with Adafruit’s NeoPixel library, or the FastLED library (my preferred choice) then you know how short these type of scripts can be. Below is an snippet of just the Ambient Chase code, and below that is the full Arduino-ready script to run this code.
Ambient Chase Function Snippet
//Theatre Chase Sytle Crawling Lights With Rainbow Effect void theaterChaseRainbow(uint8_t wait) { uint16_t i,j,q; for (j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (q=0; q < 3; q++) { for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q].setHue((i+j) % 255); //turn every third pixel on } FastLED.show(); //Update all LEDs delay(55); // controls the speed of the chase for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q] = 0; //turn every third pixel off } } } }
Full Ambient Chase Code
#include "FastLED.h" // How many leds in your strip? #define NUM_LEDS 22 #define DATA_PIN 6 #define SLOW 250 #define MEDIUM 75 CRGB leds[NUM_LEDS]; void setup() { FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); } void loop(){ theaterChaseRainbow(50); } //Theatre Chase Sytle Crawling Lights With Rainbow Effect void theaterChaseRainbow(uint8_t wait) { uint16_t i,j,q; for (j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (q=0; q < 3; q++) { for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q].setHue((i+j) % 255); //turn every third pixel on } FastLED.show(); //Update all LEDs delay(55); // controls the speed of the chase for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q] = 0; //turn every third pixel off } } } }
White Strobe Code
The second animation I had to have in this project was something that could alert players when they won on a spin. I tried several different things including a Rainbow Color Wipe (too slow), a reverse ambient chase (not evident enough), a color flipflop from blue to green (just did not look right), as well as a few others. After several different animations, I settled on a simple white strobe. I chose this for a few different reasons, the first being that it is instantly recognizable as a WIN, and the second being that it was very easy to code, and control the length. The problem with some of the other animations I tried out were their length. For a color wipe, at a decent speed that looked good, the entire animation took more than thirty seconds. The strobe animation takes less than five seconds to complete. Below is an snippet of just the White Strobe code, and below that is the full Arduino-ready script to run this code.
White Strobe Function Snippet
void whitestrobe(int NUM_OF_CYCLES) { // Turn the LED on, then pause for (int j=0; j<NUM_OF_CYCLES; j++) { fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::White); FastLED.show(); delay(20); // Now turn the LED off, then pause fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::Black); FastLED.show(); delay(50); } }
Full White Strobe Code
#include "FastLED.h" // How many leds in your strip? #define NUM_LEDS 22 #define DATA_PIN 6 #define SLOW 250 #define MEDIUM 75 CRGB leds[NUM_LEDS]; void setup() { FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); } void loop(){ whitestrobe(20); } void whitestrobe(int NUM_OF_CYCLES) { // Turn the LED on, then pause for (int j=0; j<NUM_OF_CYCLES; j++) { fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::White); FastLED.show(); delay(20); // Now turn the LED off, then pause fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::Black); FastLED.show(); delay(50); } }
Slight Issues With Raspberry Pi and NeoPixels
Almost every project I have ever built has had some form of an issue arise during its development phase, and as it turns out, this project is no different. In my initial planning phase for this project, I wanted to control the NeoPixel LEDs directly from the Raspberry Pi 3 itself. Unfortunately I forgot about the Pi’s audio channel being routed through pin 12 which is also the PWM pin on the Pi’s GPIO pins. This is a problem because the NeoPixel library for the Raspberry Pi uses this pin to send data to the WS2811 microcontroller that is embedded inside each NeoPixel LED. While I am not sure if this is an issue with just the audio jack, or if it affects the Bluetooth audio output as well, I have decided to control the NeoPixels with an Arduino Nano. This not only alleviates the issue with the PWM pin / Audio channel, it allows me more control over the NeoPixels, as well as being able to run several LED animations from a single source.
Since I will be using an Arduino Nano to control the NeoPixel Strips, I have to have a way to trigger the different LED animations from the Raspberry Pi. There are several methods to do this, and after much consideration I have decided to have the slot machine software trigger the LEDs by setting a GPIO Pin on the Raspberry Pi to High. This high signal will carry over to a GPIO pin on the Arduino Nano, which will then run a predetermined LED animation. Those of you who read my Foginator 2000 project from last Halloween will remember that this is how I controlled the NeoPixels then as well. The only difference this time is that I will be using an external interrupt on the Arduino’s Digital Pin 3, which will trigger on the rising edge of the signal going high and then tell the Arduino to break from its currently running function, and to run another. If you want to know more about how interrupts work on the Arduino, Jeremy Blum (sciguy14) made an excellent video on them for Element14 a while back.
After talking to several engineer friends, this method seems to be the correct way to handle this situation after all. The Raspberry Pi is not really well suited to control devices that need real-time control. This is because Raspbian is a multitasking operating system, and thus real-time control is not possible. Since the WS2812B LEDs on the NeoPixel Strip require a very precise and timed signal, controlling them from a Raspberry Pi is hackery at best (not that hackery is a bad thing). As I mentioned, there is a NeoPixel library for the Raspberry Pi, and it converts a PWM signal into something that the WS2812B-based NeoPixels can use, but with this, you lose functionality of the Audio port, as it runs through the PWM pin. So naturally, the best way to add NeoPixels to a Raspberry Pi project, is to offload their control to a separate microcontroller. Below is a diagram of how I connected the Raspberry Pi to the Arduino Nano.
With the circuit figured out, I created a small test board with some NeoPixel scraps I had laying around. I connected two breadboards together, and used one to lay out a square that is connected together as one strip, and another two independent strips. I also added in a female barrel jack pigtail so that I could provide proper power to the strips without overloading the Arduino Nano.
With the development setup built I was able to successfully able to combine the two Arduino scripts into a single script that allowed me to run the Ambient Chase animation when the game is powered up, and then quickly switch to the White Strobe animation when a win is detected. To do this, I attached an external interrupt to the Arduino Nano’s digital Pin 3, which is where the Atmega 328’s interrupt 1 is connected. Then all three NeoPixel strips were connected to the Arduino Nano’s digital pin 6. The full code is below.
Raspberry PySlots NeoPixel Code for Arduino
#include "FastLED.h" #define NUM_LEDS 22 #define DATA_PIN 6 #define CLOCK_PIN 13 #define SLOW 250 #define MEDIUM 75 CRGB leds[NUM_LEDS]; void setup() { pinMode(3,INPUT); pinMode(3,LOW); FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); attachInterrupt (1,react,RISING); } void loop(){ theaterChaseRainbow(50); } void react () { whitestrobe(20); // run the strobe function } void whitestrobe(int NUM_OF_CYCLES) { // Turn the LED on, then pause for (int j=0; j<NUM_OF_CYCLES; j++) { fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::White); FastLED.show(); delay(20); // Now turn the LED off, then pause fill_solid( &(leds[0]), NUM_LEDS /*number of leds*/, CRGB::Black); FastLED.show(); delay(50); } } //Theatre Chase Sytle Crawling Lights With Rainbow Effect void theaterChaseRainbow(uint8_t wait) { uint16_t i,j,q; for (j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (q=0; q < 3; q++) { for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q].setHue((i+j) % 255); //turn every third pixel on } FastLED.show(); //Update all LEDs delay(55); // controls the speed of the chase for (i=0; i+q < NUM_LEDS; i=i+3) { leds[i+q] = 0; //turn every third pixel off } } } }
In the video above you can see a demo of what I have working so far. During normal game play, the Arduino runs the Ambient Chase Function, then when a win is registered by the software, the Raspberry Pi triggers the White Strobe Function. I still need to work on the code to allow me to trigger the spin via a Tilt Switch, but that will have to wait until the next post. I simply ran out of time to figure out how to map a keyboard press to a GPIO Input Pin using PyGame, so if anyone has any suggestions there, please leave them in the comments. That is going to wrap up this post. I have one more post before the project is finished, so check back next week for the grand finale! Until then, Hack the World, and Make Awesome!
Check Out The Full Project!
- Raspberry PySlots: The Costume - Introduction
- Raspberry PySlots: The Costume - Hardware, Software, and Pretty Lights!
- Raspberry PySlots: The Costume - The Slot Machine, NeoPixels, Pull Arm, And Finishing Things Up