Maker.io main logo

RGB Matrix Slot Machine

2020-09-01 | By Adafruit Industries

License: See Original Project 3D Printing

Courtesy of Adafruit

Guide by Ruiz Brothers

Overview

Fruit Machine

This features emojis from Circuit Playground and works just like a fruit style slot machine. The handle is 3D printed and features a print-in-place spring loaded mechanism. It actuates a small push button and triggers animations that look a lot like a slot machine.

 

fruit_1

 The display is powered by an Adafruit Feather and the RGB Matrix FeatherWing. It also has a speaker and amp for playing MP3s. The FeatherWing is designed to be modular and snap fits on the back of the RGB matrix.

Using CircuitPython you can quickly show text, bitmaps, and animations on a number of different RGB Matrices. It’s designed to work with 64x32 RGB matrices.

Parts

List of parts used to build this project.

parts_2

Black LED Acrylic

A sheet of black LED acrylic used to diffuse the RGB LED Matrix. From their website:

It looks like a matte black acrylic when it is not lit but add a light behind it and watch it transform. This fantastic material has the ability to transmit light of any color.

LED_3

Black LED acrylic is used to diffuse the LEDs that are otherwise harsh and over exposed. This material features a matte finish and has the ability to transmit any color of light. That makes it perfect for bringing out the vibrance in the colors of the LEDs.

The 3D printed frame is designed with a slot on the side so it’s easy to insert a sheet of Black LED acrylic.

Recommended Cut to Size Sheet: 257mm x 128mm (10.12in x 5.04in)

Black LED Acrylic from TAP Plastics

The 3D printed parts in this project are designed for this specific RGB LED Matrix (PID 2278)

display_4

Circuit Diagram

The diagram below provides a visual reference for wiring of the components. This diagram was created using the software package Fritzing.

Adafruit Library for Fritzing

Use Adafruit's Fritzing parts library to create circuit diagrams for your projects. Download the library or just grab individual parts. Get the library and parts from GitHub - Adafruit Fritzing Parts.

fritzing_5

Wired Connections

  • A+ from PAM8302 to A0 on RGB Matrix FeatherWing
  • A- from PAM8302 to GND on PAM8302
  • Vin from PAM8302 to 3V on RGB Matrix FeatherWing
  • GND from PAM8302 to GND on RGB Matrix FeatherWing

Button Switch

  • Switch to GND on RGB Matrix FeatherWing
  • Switch to A1 on RGB Matrix FeatherWing

Speaker

  • Red to (+) Positive on PAM8302
  • Black to () Negative on PAM8302

Powering

The RGB Matrix will need to be powered by a 5V 4A wall adapter. 5V USB cable is not enough to fully power the display – Glitches and artifacts are displayed when RGB matrix is only powered by USB cable.

5V 4A (4000mA) switching power supply - UL Listed

3D Printing

Parts List

STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material. Original design source may be downloaded using the links below.

  • matrix-handle.stl
  • matrix-frame.stl
  • speaker-holder.stl
  • 2x handle-ball.stl
  • foot-left.stl
  • foot-right.stl

list_5a

The 3D printed parts in this project are designed for this specific RGB LED Matrix (PID 2278)

Spring-Loaded handle design was inspired by Turbo Sunshine https://www.thingiverse.com/thing:4382544

The frame requires a minimum bed size of 265mm x 135mm.

Install Ball to Handle

Two hemispheres (handle-ball.stl) are joined together to form the ball of the handle. They're are press fitted over the tip of the spring-loaded handle. Domes features recesses to accommodate for the handle. Optionally use glue to bond the domes to the handle.

ball_6

Spring–Loaded Handle

The handle features print-in-place parts that require good bed leveling and sufficient active cooling. Overhanging geometry is supported by surrounding features. No supports are required. The handle and gears are printed with loose tolerances to avoid fusing.

spring_7

Support Material for Frame

The frame requires support material. Enable supports in the slicing software to generate scaffolding to support overhanging geometry. Use the following recommended settings:

  • Support Placement: Everywhere
  • Support Overhang Angle: 80
  • Support Pattern: ZigZag
  • Support Density: 4%
  • Enable Support Brim: No
  • Support Z Distance: 0.22mm
  • Enable Support Interface: No

support_8

CircuitPython on Feather M4 Express

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

The following instructions will show you how to install CircuitPython. If you've already installed CircuitPython but are looking to update it or reinstall it, the same steps work for that as well!

Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)

Download the latest version of CircuitPython for this board via CircuitPython.org

Click the link above and download the latest UF2 file.

Download and save it to your desktop (or wherever is handy).

file_9

Plug your Feather M4 into your computer using a known-good USB cable.

A lot of people end up using charge-only USB cables and it is very frustrating! So, make sure you have a USB cable you know is good for data sync.

Double-click the Reset button next to the USB connector on your board, and you will see the NeoPixel RGB LED turn green. If it turns red, check the USB cable, try another USB port, etc. Note: The little red LED next to the USB connector will pulse red. That's ok!

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

Feather_10

You will see a new disk drive appear called FEATHERBOOT.

Drag the adafruit_circuitpython_etc.uf2 file to FEATHERBOOT.

 disk_11

disk_11a

The LED will flash. Then, the FEATHERBOOT drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

dragfile_12

Further Information

For more detailed info on installing CircuitPython, check out Installing CircuitPython.

CircuitPython Libraries

Installing or upgrading CircuitPython

You should ensure you have CircuitPython 5.3 or greater on your board. Plug your board in with a known good data + power cable (not the cheesy USB cable that comes with USB power packs, they are power only). You should see a new flash drive pop up.

If the drive is CIRCUITPY, then open the boot_out.txt file to ensure the version number is 5.3 or greater.

Download: file

Copy Code
Adafruit CircuitPython 5.3.0 on 2020-04-29; Adafruit Feather M4 Express with samd51j19

If the version is less than 5 -or- you only get a drive named FEATHERBOOT then follow the Feather M4 guide on installing CircuitPython.

Download the Adafruit CircuitPython Library Bundle

In order to run the code, we'll need to download a few libraries. Libraries contain code to help interface with hardware a lot easier for us.

Use the Feather M4 page on Installing Libraries to get the library that matches the major version of CircuitPython you are using noted above.

The green button below links to a file containing all the libraries available for CircuitPython. To run the code for this project, we need the large number of libraries in the Required Libraries list below. Unzip the library bundle and search for the libraries. Drag and drop the files into a folder named lib on the CIRCUITPY drive (create the folder if it is not already on the Feather M4).

Required Libraries

  • adafruit_display_text
  • adafruit_bitmap_font
  • adafruit_imageload

Required Assets

  • emoji.bmp
  • slots.mp3

Libraries_14

Once we have all the files we need, a directory listing will look similar to below as far as files and directories.

Upload Code

Click on the Download: Project Zip link below to grab the project files in a zip file directly from GitHub. Place the code.py file and bitmap graphics files onto the CIRCUITPY main (root) directory. The code will run properly when all of the files have been uploaded including libraries.

Use any text editor or favorite IDE to modify the code. We suggest using Mu as noted above.

Download: Project Zip or code.py | View on Github

Copy Code
import random
import time

import adafruit_imageload.bmp
import audioio
import audiomp3
import board
import displayio
import digitalio
import framebufferio
import rgbmatrix

displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
width=64, height=32, bit_depth=4,
rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
addr_pins=[board.A5, board.A4, board.A3, board.A2],
clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

# Each wheel can be in one of three states:
STOPPED, RUNNING, BRAKING = range(3)

# Return a duplicate of the input list in a random (shuffled) order.
def shuffled(seq):
return sorted(seq, key=lambda _: random.random())

# The Wheel class manages the state of one wheel. "pos" is a position in
# floating point coordinates, with one 1 pixel being 1 position.
# The wheel also has a velocity (in positions
# per tick) and a state (one of the above constants)
class Wheel(displayio.TileGrid):
def __init__(self, bitmap, palette):
# Portions of up to 3 tiles are visible.
super().__init__(bitmap=bitmap, pixel_shader=palette,
width=1, height=3, tile_width=20, tile_height=24)
self.order = shuffled(range(20))
self.state = STOPPED
self.pos = 0
self.vel = 0
self.termvel = 2
self.y = 0
self.x = 0
self.stop_time = time.monotonic_ns()
self.step()

def step(self):
# Update each wheel for one time step
if self.state == RUNNING:
# Slowly lose speed when running, but go at least terminal velocity
self.vel = max(self.vel * .99, self.termvel)
if time.monotonic_ns() > self.stop_time:
self.state = BRAKING
elif self.state == BRAKING:
# More quickly lose speed when baking, down to speed 0.4
self.vel = max(self.vel * .85, 0.4)

# Advance the wheel according to the velocity, and wrap it around
# after 24*20 positions
self.pos = (self.pos + self.vel) % (20*24)

# Compute the rounded Y coordinate
yy = round(self.pos)
# Compute the offset of the tile (tiles are 24 pixels tall)
yyy = yy % 24
# Find out which tile is the top tile
off = yy // 24

# If we're braking and a tile is close to midscreen,
# then stop and make sure that tile is exactly centered
if self.state == BRAKING and self.vel <= 2 and yyy < 8:
self.pos = off * 24
self.vel = 0
yyy = 0
self.state = STOPPED

# Move the displayed tiles to the correct height and make sure the
# correct tiles are displayed.
self.y = yyy - 20
for i in range(3):
self[i] = self.order[(19 - i + off) % 20]

# Set the wheel running again, using a slight bit of randomness.
# The 'i' value makes sure the first wheel brakes first, the second
# brakes second, and the third brakes third.
def kick(self, i):
self.state = RUNNING
self.vel = random.uniform(8, 10)
self.termvel = random.uniform(1.8, 4.2)
self.stop_time = time.monotonic_ns() + 3000000000 + i * 350000000


# This bitmap contains the emoji we're going to use. It is assumed
# to contain 20 icons, each 20x24 pixels. This fits nicely on the 64x32
# RGB matrix display.
the_bitmap, the_palette = adafruit_imageload.load(
"/emoji.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)

# Our fruit machine has 3 wheels, let's create them with a correct horizontal
# (x) offset and arbitrary vertical (y) offset.
g = displayio.Group(max_size=3)
wheels = []
for idx in range(3):
wheel = Wheel(the_bitmap, the_palette)
wheel.x = idx * 22
wheel.y = -20
g.append(wheel)
wheels.append(wheel)
display.show(g)

# We want a digital input to trigger the fruit machine
button = digitalio.DigitalInOut(board.A1)
button.switch_to_input(pull=digitalio.Pull.UP)

# Enable the speaker
enable = digitalio.DigitalInOut(board.D4)
enable.switch_to_output(True)

mp3file = open("/triangles-loop.mp3", "rb")
sample = audiomp3.MP3Decoder(mp3file)

# Play the sample (just loop it for now)
speaker = audioio.AudioOut(board.A0)
speaker.play(sample, loop=True)

# Here's the main loop
while True:
# Refresh the dislpay (doing this manually ensures the wheels move
# together, not at different times)
display.refresh(minimum_frames_per_second=0, target_frames_per_second=60)

all_stopped = all(si.state == STOPPED for si in wheels)
if all_stopped:
# Once everything comes to a stop, wait until the lever is pulled and
# start everything over again. Maybe you want to check if the
# combination is a "winner" and add a light show or something.

while button.value:
pass
for idx, si in enumerate(wheels):
si.kick(idx)

# Otherwise, let the wheels keep spinning...
for idx, si in enumerate(wheels):
si.step()

Double Check

See the directory listing above and double check that you have all the files listed to make this project function. If any are missing or in an incorrect directory, move them so they're in the right places.

CircuitPython Code Walkthrough

Setup

CircuitPython Libraries

The CircuitPython code begins by importing the libraries that are used.

Download: file

Copy Code
import random
import time

import adafruit_imageload.bmp
import audioio
import audiocore
import audiomp3
import board
import displayio
import digitalio
import framebufferio
import rgbmatrix

Display setup

First, any display left over from a previous code.py is released. Then, the LED matrix is initialized according to its pinout. Because we want to ensure that all 3 wheels on screen are updated at the same time, we specify auto_refresh=False. To learn more about how to write code using the rgbmatrix module, see the dedicated guide.

Download: file

Copy Code
displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
width=64, height=32, bit_depth=3,
rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
addr_pins=[board.A5, board.A4, board.A3, board.A2],
clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

Additional definitions

The three states let us track whether each wheel is stopped, running, or braking.

shuffled lets us take an input sequence and return a new sequence with the order of the elements randomized. This will be used to put the symbols in a different order on each wheel.

Wheel is a class that controls the behavior of each of the 3 fruit wheels. Check out the full source code if you want to see how it works!

Download: file

Copy Code
# Each wheel can be in one of three states:
STOPPED, RUNNING, BRAKING = range(3)

# Return a duplicate of the input list in a random (shuffled) order.
def shuffled(seq):
return sorted(seq, key=lambda _: random.random())

# The Wheel class manages the state of one wheel. "pos" is a position in
# floating point coordinates, with one 1 pixel being 1 position.
# The wheel also has a velocity (in positions
# per tick) and a state (one of the above constants)
class Wheel(displayio.TileGrid):
def __init__(self, bitmap, pixel_shader): ...
def step(self):
"""Update each wheel for one time step"""
...
def kick(self, i):
"""Set the wheel running again, using a slight bit of randomness.
The 'i' value makes sure the first wheel brakes first, the second
brakes second, and the third brakes third."""
...

Setting up the wheels

First, load the emoji bitmap into RAM memory. It is small (just a few hundred bytes) and doing this lets the display update much more fluidly than when the bitmap is in FLASH memory.

Then, create 3 wheels and put them all in a group, setting their X and Y positions so that they span the display from left to right.

Download: file

Copy Code
# This bitmap contains the emoji we're going to use. It is assumed
# to contain 20 icons, each 20x24 pixels. This fits nicely on the 64x32
# RGB matrix display.
bitmap, palette = adafruit_imageload.load(
"/emoji.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)

# Our fruit machine has 3 wheels, let's create them with a correct horizontal
# (x) offset and arbitrary vertical (y) offset.
g = displayio.Group(max_size=3)
wheels = []
for idx in range(3):
wheel = Wheel(bitmap, palette)
wheel.x = idx * 22
wheel.y = -20
g.append(wheel)
wheels.append(wheel)
display.show(g)

Setting up the activation switch

The switch is connected to A1, and closing it connects it to GND. By enabling a pull up, the button.value will be False when it is pressed and True otherwise.

Download: file

Copy Code
# We want a digital input to trigger the fruit machine
button = digitalio.DigitalInOut(board.A1)
button.switch_to_input(pull=digitalio.Pull.UP)

Setting up the background sounds

The speaker is connected to A0. Play casino-style noises in MP3 format. If you want to change the MP3, make sure you use a file with the following properties:

  • 16000 or 22050 sample rate
  • 1 channel (mono)
  • 64kbit/s or lower MP3 CBR encoding

You might also like to have multiple different sounds, for instance to play when the lever is pulled and when the wheels stop. You can change to a different sound file by open()ing a new file and assigning it to sample.file. Make sure your files all match in their sample rate, channels, and encoding.

Download: file

Copy Code
mp3file = open("/slots.mp3", "rb")
sample = audiomp3.MP3Decoder(mp3file)

speaker = audioio.AudioOut(board.A0)
speaker.play(sample, loop=True)

Main loop

Here's how the main loop runs:

  • Refresh the screen each time through the loop
  • If all the wheels are stopped, then wait for the lever to be pulled (check button.value) and when it is, set all the wheels in motion by kick()ing them
  • Otherwise, let each wheel rotate a little bit by calling step()
  • Go back to the top of the loop

Download: file

Copy Code
while True:
# Refresh the dislpay (doing this manually ensures the wheels move
# together, not at different times)
display.refresh(minimum_frames_per_second=0, target_frames_per_second=60)

all_stopped = all(si.state == STOPPED for si in wheels)
if all_stopped:
# Once everything comes to a stop, wait until the lever is pulled and
# start everything over again. Maybe you want to check if the
# combination is a "winner" and add a light show or something.
while button.value:
pass
for idx, si in enumerate(wheels):
si.kick(idx)

# Otherwise, let the wheels keep spinning...
for idx, si in enumerate(wheels):
si.step()

FeatherWing Setup

Install Feather Headers

A strip of 12-pin and 16-pin male headers are installed on to the Feather M4 Express. The headers are fitted onto the bottom of the PCB and soldered in place.

install_14

RGB Matrix FeatherWing Headers

The RGB Matrix FeatherWing includes female headers, a 2x10 socket header, a power screw-block terminal and a 2.1mm barrel jack.  

wings_15

Install 2x10 Socket Header

Place the 2x10 socket header onto the bottom of the RGB Matrix FeatherWing. Solder the pins in place to secure the socket header to the PCB.

socket_16

Install Power Connectors

Place the screw block terminal and 2.1 barrel jack on top of the RGB FeatherWing. Solder the pins in place to secure the two power connectors.

connectors_17

Install Female Headers on RGB Matrix FeatherWing

Install the 12-pin and 16-pin onto the top of the RGB Matrix FeatherWIng. Use the row of pins that are close to the edge of the PCB.

matrix_18

Solder Female Headers

Use the Feather M4 Express to keep the headers on the RGB Matrix FeatherWing steady while soldering pins.

solder_19

RGB Matrix FeatherWing Polarity Check

Match the outline of the header on the silkscreen with the outline on the RGB matrix. The notch in the center should be facing the same way. Reference the image for the correct polarity.

Polarity_20

Install RGB Matrix FeatherWing

The RGB Matrix FeatherWing is fitted on top of the IDC shrouded header on the display. Use the IDC shrouded header that features an arrow pointing to the right.

RGB_21v

Install Feather M4 Express

The Feather M4 Express is placed on top of the RGB Matrix FeatherWing with the USB port facing the 2.1mm barrel jack.

M4_22v

Wiring

Power Cable

The RGB Matrix includes a plug-in power cable. It features two female connectors that are linked together with two male terminals. The male terminals are connected to the screw block terminals on the RGB Matrix FeatherWing. The female connect is connected to the power header on the back of the RGB Matrix display.

cable_23

Trim Power Cable

The cable is cut and shortened to make the wiring more organized. The wires measure to 90mm in length.

trim_24

Slice Power Cable

Use wire strippers to remove a bit of insulation from each wire. Tin the exposed wire using a bit of solder. Add pieces of heat shrink tubing before slicing the wires together.

splice_25

Shortened Power Cable

The power cable is shortened and has heat shrink tubing to insulate the exposed wires. The whole cable measures 18cm in length.

shorten_26

Wire PAM8302 Audio Amp

Use a piece of silicone ribbon cable to wire the audio amplifier to the RGB Matrix FeatherWing. The cable measures 70mm in length. A 4-wire silicone ribbon cable is soldered to the A+, A-, GND and VIN pins. The speaker is soldered to the positive and negative pins on the PAM8302.

wire_27

Wire Speaker

The speaker can be wired directly to the PAM8302 amp. In this project, we used a 2-pin JST extension cable so that it can be easily disconnected. The cable measures 10.6cm in length.

speaker_28

Wire Button Switch

The tactile button is wired to a lengthy piece of silicone ribbon cable. The cable measures 15.2cm in length.

switch_29

Solder Wires to RGB Matrix FeatherWing

Make the following connections to the RGB Matrix FeatherWing.

  • A+ from Amp to A0 on FeatherWing
  • A- from Amp to GND on FeatherWing
  • VIN from Amp to 3V on FeatherWing
  • GND from Amp to GND on FeatherWing
  • Button to A1 on FeatherWing
  • Button to GND on FeatherWing

button_30

Wired RGB Matrix FeatherWing

Double check the wiring is correct.

correct_31

Assembly

Connect Power Cable

Insert the negative and positive leads from the power cable to the screw block terminals. Double check the polarities are correct. Use a small flat head screw driver to tighten the terminals to secure them in place.

power_32

Circuit Wiring Review

Double check the wiring to ensure components are properly connected.

review_33

Secure Amp to Mount

The PAM8302 amp snap fits into the built-in holder on the mount. Orient the mounting holes with the standoffs. Insert PCB at an angle and firmly press down to install.

amp_34

Secure Speaker to Mount

The magnet from the speaker is press fitted into the holder on the mount.

secure_35

Install Frame to Matrix

Insert the RGB LED Matrix into the frame. Reference the labels on the silkscreen to determine the "upright" orientation.

frame_36

Frame Slot

Check the side of the frame with the slot is facing the right side of the RGB LED Matrix.

slot_37

Install Acrylic to Frame

The sheet of black LED acrylic is inserted and pushed through the slot on the right side of the frame. Note: The photo is showing the matrix upside down, hence on the left side of the frame.

acrylic_38

Install Feet

Place one of the feet over the two mounting holes on the back of the RGB LED Matrix. Line up the tabs on the foot with the mounting holes.

feet_39

Secure Feet

Use the included M3 thumb screws to secure the foot to the RGB Matrix.

secure_40

Installed Feet

Proceed to secure the second foot to the RGB LED Matrix using the remaining M3 thumb screws. Reference the photo for the correct placement and orientation.

feet_40a

Install FeatherWing and Mount

Insert the RGB Matrix FeatherWing onto the 2x8 IDC shrouded header on the RGB Matrix. Place the mount over the second set of mounting holes to the right of the FeatherWing. Reference the photo for correct orientation and placement.

install_41

Secure Mount to RGB Matrix

Use 2x M3 x 8mm long machine screws to secure the mount to the back of the RGB matrix.

matrix_42v

Connect Speaker to Amp

Plug in the JST connectors from the PAM8302 amp to the speaker.

speaker_43

Plug in Power Cable to RGB Matrix

Insert the connector from the power cable to the power port on the back of the RGB Matrix. Check to ensure the tab is fully seated over the connector.

plug_44

Wire Management

Optionally adjust the cables so they're fitted underneath the speaker and amp mount. The mount can be used to hold the excess wiring in place

wire_45

Secure Handle to Frame

Place the handle mechanism over the right side of the frame. Line up the mounting holes. Insert and fasten 2x M3 x 10mm long machine screws through the two mounting holes on the handle. Fasten tight.

handle_46

Installed Handle

Check the handle is in the correct position and can be properly pulled down and springs back up automatically.

installed_47

Install Button to Handle

Place the button into the built-in holder on the side of the handle.

tohandle_48

Test Button Handle Actuation

Pull the handle down and test out the button actuation. The lever should actuate the button and trigger the slot machine animations.

pull_49

制造商零件编号 2278
64X32 RGB LED MATRIX - 4MM PITCH
Adafruit Industries LLC
制造商零件编号 3036
RGB MATRIX FEATHERWING KIT M0 M4
Adafruit Industries LLC
制造商零件编号 3857
FEATHER M4 EXPRESS ATSAMD51J19
Adafruit Industries LLC
制造商零件编号 2130
EVAL BOARD FOR PAM8302A
Adafruit Industries LLC
制造商零件编号 3968
SPEAKER 4OHM 3W TOP PORT
Adafruit Industries LLC
制造商零件编号 3890
CBL RIBN 10COND FLAT BLACK 3.28'
Adafruit Industries LLC
制造商零件编号 4111
CABLE A PLUG TO MCR B PLUG 3.28'
Adafruit Industries LLC
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum