Advertisement
GOULAGMAN

Lightsaber edited code for RP2040 propmaker by Adafruit (CIRCUITPYTHON)

Mar 1st, 2025 (edited)
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.25 KB | None | 0 0
  1. # SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
  2. #
  3. # SPDX-License-Identifier: MIT
  4. #
  5. #Modified by GOULAGMAN :)
  6.  
  7. import time
  8. import os
  9. import random
  10. import board
  11. import pwmio
  12. import audiocore
  13. import audiobusio
  14. from adafruit_debouncer import Button
  15. from digitalio import DigitalInOut, Direction, Pull
  16. import neopixel
  17. import adafruit_lis3dh
  18. import simpleio
  19.  
  20. # CUSTOMIZABLE SETTINGS (SMALLER NUMBERS = MORE SENSITIVE TO MOTION)
  21. HIT_THRESHOLD = 80
  22. SWING_THRESHOLD = 130
  23.  
  24. RED = (255, 0, 0)
  25. YELLOW = (125, 200, 0)
  26. GREEN = (0, 255, 0)
  27. CYAN = (0, 125, 255)
  28. BLUE = (0, 0, 255)
  29. PURPLE = (100, 0, 255)
  30. WHITE = (255, 255, 255)
  31. COLORS = [RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE]
  32.  
  33. # OVERALL CONDITION OF THE SABER
  34. class SaberState:
  35.     def __init__(self):
  36.         self.saber_color = 0
  37.         self.clash_color = 6
  38.         self.mode = "OFF"
  39.         self.animation_step = 0
  40.         self.animation_start_time = 0
  41.         self.last_sound_time = 0
  42.         self.sound_cooldown = 0.3  # secondes entre les sons de swing
  43.         self.is_on = False
  44.         self.current_sound = None
  45.         self.last_action_time = time.monotonic()
  46.         self.button_pressed_time = 0  # Heure à laquelle le bouton a été pressé
  47.         self.button_was_pressed = False  # Pour suivre si le bouton était déjà pressé
  48.  
  49.  
  50. saber = SaberState()
  51.  
  52. # HARDWARE INITIALIZATION
  53. # External power supply
  54. external_power = DigitalInOut(board.EXTERNAL_POWER)
  55. external_power.direction = Direction.OUTPUT
  56. external_power.value = True
  57.  
  58. # External button
  59. pin = DigitalInOut(board.EXTERNAL_BUTTON)
  60. pin.direction = Direction.INPUT
  61. pin.pull = Pull.UP
  62. switch = Button(pin, long_duration_ms=1000)
  63.  
  64. # NeoPixels
  65. num_pixels = 126
  66. pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, num_pixels, auto_write=False)
  67. pixels.brightness = 0.7
  68.  
  69. # Accelerometer
  70. i2c = board.I2C()
  71. int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT)
  72. lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
  73. lis3dh.range = adafruit_lis3dh.RANGE_4_G
  74. lis3dh.set_tap(1, HIT_THRESHOLD)
  75.  
  76. # LED RGB
  77. red_led = pwmio.PWMOut(board.D10)
  78. green_led = pwmio.PWMOut(board.D11)
  79. blue_led = pwmio.PWMOut(board.D12)
  80.  
  81. # AUDIO INITIALIZATION
  82. audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
  83.  
  84. # Loading sound files
  85. wavs = [
  86.     "/sounds/" + filename
  87.     for filename in os.listdir("/sounds")
  88.     if filename.lower().endswith(".wav") and not filename.startswith(".")
  89. ]
  90. wavs.sort()
  91.  
  92. # UTILITY FUNCTIONS
  93. def set_rgb_led(color):
  94.     """Configures the RGB LED with the specified color."""
  95.     # red led is more powerfull
  96.     red_led.duty_cycle = int(simpleio.map_range(color[0], 0, 255, 65535, 20000))
  97.     green_led.duty_cycle = int(simpleio.map_range(color[1], 0, 255, 65535, 0))
  98.     blue_led.duty_cycle = int(simpleio.map_range(color[2], 0, 255, 65535, 0))
  99.  
  100.  
  101. def play_wav(num, loop=False):
  102.     """Plays a WAV file."""
  103.     try:
  104.         if audio.playing:
  105.             audio.stop()
  106.         wave_file = open(wavs[num], "rb")
  107.         wave = audiocore.WaveFile(wave_file)
  108.         audio.play(wave, loop=loop)
  109.         saber.current_sound = num
  110.         return True
  111.     except:
  112.         return False
  113.  
  114.  
  115. def animate_sparkling():
  116.     """Random flicker animation for crackling effect."""
  117.     for i in range(num_pixels):
  118.         if random.random() < 0.9:  # Flicker probability
  119.             # Adjusts brightness randomly
  120.             brightness = random.uniform(0.7, 1.0)
  121.             pixels[i] = (
  122.                 int(COLORS[saber.saber_color][0] * brightness),
  123.                 int(COLORS[saber.saber_color][1] * brightness),
  124.                 int(COLORS[saber.saber_color][2] * brightness),
  125.             )
  126.         else:
  127.             pixels[i] = COLORS[saber.saber_color]
  128.     pixels.show()
  129.  
  130.  
  131. def check_button_press():
  132.     switch.update()
  133.     if switch.long_press:
  134.         return "LONG"
  135.     if switch.short_count > 0:
  136.         return "SHORT"
  137.     return None
  138.  
  139.  
  140. # NON-BLOCKING ANIMATION FEATURES
  141. def animate_power_on():
  142.    
  143.     """Lightsaber igniting animation."""
  144.     total_duration = 1
  145.     if saber.animation_step == 0:
  146.         saber.animation_start_time = time.monotonic()
  147.         play_wav(0, loop=False)
  148.         saber.animation_step = 1
  149.         return False
  150.     elapsed = time.monotonic() - saber.animation_start_time
  151.     if elapsed < total_duration:
  152.         leds_to_light = int((elapsed / total_duration) * num_pixels)
  153.         for i in range(min(leds_to_light + 1, num_pixels)):
  154.             pixels[i] = COLORS[saber.saber_color]
  155.         pixels.show()
  156.         return False
  157.     if saber.animation_step == 1:
  158.         pixels.fill(COLORS[saber.saber_color])
  159.         pixels.show()
  160.         play_wav(1, loop=True)
  161.         saber.animation_step = 0
  162.         saber.is_on = True
  163.         return True
  164.  
  165.  
  166. def animate_power_off():
  167.     """Lightsaber extinction animation."""
  168.     total_duration = 1.0
  169.     if saber.animation_step == 0:
  170.         saber.animation_start_time = time.monotonic()
  171.         play_wav(2, loop=False)
  172.         saber.animation_step = 1
  173.         return False
  174.     elapsed = time.monotonic() - saber.animation_start_time
  175.     if elapsed < total_duration:
  176.         leds_to_dark = int((elapsed / total_duration) * num_pixels)
  177.         for i in range(num_pixels - 1, max(num_pixels - leds_to_dark - 1, -1), -1):
  178.             pixels[i] = (0, 0, 0)
  179.         pixels.show()
  180.         return False
  181.     if saber.animation_step == 1:
  182.         pixels.fill((0, 0, 0))
  183.         pixels.show()
  184.         saber.animation_step = 0
  185.         saber.is_on = False
  186.         return True
  187.  
  188.  
  189. def animate_clash(duration=0.3):
  190.     """Collision animation."""
  191.     if saber.animation_step == 0:
  192.         saber.animation_start_time = time.monotonic()
  193.         play_wav(random.randint(3, 10), loop=False)
  194.         pixels.fill(WHITE)
  195.         pixels.show()
  196.         saber.animation_step = 1
  197.         return False
  198.     elapsed = time.monotonic() - saber.animation_start_time
  199.     if elapsed < duration:
  200.         return False
  201.     pixels.fill(COLORS[saber.saber_color])
  202.     pixels.show()
  203.     if not audio.playing:
  204.         play_wav(1, loop=True)
  205.     saber.animation_step = 0
  206.     return True
  207.  
  208.  
  209. # GESTIONNAIRES D'ÉTAT
  210. def handle_off_state():
  211.     """Handles the off state."""
  212.     if check_button_press() == "SHORT":
  213.         saber.mode = "POWERING_ON"
  214.         external_power.value = True
  215.         return True
  216.     return False
  217.  
  218.  
  219. def handle_on_state():
  220.     """Manages the on state."""
  221.     x, y, z = lis3dh.acceleration
  222.     accel_total = x * x + z * z + y * y
  223.     if lis3dh.tapped:
  224.         saber.mode = "CLASH"
  225.         return True
  226.     now = time.monotonic()
  227.     if (
  228.         accel_total >= SWING_THRESHOLD
  229.         and (now - saber.last_sound_time) > saber.sound_cooldown
  230.     ):
  231.         play_wav(random.randint(11, 18), loop=False)
  232.         saber.last_sound_time = now
  233.     button_action = check_button_press()
  234.     if button_action == "LONG":
  235.         audio.stop()
  236.         play_wav(19, loop=True)
  237.         saber.mode = "COLOR_SELECT"
  238.         return True
  239.     elif button_action == "SHORT":
  240.         saber.mode = "POWERING_OFF"
  241.         return True
  242.     if not audio.playing and saber.is_on:
  243.         play_wav(1, loop=True)
  244.     return False
  245.  
  246.  
  247. def handle_color_select():
  248.     """Manages color selection state."""
  249.     button_action = check_button_press()
  250.     if button_action == "LONG":
  251.         play_wav(1, loop=True)
  252.         saber.mode = "ON"
  253.         return True
  254.     elif button_action == "SHORT":
  255.         saber.saber_color = (saber.saber_color + 1) % 6
  256.         pixels.fill(COLORS[saber.saber_color])
  257.         pixels.show()
  258.         set_rgb_led(COLORS[saber.saber_color])
  259.         return True
  260.     return False
  261.  
  262.  
  263. # MAIN LOOP
  264. set_rgb_led(COLORS[saber.saber_color])
  265.  
  266. while True:
  267.     if saber.mode == "OFF":
  268.         handle_off_state()
  269.     elif saber.mode == "POWERING_ON":
  270.         if animate_power_on():
  271.             saber.mode = "ON"
  272.     elif saber.mode == "ON":
  273.         handle_on_state()
  274.         animate_sparkling()
  275.     elif saber.mode == "POWERING_OFF":
  276.         if animate_power_off():
  277.             external_power.value = False
  278.             saber.mode = "OFF"
  279.     elif saber.mode == "CLASH":
  280.         if animate_clash():
  281.             saber.mode = "ON"
  282.     elif saber.mode == "COLOR_SELECT":
  283.         handle_color_select()
  284.     time.sleep(0.01)
  285.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement