Close

Simon Says

A project log for Playing with MicroPython on the ESP

Instant gratification on the ESP8266 and ESP-32

hari-wigunaHari Wiguna 05/05/2019 at 01:300 Comments
# ========================
#   SIMON SAYS
#   by Hari Wiguna, 2019
# ========================

from machine import Pin, TouchPad, PWM, ADC
import time
import urandom as random

# -- Preferences --
led_pins = [32, 33, 25, 26] # EZ-SBC: [32, 33, 25, 26], Lolin: [26, 25, 33, 32]
touch_pad_pins = [15, 14, 13, 12] # EZ-SBC: [15, 14, 13, 12], Lolin: [27, 14, 12, 13]
speaker_pin = Pin(27)
volume_level = 20
frequencies = [209, 252, 310, 415]

# -- UI --
leds = []
touch_pads = []
untouched_values = []
on = 0
off = 1

# -- Game Globals --
simon = []


def set_led(index, on_or_off):
    leds[index].value(on_or_off)


def set_all_leds(on_or_off):
    for i in range(4):
        set_led(i, on_or_off)


def setup_leds():
    for pinNumber in led_pins:
        leds.append(Pin(pinNumber, Pin.OUT))
    print("led pins are {}".format(leds))


def setup_touch_pads():
    for touch_pad_pin_number in touch_pad_pins:
        touch_pads.append(TouchPad(Pin(touch_pad_pin_number)))
    print("touch pads are {}".format(touch_pads))


def read_touch_pads():
    touch_values = []
    for index in range(4):
        touch_values.append(touch_pads[index].read())
    return touch_values


def calibrate_touch_pads():
    n_times = 3
    total = [0, 0, 0, 0]
    average = [0, 0, 0, 0]
    for i in range(n_times):
        touch_values = read_touch_pads()
        for index in range(4):
            total[index] += touch_values[index]
    for index in range(4):
        average[index] = total[index] / n_times
    global untouched_values
    untouched_values = average
    print("untouched_values are {}".format(untouched_values))


def indicate_game_over():
    set_all_leds(on)
    play_game_over_tone()


def play_tone(index):
    PWM(speaker_pin, freq=frequencies[index], duty=volume_level)


def stop_tone():
    p = PWM(speaker_pin)
    p.deinit()


def play_game_over_tone():
    p = PWM(speaker_pin, freq=100, duty=volume_level)
    time.sleep(1)
    p.deinit()


def simon_says():
    for num in simon:
        time.sleep(.5)     # short gap between simon's sequence so same number won't run together.
        print("Simon says {}".format(num))
        set_led(num, on)   # turn on the one simon picked
        play_tone(num)     # play appropriate tone for that number
        time.sleep(.5)     # let human hear the tone and see his chosen led
        stop_tone()        # enough of this cacophony
        set_led(num, off)  # get ready for next simon sequence


def wait_for_button_press():
    while True:  # Todo make this time out instead of waiting forever
        touch_values = read_touch_pads()
        for index in range(4):
            is_touched = touch_values[index] < untouched_values[index] * 3 / 4
            if is_touched:
                print("Human pressed {}".format(index))
                return index  # Todo how to get out of nested loops?
    return -1  # -1 means human failed to press a button in time.


def human_says():
    for num in simon:  # Loop through the current sequence length
        print("Simon expects {}".format(num))
        button_pressed = wait_for_button_press()
        set_led(button_pressed, on)  # User feedback, turn on the led human pressed (not necessarily correct)
        print("Simon said {}, Human said {}".format(num, button_pressed))
        if button_pressed == num:
            print("Good job human!")
            play_tone(button_pressed)
            time.sleep(0.5)
            stop_tone()
            set_led(button_pressed, off)  # Turn it back off to prepare for next simon sequence
        else:
            print("BAD HUMAN! GAME OVER!")
            return True  # True = Game Over.  return to get out of this function!

    return False  # False = NOT Game over. ( human successfully repeated what Simon said)


def init_random_seed():
    a = ADC(Pin(34))
    s = 0
    while s == 0:
        s = a.read()
        time.sleep(0.1)
    random.seed(s)


def play():
    global simon
    simon = []
    game_over = False
    while not game_over:
        new_number = random.randint(0, 3)
        print("\nSimon picked a new number: {}".format(new_number))
        simon.append(new_number)
        print("Simon sequence is now: {}".format(simon))

        simon_says()
        game_over = human_says()

    indicate_game_over()


def wait_for_game_start():
    set_all_leds(on)
    wait_for_button_press()
    set_all_leds(off)


def loop():
    while True:
        wait_for_game_start()
        play()


def main():
    print("main starts...")
    init_random_seed()
    setup_leds()
    setup_touch_pads()
    calibrate_touch_pads()
    loop()


print("Main imported!!")
main()

Discussions