Radio Pi Plate

Adafruit LCD Pi Plate Rocks!

The RGB 16×2 LCD+Keypad Pi Plate from Adafruit Industries makes a great addition to an Internet radio project.  This little circuit board makes it easy to add an LCD display and numeric keypad to a Raspberry Pi.

One of the neat things about this board is that it uses only two pins on the R-Pi GPIO header to control: the 16×2 Character LCD; up to 3 backlight pins; and 5 keypad buttons.  It uses an MCP23017-E/SP I2C port expander chip to make this happen.

The best part is you don’t really lose those two pins either, since you can still add I2C based sensors, RTCs, etc and have them share the I2C bus.

In this post, we update our original Raspberry Pi Internet Radio project using this cool Pi Plate.  Our new radio shares many of the features of the original radio and adds: compact size; a color-changing LCD display; and more buttons for an improved user interface.

The Software

Once again, we’re using the Adafruit Occidentalis Linux distribution for the R-Pi.  Three python classes from Adafruit handle all of the heavy-lifting for the LCD display, I2C bus, and MCP23017 port expander.  We did have to make a few small changes to the Adafruit python classes to allow us to read all the buttons at once.  You’ll find the details below.

Programming this beauty turned out to be a little trickier than the previous radio.  The Pi Plate uses a serial interface to the R-Pi that’s quite a bit slower than a directly connected LCD.  Polling for button changes in the same loop that handles the LCD made the buttons feel somewhat unresponsive.  The solution was to move the LCD display to a separate worker thread and to use a Python queue to pass update messages.  You’ll see the details in the source code below.

Parts

To build this project, I used:

I like the Edimax WiFi dongle because it works well and requires low power which makes it perfect for use with the Raspberry Pi without a powered USB hub.

YouTube Video

Here’s a 14-minute YouTube video that describes the radio in detail and demonstrates many of its features, including:

  • the multi-color LCD display;
  • key pad controls;
  • volume control;
  • menu system; and
  • loadable playlist.

The Python Source Code

The source code for this project is in one file called radio.py, version 2.1.

#!/usr/bin/python

# radio.py, version 2.1 (RGB LCD Pi Plate version)
# February 17, 2013
# Written by Sheldon Hartling for Usual Panic
# BSD license, all text above must be included in any redistribution
#

#
# based on code from Kyle Prier (http://wwww.youtube.com/meistervision)
# and AdaFruit Industries (https://www.adafruit.com)
# Kyle Prier - https://www.dropbox.com/s/w2y8xx7t6gkq8yz/radio.py
# AdaFruit   - https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git, Adafruit_CharLCDPlate
#

#dependancies
from Adafruit_I2C          import Adafruit_I2C
from Adafruit_MCP230xx     import Adafruit_MCP230XX
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate
from datetime              import datetime
from subprocess            import *
from time                  import sleep, strftime
from Queue                 import Queue
from threading             import Thread

import smbus

# initialize the LCD plate
#   use busnum = 0 for raspi version 1 (256MB) 
#   and busnum = 1 for raspi version 2 (512MB)
LCD = Adafruit_CharLCDPlate(busnum = 1)

# Define a queue to communicate with worker thread
LCD_QUEUE = Queue()

# Globals
PLAYLIST_MSG   = []
STATION        = 1
NUM_STATIONS   = 0

# Buttons
NONE           = 0x00
SELECT         = 0x01
RIGHT          = 0x02
DOWN           = 0x04
UP             = 0x08
LEFT           = 0x10
UP_AND_DOWN    = 0x0C
LEFT_AND_RIGHT = 0x12



# ----------------------------
# WORKER THREAD
# ----------------------------

# Define a function to run in the worker thread
def update_lcd(q):
   
   while True:
      msg = q.get()
      # if we're falling behind, skip some LCD updates
      while not q.empty():
         q.task_done()
         msg = q.get()
      LCD.setCursor(0,0)
      LCD.message(msg)
      q.task_done()
   return



# ----------------------------
# MAIN LOOP
# ----------------------------

def main():
   global STATION, NUM_STATIONS, PLAYLIST_MSG

   # Stop music player
   output = run_cmd("mpc stop" )

   # Setup AdaFruit LCD Plate
   LCD.begin(16,2)
   LCD.clear()
   LCD.backlight(LCD.VIOLET)

   # Create the worker thread and make it a daemon
   worker = Thread(target=update_lcd, args=(LCD_QUEUE,))
   worker.setDaemon(True)
   worker.start()
   
   # Display startup banner
   LCD_QUEUE.put('Welcome to\nUsualPanic Radio', True)

   # Load our station playlist
   load_playlist()
   sleep(2)
   LCD.clear()



# ----------------------------
# START THE MUSIC!
# ----------------------------

   # Start music player
   LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
   run_cmd("mpc volume +100")
   mpc_play(STATION)
   countdown_to_play = 0
      
   # Main loop
   while True:
      press = read_buttons()

      # LEFT button pressed
      if(press == LEFT):
         STATION -= 1
         if(STATION < 1):
            STATION = NUM_STATIONS
         LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
         # start play in 300msec unless another key pressed
         countdown_to_play = 3

      # RIGHT button pressed
      if(press == RIGHT):
         STATION += 1
         if(STATION > NUM_STATIONS):
            STATION = 1
         LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
         # start play in 300msec unless another key pressed
         countdown_to_play = 3

      # UP button pressed
      if(press == UP):
         output = run_cmd("mpc volume +2")

      # DOWN button pressed
      if(press == DOWN):
         output = run_cmd("mpc volume -2")

      # SELECT button pressed
      if(press == SELECT):
         menu_pressed()

      # If we haven't had a key press in 300 msec
      # go ahead and issue the MPC command
      if(countdown_to_play > 0):
         countdown_to_play -= 1
         if(countdown_to_play == 0):
            # Play requested station
            mpc_play(STATION)

      delay_milliseconds(99)
   update_lcd.join()


# ----------------------------
# READ SWITCHES
# ----------------------------

def read_buttons():

   buttons = LCD.readButtons()
   # Debounce push buttons
   if(buttons != 0):
      while(LCD.readButtons() != 0):
         delay_milliseconds(1)
   return buttons



def delay_milliseconds(milliseconds):
   seconds = milliseconds / float(1000)	# divide milliseconds by 1000 for seconds
   sleep(seconds)



# ----------------------------
# LOAD PLAYLIST OF STATIONS
# ----------------------------

def load_playlist():
   global STATION, NUM_STATIONS, PLAYLIST_MSG

   # Run shell script to add all stations
   # to the MPC/MPD music player playlist
   output = run_cmd("mpc clear")
   output = run_cmd("/home/pi/projects/radio/radio_playlist.sh")

   # Load PLAYLIST_MSG list
   PLAYLIST_MSG = []
   with open ("/home/pi/projects/radio/radio_playlist.sh", "r") as playlist:
      # Skip leading hash-bang line
      for line in playlist:
         if line[0:1] != '#!':  
               break
      # Remaining comment lines are loaded
      for line in playlist:
         if line[0] == "#" :
            PLAYLIST_MSG.append(line.replace(r'\n','\n')[1:-1] + "                ")
   playlist.close()
   NUM_STATIONS = len(PLAYLIST_MSG)


# ----------------------------
# RADIO SETUP MENU
# ----------------------------

def menu_pressed():
   global STATION

   MENU_LIST = [
      '1. Display Time \n   & IP Address ',
      '2. Output Audio \n   to HDMI      ',
      '3. Output Audio \n   to Headphones',
      '4. Auto Select  \n   Audio Output ',
      '5. Reload       \n   Playlist File',
      '6. System       \n   Shutdown!    ',
      '7. Exit         \n                ']

   item = 0
   LCD.clear()
   LCD.backlight(LCD.YELLOW)
   LCD_QUEUE.put(MENU_LIST[item], True)

   keep_looping = True
   while (keep_looping):

      # Wait for a key press
      press = read_buttons()

      # UP button
      if(press == UP):
         item -= 1
         if(item < 0):
            item = len(MENU_LIST) - 1
         LCD_QUEUE.put(MENU_LIST[item], True)

      # DOWN button
      elif(press == DOWN):
         item += 1
         if(item >= len(MENU_LIST)):
            item = 0
         LCD_QUEUE.put(MENU_LIST[item], True)

      # SELECT button = exit
      elif(press == SELECT):
         keep_looping = False

         # Take action
         if(  item == 0):
            # 1. display time and IP address
            display_ipaddr()
         elif(item == 1):
            # 2. audio output to HDMI
            output = run_cmd("amixer -q cset numid=3 2")
         elif(item == 2):
            # 3. audio output to headphone jack
            output = run_cmd("amixer -q cset numid=3 1")
         elif(item == 3):
            # 4. audio output auto-select
            output = run_cmd("amixer -q cset numid=3 0")
         elif(item == 4):
            # 5. reload our station playlist
            LCD_QUEUE.put("Reloading\nPlaylist File...", True)
            load_playlist()
            sleep(2)
            STATION = 1
            LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
            mpc_play(STATION)
         elif(item == 5):
            # 6. shutdown the system
            LCD_QUEUE.put('Shutting down\nLinux now! ...  ', True)
            LCD_QUEUE.join()
            output = run_cmd("mpc clear")
            output = run_cmd("sudo shutdown now")
            LCD.clear()
            LCD.backlight(LCD.OFF)
            exit(0)
      else:
         delay_milliseconds(99)

   # Restore display
   LCD.backlight(LCD.VIOLET)
   LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)



# ----------------------------
# DISPLAY TIME AND IP ADDRESS
# ----------------------------

def display_ipaddr():
   show_wlan0 = "ip addr show wlan0 | cut -d/ -f1 | awk '/inet/ {printf \"w%15.15s\", $2}'"
   show_eth0  = "ip addr show eth0  | cut -d/ -f1 | awk '/inet/ {printf \"e%15.15s\", $2}'"
   ipaddr = run_cmd(show_eth0)
   if ipaddr == "":
      ipaddr = run_cmd(show_wlan0)

   LCD.backlight(LCD.VIOLET)
   i = 29
   muting = False
   keep_looping = True
   while (keep_looping):

      # Every 1/2 second, update the time display
      i += 1
      #if(i % 10 == 0):
      if(i % 5 == 0):
         LCD_QUEUE.put(datetime.now().strftime('%b %d  %H:%M:%S\n')+ ipaddr, True)

      # Every 3 seconds, update ethernet or wi-fi IP address
      if(i == 60):
         ipaddr = run_cmd(show_eth0)
         i = 0
      elif(i == 30):
         ipaddr = run_cmd(show_wlan0)

      # Every 100 milliseconds, read the switches
      press = read_buttons()
      # Take action on switch press
      
      # UP button pressed
      if(press == UP):
         output = run_cmd("mpc volume +2")

      # DOWN button pressed
      if(press == DOWN):
         output = run_cmd("mpc volume -2")

      # SELECT button = exit
      if(press == SELECT):
         keep_looping = False

      # LEFT or RIGHT toggles mute
      elif(press == LEFT or press == RIGHT ):
         if muting:
            #amixer command not working, can't use next line
            #output = run_cmd("amixer -q cset numid=2 1")
            mpc_play(STATION)
            # work around a problem.  Play always starts at full volume
            delay_milliseconds(400)
            output = run_cmd("mpc volume +2")
            output = run_cmd("mpc volume -2")
         else:
            #amixer command not working, can't use next line
            #output = run_cmd("amixer -q cset numid=2 0")
            output = run_cmd("mpc stop" )
         muting = not muting
         
      delay_milliseconds(99)



# ----------------------------

def run_cmd(cmd):
   p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT)
   output = p.communicate()[0]
   return output



#def run_cmd_nowait(cmd):
#   pid = Popen(cmd, shell=True, stdout=NONE, stderr=STDOUT).pid



def mpc_play(STATION):
   pid = Popen(["/usr/bin/mpc", "play", '%d' % ( STATION )]).pid



if __name__ == '__main__':
  main()

Modifying the Adafruit Python Classes

We made a few small changes to the Adafruit Python classes to allow us to read all the buttons at once.  To implement these changes, you’ll need to add the following code.

Add this new method to the Adafruit_CharLCDPlate.py class; and

    def readButtons(self):
        return self.mcp.input_all()

Add this new method to the Adafruit_MCP230xx.py class.

    def input_all(self):
        """ return all 5 switches, as a bitmap """
        assert self.num_gpios >= 16, "16bits required"
        return  0x1f ^ ( self.i2c.readU8(MCP23017_GPIOA) & 0x1F )

The Radio Play List

The radio play list / channel line-up is loaded dynamically from a file called radio_playlist.sh.  This file is processed twice by the radio.py program.  The first time it is executed as a shell script and the second time it is read as a text file to load the channel display names.

Don’t forget to change the Linux file permissions on this file to allow execute access.

> chmod 775 radio_playlist.sh

Here’s the sample file I used in my video …

#! /bin/sh

#1 WOLF Seattle  \ncountry
mpc add http://7279.live.streamtheworld.com:80/KKWFFMAAC_SC

#2 CHFX Halifax  \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CHFX

#3 CJCB Sydney   \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CJCB

#4 CFCY CharTown \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CFCY

#5 SomaFM, Lush  \nfemale vocals
mpc add http://mp1.somafm.com:8800

#6 CHNS Halifax  \nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CHNS

#7 CFQM Moncton  \nadult contempory
mpc add mms://ysj00ms01s0.aliant.net/CFQM

#8 CBD  St John  \npublic radio
mpc add mms://ysj00ms01s0.aliant.net/CBD

#9 CJYC St John  \nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CJYC

#10 CHLQ ChrlTown \nhot AC
mpc add mms://ysj00ms01s0.aliant.net/CHLQ

#11 CHTN ChrlTown\nclassic hits
mpc add mms://ysj00ms01s0.aliant.net/CHTN

#12 CJRW ChrlTown\nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CJRW

#13 CBC Halifax  \nradio 2
mpc add http://3143.live.streamtheworld.com:80/CBC_R2_HFX_H_SC

#14 BBC London UK\nradio 1
mpc add "mms://wmlive-nonacl.bbc.net.uk/wms/bbc_ami/radio1/radio1_bb_live_int_ep1_sl0?BBC-UID=349e068f3a2633271418974ca119245ef80d149440d04184c40fc9b78246568b&SSO2-UID="

#15 CBC Halifax  \nradio 1
mpc add http://3383.live.streamtheworld.com:80/CBC_R1_HFX_H_SC

Raspberry Pi Internet Radio

Getting started …

How cool would it be take a vintage 1940′s or 1950′s wooden radio and rebuild it as a modern Internet Radio?  That’s the premise I had in mind when I started out on my first Raspberry Pi project.

I used the Adafruit Occidentalis Linux distribution and Adafruit python class for the 16×2 LCD display.

I found a great YouTube tutorial by MeisterVision that covered the basics of the MPC/MPD Linux Music Player really well.

RaspberryPiRadio1

Parts

To build this project, I used:

I like the Edimax WiFi dongle because it works well and requires low power which makes it perfect for use with the Raspberry Pi without a powered USB hub.

YouTube Video

Here’s a 20-minute YouTube video that describes the radio in detail and demonstrates many of its capabilities, including:

  • an LCD display;
  • push button control;
  • a menu system; and
  • a loadable playlist.

The Python Source Code

The source code for this project is in one file called radio.py.  This is my first ever Python program so please forgive any non-pythonesque usage. ☺

#!/usr/bin/python

# radio.py
# January 23, 2013
# Written by Sheldon Hartling for Usual Panic.
# MIT license, all text above must be included in any redistribution
#

#
# based on code from Kyle Prier (http://wwww.youtube.com/meistervision)
# and AdaFruit Industries (https://www.adafruit.com)
# Kyle Prier - https://www.dropbox.com/s/w2y8xx7t6gkq8yz/radio.py
# AdaFruit   - https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git, Adafruit_CharLCD
#


# Define GPIO output pins for Radio Controls
PIN_SW1_PREV = 18
PIN_SW2_NEXT = 4

# The Adafruit_CharLCD class controls the LCD display
# using six GPIO output pins:
#  25 = RS (register select)
#  24 = E (enable/strobe)
#  23 = Data bit 4
#  17 = Data bit 5
#  27 = Data bit 6
#  22 = Data bit 7

#dependancies
from Adafruit_CharLCD import Adafruit_CharLCD
from datetime         import datetime
from subprocess       import *
from time             import sleep, strftime
import RPi.GPIO as GPIO

# globals
LCD = Adafruit_CharLCD()
PLAYLIST_MSG = []
STATION = 1
NUM_STATIONS = 0



def main():
   global STATION, NUM_STATIONS, PLAYLIST_MSG

   # Stop music player
   output = run_cmd("mpc stop" )

   # Setup GPIO
   GPIO.setmode(GPIO.BCM)            # Use BCM GPIO numbers
   GPIO.setup(PIN_SW1_PREV, GPIO.IN) # Previous Channel button
   GPIO.setup(PIN_SW2_NEXT, GPIO.IN) # Next Channel button

   # Setup AdaFruit LCD
   LCD.begin(16,2)
   LCD.clear()

# ----------------------------
# LOAD PLAYLIST OF STATIONS
# ----------------------------

   # Startup banner
   LCD.message('Welcome to\nSheldon Radio!')

   # Run shell script to add all stations
   # to the MPC/MPD music player playlist
   output = run_cmd("/home/pi/projects/radio/radio_playlist.sh")

   # Load PLAYLIST_MSG list
   with open ("/home/pi/projects/radio/radio_playlist.sh", "r") as playlist:
      # Skip leading hash-bang line
      for line in playlist:
         if line[0:1] != '#!':  
               break
      # Remaining comment lines are loaded
      for line in playlist:
         if line[0] == "#" :
            PLAYLIST_MSG.append(line.replace(r'\n','\n')[1:-1] + "                ")
   playlist.close()

   sleep(1.5)
   NUM_STATIONS = len(PLAYLIST_MSG)

# ----------------------------
# START THE MUSIC!
# ----------------------------

   # Start music player
   LCD.clear()
   LCD.message(PLAYLIST_MSG[STATION - 1] )
   mpc_play(STATION)
   countdown_to_play = 0
      
   # Main loop
   while True:
      press = read_switches()

      # PREV button pressed
      if(press == 1):
         STATION -= 1
         if(STATION < 1):
            STATION = NUM_STATIONS
         LCD.setCursor(0,0)
         LCD.message(PLAYLIST_MSG[STATION - 1] )
         # start play in 300msec unless another key pressed
         countdown_to_play = 3

      # NEXT button pressed
      if(press == 2):
         STATION += 1
         if(STATION > NUM_STATIONS):
            STATION = 1
         LCD.setCursor(0,0)
         LCD.message(PLAYLIST_MSG[STATION - 1] )
         # start play in 300msec unless another key pressed
         countdown_to_play = 3

      # BOTH buttons pressed together
      if(press == 3):
         menu_pressed()
      # If we haven't had a key press in 300 msec
      # go ahead and issue the MPC command
      if(countdown_to_play > 0):
         countdown_to_play -= 1
         if(countdown_to_play == 0):
            # Play requested station
            mpc_play(STATION)

      delay_milliseconds(100)


def read_switches():

   # Initialize
   got_prev = False
   got_next = False

   # Read switches
   sw1_prev = GPIO.input(PIN_SW1_PREV)
   sw2_next = GPIO.input(PIN_SW2_NEXT)

   # Debounce switches and look for two-button combo
   while(sw1_prev or sw2_next):
      if(sw1_prev):
         got_prev = True
      if(sw2_next):
         got_next = True
      delay_milliseconds(1)
      sw1_prev = GPIO.input(PIN_SW1_PREV)
      sw2_next = GPIO.input(PIN_SW2_NEXT)
   if(got_prev and got_next):
      return 3
   if(got_next):
      return 2
   if(got_prev):
      return 1
   return 0



def delay_milliseconds(milliseconds):
   seconds = milliseconds / float(1000)	# divide milliseconds by 1000 for seconds
   sleep(seconds)



# ----------------------------
# RADIO SETUP MENU
# ----------------------------

MENU_LIST = [
   '1. Display Time \n   & IP Address ',
   '2. Output Audio \n   to HDMI      ',
   '3. Output Audio \n   to Headphones',
   '4. Auto Select  \n   Audio Output ',
   '5. System       \n   Shutdown!    ',
   '6. Exit         \n                ']

def menu_pressed():
   global MENU_LIST

   item = 0
   LCD.clear()
   LCD.message(MENU_LIST[item] )

   keep_looping = True
   while (keep_looping):

      # Wait for a key press
      press = read_switches()

      # PREV button
      if(press == 1):
         item -= 1
         if(item < 0):
            item = len(MENU_LIST) - 1
         LCD.clear()
         LCD.message(MENU_LIST[item] )

      # NEXT button
      elif(press == 2):
         item += 1
         if(item >= len(MENU_LIST)):
            item = 0
         LCD.clear()
         LCD.message(MENU_LIST[item] )

      # BOTH buttons together, This is "Select"
      elif(press == 3):
         keep_looping = False

         # Take action
         if(  item == 0):
            # display time and IP address
            display_ipaddr()
         elif(item == 1):
            # output to HDMI
            output = run_cmd("amixer -q cset numid=3 2")
         elif(item == 2):
            # output to headphone jack
            output = run_cmd("amixer -q cset numid=3 1")
         elif(item == 3):
            # output auto-select
            output = run_cmd("amixer -q cset numid=3 0")
         elif(item == 4):
            #shutdown the system
            output = run_cmd("mpc clear")
            output = run_cmd("sudo shutdown now")
      else:
         delay_milliseconds(100)

   # Restore display
   LCD.clear()
   LCD.message(PLAYLIST_MSG[STATION - 1] )



def display_ipaddr():
   show_wlan0 = "ip addr show wlan0 | cut -d/ -f1 | awk '/inet/ {printf \"w%15.15s\", $2}'"
   show_eth0  = "ip addr show eth0  | cut -d/ -f1 | awk '/inet/ {printf \"e%15.15s\", $2}'"
   ipaddr = run_cmd(show_eth0)
   if ipaddr == "":
      ipaddr = run_cmd(show_wlan0)

   i = 0
   keep_looping = True
   while (keep_looping):

      # Every second, update the time display
      i += 1
      if(i % 10 == 0):
         LCD.setCursor(0,0)
         LCD.message(datetime.now().strftime('%b %d  %H:%M:%S\n'))
         LCD.message(ipaddr)
         #LCD.message('e%15.15s' % ( ipaddr ) )

      # Every 3 seconds, update ethernet or wi-fi IP address
      if(i == 60):
         ipaddr = run_cmd(show_eth0)
         i = 0
      elif(i == 30):
         ipaddr = run_cmd(show_wlan0)

      # Every 100 milliseconds, read the switches
      press = read_switches()
      if(press == 3):
         keep_looping = False
      delay_milliseconds(99)



def run_cmd(cmd):
   p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT)
   output = p.communicate()[0]
   return output



def run_cmd_nowait(cmd):
   pid = Popen(cmd, shell=True, stdout=NONE, stderr=STDOUT).pid



def mpc_play(STATION):
   pid = Popen(["/usr/bin/mpc", "play", '%d' % ( STATION )]).pid



if __name__ == '__main__':
  main()

The Radio Play List

The radio play list / channel line-up is loaded dynamically from a file called radio_playlist.sh.  This file is processed twice by the radio.py program: the first time it is executed as a shell script; and the second time it is read as a text file to load the channel display names.

Here’s the sample file I used in my video …

#! /bin/sh
mpc clear 1>/dev/null

#1 SomaFM, Lush  \nfemale vocals
mpc add http://mp1.somafm.com:8800

#2 CHFX Halifax  \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CHFX

#3 CJCB Sydney   \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CJCB

#4 CHNS Halifax  \nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CHNS

#5 CFQM Moncton  \nadult Contempora
mpc add mms://ysj00ms01s0.aliant.net/CFQM

#6 CBD  St John  \npublic radio
mpc add mms://ysj00ms01s0.aliant.net/CBD

#7 CJYC St John  \nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CJYC

#8 CFCY CharTown \ncountry
mpc add mms://ysj00ms01s0.aliant.net/CFCY

#9 CHLQ ChrlTown \nhot AC
mpc add mms://ysj00ms01s0.aliant.net/CHLQ

#10 CHTN ChrlTown\nclassic hits
mpc add mms://ysj00ms01s0.aliant.net/CHTN

#11 CJRW ChrlTown\nclassic rock
mpc add mms://ysj00ms01s0.aliant.net/CJRW

#12 CBC Halifax  \nradio 2
mpc add http://3143.live.streamtheworld.com:80/CBC_R2_HFX_H_SC

#13 CFMJ Toronto \ntalk radio
mpc add http://208.92.54.5:80/CFMJAMAAC_SC

#14 CBC Halifax  \nradio 1
mpc add http://3383.live.streamtheworld.com:80/CBC_R1_HFX_H_SC

Next Up …

Radio Pi Plate.  A Raspberry Pi Internet Radio that uses Adafruit’s RGB Negative 16×2 LCD+Keypad Kit for Raspberry Pi pi plate. (coming soon)

radiopiplate