Pi Node-Red Internet Radio

Node-Red is graphical programming interface that allows logic to be created using nodes that are wired together. Node-Red has been pre-installed in the Raspian OS since 2015 and it has a very easy learning curve.

In this blog I wanted to show an example of Node-Red being used with a five button LCD faceplate to play Internet radio stations.

For this project I used a basic USB powered speaker and a Raspberry Pi 5-button LCD faceplate. The cost of the faceplates start at about $10.

Getting Started with Internet Radio

There are a good number of Internet radio resources, https://www.internet-radio.com has a good selection of stations to choose from.

To find a URL of a radio station, look through the stations until you find what you like and then right click on the .pls link, and “Save Link as…”. Save this link as a file and then open the file in a text editor to get the URL.

radio_stations

 

MPD – Music Player Daemon

MPD is a Linux based music service that supports the playing of both music files and internet based radio stations. For command line operations that is also a MPD client application called mpc. To install both the service and client:

sudo apt-get install mpd mpc

Before I started building the node-red application I played with the mpc commands to ensure that I understood the basics.

Internet radio stations are added like a song list:

mpc add 'http://uk2.internet-radio.com:8062'
mpc add 'http://live.leanstream.co/CKNXFM'
mpc add 'http://66.85.88.2:7136'

Some key mpc control commands are:

mpc play  # play the current station
mpc play 3 # play radio station 3
mpc pause  # pause the music
mpc next  # play the next radio station
mpc prev  # play the previous radio station 

mpc volume 90 # set the volume to 90%
mpc volume +5 # increase the volume 5%
mpc volume -5 # decrease the volume 5%

The mpc status command will show the volume, what is playing along with the current station number and total number of stations:

$ mpc status
Comedy104 - A Star104.net Station: Doug Stanhope - To Tell You the Truth
[playing] #2/4 1:45/0:00 (0%)
volume: 75% repeat: off random: off single: off consume: off

Node-Red Logic

Node-Red can be started from the Raspberry Pi menus, or from the command line:

node-red-start &

To access the Node-Red web page, enter the Raspberry Pi ip address with port 1880, for example : http://192.168.0.121:1880

For this project two extra Node-Red components are needed, and they are for the LCD faceplate and the MPD music player. To add components use the “Palette Manager” option.

palette

For the LCD faceplate, search for Adafruit, and select the i2c-lcd-adafruit-sainsmart component.

adafruit_palette

Similarly search for mpd and add the node-red-contrib-mpd component.

mpd_palette To create logic select a node from the left node panel and drag it onto the center flow palette, and then “wire” the nodes together.

For the Internet music example I used four function nodes, and the two i2cLED and the two MPD nodes. (Comment nodes are only used to explain the logic).

node_red_radio

The first step is to double click on the MPD nodes and add an MPD server.

Select Button Logic

I used the select button to turn on and off the music player.

A context variable is created to save the current state of the player. Note: a context variable is only accessible for the node where it is defined..

The ic2_LCD_Input node message has a msg.button_name and msg.button_state item that is used to determine which button is pushed.

For the select button logic a group of messages was used to add the different radio stations.


// create an "is setup" variable
var issetup = context.get('issetup')||0;

if ((msg.button_name == "SELECT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (issetup === 0) {
context.set('issetup',1);
var msg0 = { payload:"clear"};
var msg1 = { payload:"add http://185.33.21.112:11029" }; // 1.FM Trance
var msg2 = { payload:"add http://66.85.88.2:7136" }; // Comedy 104
var msg3 = { payload:"add http://live.leanstream.co/CKNXFM"}; // The One - Wingham
var msg4 = { payload:"add http://185.33.21.112:11269" }; // Baroque
var msg5 = { payload:"play" };
return [ [ msg0, msg1, msg2, msg3, msg4, msg5] ];
} else {
context.set('issetup',0);
msg0 = { payload:"pause"};
return msg0;
}
}

Up/Down Button Logic

The UP button will issue an MPD command equivalent to :

mpc volume +5

This will up the volume by 5%. The total volume will max at 100%.

The DOWN button will issue an MPD command equivalent to :

mpc volume -5

// Raise/Lower the volume
var msg1;
var thevolume = 5; //volume % increment to change

if ((msg.button_name == "UP") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
msg1 = { payload:"volume +" + thevolume };
return msg1;
}
if ((msg.button_name == "DOWN") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
msg1 = { payload:"volume -" + thevolume};
return msg1;
}

Current and Max Radio Station Logic

The ‘Current and Max Radio Stations’ node is updated from the MPD in node when there are any changes to the volume or when a new song or station is played.

This logic creates two flow variables (stnmax, stncnt) that are available in any node in this flow.  The station max (stnmax) and current radio station (stncnt) variables are used in the LEFT/RIGHT button logic to determine which station to change to.


// Get the max number of radio stations and the current radio statio
// Make context variables that can be used in other node, like the LEFT/RIGHT button

var msg1 = msg.payload.status ; //create a simplier message
var stnmax = msg1.playlistlength;
flow.set('stnmax',stnmax);
var stncur = msg1.nextsong;
if (isNaN(stncur)) {stncur = stnmax;} // ensure a valid station

flow.set('stncur',stncur);

return msg1; // only needed for possible debugging

While the code is running it is possible to view the context date.

context_flow

UP/DOWN Button Logic

The UP / DOWN logic changes between the radio stations using the mpc commands:

mpc next
mpc prev

It is important to not move past the range of the radio stations or MPD will hang. The stnmax and stncur variables are used to determine if the next or previous commands are to be allowed.


// Move left and right in radion stations
var stnmax = flow.get('stnmax');
var stncur = flow.get('stncur');
if ((msg.button_name == "LEFT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (stncur > 1) {
var msg0 = {payload:"previous"};
return msg0;
}
}
if ((msg.button_name == "RIGHT") && (msg.button_state == "pressed" )){
// if the setup hasn't been run, add a radio station playlist
if (stncur < stnmax)
var msg1 = {payload:"next"};
return msg1;

}

Final Comments

The Pi LCD faceplate is an excellent hardware add-on for any Raspberry Pi project. However it important to know that clone hardware may work as expected. For my hardware I was not able to easily turn off the extra LED.

A future enhancement would be to add a Web interface so that you could change the volume or stations without using the 5 button Pi faceplate.

 

Gesture Controlled Radio

Our goal was to make an kitchen Internet radio player where we could change the volume and stations without touching the player. For this project we used:

  • Raspberry Pi 3 (or 2 with a Wifi dongle)
  • Pimoroni SkyWriter (~$20US)
  • USB powered portable speaker

The Pimoroni SkyWriter supports both gesture and touch control.

Setup

To install the Pimoroni SkyWriter python libraries:

curl -sS get.pimoroni.com/skywriter | bash

For the internet music interface we used the mpd , Music Player Daemon, it can be installed by:

sudo apt-get install mpd mpc

The SkyWriter can be wrapped in plastic wrap and the gestures can still be picked up. For a more robust project it would probably be best to design a more waterproof enclosure.

Python Code

A few years ago we found that the mpc calls were bullet-proof, but when we did this project we found that the mpd service would often lock up  if a new internet radio station was selected (mpc next). For this reason we used external calls to adjust the volume and we restarted the mpd service when we changed radio stations

We used a gesture of up/down to adjust the volume, and a left/right to change the station.

OLYMPUS DIGITAL CAMERA

Below is our final code:

#!/usr/bin/env python
import skywriter
import signal
import os

thevolume = 70 #starting volume
thestation = 1 #starting station

# Some internet radio stations
stations = (('http://185.33.21.112:11029','1.FM Amsterdam Trance Radio'),
            ('http://www.partyviberadio.com:8000','Raggae Roots'),
            ('http://66.85.88.2:7136','Comedy 104'),
            ('http://eu.radioboss.fm:8121','Yoga'),
            ('http://185.33.21.112:11269','Baroque'))

def setvolume(voldif):
  global thevolume
  if (thevolume + voldif >= 0) and (thevolume + voldif 0) and (direction + thestation <= len(stations))):
    thestation = thestation + direction
    os.system('mpc clear')
    os.system('sudo service mpd restart')
    os.system('mpc add ' + stations[thestation][0] )
    os.system('mpc play')
    print thestation, stations[thestation][0], stations[thestation][1]

@skywriter.flick()
def flick(start,finish):
  print('Got a flick!', start, finish)
  if (finish == 'north'):
    setvolume(5)
  if (finish == 'south'):
    setvolume(-5)
  if (finish == 'west'):
    newstation(-1)
  if (finish == 'east'):
    newstation(1)

# start playing the radio at defaults
setvolume(0)
newstation(0)

signal.pause() # wait for a new gesture

House Music

Our goal was to have the same music playing throughout the house, and then use a phone to control the music station and volume.

screenshot

For this project we used a technology called MQTT (Message Queue Telemetric Transport). MQTT is based on the following standard concepts:
– MQTT Broker manages and distributes messages
– MQTT Publisher a source for the data
– MQTT Subscriber an application that wants the data

layout2

Pi Setup

On the Raspberry Pi’s we needed to load a Python MQTT library and a music player (mpd – music server, mpc – music client):

sudo pip install paho-mqtt
sudo apt-get install mpd mpc

The key commands to manage playing of Internet radio stations are:
mpc add radio station url– this will add a radio station to a play list
mpc play play list number – play a selected play list
mpc volume number – change the volume from 0-100%
– mpc clear – clear the play lists

Internet radio stations can be found by going to https://www.internet-radio.com. To find a radio station URL right click on any of the radio station links and select “Copy link address”

On each of the Pi’s the Python program subscribes to an MQTT topic called Radio. New Internet Radio Stations are passed as strings, and new volume settings are passed as numbers between 0 and 100. For this application the MQTT payload could be either an integer or a string so a decode function (msg.payload.decode(“utf-8”)) is used to convert from a byte array to a generic string.

When a new radio station is received:
– the current play list is cleared (mpc clear),
– the new radio station is added to the play list (mpc add),
– the volume is reset to it’s original (mpc volume), and
– the radio station is played (mpc play).

If the payload is numeric only the volume is changed. The full Pi Python program is shown below:

# mqtt_2_mpc.py - have MQTT change Internet Radio Stations
#
import paho.mqtt.client as mqtt
import os

thevolume = 75  # save the volume

# Subscribe to topic: Radio

def on_connect(client, userdata, flags, rc):
  print("Connected with result code "+str(rc))
  client.subscribe("Radio")

def on_message(client, userdata, msg):
  global thevolume
  print( msg.payload)
  themsg = msg.payload.decode("utf-8")
  # if the message is a number it's the volume
  if themsg.isnumeric():
    thevolume = themsg
    os.system("mpc volume " + thevolume)
  # if the message is a string it's the station URL
  else:
    os.system("mpc clear")
    os.system("mpc add '" + themsg + "'")
    os.system("mpc volume " + thevolume)
    os.system("mpc play")

client = mqtt.Client()
client.connect("iot.eclipse.org",1883,60)

client.on_connect = on_connect
client.on_message = on_message

print ("Waiting for messages .....")
client.loop_forever()

Web Page Setup

On the Web page the Paho JavaScript client is used to connect to an MQTT broker. It is important to note that the JavaScript interface uses Web Sockets to communicate with the MQTT broker, (typically on port 80) rather than the native MQTT port of 1883. If you install your own MQTT broker, you will need check the document for Web Socket setup and support.

layout3

The final Web page that we used is listed below.

websock1