from machine import Pin, ADC, PWM, I2C
from utime import sleep_ms
from random import randint, seed
from ssd1306 import SSD1306_I2C
tones = {
'G3': 196,
'C4': 262,
'E4': 330,
'G4': 392,
'DS5': 622,
'CS5': 554,
'C5': 523,
'E5': 659,
'D5': 587,
'G5': 784
}
# Constants - define pin numbers for LEDs, buttons and speaker
led_pins = [Pin(i, Pin.OUT) for i in (10, 11, 12, 13)]
button_pins = [Pin(i, Pin.IN, Pin.PULL_UP) for i in (21, 20, 19, 18)]
speaker = PWM(16)
game_tones = (tones['G3'], tones['C4'], tones['E4'], tones['G5'])
# Global variales - store the game state
game_sequence = [0]*100 # max game lenght 100
game_index = 0
the_best_score = '0'
# The following line primes the random number generator. It assumes pin 26 is floating (disconnected)
seed(ADC(26).read_u16())
# Monochrome 128x64 OLED display with I2C interface
i2c = I2C(1, scl=Pin(15), sda=Pin(14), freq=400000)
display = SSD1306_I2C(128, 64, i2c)
# Generate a tone
def tone(freq):
speaker.duty_u16(32767)
speaker.freq(freq)
def no_tone():
speaker.duty_u16(0)
# Lights the given led and plays the suitable tone
def light_led_and_play_sound(led_index):
led_pins[led_index].on()
tone(game_tones[led_index])
sleep_ms(300)
led_pins[led_index].off()
no_tone()
# Plays the current sequence of NOTEs that the user has to repeat
def play_sequence():
for i in range(game_index):
current_led = game_sequence[i]
light_led_and_play_sound(current_led)
sleep_ms(50)
# Waits until the user pressed one of the buttons, and returns the index of that button
def read_button():
while True:
for i in range(4):
button_pin = button_pins[i]
if not button_pin.value():
return i
sleep_ms(1)
# Play the game over sequence, and report the game score
def game_over():
global game_index
display_score(game_index-1, True)
game_index = 0
sleep_ms(200)
# Play a Wah-Wah-Wah-Wah sound
for i in (tones['DS5'], tones['D5'], tones['CS5']):
tone(i)
sleep_ms(300)
for i in range(200):
tone(tones['C5'] + (i % 20 - 10))
sleep_ms(5)
no_tone()
sleep_ms(500)
test_leds_and_sounds()
# Get the user input and compare it with the expected sequence. If the user fails, play the game over sequence and restart the game.
def check_user_sequence():
for i in range(game_index):
expected_button = game_sequence[i]
actual_button = read_button()
light_led_and_play_sound(actual_button)
if expected_button != actual_button:
game_over()
break
# Plays an hooray sound whenever the user finishes a level
def level_up_sound():
for i in (tones['E4'], tones['G4'], tones['E5'], tones['C5'], tones['D5'], tones['G5']):
tone(i)
sleep_ms(150)
no_tone()
def display_score(game_index, end_of_the_game = False):
global the_best_score
if int(the_best_score) < game_index:
the_best_score = str(game_index)
if game_index:
display.text('YOUR GAME:', 0, 0)
else:
display.text('NEW GAME!', 0, 0)
display.text('The best', 0, 13)
display.text('score is:', 0, 23)
display.text(the_best_score, 75, 23)
display.text('Your score:', 0, 40)
display.text(str(game_index), 90, 40)
if end_of_the_game:
display.text('GAME OVER!', 0, 56)
display.show()
display.fill(0)
def test_leds_and_sounds():
display.fill(0)
display.show()
for i in range(4):
light_led_and_play_sound(i)
sleep_ms(1000)
test_leds_and_sounds()
# The main game loop
while True:
# Add a random color to the end of the sequence
game_sequence[game_index] = randint(0, 3)
display_score(game_index)
game_index += 1
play_sequence()
check_user_sequence()
sleep_ms(300)
if game_index:
level_up_sound()
sleep_ms(300)