Once you've finished setting up your MatrixPortal M4 with CircuitPython, you can access the code, audio file, bitmap and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2021 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import random
import board
import pwmio
import displayio
import adafruit_imageload
from audiocore import WaveFile
from adafruit_motor import servo
from digitalio import DigitalInOut, Direction, Pull
from adafruit_matrixportal.matrix import Matrix
I2S_VERSION = False # set to True if using I2S audio out
# import the appropriate audio module
if I2S_VERSION:
from audiobusio import I2SOut
else:
from audioio import AudioOut
# setup for down button on matrixportal
switch = DigitalInOut(board.BUTTON_DOWN)
switch.direction = Direction.INPUT
switch.pull = Pull.UP
# setup for break beam sensor
break_beam = DigitalInOut(board.A1)
break_beam.direction = Direction.INPUT
break_beam.pull = Pull.UP
# pwm for servo
servo_pwm = pwmio.PWMOut(board.A4, duty_cycle=2 ** 15, frequency=50)
# servo setup
servo = servo.Servo(servo_pwm)
servo.angle = 90
# import dreidel song audio file
wave_file = open("dreidel_song.wav", "rb")
wave = WaveFile(wave_file)
# setup for audio out
if I2S_VERSION:
audio = I2SOut(board.A2, board.A3, board.TX)
else:
audio = AudioOut(board.A0)
# setup for matrix display
matrix = Matrix(width=32, height=32)
display = matrix.display
group = displayio.Group()
# import dreidel bitmap
dreidel_bit, dreidel_pal = adafruit_imageload.load("/dreidel.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
dreidel_grid = displayio.TileGrid(dreidel_bit, pixel_shader=dreidel_pal,
width=1, height=1,
tile_height=32, tile_width=32,
default_tile=0,
x=0, y=0)
group.append(dreidel_grid)
# show dreidel bitmap
display.root_group = group
timer = 0 # time.monotonic() holder
spin = 0 # index for tilegrid
speed = 0.1 # rate that bitmap updates
clock = 0 # initial time.monotonic() holder to act as time keeper
gimel = 3 # bitmap index for gimel, the winning character
countdown = 5 # countdown for length of game. default is 5 seconds
beam_state = False # state machine for break beam
reset = False # state for reset of game
dreidel = False # state to track if dreidel game is running
clock = time.monotonic() # initial time.monotonic()
while True:
# debouncing for break beam sensor
if not break_beam.value and not beam_state:
beam_state = True
# if the break beam sensor is triggered or the down button is pressed...
if (not break_beam.value and beam_state) or not switch.value:
# update break beam state
beam_state = False
# begin reset for game states
reset = True
print("pressed")
# quick delay
time.sleep(0.1)
# if reset state...
if reset:
# hold time.monotonic() value
clock = time.monotonic()
# reset countdown
countdown = 5
# choose random side of dreidel to begin spinning on
spin = random.randint(0, 3)
# choose random speed spin the dreidel
speed = random.uniform(0.05, 0.1)
# set game state to True
dreidel = True
# turn off reset state
reset = False
# if the game is running...
if dreidel:
# play the dreidel song
audio.play(wave)
# while the dreidel song is playing...
while audio.playing:
# if more time has passed than the random delay setup in reset...
if (timer + speed) < time.monotonic():
# dreidel grid index is set to spin value
dreidel_grid[0] = spin
# spin is increased by 1
spin += 1
# timer is updated to current time
timer = time.monotonic()
# if a second has passed...
if time.monotonic() > (clock + 1):
print(clock)
print(spin)
# the delay is increased to slow down the dreidel
speed += 0.05
# clock is set to current time
clock = time.monotonic()
# countdown value is decreased by 1
countdown -= 1
# if countdown is 0 aka 5 seconds has passed since the start of game...
if countdown == 0:
# if the bitmap is showing gimel...
# you win!
if spin is gimel:
# the servo turns 90 degrees to dump out chocolate coins
servo.angle = 0
# 2 second delay
time.sleep(2)
# servo turns back to default position
for i in range(0, 90, 2):
servo.angle = i
time.sleep(0.1)
# ensures servo is in default position
servo.angle = 90
# stop playing the dreidel song
audio.stop()
# game state is turned off
dreidel = False
# if you didn't win...
else:
# the dreidel song stops
audio.stop()
# game state is turned off
dreidel = False
# if you are at the end of the sprite sheet...
if spin > 3:
# index is reset to 0
spin = 0
I2S Audio Version Changes
If you have built the I2S Audio version of the circuit, you will need to change I2S_VERSION to True before uploading your code.
After downloading the Project Bundle, plug your MatrixPortal M4 into the computer's USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the MatrixPortal M4's CIRCUITPY drive.
- lib folder
- code.py
- dreidel_song.wav
- dreidel.bmp
Your MatrixPortal M4 CIRCUITPY drive should look like this after copying the lib folder and the code.py, dreidel_song.wav and dreidel.bmp files.
The MatrixPortal M4's onboard down button and an IR breakbeam sensor are setup as digital inputs that can be read to start up the dreidel game sequence. If either input is triggered in the loop, then the reset state is set to True. This resets all of the game's values to launch a new sequence.
# if the break beam sensor is triggered or the down button is pressed...
if (not break_beam.value and beam_state) or not switch.value:
# update break beam state
beam_state = False
# begin reset for game states
reset = True
print("pressed")
# quick delay
time.sleep(0.1)
The reset state acts as a way to make sure everything is ready for a new round of dreidel. Most importantly, the values of spin and speed are set to random values. This allows for some variation in how the dreidel spins. spin affects the first index that is shown in the tilegrid and speed affects the speed at which the indexes change on the matrix.
# if reset state...
if reset:
# hold time.monotonic() value
clock = time.monotonic()
# reset countdown
countdown = 5
# choose random side of dreidel to begin spinning on
spin = random.randint(0, 3)
# choose random speed spin the dreidel
speed = random.uniform(0.05, 0.1)
# set game state to True
dreidel = True
# turn off reset state
reset = False
When the dreidel state is True, then the dreidel song will play through the STEMMA speaker and you'll see the dreidel sprite sheet iterate through the four sides with the different characters.
speed is used as the delay, with timer being reset to the current time with each iteration. This lets you avoid using time.sleep(), which would pause the entire loop.
# if the game is running...
if dreidel:
# play the dreidel song
audio.play(wave)
# while the dreidel song is playing...
while audio.playing:
# if more time has passed than the random delay setup in reset...
if (timer + speed) < time.monotonic():
# dreidel grid index is set to spin value
dreidel_grid[0] = spin
# spin is increased by 1
spin += 1
# timer is updated to current time
timer = time.monotonic()
The game is setup to "spin" the dreidel for 5 seconds. With each passing second, the value of speed is increased by 0.05 seconds. This slows the dreidel down gradually to mimic how it would spin in real life. countdown keeps track of how many seconds are left in the game.
# if a second has passed...
if time.monotonic() > (clock + 1):
print(clock)
print(spin)
# the delay is increased to slow down the dreidel
speed += 0.05
# clock is set to current time
clock = time.monotonic()
# countdown value is decreased by 1
countdown -= 1
Once the game is over, it's determined whether or not you've won the pot of chocolate coins. If the matrix is showing gimel, which is the third index of the sprite sheet, then you've won! As a result, the servo will turn to dump out the chocolate coins for you and then turn back to its default position. The dreidel song will also stop playing and dreidel is set to False, stopping the game sequence.
If you didn't win, the song stops playing and the dreidel state is set to False, but there isn't any servo action.
# if countdown is 0 aka 5 seconds has passed since the start of game...
if countdown == 0:
# if the bitmap is showing gimel...
# you win!
if spin is gimel:
# the servo turns 90 degrees to dump out chocolate coins
servo.angle = 0
# 2 second delay
time.sleep(2)
# servo turns back to default position
for i in range(0, 90, 2):
servo.angle = i
time.sleep(0.1)
# ensures servo is in default position
servo.angle = 90
# stop playing the dreidel song
audio.stop()
# game state is turned off
dreidel = False
# if you didn't win...
else:
# the dreidel song stops
audio.stop()
# game state is turned off
dreidel = False
Page last edited January 21, 2025
Text editor powered by tinymce.