from machine import Pin, ADC, PWM
import time
import urandom
# StepperMotor class
class StepperMotor:
def __init__(self, pins):
self.pins = [Pin(pin, Pin.OUT) for pin in pins]
self.seq = [
[1,0,0,1],
[1,0,0,0],
[1,1,0,0],
[0,1,0,0],
[0,1,1,0],
[0,0,1,0],
[0,0,1,1],
[0,0,0,1]
]
self.step_count = len(self.seq)
self.current_step = 0
def step(self, steps, delay=0.01):
for _ in range(abs(steps)):
if steps > 0:
self.current_step = (self.current_step + 1) % self.step_count
else:
self.current_step = (self.current_step - 1) % self.step_count
for pin, val in zip(self.pins, self.seq[self.current_step]):
pin.value(val)
time.sleep(delay)
def release(self):
for pin in self.pins:
pin.value(0)
# Lock picking game class
class LockPickingGame:
def __init__(self, stepper_pins, pot_pin, led_pins, button_pin, speaker_pin):
self.stepper = StepperMotor(stepper_pins)
self.pot = ADC(pot_pin)
self.button = Pin(button_pin, Pin.IN, Pin.PULL_DOWN)
self.led_success = Pin(led_pins['success'], Pin.OUT)
self.led_failure = Pin(led_pins['failure'], Pin.OUT)
self.led_hint = Pin(led_pins['hint'], Pin.OUT)
self.speaker = PWM(Pin(speaker_pin))
self.speaker.duty_u16(0) # Start silent
self.close_margin = 5000
self.max_attempts = 5
self.attempts = 0
self.last_pot_value = self.read_pot()
self.last_motor_pos = 0 # Track motor steps
self.max_motor_steps = int(4000 * 0.4) # limit movement to 40%
self.set_random_unlock_range()
def set_random_unlock_range(self):
start = urandom.getrandbits(16) % 50000
width = 5000
self.unlock_range = (start, start + width)
print("New unlock range:", self.unlock_range)
# Play tone helper
def play_tone(self, freq, duration_ms):
self.speaker.freq(freq)
self.speaker.duty_u16(20000)
time.sleep(duration_ms / 1000)
self.speaker.duty_u16(0)
time.sleep(0.05)
def play_start_sound(self):
self.play_tone(523, 200)
self.play_tone(659, 200)
self.play_tone(783, 300)
def play_fail_sound(self):
self.play_tone(220, 300)
self.play_tone(196, 300)
def play_lose_sound(self):
for _ in range(3):
self.play_tone(150, 150)
self.play_tone(100, 150)
def play_win_sound(self):
self.play_tone(784, 200)
self.play_tone(880, 200)
self.play_tone(988, 400)
def read_pot(self):
return self.pot.read_u16()
def move_motor_with_pot(self):
pot_value = self.read_pot()
if abs(pot_value - self.last_pot_value) > 500: # threshold to avoid jitter
target_pos = (pot_value * self.max_motor_steps) // 65535
delta = target_pos - self.last_motor_pos
if abs(delta) > 0:
step_dir = 1 if delta > 0 else -1
self.stepper.step(step_dir, delay=0.005)
self.last_motor_pos += step_dir
self.last_pot_value = pot_value
def show_feedback(self, pot_value):
low, high = self.unlock_range
cm = self.close_margin
if low <= pot_value <= high:
self.led_success.value(1)
self.led_failure.value(0)
self.led_hint.value(0)
elif (low - cm <= pot_value < low) or (high < pot_value <= high + cm):
self.led_success.value(0)
self.led_failure.value(0)
self.led_hint.value(1)
else:
self.led_success.value(0)
self.led_failure.value(1)
self.led_hint.value(0)
def flash_red(self, times=6, interval=0.3):
for _ in range(times):
self.led_failure.value(1)
time.sleep(interval)
self.led_failure.value(0)
time.sleep(interval)
def play(self):
self.play_start_sound()
print("Lock Picking Game started! Turn the potentiometer and press the button to try unlocking.")
while self.attempts < self.max_attempts:
self.move_motor_with_pot()
if self.button.value() == 1:
time.sleep(0.05) # debounce
pot_value = self.read_pot()
print(f"Potentiometer reading: {pot_value}")
self.show_feedback(pot_value)
if self.unlock_range[0] <= pot_value <= self.unlock_range[1]:
print("Lock picked! Turning stepper motor...")
self.play_win_sound()
self.stepper.step(512) # full turn
self.stepper.release()
break
else:
print("Wrong position, try again!")
self.play_fail_sound()
self.stepper.step(-50) # small back movement
self.attempts += 1
time.sleep(1)
self.led_failure.value(0)
while self.button.value() == 1:
time.sleep(0.01)
else:
print("Failed to pick the lock!")
self.play_lose_sound()
self.flash_red()
self.led_success.value(0)
self.led_failure.value(0)
self.led_hint.value(0)
self.speaker.duty_u16(0)
# Pins setup
stepper_pins = [0, 1, 2, 3]
pot_pin = 26
led_pins = {
'success': 15,
'failure': 14,
'hint': 13
}
button_pin = 12
speaker_pin = 11
# Create and run game
game = LockPickingGame(stepper_pins, pot_pin, led_pins, button_pin, speaker_pin)
game.play()