Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
- #
- # SPDX-License-Identifier: MIT
- #
- #Modified by GOULAGMAN :)
- import time
- import os
- import random
- import board
- import pwmio
- import audiocore
- import audiobusio
- from adafruit_debouncer import Button
- from digitalio import DigitalInOut, Direction, Pull
- import neopixel
- import adafruit_lis3dh
- import simpleio
- # CUSTOMIZABLE SETTINGS (SMALLER NUMBERS = MORE SENSITIVE TO MOTION)
- HIT_THRESHOLD = 80
- SWING_THRESHOLD = 130
- RED = (255, 0, 0)
- YELLOW = (125, 200, 0)
- GREEN = (0, 255, 0)
- CYAN = (0, 125, 255)
- BLUE = (0, 0, 255)
- PURPLE = (100, 0, 255)
- WHITE = (255, 255, 255)
- COLORS = [RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE]
- # OVERALL CONDITION OF THE SABER
- class SaberState:
- def __init__(self):
- self.saber_color = 0
- self.clash_color = 6
- self.mode = "OFF"
- self.animation_step = 0
- self.animation_start_time = 0
- self.last_sound_time = 0
- self.sound_cooldown = 0.3 # secondes entre les sons de swing
- self.is_on = False
- self.current_sound = None
- self.last_action_time = time.monotonic()
- self.button_pressed_time = 0 # Heure à laquelle le bouton a été pressé
- self.button_was_pressed = False # Pour suivre si le bouton était déjà pressé
- saber = SaberState()
- # HARDWARE INITIALIZATION
- # External power supply
- external_power = DigitalInOut(board.EXTERNAL_POWER)
- external_power.direction = Direction.OUTPUT
- external_power.value = True
- # External button
- pin = DigitalInOut(board.EXTERNAL_BUTTON)
- pin.direction = Direction.INPUT
- pin.pull = Pull.UP
- switch = Button(pin, long_duration_ms=1000)
- # NeoPixels
- num_pixels = 126
- pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, num_pixels, auto_write=False)
- pixels.brightness = 0.7
- # Accelerometer
- i2c = board.I2C()
- int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT)
- lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
- lis3dh.range = adafruit_lis3dh.RANGE_4_G
- lis3dh.set_tap(1, HIT_THRESHOLD)
- # LED RGB
- red_led = pwmio.PWMOut(board.D10)
- green_led = pwmio.PWMOut(board.D11)
- blue_led = pwmio.PWMOut(board.D12)
- # AUDIO INITIALIZATION
- audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
- # Loading sound files
- wavs = [
- "/sounds/" + filename
- for filename in os.listdir("/sounds")
- if filename.lower().endswith(".wav") and not filename.startswith(".")
- ]
- wavs.sort()
- # UTILITY FUNCTIONS
- def set_rgb_led(color):
- """Configures the RGB LED with the specified color."""
- # red led is more powerfull
- red_led.duty_cycle = int(simpleio.map_range(color[0], 0, 255, 65535, 20000))
- green_led.duty_cycle = int(simpleio.map_range(color[1], 0, 255, 65535, 0))
- blue_led.duty_cycle = int(simpleio.map_range(color[2], 0, 255, 65535, 0))
- def play_wav(num, loop=False):
- """Plays a WAV file."""
- try:
- if audio.playing:
- audio.stop()
- wave_file = open(wavs[num], "rb")
- wave = audiocore.WaveFile(wave_file)
- audio.play(wave, loop=loop)
- saber.current_sound = num
- return True
- except:
- return False
- def animate_sparkling():
- """Random flicker animation for crackling effect."""
- for i in range(num_pixels):
- if random.random() < 0.9: # Flicker probability
- # Adjusts brightness randomly
- brightness = random.uniform(0.7, 1.0)
- pixels[i] = (
- int(COLORS[saber.saber_color][0] * brightness),
- int(COLORS[saber.saber_color][1] * brightness),
- int(COLORS[saber.saber_color][2] * brightness),
- )
- else:
- pixels[i] = COLORS[saber.saber_color]
- pixels.show()
- def check_button_press():
- switch.update()
- if switch.long_press:
- return "LONG"
- if switch.short_count > 0:
- return "SHORT"
- return None
- # NON-BLOCKING ANIMATION FEATURES
- def animate_power_on():
- """Lightsaber igniting animation."""
- total_duration = 1
- if saber.animation_step == 0:
- saber.animation_start_time = time.monotonic()
- play_wav(0, loop=False)
- saber.animation_step = 1
- return False
- elapsed = time.monotonic() - saber.animation_start_time
- if elapsed < total_duration:
- leds_to_light = int((elapsed / total_duration) * num_pixels)
- for i in range(min(leds_to_light + 1, num_pixels)):
- pixels[i] = COLORS[saber.saber_color]
- pixels.show()
- return False
- if saber.animation_step == 1:
- pixels.fill(COLORS[saber.saber_color])
- pixels.show()
- play_wav(1, loop=True)
- saber.animation_step = 0
- saber.is_on = True
- return True
- def animate_power_off():
- """Lightsaber extinction animation."""
- total_duration = 1.0
- if saber.animation_step == 0:
- saber.animation_start_time = time.monotonic()
- play_wav(2, loop=False)
- saber.animation_step = 1
- return False
- elapsed = time.monotonic() - saber.animation_start_time
- if elapsed < total_duration:
- leds_to_dark = int((elapsed / total_duration) * num_pixels)
- for i in range(num_pixels - 1, max(num_pixels - leds_to_dark - 1, -1), -1):
- pixels[i] = (0, 0, 0)
- pixels.show()
- return False
- if saber.animation_step == 1:
- pixels.fill((0, 0, 0))
- pixels.show()
- saber.animation_step = 0
- saber.is_on = False
- return True
- def animate_clash(duration=0.3):
- """Collision animation."""
- if saber.animation_step == 0:
- saber.animation_start_time = time.monotonic()
- play_wav(random.randint(3, 10), loop=False)
- pixels.fill(WHITE)
- pixels.show()
- saber.animation_step = 1
- return False
- elapsed = time.monotonic() - saber.animation_start_time
- if elapsed < duration:
- return False
- pixels.fill(COLORS[saber.saber_color])
- pixels.show()
- if not audio.playing:
- play_wav(1, loop=True)
- saber.animation_step = 0
- return True
- # GESTIONNAIRES D'ÉTAT
- def handle_off_state():
- """Handles the off state."""
- if check_button_press() == "SHORT":
- saber.mode = "POWERING_ON"
- external_power.value = True
- return True
- return False
- def handle_on_state():
- """Manages the on state."""
- x, y, z = lis3dh.acceleration
- accel_total = x * x + z * z + y * y
- if lis3dh.tapped:
- saber.mode = "CLASH"
- return True
- now = time.monotonic()
- if (
- accel_total >= SWING_THRESHOLD
- and (now - saber.last_sound_time) > saber.sound_cooldown
- ):
- play_wav(random.randint(11, 18), loop=False)
- saber.last_sound_time = now
- button_action = check_button_press()
- if button_action == "LONG":
- audio.stop()
- play_wav(19, loop=True)
- saber.mode = "COLOR_SELECT"
- return True
- elif button_action == "SHORT":
- saber.mode = "POWERING_OFF"
- return True
- if not audio.playing and saber.is_on:
- play_wav(1, loop=True)
- return False
- def handle_color_select():
- """Manages color selection state."""
- button_action = check_button_press()
- if button_action == "LONG":
- play_wav(1, loop=True)
- saber.mode = "ON"
- return True
- elif button_action == "SHORT":
- saber.saber_color = (saber.saber_color + 1) % 6
- pixels.fill(COLORS[saber.saber_color])
- pixels.show()
- set_rgb_led(COLORS[saber.saber_color])
- return True
- return False
- # MAIN LOOP
- set_rgb_led(COLORS[saber.saber_color])
- while True:
- if saber.mode == "OFF":
- handle_off_state()
- elif saber.mode == "POWERING_ON":
- if animate_power_on():
- saber.mode = "ON"
- elif saber.mode == "ON":
- handle_on_state()
- animate_sparkling()
- elif saber.mode == "POWERING_OFF":
- if animate_power_off():
- external_power.value = False
- saber.mode = "OFF"
- elif saber.mode == "CLASH":
- if animate_clash():
- saber.mode = "ON"
- elif saber.mode == "COLOR_SELECT":
- handle_color_select()
- time.sleep(0.01)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement