I am THIS >< CLOSE to wrapping up a project but have hit a snag that I just can't figure out. I'd really appreciate a fresh pair of eyes. The project combines LED control, audio playback, and touch sensor input. I recently posted about one portion of it over here (solved-ish): viewtopic.php?t=374114&sid=4995256cf37d ... 906fdca8b0
I'm working with the latest Raspberry Pi Lite install on an RPi3. Python v3.11.2 is in a virtual environment.
Project Overview:
- Uses two NeoPixel LED strips (11 LEDs on GPIO18, 65 LEDs on GPIO21). First strip is powered by 5V GPIO pin on the Pi. I'm using a 5V adapter for the second strip. Both strips are grounded to the Pi.
- Implements a capacitive touch sensor on GPIO10.
- Plays MP3 audio files.
- Sends MQTT messages.
I'm using the following libraries:
- RPi.GPIO
- board
- neopixel
- paho.mqtt
- subprocess (for audio playback with mpg123)
I did my script development in stages. This first proof-of-concept script watches for a capacitive sensor touch on GPIO10 and then does some color changes, etc. on the two LED strips connected to GPIO18 and GPIO21. Repeated touches = repeating the script. I run the script from the command line:
No issues with this script, it works fine.
This second also script watches GPIO10. When the sensor is touched, it sends an MQTT ON mesage, plays 1 of 29 MP3 files at random, and then sends MQTT OFF. Repeated touches = repeating the script. I also run this script from the command line:
Also runs just fine, no issues at all.
I'm trying to bring those two scripts together into a single script. Here's what I'm after:
1. the amber LED pulse begins looping
2. the capacitive sensor senses touch
3. the MQTT ON message is sent
4. audio begins playing
5. the LEDs do their thing as per the led_audio_begin() function (flashes, colors change)
6. audio finishes playing
7. the LEDs do their thing as per the led_audio_end() function (flashes, go dark)
8. brief pause, and then the amber pulse sequence resumes
9. the script waits for the next capacitive sensor touch and then repeats
And here's the script I've come up with, run from the command line as:
What's happening:
1. the amber pulse begins looping
2. the capacitive sensor senses touch
3. the MQTT ON message is sent
4. audio begins playing
5. the LEDs do their thing as per the led_audio_begin() function
6. audio finishes playing
7. the LEDs do their thing as per the led_audio_end() function
8. the LEDs go dark, pause briefly, and then... the GPIO18 LEDs seem to get stuck.
At this point, only the first 1 or 2 LEDs on the strip illumine. No amber pulse sequence. Here's what I see in the terminal window:
If I break the script at this point and then run the script again:
If I don't break the script but instead just try touching the capcitive sensor again, the GPIO21 LEDs do their led_audio_begin function, but the GPIO18 LEDs don't. There's no audio. Then the GPIO18 LEDs do their led_audio_end function, and... everything just kind of deteriorates from there.
Whether the Pi locks up or not, in all cases I have to reboot the Pi in order to make another attempt.
I'm just stymied, cannot for the life of me see where things are going wrong. If both of these sequences are working correctly in separate scripts, there's GOT to be a way to successfully combine them. I'd be most grateful for any input.
I'm working with the latest Raspberry Pi Lite install on an RPi3. Python v3.11.2 is in a virtual environment.
Project Overview:
- Uses two NeoPixel LED strips (11 LEDs on GPIO18, 65 LEDs on GPIO21). First strip is powered by 5V GPIO pin on the Pi. I'm using a 5V adapter for the second strip. Both strips are grounded to the Pi.
- Implements a capacitive touch sensor on GPIO10.
- Plays MP3 audio files.
- Sends MQTT messages.
I'm using the following libraries:
- RPi.GPIO
- board
- neopixel
- paho.mqtt
- subprocess (for audio playback with mpg123)
I did my script development in stages. This first proof-of-concept script watches for a capacitive sensor touch on GPIO10 and then does some color changes, etc. on the two LED strips connected to GPIO18 and GPIO21. Repeated touches = repeating the script. I run the script from the command line:
Code:
sudo ~/led_env/bin/python3 leds_for_audio_with_flashes_touch.py
Code:
import timeimport boardimport neopixelimport signalimport sysimport randomimport RPi.GPIO as GPIO# Initialize the LED stripspixels_18 = neopixel.NeoPixel(board.D18, 11, brightness=0.35, auto_write=True)pixels_21 = neopixel.NeoPixel(board.D21, 65, brightness=0, auto_write=False)# Color definitionsAMBER = (255, 147, 41)DARK_PURPLE = (44, 0, 88)GREEN = (0, 255, 0)WHITE = (255, 255, 255)# GPIO Setup for touch sensorTOUCH_PIN = 10GPIO.setmode(GPIO.BCM)GPIO.setup(TOUCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)def set_brightness(pixels, color, target_brightness, duration=0.5): start_brightness = pixels.brightness steps = 50 for i in range(steps + 1): t = i / steps current_brightness = start_brightness + (target_brightness - start_brightness) * t pixels.brightness = current_brightness pixels.fill(color) if pixels == pixels_21: pixels.show() time.sleep(duration / steps) if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True return Falsedef random_flashes(duration=1.5): end_time = time.time() + duration while time.time() < end_time: flash_18 = random.choice([True, False]) flash_21 = random.choice([True, False]) if flash_18: pixels_18.brightness = random.uniform(0.5, 1.0) pixels_18.fill(WHITE) else: pixels_18.brightness = 0 pixels_18.fill((0, 0, 0)) if flash_21: pixels_21.brightness = random.uniform(0.5, 1.0) pixels_21.fill(WHITE) else: pixels_21.brightness = 0 pixels_21.fill((0, 0, 0)) pixels_21.show() time.sleep(random.uniform(0.01, 0.05))def single_rapid_flash(): pixels_18.brightness = 1.0 pixels_21.brightness = 1.0 pixels_18.fill(WHITE) pixels_21.fill(WHITE) pixels_21.show() time.sleep(0.05) pixels_18.fill((0, 0, 0)) pixels_21.fill((0, 0, 0)) pixels_21.show()def amber_pulse(): # Start with a fade-in from off to the lowest brightness if set_brightness(pixels_18, AMBER, 0.175, 1): return True if set_brightness(pixels_18, AMBER, 0.65, 4): return True if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True time.sleep(1) if set_brightness(pixels_18, AMBER, 0.175, 4): return True if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True time.sleep(1) return Falsedef led_audio_begin(): random_flashes(1.5) pixels_18.brightness = 0 pixels_21.brightness = 0 set_brightness(pixels_18, GREEN, 0.6) set_brightness(pixels_21, DARK_PURPLE, 1.0)def led_audio_end(): single_rapid_flash() time.sleep(3) # 3-second dark period # Ensure LEDs are off before resuming AmberPulse pixels_18.brightness = 0 pixels_18.fill((0, 0, 0))def cleanup(signal, frame): print("\nTurning off LEDs") pixels_18.fill((0, 0, 0)) pixels_21.fill((0, 0, 0)) pixels_21.show() GPIO.cleanup() sys.exit(0)signal.signal(signal.SIGINT, cleanup)try: print("Starting LED sequence. Touch the sensor to trigger the audio flash sequence.") last_touch_time = 0 debounce_time = 1 # 1 second debounce # Initial fade-in for AmberPulse set_brightness(pixels_18, AMBER, 0.175, 1) while True: current_time = time.time() touch_state = GPIO.input(TOUCH_PIN) if touch_state == GPIO.HIGH and (current_time - last_touch_time > debounce_time): print("Touch detected! Starting audio flash sequence.") led_audio_begin() time.sleep(5) # Keep the 5-second delay between begin and end led_audio_end() print("Resuming amber pulse sequence.") last_touch_time = current_time else: if amber_pulse(): # Touch detected during amber pulse print("Touch detected during amber pulse! Starting audio flash sequence.") led_audio_begin() time.sleep(5) # Keep the 5-second delay between begin and end led_audio_end() print("Resuming amber pulse sequence.") last_touch_time = current_timeexcept Exception as e: print(f"An error occurred: {e}") cleanup(None, None)
Code:
sudo ~/led_env/bin/python3 test_random_audio.py
Code:
import RPi.GPIO as GPIOimport osimport timeimport randomimport subprocess # Added this import for subprocess.runimport paho.mqtt.client as mqtt# GPIO Setup for touch sensorTOUCH_PIN = 10GPIO.setmode(GPIO.BCM)GPIO.setup(TOUCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)# MQTT SetupMQTT_BROKER = '192.168.1.35'MQTT_PORT = 1883MQTT_TOPIC = 'home/ham_radio_touch'MQTT_USERNAME = <redacted>MQTT_PASSWORD = <redacted>MQTT_KEEPALIVE = 60client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)client.will_set(MQTT_TOPIC, 'DISCONNECTED', qos=1, retain=True)def on_connect(client, userdata, flags, reason_code, properties): if reason_code != 0: print(f"Connection failed with reason code: {reason_code}") # Added print for debuggingdef on_disconnect(client, userdata, flags, reason_code, properties): if reason_code != 0: print(f"Disconnected with reason code: {reason_code}") # Added print for debugging try: client.reconnect() except Exception as e: print(f"Reconnection failed: {e}") # Added print for debuggingclient.on_connect = on_connectclient.on_disconnect = on_disconnectclient.connect(MQTT_BROKER, MQTT_PORT, MQTT_KEEPALIVE)client.loop_start()# Audio setupAUDIO_FILES_DIR = "/home/pi/audio" # Directory containing the audio filesAUDIO_FILES = [f"{i:02d}.mp3" for i in range(1, 29)] # List of audio files (01.mp3, 02.mp3, ..., 29.mp3)# State definitionsIDLE = 0TOUCH_DETECTED = 1DEBOUNCE_TIME = 2 # secondsprevious_state = IDLElast_touch_time = time.time()def play_audio(): selected_file = random.choice(AUDIO_FILES) file_path = os.path.join(AUDIO_FILES_DIR, selected_file) subprocess.run(['mpg123', file_path])try: while True: current_state = GPIO.input(TOUCH_PIN) current_time = time.time() if current_state == GPIO.HIGH and previous_state == IDLE and (current_time - last_touch_time > DEBOUNCE_TIME): client.publish(MQTT_TOPIC, 'ON') play_audio() previous_state = TOUCH_DETECTED last_touch_time = current_time elif current_state == GPIO.LOW and previous_state == TOUCH_DETECTED and (current_time - last_touch_time > DEBOUNCE_TIME): client.publish(MQTT_TOPIC, 'OFF') previous_state = IDLE last_touch_time = current_time time.sleep(0.1)except KeyboardInterrupt: GPIO.cleanup() client.disconnect() client.loop_stop() # Added this to properly stop the MQTT client loop
1. the amber LED pulse begins looping
2. the capacitive sensor senses touch
3. the MQTT ON message is sent
4. audio begins playing
5. the LEDs do their thing as per the led_audio_begin() function (flashes, colors change)
6. audio finishes playing
7. the LEDs do their thing as per the led_audio_end() function (flashes, go dark)
8. brief pause, and then the amber pulse sequence resumes
9. the script waits for the next capacitive sensor touch and then repeats
And here's the script I've come up with, run from the command line as:
Code:
sudo ~/led_env/bin/python3 touch_play_random.py
Code:
import timeimport boardimport neopixelimport signalimport sysimport randomimport RPi.GPIO as GPIOimport osimport subprocessimport paho.mqtt.client as mqtt# Initialize the LED stripspixels_18 = neopixel.NeoPixel(board.D18, 11, brightness=0.35, auto_write=True)pixels_21 = neopixel.NeoPixel(board.D21, 65, brightness=0, auto_write=False)# Color definitionsAMBER = (255, 147, 41)DARK_PURPLE = (44, 0, 88)GREEN = (0, 255, 0)WHITE = (255, 255, 255)# GPIO Setup for touch sensorTOUCH_PIN = 10GPIO.setmode(GPIO.BCM)GPIO.setup(TOUCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)# MQTT SetupMQTT_BROKER = '192.168.1.35'MQTT_PORT = 1883MQTT_TOPIC = 'home/ham_radio_touch'MQTT_USERNAME = <redacted>MQTT_PASSWORD = <redacted>MQTT_KEEPALIVE = 60client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)client.will_set(MQTT_TOPIC, 'DISCONNECTED', qos=1, retain=True)def on_connect(client, userdata, flags, reason_code, properties): if reason_code != 0: print(f"Connection failed with reason code: {reason_code}")def on_disconnect(client, userdata, flags, reason_code, properties): if reason_code != 0: print(f"Disconnected with reason code: {reason_code}") try: client.reconnect() except Exception as e: print(f"Reconnection failed: {e}")client.on_connect = on_connectclient.on_disconnect = on_disconnectclient.connect(MQTT_BROKER, MQTT_PORT, MQTT_KEEPALIVE)client.loop_start()# Audio setupAUDIO_FILES_DIR = "/home/pi/audio"AUDIO_FILES = [f"{i:02d}.mp3" for i in range(1, 29)]def set_brightness(pixels, color, target_brightness, duration=0.5): start_brightness = pixels.brightness steps = 50 for i in range(steps + 1): t = i / steps current_brightness = start_brightness + (target_brightness - start_brightness) * t pixels.brightness = current_brightness pixels.fill(color) if pixels == pixels_21: pixels.show() time.sleep(duration / steps) if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True return Falsedef random_flashes(duration=1.5): end_time = time.time() + duration while time.time() < end_time: flash_18 = random.choice([True, False]) flash_21 = random.choice([True, False]) if flash_18: pixels_18.brightness = random.uniform(0.5, 1.0) pixels_18.fill(WHITE) else: pixels_18.brightness = 0 pixels_18.fill((0, 0, 0)) if flash_21: pixels_21.brightness = random.uniform(0.5, 1.0) pixels_21.fill(WHITE) else: pixels_21.brightness = 0 pixels_21.fill((0, 0, 0)) pixels_21.show() time.sleep(random.uniform(0.01, 0.05))def single_rapid_flash(): pixels_18.brightness = 1.0 pixels_21.brightness = 1.0 pixels_18.fill(WHITE) pixels_21.fill(WHITE) pixels_21.show() time.sleep(0.05) pixels_18.fill((0, 0, 0)) pixels_21.fill((0, 0, 0)) pixels_21.show()def amber_pulse(): # Start with a fade-in from off to the lowest brightness if set_brightness(pixels_18, AMBER, 0.175, 1): return True if set_brightness(pixels_18, AMBER, 0.65, 4): return True if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True time.sleep(1) if set_brightness(pixels_18, AMBER, 0.175, 4): return True if GPIO.input(TOUCH_PIN) == GPIO.HIGH: return True time.sleep(1) return Falsedef led_audio_begin(): random_flashes(1.5) pixels_18.brightness = 0 pixels_21.brightness = 0 set_brightness(pixels_18, GREEN, 0.6) set_brightness(pixels_21, DARK_PURPLE, 1.0)def led_audio_end(): single_rapid_flash() time.sleep(3) # 3-second dark period # Ensure LEDs are off before resuming AmberPulse pixels_18.brightness = 0 pixels_18.fill((0, 0, 0))def play_audio(): selected_file = random.choice(AUDIO_FILES) file_path = os.path.join(AUDIO_FILES_DIR, selected_file) subprocess.run(['mpg123', file_path])def cleanup(signal, frame): print("\nTurning off LEDs") pixels_18.fill((0, 0, 0)) pixels_21.fill((0, 0, 0)) pixels_21.show() GPIO.cleanup() client.disconnect() client.loop_stop() sys.exit(0)signal.signal(signal.SIGINT, cleanup)try: print("Starting LED sequence. Touch the sensor to trigger the audio flash sequence.") last_touch_time = 0 debounce_time = 1 # 1 second debounce # Initial fade-in for AmberPulse set_brightness(pixels_18, AMBER, 0.175, 1) while True: current_time = time.time() touch_state = GPIO.input(TOUCH_PIN) if touch_state == GPIO.HIGH and (current_time - last_touch_time > debounce_time): print("Touch detected! Starting audio flash sequence.") client.publish(MQTT_TOPIC, 'ON') led_audio_begin() play_audio() led_audio_end() client.publish(MQTT_TOPIC, 'OFF') print("Resuming amber pulse sequence.") last_touch_time = current_time else: if amber_pulse(): # Touch detected during amber pulse print("Touch detected during amber pulse! Starting audio flash sequence.") client.publish(MQTT_TOPIC, 'ON') led_audio_begin() play_audio() led_audio_end() client.publish(MQTT_TOPIC, 'OFF') print("Resuming amber pulse sequence.") last_touch_time = current_timeexcept Exception as e: print(f"An error occurred: {e}") cleanup(None, None)
1. the amber pulse begins looping
2. the capacitive sensor senses touch
3. the MQTT ON message is sent
4. audio begins playing
5. the LEDs do their thing as per the led_audio_begin() function
6. audio finishes playing
7. the LEDs do their thing as per the led_audio_end() function
8. the LEDs go dark, pause briefly, and then... the GPIO18 LEDs seem to get stuck.
At this point, only the first 1 or 2 LEDs on the strip illumine. No amber pulse sequence. Here's what I see in the terminal window:
Code:
pi@hallicrafterss20r:~ $ sudo ~/led_env/bin/python3 touch_play_random.pyStarting LED sequence. Touch the sensor to trigger the audio flash sequence.Touch detected during amber pulse! Starting audio flash sequence.High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3version 1.31.2; written and copyright by Michael Hipp and othersfree software (LGPL) without any warranty but with best wishesDirectory: /home/pi/audio/Terminal control enabled, press 'h' for listing of keys and functions.Playing MPEG stream 1 of 1: 27.mp3 ...MPEG 1.0 L III vbr 44100 j-s[0:53] Decoding of 27.mp3 finished.Resuming amber pulse sequence.
Code:
^CTurning off LEDspi@hallicrafterss20r:~ $ sudo ~/led_env/bin/python3 touch_play_random.pyStarting LED sequence. Touch the sensor to trigger the audio flash sequence.Touch detected during amber pulse! Starting audio flash sequence.High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3version 1.31.2; written and copyright by Michael Hipp and othersfree software (LGPL) without any warranty but with best wishesDirectory: /home/pi/audio/Terminal control enabled, press 'h' for listing of keys and functions.Playing MPEG stream 1 of 1: 20.mp3 ...MPEG 1.0 L III vbr 44100 j-s[src/libout123/libout123.c:out123_play():700] error: Error in writing audio, wrote only 2836 of 4608 (Success?)!main: [src/mpg123.c:play_frame():852] error: Deep trouble! Cannot flush to my output anymore!Resuming amber pulse sequence.
Whether the Pi locks up or not, in all cases I have to reboot the Pi in order to make another attempt.
I'm just stymied, cannot for the life of me see where things are going wrong. If both of these sequences are working correctly in separate scripts, there's GOT to be a way to successfully combine them. I'd be most grateful for any input.
Statistics: Posted by grantalewis — Sat Aug 03, 2024 10:10 pm