I've been working on building the updated HAL9000 using the Propmaker Feather, and tweaked the code a bit to add a couple of features. Mainly:
- Animate the LED using a slow pulse animation when HAL9000 is "idle" (in other words, not playing a sound)
- Shuffle the list of wave files, and keep track as we play our way through them; then, once we've reached the end, re-shuffle the list and start over. This prevents the same sound repeating over and over, which the current approach is prone to. It also ensures you'll hear every sound if you keep pressing the button (until the list eventually has to repeat).
I ordered a decal to use instead of 3D printing the "HAL9000" label, so I'm waiting on that before I post the photos of my build, but in the meantime, here's my updated code if anyone is interested (you'll need to add the adafruit_ticks.mpy
library to your lib
folder in order to use this).
# SPDX-FileCopyrightText: 2023 Phil Burgess for Adafruit Industries (with modifications by Aaron Pendley) # # SPDX-License-Identifier: MIT """ HAL 9000 demo project for Adafruit Prop Maker Feather RP2040 and "Massive Arcade Button with LED - 100mm red." This simply monitors for button presses and then randomly plays one WAV file from the CIRCUITPY filesystem. No soldering required; using quick-connects, LED and button can be wired to screw terminals on the Prop Maker Feather board. NOTE: WAV FILES MUST BE 16-BIT. This will be fixed (allowing 8-bit WAVs if desired) in CircuitPython 9.0. """ # pylint: disable=import-error import os import random import time import audiocore import audiobusio import board import digitalio import pwmio import math from adafruit_ticks import ticks_ms, ticks_diff # HARDWARE SETUP ----------------------------------------------------------- led = pwmio.PWMOut(board.EXTERNAL_NEOPIXELS) led.duty_cycle = 0 led_pulse_period = 8000 led_timer = 0 # Button is wired to GND and "Btn" on screw terminal header: button = digitalio.DigitalInOut(board.EXTERNAL_BUTTON) button.direction = digitalio.Direction.INPUT button.pull = digitalio.Pull.UP # Enable power to audio amp, etc. external_power = digitalio.DigitalInOut(board.EXTERNAL_POWER) external_power.direction = digitalio.Direction.OUTPUT external_power.value = True # I2S audio out audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA) # Find all Wave files in CIRCUITPY storage: wavefiles = [ file for file in os.listdir("/sounds/") if (file.endswith(".wav") and not file.startswith("._")) ] print("\nAudio files found:", wavefiles) current_sound_index = 0 # FUNCTIONS ---------------------------------------------------------------- def shuffle_sounds(): global wavefiles global current_sound_index wavefiles = sorted(wavefiles, key=lambda x: random.random()) current_sound_index = 0 print("Shuffled sounds:", wavefiles) def play_next_sound(): global wavefiles global current_sound_index filename = wavefiles[current_sound_index] print("Playing sound", current_sound_index, "-", filename) # Go to the next random sound and reshuffle list if we're at the end current_sound_index += 1 if current_sound_index >= len(wavefiles): shuffle_sounds() """Plays a WAV file in its entirety (function blocks until done).""" with open(f"/sounds/{filename}", "rb") as file: audio.play(audiocore.WaveFile(file)) # Randomly flicker the LED a bit while audio plays while audio.playing: led.duty_cycle = random.randint(5000, 30000) time.sleep(0.1) # MAIN LOOP ---------------------------------------------------------------- shuffle_sounds() last_ticks = ticks_ms() while True: # Update time tracking now = ticks_ms() dt = ticks_diff(now, last_ticks) last_ticks = now if button.value is False: play_next_sound() # Set the LED timer to fade out from 25% brightness led_timer = led_pulse_period - (led_pulse_period / 4) # Update last_ticks since play_next_sound() blocks our timing updates until it's done last_ticks = ticks_ms() else: # Update pulse animation led_timer = (led_timer + dt) % led_pulse_period sinValue = math.sin((led_timer * math.pi * 2 / led_pulse_period) + 2) + 1 gammaCorrected = pow(1 - (sinValue / 2), 1.8) led.duty_cycle = int(gammaCorrected * 65535)