icon

Listen internet radio via UNIHIKER

0 1090 Easy

This tutorial is intended to show you how quickly and easily you can transform your UNIHIKER into an internet radio. In the end you will be able to develop really cool apps independently. As with most of my guides, Python is used as the development language.

HARDWARE LIST
1 UNIHIKER
1 USB Speaker

Just connect the USB speaker with your UNIHIKER device.

 

STEP 1
Create the project and files

Create a new folder on your local device (Windows, macOS, Linux, etc.), for example called "Radio". Within this folder you create another folder “lib”. Next, create 3 Python files. The first file is named "main.py" directly in the "Radio" project folder. The other two files "radiostream.py" and "sidepanel.py" should created inside the "lib" folder. Nothing more is needed! When you're finished, your project should look like this:

 

STEP 2
Develop the code

Let's start with the two files in the "lib" folder. These two files are developed so that the code remains clear and modular. Both files will contain classes that you can later reuse (and extend) for your own projects. One of the classes is used to start and stop the radio stream and control the volume. The other class is used to create a side panel (via Tkinter). In this tutorial we use a Tkinter extension called “customtkinter”! This saves time and should show you further options with UNIHIKER/Python.

 

Here is the code (documented with DocStrings) for "radiostream.py".

CODE
from vlc import MediaPlayer


class RadioStream:
    """
    Class to create vlc radio stream instance
    """
    def __init__(self, volume: int = 50):
        """
        RadioStream constructor
        :param volume: start volume (between 0 and 100, default is 50)
        """
        self._player = None

        if 0 <= volume <= 100:
            self._volume = volume
        else:
            self._volume = 50

    def _set_volume(self, volume: int) -> None:
        """
        Set RadioStream volume
        :param volume: set volume (between 0 and 100)
        :return: None
        """
        if 0 <= volume <= 100:
            volume_int = int(volume)

            if self._player is not None:
                try:
                    self._player.audio_set_volume(volume_int)
                except ValueError:
                    pass

    def stop_stream(self):
        """
        Stop RadioStream
        :return: None
        """
        if self._player is not None:
            self._set_volume(volume=0)
            self._player.stop()

    def start_stream(self, station: str) -> None:
        """
        Start RadioStream
        :param station: radio station url for stream
        :return: None
        """
        self.stop_stream()

        self._player = MediaPlayer(station)
        self._set_volume(volume=self._volume)
        self._player.play()

    def increase_volume(self) -> None:
        """
        Increase RadioStream volume
        :return: None
        """
        self._volume += 5
        self._set_volume(self._volume)

    def decrease_volume(self) -> None:
        """
        Decrease RadioStream volume
        :return: None
        """
        self._volume -= 5
        self._set_volume(self._volume)

And here is the code (documented with DocStrings) for "sidepanel.py".

CODE
from customtkinter import CTkFrame


class SlidePanel(CTkFrame):
    """
    Class to create a Tkinter SlidePanel instance
    """
    def __init__(self, parent, width: int, height: int, start_pos: int, end_pos: int):
        """
        SlidePanel constructor
        :param parent: tkinter window
        :param width: width of the SlidePanel
        :param height: height of the SlidePanel
        :param start_pos: start position of the SlidePanel
        :param end_pos: end position of the SlidePanel
        """
        super().__init__(master=parent, width=int(width), height=int(height))

        self._start_position = int(start_pos)
        self._end_position = int(end_pos)
        self._current_position = int(start_pos)
        self._toggle_in = True

        self.place(x=self._start_position, y=0)

    def _move_panel_in(self) -> None:
        """
        Slide in animation for SlidePanel instance
        :return: None
        """
        if self._current_position > self._end_position:
            self._current_position -= 10
            self.place(x=self._current_position, y=0)
            self.after(10, self._move_panel_in)
        else:
            self._toggle_in = False

    def _move_panel_out(self) -> None:
        """
        Slide out animation for SlidePanel instance
        :return: None
        """
        if self._current_position < self._start_position:
            self._current_position += 10
            self.place(x=self._current_position, y=0)
            self.after(10, self._move_panel_out)
        else:
            self._toggle_in = True

    def toggle_position(self) -> None:
        """
        Toggle slide in or out animation for SlidePanel instance
        :return: None
        """
        if self._toggle_in:
            self._move_panel_in()
        else:
            self._move_panel_out()

Now comes the code for “main.py”. All necessary Python modules/packages are imported here, a few constants are defined, as well as other functions and program logic. Put your attention on the constant RADIO_STATIONS! Here you can specify each station as you wish.

CODE
from customtkinter import CTk, set_appearance_mode, CTkButton, CTkLabel
from tkinter import StringVar
from pinpong.board import Board, Pin
from pinpong.extension.unihiker import button_a, button_b
from lib.radiostream import RadioStream
from lib.sidepanel import SlidePanel


SCREEN_WIDTH: int = 240
SCREEN_HEIGHT: int = 320

START_VOLUME: int = 60
RADIO_STATIONS: dict = {
    "Vintage Radio Switzerland": "https://vintageradio.ice.infomaniak.ch/vintageradio-high.mp3",
    "Classics Radio": "https://stream.klassikradio.de/newclassics/mp3-192/",
    "STAR FM Berlin": "https://stream.starfm.de/berlin/mp3-192/",
    "Fnoob Techno Radio": "https://streams.radiomast.io/8846a94e-9874-4692-a1a0-ec7aadbe2771",
    "HipHop": "https://hiphop24.stream.laut.fm/hiphop24",
    "Rádio Bossa Jazz Brasil": "https://centova5.transmissaodigital.com:20104/",
    "Rouge Reggae": "http://rouge-reggae.ice.infomaniak.ch/rouge-reggae-128.mp3",
    "STAR FM Rock Ballads": "https://stream.starfm.de/ballads/mp3-128/"
}


def btn_stop_click() -> None:
    """
    Change the state of the buttons after press stop button
    :return: None
    """
    global btn_stop
    global btn_play

    btn_stop.configure(state='disabled')
    btn_play.configure(state='normal')


def btn_play_click() -> None:
    """
    Change the state of the buttons after press play button
    :return: None
    """
    global btn_stop
    global btn_play

    btn_stop.configure(state='normal')
    btn_play.configure(state='disabled')


def select_music(url: str, text: str) -> None:
    """
    Select music stream to play
    :param url: radio station url to stream
    :param text: radio station name
    :return: None
    """
    global panel
    global stream
    global label_current_station
    global btn_stop
    global btn_play

    panel.toggle_position()
    stream.start_stream(station=url)
    label_current_station.configure(text=text)
    btn_stop.configure(state='normal', command=lambda: [btn_stop_click(), stream.stop_stream()])
    btn_play.configure(state='disabled', command=lambda: [btn_play_click(), stream.start_stream(station=url)])


def btn_a_raising_handler(pin) -> None:
    """
    Interrupt event callback function for button A rising edge
    :param pin: pin instance of the button
    :return: None
    """
    global stream

    _ = pin
    stream.increase_volume()


def btn_b_raising_handler(pin) -> None:
    """
    Interrupt event callback function for button B rising edge
    :param pin: pin instance of the button
    :return: None
    """
    global stream

    _ = pin
    stream.decrease_volume()


if __name__ == '__main__':
    Board().begin()

    window = CTk()
    window.geometry(f'{SCREEN_WIDTH}x{SCREEN_HEIGHT}+0+0')
    window.resizable(False, False)
    window.configure(bg='black')
    set_appearance_mode("dark")

    stream = RadioStream(volume=START_VOLUME)

    btn_station_select = CTkButton(window, text="Select Radio Station")
    btn_station_select.place(relx=.5, rely=.85, anchor='center')

    label_font = ("Times", 18, 'bold')
    label_current_station = CTkLabel(window, width=SCREEN_WIDTH, height=SCREEN_HEIGHT // 2)
    label_current_station.configure(anchor='center', font=label_font, text="UNIHIKER Radio Station")
    label_current_station.place(x=0, y=0)

    btn_stop_text = StringVar(value="⬛")
    btn_stop = CTkButton(window, textvariable=btn_stop_text, width=40, height=40, state='disabled')
    btn_stop.configure(command=lambda: [btn_stop_click(), stream.stop_stream()])
    btn_stop.place(x=75, y=SCREEN_HEIGHT // 2 + 10)

    btn_play_text = StringVar(value="▶")
    btn_play = CTkButton(window, textvariable=btn_play_text, width=40, height=40, state='disabled')
    btn_play.place(x=125, y=SCREEN_HEIGHT // 2 + 10)

    panel = SlidePanel(window, width=SCREEN_WIDTH, height=SCREEN_HEIGHT, start_pos=SCREEN_WIDTH, end_pos=0)

    for key, value in RADIO_STATIONS.items():
        btn = CTkButton(panel, text=key, width=SCREEN_WIDTH - 10)
        btn.pack(expand=True, fill='both', padx=5, pady=5)
        btn.configure(command=lambda val_url=value, val_txt=key: select_music(url=val_url, text=val_txt))

    btn_station_select.configure(command=panel.toggle_position)

    button_a.irq(trigger=Pin.IRQ_RISING, handler=btn_a_raising_handler)
    button_b.irq(trigger=Pin.IRQ_RISING, handler=btn_b_raising_handler)

    window.mainloop()

That's it already! I have intentionally not provided some features (e.g. scroll bar inside the panel, loading animation after select radio station or further information about the current Music stream) so that even beginners can get started easily. That would be something you can add yourself.

STEP 3
Prepare UNIHIKER libraries

As you already recognized in the code, you need to install additional libraries and packages on the UNIHIKER. Don't worry, it's relatively easy, safe and quick! To do this, connect to UNIHIKER via SSH (you can use Putty on Windows). The password for the root user is dfrobot.

 

 

$ ssh [email protected]

 

Important! Your UNIHIKER must now be connected to the Internet! Then enter the following commands one after the other in the terminal (confirm each command with ENTER and wait briefly until the respective command has been executed).

CODE
# update apt packages and install
apt update
apt install -y vlc

# update pip and install
python3 -m pip install --upgrade pip
pip3 install python-vlc
pip3 install customtkinter

If all commands have been executed successfully, you can close the SSH connection.

 

$ exit

STEP 4
Upload the project

You're almost done! Now all you have to do is upload your project to the UNIHIKER and then you can enjoy the radio streams. Here you can use SCP (on Windows WinSCP):

 

$ scp -r Radio [email protected]:/root/

 

The password for the root user is dfrobot.

STEP 5
Start the stream and listen music

Press the HOME button and select 2-Run Programs from the menu. Then select root/ and Radio/. If you used other names and folders, adjust the selection. To start the application, select main.py and wait a few seconds.

 

Depending on the connection (bandwidth) and the radio streams selected, it may take a while until you hear something! As already mentioned, expand the code as you like and need and come up with cool things!

License
All Rights
Reserved
licensBg
0