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

125 thoughts on “Radio Pi Plate

  1. Tangoman

    This is perfect.

    I had been struggling with my own program to do exactly this with no python or programming skills – managed to get mine working but no were as functional or nice as yours. (i did use Mplayer not MPC, as I could not get bbc live radio to work with MPC)

    But I am having one problem with your script.
    LCD.readButtons – I get an error, that doesn’t exist at all in my Adafruit_CharLCDPlate .py typed to change it to LCD.buttonPressed but doesnt seem that simple.

    Are you using an old GIT version of CharLCDPlate or is there something else i am missing?

    Hope you can help – your program would get me a home run with the wife!

    Cheers

    Reply
    1. sheldon Post author

      Hi “Tangoman”

      Great catch! My apologies, I wrote this code months ago and forgot that I had to modify the Adafruit classes. I’ve added the details to the post. Good luck with your project and the “home run”!

      Reply
      1. Gary Payne

        Could you please send me the updates to the adafruit class files.

        The radio iscurrently playing on the first channel but the buttons are not working. The LCD test does indicate they are functional.

        I saw someone else whose similar problem was solved with the 3 new files.

        Thanks, Gary

        Reply
      2. Lionel Rees

        Hi Sheldon,
        Thanks for a great project.

        I have tried to duplicate your Pi Plate radio.I have been able to get
        to the point where the radio is functioning with the first station loaded
        and displayed on the LCD. My remaining problem relates to the push buttons; I can’t get them to work. Do you have a working radio.py and
        the associated Adafruit files? If you can send me a set of working files
        for the Pi Plate radio I would be grateful. I am new to python and trying
        to learn.

        Regards,
        Lionel

        Reply
  2. Tangoman

    All working perfectly – it’s great!

    I am just looking around now to see what case i can put it all into – but this saved me heaps of time on the programming side of things.

    All credit and kudos to usualpanic.

    Reply
    1. Lionel Rees

      Hi ,
      I am having problems getting my push buttons to work. Could I get
      a copy of you script? I have see other posts relating to this issue by
      still unable to solve the problem.

      Thanks

      Reply
  3. Mike

    The Halifax stations caught my eye because I work in halifax, Nova Scotia. Most of the posts are from the US. Cool.

    Reply
  4. Barry

    A great little radio project using the raspberry pi and Adafruit’s nice RGB Character lcd plate!
    A big thanks to Sheldon for his help in obtaining the original class files for this project.
    Works great! Nice Canadian Halifax Radio Stations to listen too!
    Regards Barry

    Reply
  5. Olaf

    Hello,
    I’m so sorry this nice application for my raspi v2 with the rgb-lcd-plate is not running here.

    This are the error messages:
    Traceback (most recent call last):
    File “/home/pi/radio/radio.py”, line 356, in
    main()
    File “/home/pi/radio/radio.py”, line 109, in main
    press = read_buttons()
    File “/home/pi/radio/radio.py”, line 159, in read_buttons
    buttons = LCD.readButtons()
    File “/home/pi/radio/Adafruit_CharLCDPlate.py”, line 82, in readButtons
    return self.mcp.input_all()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘mcp’

    Changes to the latest Adafruit Python classes are made
    Music is playing and station info is displayed on lcd but the buttons are not functioning.
    Hoping if there is any help possible
    Olaf

    Reply
        1. Davy

          Hi,

          I like your project, but ‘m a bit stuck with the library files, would you mind sending them to me?

          Thanks in advance.

          Reply
  6. Nerdykit

    The versions of the Adafruit classes available via Git (as of July 2) don’t work with the current version of radio.py. (The buttons won’t respond) Replacing the new Adafruit files with the three that Sheldon has used instantly resolves the issue. Ask Sheldon to send you the zip of his versions after you’ve used LCDtest.py to confirm that all buttons on your plate are working correctly. Otherwise, this is a great project. Now I have to buy another Pi to play with, this one is now my Internet Radio. Thanks, Sheldon

    Reply
  7. Joe

    Greetings,

    I built the adafruit plate /lcd and load os v2 and used Bob Rathbone pi radio software – it works perfect.
    However, I like your menu layouts better.

    I followed your tutorial but am have some problems.
    I suspect, it is because i did not put the changes in the correct location.

    Exactly where do I put the changes for:
    Adafruit_CharLCDPlate.py class
    and
    Adafruit_MCP230xx.py

    Thanks for the great article, looking forward to getting it running
    Joe

    Reply
  8. erf

    Thanks for the work on this. I’ve been looking for a project of interest to dabble in python on the RPi.
    I’m getting the following error when I try to run. I’ve reloaded everything and applied the mods as described, but I’m still getting the same error. I’m wondering if something is out-of-date.
    Thanks,
    erf

    > sudo python radio.py
    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 115, in main
    press = read_buttons()
    File “radio.py”, line 165, in read_buttons
    buttons = LCD.readButtons()
    File “/home/pi/radio2/Adafruit_CharLCDPlate.py”, line 76, in readButtons
    return self.mcp.input_all()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘mcp’
    http://mp1.somafm.com:8800
    [playing] #1/4 0:00/0:00 (0%)
    volume: 0% repeat: off random: off single: off consume: off

    Reply
    1. sheldon Post author

      Thanks Kit for the “PulseAudio” work-around. I haven’t personally experienced the crackling and popping noises when changing streams. I’m using Occidentalis v0.2.

      Reply
  9. sheldon Post author

    For those of you having problems with the latest versions of the Adafruit classes, you can work around them by replacing “LCD.readButtons()” with “LCD.buttons()” in lines 165 and 169 of radio.py

    Adafruit has rewritten the CharLCDPlate and I2C classes and done a kick-ass job of speeding things up. My modifications to these classes are no longer required!

    I’ll be updating this blog entry in the next few days with a new version of radio.py designed to work with the updated Adafruit classes.

    Sheldon

    Reply
    1. Nick

      Thanks for the info on what needs to be changed, I’m looking forward to the updated blog entry. Have you added any more features? This was my first project with the Pi. I learned how to solder and now learning python due to this project. Thanks for making it possible.

      Reply
  10. john

    Thanks very much for your detailed overview on how you got this running. I think I am close to success but I am getting the following error when kicking off with ‘sudo python radio.py’. Any suggestions?

    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 110, in main
    mpc_play(STATION)
    File “radio.py”, line 372, in mpc_play
    pid = Popen(["/usr/bin/mpc", "play", '%d' % ( STATION )]).pid
    File “/usr/lib/python2.7/subprocess.py”, line 679, in __init__
    errread, errwrite)
    File “/usr/lib/python2.7/subprocess.py”, line 1259, in _execute_child
    raise child_exception
    OSError: [Errno 2] No such file or directory

    Sorry if I’m missing an obvious issue but I am new to python so trying to figure this out.

    Reply
  11. sheldon Post author

    You’ll need the Music Player Daemon (MPD) installed on your R-pi. To install it …

    1. Update raspian/occidentalis to the latest packages
    $ sudo apt-get update

    2. Then install the mpd / mpc packages
    $ sudo apt-get install mpc mpd

    This will install the Music Player Daemon (mpd) and it’s client mpc.

    Good luck with your project!

    Reply
  12. Dave

    Hi sorry for what is probably a very simple question, but I’m very new to linux/python etc.

    How do I get the radio.py script to run on startup?

    I googled around and some people were suggesting putting things in the rc.local file but so far I haven’t been able to get it to work.

    Thanks in advance :)

    Reply
    1. sheldon Post author

      Hi Dave

      I use a startup script in the /etc/init.d directory that I registered using the update-rc.d command. I’ll send you more detail and a copy of the script in an email.

      Good luck with your project!
      Sheldon

      Reply
  13. Sander

    Great tutorial! I have only one problem from getting it to work and that is this:

    Traceback (most recent call last):
    File “./radio.py”, line 218, in
    main()
    File “./radio.py”, line 115, in main
    press = read_buttons()
    File “./radio.py”, line 157, in read_buttons
    buttons = LCD.Buttons()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘Buttons’

    Thanks

    Reply
  14. ld

    Hi!
    Great script! Please tell me, how you starting up this script with system for embedded use, w/o monitor etc.
    Thank you for help!

    Reply
    1. sheldon Post author

      Hi ld

      I created an initialization script called “radio” and placed it in the /etc/init.d directory; and then I registered it to be run at start-up and shutdown using using update-rc.d.

      Reply
  15. Joel

    Hi everyone

    I’m pretty sure i have installed every thing as i should of done. I have run LCDtest.py and Adafruit_CharLCDPlate.py and the screen and buttons all seem to be working all ok. But i get this error message when attempting to run radio.py.

    “Traceback (most recent call last):
    File “radio.py”, line 31, in
    LCD = Adafruit_CharLCDPlate(busnum = 1)
    File “/home/pi/Projects/Radio/Adafruit_CharLCDPlate.py”, line 94, in __init__
    self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
    IOError: [Errno 5] Input/output error”

    Anybody got any ideas I’m very new to all this lol

    Thanks in advance.

    Reply
  16. Sander

    Hi Sheldon,

    Can I also have the script to launch the radio.py on boot?

    I tried creating a boot file in init.d but that seems to wipe the radio_playlist.sh…

    Thanks,

    Sander

    Reply
    1. sheldon Post author

      Hi Sander

      I emailed you a copy of the “radio” script I use in /etc/init.d to run the program automatically on start-up. When I get a chance, I’ll update this post to add a section on starting the program automatically.

      Sheldon

      Reply
  17. Manuel

    hi, Sheldon

    I have these errors, I have sound and I change up /down volume but I don’t have a display and left /right give me these error can you help.

    $ sudo python radio.py
    mms://ysj00ms01s0.aliant.net/CHFX
    [playing] #1/15 0:00/0:00 (0%)
    volume:100% repeat: off random: off single: off consume: off

    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 122, in main
    LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
    IndexError: list index out of range

    regard´s
    Manuel

    Reply
    1. sheldon Post author

      Hi Manuel

      I suspect a possible problem with your radio_playlist.sh file. I emailed a ZIP archive with all of the files I used. Please try the radio_playlist.sh file from this ZIP as a replacement for yours to see if it helps. Also, remember the radio_playlist.sh file is run as a Linux shell command so you’ll need to set execute permissions.

      Sheldon

      Reply
      1. Manuel

        hi ,Sheldon

        it´s work very god ,
        and yes the problem was in the file radio_playlist.sh ,i changed by your file and now everything works 100%
        can I also have the script to launch the radio.py on boot ! ,I already tried all ways and I can not do boot .

        thanks for your great project .

        best regard´s

        Manuel

        Reply
  18. Joel

    Hi Sheldon

    Great work I’ve got my radio working a treat using your code.

    I was just wondering if there was any way of adding some code in so that the station info (i.e. what song is playing) can be scrolling on the second line of the LCD screen. I do not have a clue where to start or if its even possible.

    Cheers
    Joel

    Reply
  19. Tony Hopkins

    Hello Sheldon,
    Sorry, another nooby here, I also cannot get the radio working, I have made the changes that you have posted to the Adafruit_*.pi files but still no luck.

    Here are the error reports:
    Traceback (most recent call last):
    File “radio.py”, line 292, in
    main()
    File “radio.py”, line 92, in main
    LCD.message(PLAYLIST_MSG[STATION - 1] )
    IndexError: list index out of range

    Traceback (most recent call last):
    File “LCDtest.py”, line 4, in
    from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate
    File “/home/pi/projects/radio/Adafruit_CharLCDPlate.py”, line 11, in
    from Adafruit_I2C import Adafruit_I2C
    ImportError: No module named Adafruit_I2C

    This is my first R-Pi project and don’t know if it’s something I am doing wrong.

    Can you help?
    Thanks

    Best regards

    Tony

    Reply
  20. John Matthews

    Great Project. Newbie at this (still learning at 61!).Have got LCD display and buttons all working OK. However no music output and following error message when selecting a station:- “song number greater than playlist length”
    Any help you can give me would be much appreciated.
    Best Regards
    John

    Reply
  21. Tony Hopkins

    Hello Sheldon,

    Thanks for the files, everything ‘appears’ to be working but I have the same problem as John Matthews, No Sound!

    Any ideas?

    Best regards

    Tony

    Reply
    1. sheldon Post author

      Hi Tony

      If you’re seeing the “song number greater than playlist length” error check to make sure you have execute permissions on your playlist file. A “chmod 775 radio_playlist.sh” should do it.

      Best regards,
      Sheldon

      Reply
  22. Hans Ringdahl

    Hi!
    Like this project, but get this result.

    Traceback (most recent call last):
    File “radio.py”, line 17, in
    from Adafruit_I2C import Adafruit_I2C
    ImportError: No module named Adafruit_I2C

    I have Occidentalis as OS, I’ve cloned the library files needed from Adafruit.
    When looking in the map Adafruit-Raspberry-Pi-Python-Code, everything supposted to be there is there…

    What have I done wrong, please help me!?

    //Hans

    Reply
    1. sheldon Post author

      Hi Hans

      Please make sure your Adafruit_CharLCDPlate.py, Adafruit_I2C.py and Adafruit_MCP230xx.py files are in the same directory as your radio.py file.

      Good luck,
      Sheldon

      Reply
  23. Mark Thomas

    I have the same problem with “song number greater than playlist” and I changed permissions to 775. I even tried 777 and I get the same error. Thanks for the help.

    Mark

    Reply
    1. sheldon Post author

      Hi Mark

      Try running “sudo radio_playlist.sh” as a Linux terminal command to see if the shell script will run okay by itself. I sent you an email with a zip archive containing the files I used for comparison to your playlist.

      Good luck,
      Sheldon

      Reply
      1. John Edwards

        I found that I had to change the absolute reference to the location of radio_playlist.sh on lines 133 and 136 of radio.py from
        “/home/pi/projects/radio/radio_playlist.sh” to its location on my system to make the “song number greater than playlist length” error go away.

        Thanks! Great project!

        (Now if only I could be sure that I really soldered the pi plate together properly… display is a little flaky — I am afraid to push any buttons for fear of disturbing it….)

        Reply
  24. Andy

    Hi sheldon

    Great job! The script is working like a charm.
    Could you please send me the startup script to run it automaticly?

    And an other question.
    Is it possible to use the second line of the display to show the titel information?
    And if the answer is yes how do i make it working?

    kind regards

    Andy

    Reply
    1. sheldon Post author

      Thanks, Andy

      I emailed a copy of my start-up script to you.
      A second line display of title information is on my list for a version 2. :-)

      Best regards,
      Sheldon

      Reply
  25. Matt

    Hi Sheldon,

    Great script! I’m new to python but this has inspired me to delve in & get learning!

    I’ve added a slight mod in that I’ve got Airpi (http://trouch.com/2012/08/03/airpi-airplay-audio-with-raspberry/) running on my pi as well.

    I’ve added a new function:

    —————————-
    # CHECK FOR AIRPLAY
    # —————————-

    def check_airplay():
    netstat = subprocess.Popen(“sudo /bin/netstat -t”.split(), stdout=subprocess.PIPE).stdout
    grep = subprocess.Popen(["/bin/grep", "rfe"], stdin=netstat, stdout=subprocess.PIPE).stdout
    grepout = grep.read().split(“\n”)
    vals = map(string.strip, grepout[0].split())
    if ‘raspberrypi.local:rfe’ in vals:
    # Start Airplay mode
    LCD.clear()
    LCD.backlight(LCD.GREEN)
    LCD_QUEUE.put(‘Airplay Mode \n Airpi ‘, True)
    LCD_QUEUE.join()
    output = run_cmd(“mpc pause -q”)
    else:
    output = run_cmd(“mpc play -q”)

    That actively checks for airplay connections and pauses mpc if one is detected… the else needs some work with intelligence around if mpc is actually already playing…..

    I’ve added this at the start of the main loop straight after read_buttons… and I’ve got unresponsive buttons, similiar to what you had before adafruit re-worked their code.

    Trying to optimise this but enjoying the challenge.

    Thanks.

    Reply
  26. Nick

    Love this I have been using a RPI as a internet radio using ssh or ampache to listen to radio for a while this is a great improvement in that I do not need another device to control it. Only thing I would change and my do some research on doing is getting the stream data to post up on an update from mpd through an mpc command or “watch -n 1 mpc” which I use in ssh to see whats playing on the streams that update for each song. I do not know python but it looks like its approachable with the very basic programming knowledge that I have.
    which gives you this for SOG:

    South of Gilman St.: Dead Kennedys – I Fought The Law
    [playing] #4/14 33:12/0:00 (0%)
    volume: 92% repeat: on random: off single: off consume: off

    Nice work love it Thank You for sharing this!

    Nick

    Reply
  27. Stephen Vowles

    Have got it scrolling through the menus, but am having the same problem of “song number greater than playlist length” error and no audio. Have tried to chmod the radio_playlist.sh file without any joy.

    So close, but yet so far. : )

    Reply
  28. Stephen Vowles

    Thanks Sheldon
    Cheers for the email and files along with the tips.
    I think I was able to get it going by cleaning up the radio_playlist.sh. I think I must have picked up
    and added some invisibles. I ran the script through a text cleaner as your script would run and mine wouldn’t.
    Anyway after scrubbing my playlist to Unix format all is beautiful.

    You are real gent.
    Thanks again.

    Reply
  29. Nick

    This gives you “mpc current” stream data where avalable with a quick double click of the select button, on menu item 1. with mute and volume

    <
    # —————————-
    def menu_pressed():
    global STATION

    MENU_LIST = [
    '1. Stream Data \n mpc current ',
    '2. Output Audio \n to HDMI ',
    '3. Output Audio \n to Headphones',
    '4. Auto Select \n Audio Output ',
    '5. Reload \n Playlist File',
    '6. Display Time \n & IP Address ',
    '7. System \n Shutdown! ',
    '8. 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 = 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. mpc_data
    mpc_current()
    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. display time and IP address
    display_ipaddr()
    elif(item == 6):
    # 7. 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.RED)
    LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)

    # —————————-
    # ———————————–
    # mpc data
    # ———————————–
    # read data from comand “mpc current”
    # must add “import subprocess”
    # ———————————–

    def mpc_station():
    mpc_data = subprocess.check_output(['mpc', 'current'])
    station = mpc_data[:mpc_data.rfind(":")]
    print “station – ” + station
    return station;

    def mpc_song():
    mpc_data = subprocess.check_output(['mpc', 'current'])
    mpc_data2 = mpc_data.rfind(“-”)
    mpc_data3 = mpc_data.rfind(“\n”)
    song = mpc_data[mpc_data2:mpc_data3]
    print “song – ” + song
    return song;

    def mpc_artist():
    mpc_data = subprocess.check_output(['mpc', 'current'])
    mpc_data1 = mpc_data.rfind(“:”) + 2
    mpc_data2 = mpc_data.rfind(“-”)
    artist = mpc_data[mpc_data1:mpc_data2]
    print “artist – ” + artist
    return artist;

    # ———————————–

    # ———————————–
    # Display mpc current
    # ———————————–
    def mpc_current():
    LCD.backlight(LCD.RED)
    i = 29
    muting = False
    keep_looping = True
    while (keep_looping):

    i += 1
    # # Every 3 seconds, update music data
    if(i == 60):
    LCD.clear()
    LCD_QUEUE.put(mpc_station() + “\n” + mpc_artist())
    i = 0
    elif(i == 30):
    song = mpc_song()
    LCD.clear()
    LCD_QUEUE.put(song[2:18] + “\n” + song[18:34])

    # 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)

    # —————————-

    >

    Reply
    1. Simon

      Hi Nick,
      thank you very much for your work. Would it be possibly that you paste your code on pastebin or so? Line intents :)
      Additionally big thx to you Sheldon for sharing your code!
      simon

      Reply
    2. Jacky Niewiadomsky

      Hello,

      Would it be possible to have this code with good indentation python.
      I suck at programming and I’m not really able to properly reformat the code.

      In advance thank you.

      Best regards
      Jacky

      Reply
  30. Nick

    debug print not commented out in mpc data section it will output to console.

    print “station – ” + station
    print “song – ” + song
    print “artist – ” + artist

    Reply
  31. Jim Farrall

    got as far as this error:

    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘mcp’

    believe for reading comments need differant versions of files from GitHub

    could you please email the file required

    thanks for your work ans assistants

    Jim

    Reply
    1. Simon

      Hi Jim,
      already read this comment by Sheldon:

      For those of you having problems with the latest versions of the Adafruit classes, you can work around them by replacing “LCD.readButtons()” with “LCD.buttons()” in lines 165 and 169 of radio.py

      Adafruit has rewritten the CharLCDPlate and I2C classes and done a kick-ass job of speeding things up. My modifications to these classes are no longer required!

      Check that you are using the latest libraries from Adafruit…
      cheers,
      Simon

      Reply
    1. sheldon Post author

      Great Gary!
      Glad you found the comment on the change to LCD.buttons!

      I have some plans for an improved version 2.0 but work commitments are keeping me away from the fun stuff at the moment.

      All the best,
      Sheldon

      Reply
  32. Tomas

    Hello,
    how to create a menu item to change the color lcd?
    Menu – LCD color change (up and down button – change colors, select – save color)
    You write code?
    Thank you.

    Reply
  33. lepeteiclement

    Hey Sheldon,
    Unfortunately it seems that the new Adafruit Python Classes are not working for me…
    Could you please upload the classes you are using?

    Keep on the great work!

    Reply
  34. lepetitclement

    Hey there, thanks for your great work!
    Unfortunately it seems, that the new python classes from Adafruit aren’t working for me. Radio is starting and playing, but the buttons don’t work and the LCD is only showing the first radio station.
    Could you please upload the python classes you are using?

    Thanks in advance!

    Reply
    1. lepetitclement

      Nevermind! I found the fix in a comment of yours:
      “by replacing “LCD.readButtons()” with “LCD.buttons()”
      that was the fix! Thanks!

      Reply
  35. Dave

    Sheldon, I’m having the same issues as some others with the ‘song number greater than playlist length’. Tried creating a new file, changing permissions and still not working. The rest of the radio.py and the LCD all work, and the titles under # in the playlist all work on the display and can be scrolled through. Any help or tips are appreciated, it’s a great project and I have it ‘almost’ working.

    Reply
  36. Dave

    Hi all
    First thing is to say thanks for all the comments, as it has solved sevral of my faults getting the wifi radio up and running..
    Just to give an update on my headaches getting it working,
    I started the radio project with the Pi Plate at the begining of december, and tried so many things to get it working, I could run the test program fine, but not the radio… nothing… just errors..
    A big thanks to you lot and sevral errors later including the buttons error, it now works as far as the buttons and the display works fine.
    But I get no sound…. (i have tried mpc add, and play, this works fine)
    Now for the strange bit…
    If i remove the hashbang ( #! /bin/sh ) at the begining of the playlist, it plays the first 3 tracks on the play list in any order, but not any more, unless I duplicate an entry at the end, and this one plays as well..??? (I didn’t think it would read the list correctly with out the hashbang …?? desperation….) I did set the file permissions correctly.
    If I keep the hashbang I get the error “song number greater than playlist length.” (and no music)
    Any ideas ……

    Another thing I did wrong, was to de-crackle the audio by installing pulse audio, this worked ok but made it slugish, (this could have been due to the so many things I installed trying to get it to work)

    I then read an update to reanable alsa and disable pulse audio and update the files and op system. (big mistake)
    This took ages and then locked up…. on a reboot it had killed the boot elf.. a quick copy over from a backup and it was working.. sort of.
    with lotts of errors.
    So A wipe and reinstall, a quick reconfig and i’m back up and running in just over an hour, (pew)
    I still have the same problems outlined earlyer.. (help)..

    Any ideas, as this is driving me mad…
    Cheers….

    Reply
  37. Dave

    An update, I’ve had a play with the play list,
    If I try to run it as a script as sugested earlyer I get the error “sudo: radio_playlist.sh : command not found”, (ls shows the file)

    This is my play list, (partly modified from thr original)

    #! /bin/sh

    #1 Absolute 80′s \n 80′s Music
    mpc add http://stream.timlradio.co.uk/ABSOLUTE80SIRMP3?ats=1

    #2 Absolute 70′s \n 70′s Music
    mpc add http://stream.timlradio.co.uk/ABSOLUTE70SIRMP3?ats=1

    #3 Absolute 90′s \n 90′s Music
    mpc add http://stream.timlradio.co.uk/ABSOLUTE90SIRMP3?ats=1

    #4 Gem 106 \n Varied Music
    mpc add http://stream2.radiomonitor.com:80/Gem-128

    #5 Heart 100.7 \n Varied Music
    mpc add http://ice-sov.musicradio.com:80/HeartPeterboroughMP3

    #6 SomaFM, Lush \n female vocals
    mpc add http://mp1.somafm.com:8800

    #7 SOMA FM lush \n chillout
    mpc add http://sfstream1.somafm.co.:8800

    #8 KIIS-FM \n 102.7 Kiss FM
    mpc add http://kiis-fm.akacast.akamaistream.net/7/572/1977

    Reply
  38. Dave

    Hi all
    just to update readers,
    Thanks to sheldon for his help I have got it up and running,
    It turned out I had compiled the file in dos so had the wrong file extention type,
    Once I had used the correct format it and worked fine..

    Cheers and thanks to all…

    Reply
  39. Aidan

    Sheldon,
    Many thanks for the code, all works great…………….Kinda.

    Is it possible to get a copy of your startup script please?

    At the moment if I start through putty everything works fine, all buttons work, but if I close my putty session the radio keeps playing but the buttons no longer work. Have I missed something?

    Aidan

    Reply
    1. sheldon Post author

      Hi Aidan

      The thing you’re missing is an & at the end of your command line in putty. This will run the command as a background task that keeps running even after your putty session is closed.

      I emailed a copy of the “radio” startup script I wrote and added to /etc/init.d to automatically start the radio program on startup.

      Good luck with your project!
      Sheldon

      Reply
  40. Nick

    To start at boot this worked for me:
    sudo crontab -e

    Add at the bottom of the file or the location of your radio.py:
    @reboot python /home/pi/projects/radio/radio.py &

    Reply
  41. Giorgio

    Many compliments, it work perfectly. Please add to main page the “by replacing “LCD.readButtons()” with “LCD.buttons()”” mod.

    I’ve some suggestions for V2:

    -Add display of the song title on LCD (very important and useful for me!)
    -Add change color option
    -Add Sleep Timer, before shutdown the raspberry, for example 10,20,30,60,90 minutes

    Thanks in advance
    kind regards
    Giorgio

    Reply
  42. Nick

    Here is a working example of MPC scrolling text not mine but could be good to look at in implementing it for those interested. If I have time to play I will be looking at it.

    Reply
  43. Rajarajan Rajamani

    Great post. I think the usage of threads/queues to delink the display from the reading of the buttons was a great enhancement over similar projects (I was using Adafruit’s Pandora radio – but am now sold on this).
    The issues I faced were
    1) The Adafruit_CharLCDPlate changes didn’t work. Then I read the posts below the main article and was able to fix it. Could I request that a note to that be put in the main article
    2) The default raspbian install did not seem to have mpd/mpc which I fixed by an apt-get install. The article assumes an Occidentalis distrib.

    The next thing for me is to box it up and use a single 5 way button like http://www.adafruit.com/products/504

    Reply
    1. sheldon Post author

      Thanks, Rajaramani.

      I haven’t had much time to spend on this – but I do have some big plans for a V2.0. :-)
      I’m building a vintage radio project around a 1940 Addison wooden tube radio using a combination of a RPi and an Arduino. The Arduino will drive an OLED display and include a rotary encoder for channel selection along with a micro-servo to move the old radio dial (just for fun). I’ve worked out the interrupt-driven encoder code and have some of the hardware built on a prototype board. … Figuring out where to cut the wooden case to add the OLED display will be next.

      Sheldon

      Reply
  44. William Birnie

    Hi Sheldon

    Thanks for sharing your efforts with us. I have done everything except playing with class files and i’m still getting a
    station on the lcd and buttons not working. Could you please send me the class files so I can give that a try.

    Thanks for your time and consideration.
    Bill

    Reply
  45. Rajarajan Rajamani

    I’d love to see your observations on V2.0 once you finish. I’ll just be making minor mods to your code to make the following changes
    (a) Have some config (or ini) file which keeps track of the volume / station # across a device reboot. Otherwise it always seems to come up at full volume on station 1.
    (b) Add some optional extras like occasionally scrolling the current time on the screen, give a temp display etc.

    Reply
  46. Dave

    Hi all,
    Has anyone had a problem with the play list being jumbled / ignoring / or playing wrong station being displayed.
    But ONLY when you reach channel 20 and plus stations.
    All other work fine.

    And another one,,,, slow password prompt when connecting using ssh, once connected its quick as before.

    Any ideas…

    Apart from these slight bugs I find the radio fantastic… I’ve even built a second one…
    I recommend persevering on any problems as its well worth it…..

    Reply
    1. Dave

      An update to my problems,
      The play list not displaying or reading correctly was due to a typo (a , instead of a .) by me very early on in the listing, but didn’t show up till I had more than 20 entries… strange… But sorted..

      Still having a 30 to 40 second delay from user name to pass word prompt when logging on to via http://ftp..

      Many thanks as always to sheldon for all his help,
      This project is now my main radio i tend to listen to, and I will be building a second..
      Best use of a raspberry pi to date…

      Reply
  47. Karl

    Hi Sheldon!

    Great stuff if i get it up and running! :-(

    After a a while “try and fault” i´m at the end of my python knowing.
    I cant resolve this error:

    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 115, in main
    press = read_buttons()
    File “radio.py”, line 165, in read_buttons
    buttons = LCD.readbuttons()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘readbuttons’
    song number greater than playlist length.
    volume: 0% repeat: off random: off single: off consume: off

    And, if you can solve my problen, could you send me your startup schript?

    With greetings from lower Austria and many thanks!

    Karl

    Reply
    1. sheldon Post author

      For those of you having problems with the latest versions of the Adafruit classes, you can work around them by replacing “LCD.readButtons()” with “LCD.buttons()” in lines 165 and 169 of radio.py

      Adafruit has rewritten the CharLCDPlate and I2C classes and done a kick-ass job of speeding things up. My modifications to these classes are no longer required!

      Karl, I’ve emailed a copy of my files to you.

      Good luck with your project!
      Sheldon

      Reply
  48. Ray

    Hi I just found your page and bought the pi 512 and the LCD screen, i managed to make it display the left right up down slect etc..
    However i could not download your file and install it… it says could not find file radio.py what i am doing wrong.
    Please can you help me i am a newbie in linux and pi.

    Thank you for your help.

    Ray

    Reply
    1. sheldon Post author

      Hi Ray

      I tried to email a copy of my files to you, but your email bounced. Please contact me directly through the contact form and I’ll send you the files you need.

      Good luck with your project!
      Sheldon

      Reply
  49. Mike

    I love this project, and I was very pleased to see it respond for the first time. I did, however, run into an error like one of your previous commenters. I have pasted the message I get below:

    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 115, in main
    press = read_buttons()
    File “radio.py”, line 165, in read_buttons
    buttons = LCD.readButtons()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘readButtons’

    What do I need to do to fix this?

    Thank you,
    Mike

    Reply
    1. sheldon Post author

      For those of you having problems with the latest versions of the Adafruit classes, you can work around them by replacing “LCD.readButtons()” with “LCD.buttons()” in lines 165 and 169 of radio.py

      Adafruit has rewritten the CharLCDPlate and I2C classes and done a kick-ass job of speeding things up. My modifications to these classes are no longer required!

      Mike, good luck with your project!

      Sheldon

      Reply
  50. NIck

    Very cool Sheldon, i do furniture restoration for a living and we get some interesting radios come through at times. I have played with my setup a little and added IrRemote and sleep timer function through the remote. I have not looked at keeping your playlist in line with MPC at all though. Remote just passes commands to MPC but in all reality it could pass the commands to the PiRadio to make a neater package. I am also using cron for my morning wake-up.

    FYI: thanks for your work on this it is used daily and fully enjoyed.

    Nick

    Reply
  51. John CUENI

    Amazing job man, your scripts have traveled to France where I’ve successfully made it work almost out from the box, thanks to all the replyz !!
    I’ve witnessed a weird thing though, the display keep some letters @ the end of the upper line …. is there a way to refresh the display ?
    Thanks again for your great share !
    John

    Reply
    1. sheldon Post author

      Hi John

      In your radio_playlist.sh file, pad out the first line with spaces to 16 characters. For example:

      #1 CBC Halifax \nradio 2

      and not
      #1 CBC Halifax\nradio 2

      Sheldon

      Reply
  52. John CUENI

    Howdy,
    Also, I’ve witnessed something else, the shutdown now command does not shut down the system actually it fails to do so … but with “init 0″ it works. ;o)

    Reply
  53. Dave

    Hi Sheldon
    Many thanks for all your help with the internet radio.
    It has become one of my main audio units I listen to to pass the time.
    I have been looking at your forum posts and came across this, http://didtheypush.blogspot.nl/2013/10/raspyfi-and-adafruit-16×2-char-lcd-plate.html

    It utilises a scrolling text display from the station, which looks and is very useful as quite a few stations don’t ID or state what’s playing, as I have now got over 40 to go at.
    What I would like to know is do you think it would be possible to just import just the scrolling info line to the second display line of your program , as I like your program layout and ease of use and don’t want to change that.
    Ideas ???

    Cheers
    Dave…

    Reply
    1. sheldon Post author

      Hi Dave,

      Thanks for the link! I’ll have a look. Scrolling text display of current content on line 2 is one of the things I have planned for version 2.0.

      Sheldon

      Reply
      1. Dave

        Much appreciated,,,, I will look forward to the updated program,
        It will put the cherry on the top of an already brilliant use of a raspberry pi.

        Cheers
        Dave…

        Reply
  54. Dave

    Hi Sheldon

    Wonder if you or anyone knows of a way to set the wifi user name and password (or have more than one set) without using a pc,
    As I would like to take my wifi radio to and from my works, and just need to be able to plug in and go.

    Cheers
    Dave…

    Reply
  55. glenn lake

    Hi Sheldon,

    I really like what you done here but I need your help with how to set this up. I have no experience with python but a little with linux and the command line. I have set up the Adafruit and it works OK but I don’t want to be tide to the pandora web site. What I need to know is just where do I put the python scripts (what folder) to make it work? Sorry if you have covered this already. I just found your site and haven’t gone read all the comments yet.

    Glenn

    Reply
  56. Dave

    Hi Sheldon

    Just something I noticed, and may be of help.
    I finally got around to getting a HDMI to VGA adapter, and watching the radio working live , not via ftp.
    Upon booting I noticed afsck warning, telling me it hadn’t been shut down properly,
    I also noticed after shutting the radio down I could still type and command directly.
    So to be on the safe side I added the -H switch to line 278 …….. output = run_cmd(“sudo shutdown now -h”)
    This seems to shut down better and also flash the green led 10 times,
    Which I found to be a nice indicator it was shut down and safe to pull the power.
    As I have found the little radio to be so useful I wouldn’t want to corrupt the sd card. (better safe than sorry)

    Cheers
    Dave…

    Reply
  57. Harold

    Hi Sheldon,

    Just like some other fans of your project I would like to have the LCD showing some extra info.
    I was searching around and at the raspberry forum i found some info about python-mpd2. I have installed the library and it works great, any easy way to get the info.

    http://www.raspberrypi.org/forums/viewtopic.php?f=38&t=48359

    By the way, your project is mentioned in the first post!

    Cheers
    Harold

    Reply
  58. Claude

    Hi Sheldon

    So close but still no cigar! I get the welcome message on my home brewed LCD Plate but I still keep getting this message:
    L:CD_QUEUE .put(PLAYLIST_MSG(STATION – 1) TRUE)
    Line 108.

    I read it was part of the playlist but after trying everything I copied yours and renamed & chmod’d it so the file turned from grey to green and in the right directory. But that is as far as thing go.

    Sure would appreciate some help.

    Thank you very much. I can do pretty good with a soldering iron but python… not so much

    Thanks for all the work it took to get it all on the net.

    Claude

    Reply
    1. sheldon Post author

      Hi Claude

      I suspect you may have SFTP’ed the radio_playlist.sh file from Windows to Linux as a “text” file and not a “binary” file. The radio_playlist.sh file must have UNIX line terminators and not Windows/DOS line terminators (LF and not CRLF). Try running radio_playlist.sh from a terminal command line to see if it runs without errors.

      Hope this helps. Good luck with your project,
      Sheldon

      Reply
  59. Ben

    Hi.
    Nice guide. I had to try it.
    Everything went fine untill I hit ENTER after typing ‘sudo python radio.py’.
    I am no Python guru so what do I do?
    The message I got is as follows:

    Traceback (most recent call last):
    File “radio.py”, line 377, in
    main()
    File “radio.py”, line 115, in main
    press = read_buttons()
    File “radio.py”, line 165, in read_buttons
    buttons = LCD.readButtons()
    AttributeError: Adafruit_CharLCDPlate instance has no attribute ‘readButtons’
    http://7279.live.streamtheworld.com:80/KKWFFMAAC_SC
    [playing] #1/15 0:00/0:00 (0%)
    volume:100% repeat: off random: off single: off consume: off

    It looks to me that the problem is in radio_playlist.sh….

    Reply
    1. sheldon Post author

      Hi Ben

      This has been answered a number of times in the comments. Try searching the comments for “readButtons” …

      “For those of you having problems with the latest versions of the Adafruit classes, you can work around them by replacing “LCD.readButtons()” with “LCD.buttons()” in lines 165 and 169 of radio.py Adafruit has rewritten the CharLCDPlate and I2C classes and done a kick-ass job of speeding things up. My modifications to these classes are no longer required!”

      Good luck with your project, Ben!

      Sheldon

      Reply
  60. Dave

    Hi All
    Has anyone tried to scroll the second line with the station info,
    As I have followed several links and all I seem to do no matter what I try is kill the radio program,
    I have also been trying to mod the pi plate files so I can use a rotary encoder as the channel selector to no avail.
    (as I have now got over 60 channels and two Pi radios)

    Electronics and soldering I can do in my sleep, but python is twisting my melons….
    Can anyone throw me a line and point me in the right direction..

    Cheers,
    And a big thanks to Sheldon for the best use of a Pi yet.

    Reply
  61. Ben

    Hi, love your work. I have one of these set up on my work bench. Good stuff.

    I have a feature request for version 2. it would be great to have ‘long press’ buttons. I’m working on it at the moment with no success. Below is what I am aiming for.

    * Select Button *
    If playing = Stop
    If Stopped = Play
    Long Press = Menu

    * Left Button *
    Short Press = Previous Track
    Long Press = Show IP address for 5 Seconds

    * Right Button *
    Short Press = Next Track
    Long Press = Reload Playlist

    * Up Button *
    Short Press = Volume Up
    Long Press =

    * Down Button *
    Short Press = Volume Down
    Long Press = Shutdown

    Reply
  62. Ben

    Hi, I’ve had a crack at modifying you work. I don’t use the volume control, as I hook up to a stereo, so I’ve swapped them for a play and stop button. You can get rid of the references to Adafruit_I2C and Adafruit_MCP230xx. You don’t use them.

    Also, I don’t think you licence notice is valid. Without the copyright notice and the actual text of the BSD licence it’s not valid. But I think that is a good thing. Even an open source licence imposes restrictions, and for something like this, that is a learning tool, I think it’s better to let people do whatever they want with it, much like the work that you reference at the top of your script. The community is strong enough that the information will get shared, and a licence just scares people off.

    Anyway, here is my version of your code.
    #!/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_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 No argument needed for autodetect
    # use busnum = 0 for raspi version 1 (256MB)
    # and busnum = 1 for raspi version 2 (512MB)
    LCD = Adafruit_CharLCDPlate()

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

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

    # Buttons
    NONE = 0×00
    SELECT = 0×01
    RIGHT = 0×02
    DOWN = 0×04
    UP = 0×08
    LEFT = 0×10
    UP_AND_DOWN = 0x0C
    LEFT_AND_RIGHT = 0×12

    # —————————-
    # 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” )
    LCD.backlight(LCD.RED) #Set the backlight to red, as we will used red for stop

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

    # 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, and flash the display a few colors
    LCD_QUEUE.put(‘ Raspberry Pi \n Internet Radio ‘, True)
    LCD.backlight(LCD.RED)
    sleep(0.5)
    LCD.backlight(LCD.VIOLET)
    sleep(0.5)
    LCD.backlight(LCD.BLUE)
    sleep(0.5)
    LCD.backlight(LCD.TEAL)
    sleep(0.5)
    LCD.backlight(LCD.GREEN)
    sleep(0.5)

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

    # —————————-
    # START THE MUSIC!
    # —————————-
    # Start music player
    LCD_QUEUE.put(PLAYLIST_MSG[STATION - 1], True)
    run_cmd(“mpc volume +100″) # sets the volume to maximum. This is good if you want to output to a line in, but might be a bit loud if you are going to use headphones.
    mpc_play(STATION)
    countdown_to_play = 0

    # Main loop
    while True:
    press = read_buttons()

    # LEFT button pressed
    if(press == LEFT):
    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):
    mpc_play(STATION) #start playing the music again

    # DOWN button pressed
    if(press == DOWN):
    output = run_cmd(“mpc stop”) #stop the music
    LCD.backlight(LCD.RED) # and make the display red

    # 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.buttons()
    # Debounce push buttons
    if(buttons != 0):
    while(LCD.buttons() != 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.BLUE) #set the backlight to blue for the menu
    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 = 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)

    # Need to add some code that lets us remember if MPC was playing or not, and choose the colour accordingly until then, just make it white
    LCD.backlight(LCD.WHITE)
    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.BLUE)
    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):
    keep_looping = False
    #output = run_cmd(“mpc volume +2″)

    # DOWN button pressed
    if(press == DOWN):
    keep_looping = False
    #output = run_cmd(“mpc volume -2″)

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

    # DOWN button pressed
    if(press == LEFT):
    keep_looping = False
    #output = run_cmd(“mpc volume -2″)

    # SELECT button = exit
    if(press == RIGHT):
    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 mpc_play(STATION):
    pid = Popen(["/usr/bin/mpc", "play", '%d' % ( STATION )]).pid
    LCD.backlight(LCD.GREEN) # if we are playing music, make the screen green

    if __name__ == ‘__main__’:
    main()
    # End

    Reply
  63. Jacky Niewiadomsky

    Hi sheldon

    I made ​​the change line 165 and 168 (not 169), so the program now displays correctly.
    But I have a problem with audio, no sound on the “headphone” output even if I validate this output in the “Menu”.
    Buttons “up” and “down” does not display the volume. The buttons work properly because they act in the “Menu”.

    If I test the command “mpc play” outside of your script, the sound works correctly.

    Thank you for your advice.

    Best regards
    Jacky

    Reply
  64. Jacky Niewiadomsky

    Hi Sheldon,

    I think my problem is no sound because my “radio_playlist.sh” file does not have proper permissions “775″
    For the “volume” I realized that when you press the “up” button and “down” there is no display on the display.
    I would like to test the code “song info, artist” who is on this link:
    http://didtheypush.blogspot.nl/2013/10/raspyfi-and-adafruit-16 × 2-char-lcd-plate.html

    Could you send me your startup script? If possible.

    Your work is excellent, and I hope you find the time to change your script to display scrooling current title and artist name.

    Again thank you and congratulations.

    Best regards.
    Jacky

    Reply
    1. sheldon Post author

      The “radio_playlist.sh” file is executed by the program as a shell script. It must be a properly formatted shell script (with LF UNIX line terminators and not CRLF Windows line terminators) and you must have execute permissions. A “chmod 755″ usually does the trick.

      I emailed you the Startup script.

      Good luck with your project,
      Sheldon

      Reply
  65. Polonium73

    sorry for my bad language.

    if you are the problem:
    song number greater than playlist length

    Just add two line, line 203:
    else:
    run_cmd(line)

    after:
    # Remaining comment lines are loaded
    for line in playlist:
    if line[0] == “#” :

    There is no song in your list of song of mpc.

    a++
    thank’s Sheldon, very good job

    Reply
    1. sheldon Post author

      Thanks, Polonium73

      The “song number greater than playlist length” error is most often caused by problems with the “radio_playlist.sh” file. This file is executed by the program as a shell script and it must have proper Linux/UNIX line terminators and execute permissions. A “chmod 755″ usually does the trick.

      Reply
  66. Andrej

    Hi Sheldon,

    would you be so kind and send me start-up skript please? I tried solve it, but without success. Radio scipt working great.

    Thank you.
    Andrej

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>