Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4829

Troubleshooting • Internet Radio Project

$
0
0
Hi everyone,

I'm new here, and pretty new to programming too.

I'm trying to build a "simple" internet radio, with minimal controls and options.

Here is a list of parts I'm using :

Raspberry Pi 5
AudioBerry Amp2 (DAC + 20W Amp)
Dayton Audio DMA 70-4 Speakers (20W @ 4 ohms)
AZDelivery OLED Screen 1.3 inches, 128x64 px, SSH1106 Driver
2x KY-040 Rotary Encoders with push button and resistor
1x KY-004 Push Button Module with resistor (J2 Power extension button)
19V 3.4 Amps Power Adapter

Here are the OS and libraries I'm using :

Raspberry OS Bookworm
lgpio (GPIO)
mpd (music player daemon)
luma.oled sh1106 (display driver)

Software goal :

Power ON Raspberry
Autostart of the "radio" program (Extension power button)
1x Encoder to scroll throught a small preset of internet radio stations (Push for future function)
1x Encoder to set Volume (Push for Mute)
Power OFF Raspberry Safely (Extension power button)


I'm really new to programming.
I watched all the videos that I could and read a ton of things online, mostly from https://bobrathbone.com/.
I tried to implement his software in my project, which is very similar.
Unfortunately, I haven't been able to make it work.
I suspect some compatibility issues to use his program straightforward with Raspberry PI 5.

So I tried to write it from scratch with the help of AI (please don't judge me, I'm trying my best to understand the answers...).
After a very long process of trial and errors, I modified a lot of CONFIG files and tried a lot of differents scripts and drivers, I made a nearly functionnal version of the software.
I could start the RPI, and everything was more or less working as expected when started from Thonny's GUI on the PI.
Then, I tried to modify the script to make it boot with the PI, to not have to use the GUI and run it "headless" (I mean, only on the little OLED Screen).

I don't really know how I managed to do it, but I nearly "broke" all the program.
Now, when I launch it from Thonny, the Radio starts OK and the text displays OK on the OLED.
I can turn Station or Volume encoder for one or two clicks and then my program stops.
The radio is still running in the background because MPD doesn't stop, but as the software crashes, I can't control anything and I have to reboot.
It has been a nightmare figuring out what's going wrong with my script (mostly because of my lack of experience) but I can also feel that I'm pretty close to make it work.
I mean all in all, it should be quite a basic program.

What I would like to do now is :
- Start the software at bootup automatically.
- Stop MPD when I stop the program from GUI, or when it crashes.
- Stop the OLED screen when I stop the program from GUI, or when it crashes.
- Have a Safe Shutdown when I press the extension power button (maybe this one is ok).
- Have a stable software that keeps running and where I can easily change stations and volume.

Please, could someone help me understand what's wrong with my script ?
Any help would be very much appreciated.
I will try to understand anything to make it work.
If it's not the perfect place for this topic, you can move it, or point me to a better direction.

Thanks !

Code:

import timeimport lgpioimport subprocess  # Import subprocess to use system commandsimport signalfrom mpd import MPDClientfrom luma.core.interface.serial import i2cfrom luma.oled.device import sh1106from luma.core.render import canvasfrom PIL import ImageFont# GPIO pin configuration for the two rotary encodersclk_station = 5  # Clock pin for changing stationdt_station = 6  # Data pin for changing stationbtn_station = 12  # GPIO for station buttonclk_volume = 13   # Clock pin for changing volumedt_volume = 22    # Data pin for changing volumebtn_volume = 26    # GPIO for volume button# Initial states for encodersstation_counter = 0volume_counter = 20  # Initial volume set to 20%last_state_clk_station = Nonelast_state_clk_volume = Nonedebounce_time = 0.002  # 2ms debounce time# Open GPIO chiph = lgpio.gpiochip_open(0)# Set the pins as inputlgpio.gpio_claim_input(h, clk_station)lgpio.gpio_claim_input(h, dt_station)lgpio.gpio_claim_input(h, clk_volume)lgpio.gpio_claim_input(h, dt_volume)lgpio.gpio_claim_input(h, btn_volume)  # Claim volume button as inputlgpio.gpio_claim_input(h, btn_station)  # Claim station button as input# Setup OLED display (I2C)serial = i2c(port=1, address=0x3C)  # Default I2C address for SH1106device = sh1106(serial)font = ImageFont.load_default()# Connect to MPDclient = MPDClient()client.connect("localhost", 6600)# List of preset radio stations with custom namesstations = [    ("http://icecast.radiofrance.fr/franceinter-midfi.mp3", "France Inter"),    ("http://icecast.radiofrance.fr/fip-midfi.mp3", "FIP"),    ("http://icecast.radiofrance.fr/franceculture-midfi.mp3", "France Culture"),    ("http://icecast.radiofrance.fr/franceinfo-midfi.mp3", "France Info"),    ("http://radionova.ice.infomaniak.ch/radionova-256.aac", "NOVA"),    ("https://stream-relay-geo.ntslive.net/stream", "NTS 1"),    ("https://stream.subfm.sk/subfmhi", "Sub.FM"),    ("http://alphaboys-live.streamguys1.com/alphaboys.mp3", "Alpha Boys School"),]# Function to display station and volume on the OLED, centereddef display_info(station_index, volume_level):    station_name = stations[station_index][1]  # Get the station name    with canvas(device) as draw:        # Calculate position for centered text using textbbox        station_text = f"{station_index + 1} - {station_name}"        volume_text = f"Volume: {volume_level}%"                # Get bounding box for the text        bbox_station = draw.textbbox((0, 0), station_text, font=font)        bbox_volume = draw.textbbox((0, 0), volume_text, font=font)                w_station = bbox_station[2] - bbox_station[0]        w_volume = bbox_volume[2] - bbox_volume[0]        # Center the text horizontally        draw.text(((device.width - w_station) // 2, 20), station_text, font=font, fill="white")        draw.text(((device.width - w_volume) // 2, 40), volume_text, font=font, fill="white")# Function to select and play the station at the given indexdef play_station(index):    global stations    client.clear()  # Clear the current MPD playlist    client.add(stations[index][0])  # Add the selected station (URL)    client.play()  # Start playing the station    display_info(index, volume_counter)  # Update the OLED display# Function to stop MPD playback using systemctl without sudodef stop_playback():    try:        # Stop MPD without asking for password        subprocess.run(["systemctl", "stop", "mpd.service"], check=True)        print("MPD stopped successfully.")    except subprocess.CalledProcessError as e:        print(f"Failed to stop MPD: {e}")# Function to clean up resources and stop the programdef cleanup(signum, frame):    stop_playback()  # Stop MPD playback    device.hide()  # Turn off the OLED display    lgpio.gpiochip_close(h)  # Close the GPIO    print("Cleaned up resources and exiting.")    exit(0)  # Ensure the program exits cleanly# Register signal handler for stopping the program via Thonny or terminal stopsignal.signal(signal.SIGTERM, cleanup)signal.signal(signal.SIGINT, cleanup)  # SIGINT is for Ctrl+C or Thonny stop# Initial displayplay_station(station_counter)# Main looptry:    last_state_clk_station = lgpio.gpio_read(h, clk_station)    last_state_clk_volume = lgpio.gpio_read(h, clk_volume)    while True:        # === Station Encoder ===        current_state_clk_station = lgpio.gpio_read(h, clk_station)        if current_state_clk_station != last_state_clk_station and current_state_clk_station == 1:            # Detect the direction based on the dt pin            if lgpio.gpio_read(h, dt_station) != current_state_clk_station:                station_counter = (station_counter + 1) % len(stations)            else:                station_counter = (station_counter - 1) % len(stations)            # Play the new station            play_station(station_counter)        last_state_clk_station = current_state_clk_station        # === Volume Encoder ===        current_state_clk_volume = lgpio.gpio_read(h, clk_volume)        if current_state_clk_volume != last_state_clk_volume and current_state_clk_volume == 1:            # Detect the direction based on the dt pin            if lgpio.gpio_read(h, dt_volume) != current_state_clk_volume:                volume_counter = min(100, volume_counter + 5)  # Increase volume by 5%            else:                volume_counter = max(0, volume_counter - 5)  # Decrease volume by 5%            # Set the new volume            client.setvol(volume_counter)            display_info(station_counter, volume_counter)        last_state_clk_volume = current_state_clk_volume        # === Check Volume Button ===        if lgpio.gpio_read(h, btn_volume) == 0:  # Button pressed            print("Volume button pressed")  # Placeholder for future functionality        # === Check Station Button ===        if lgpio.gpio_read(h, btn_station) == 0:  # Button pressed            print("Station button pressed")  # Placeholder for future functionality        # Heartbeat: Update display periodically to keep it alive        display_info(station_counter, volume_counter)        # Small delay to debounce        time.sleep(0.1)except KeyboardInterrupt:    cleanup(signal.SIGINT, None)  # Cleanup on user interrupt

Statistics: Posted by kingj — Tue Oct 01, 2024 11:28 pm



Viewing all articles
Browse latest Browse all 4829

Trending Articles