tisdag 8 oktober 2013

RaspyFi and Adafruit 16x2 Char LCD Plate

I just found out about the RaspyFi distribution for Raspberry Pi. From RaspyFi homepage:
RaspyFi is an open source linux distribution. It will transform your Raspberry Pi into anaudiophile source, in 10 minutes without hassles. It comes ready to play, and it’s compatible with almost every USB DAC available.
This was perfect for me because I had for some time been thinking of building some kind of "music station" that I could use together with my NuForce uDAC 2 and my Sennheiser HD598. I was thinking about using an old laptop but an Raspberry Pi is even better. Small, quiet, power efficient and just very cool.

After writing the image to an SD-card I was playing music within minutes and it worked great! I controlled the music from a web-browser and my Android phone which was ok but it made me start thinking about building some control functionality into the device itself. Then I realized was that I had the solution in one my electronics storage boxes! I had a Adafruit Blue&White 16x2 LCD+Keypad Kit for Raspberry Pi that I didn't had time to put together earlier.

After some soldering I had a nice display with five buttons on top of the Raspberry Pi. The soldering and software installation was very easy thanks to Adafruits excellent user-guide that can be found here.

The last piece of the puzzle was to display song information on the 16x2 LCD display and use the keypad to control the playback. First I was inspired by this thread in the RaspyFi forums, they used lcdproc and mpdlcd but I didn't find drivers for the Adafruit LCD and I didn't feel like writing my own drivers (maybe its not that hard?).

Instead of using something "ready" I decided to make my own "driver" based on a simple python script using Adafruits python library for communicating with the display and the command line program mpc for communicating with mpd.

The software features:
  • Presentation of play mode (playing/paused/stopped), Artist and Song name scrolling on line 1
  • The elapsed song time, total song time and percentage of song played on line 2.
  • Left button: Skip back
  • Right button: Skip forward
  • Up button: Play
  • Down button: Stop
  • Select button: Toggle Play/Pause


Here is the code (showSongInfo.py):

#!/usr/bin/python
# Todo
# * display brightness timout, i.e. turn off display when stopped or paused for a while
#
import subprocess
import textwrap
from time import sleep
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate

class TextScroller:
	'Class for scrolling text'
	text = ''
	position = 0
	textLength = 0

	def __init__(self, initialText, textLength):
		self.text = initialText
		self.textLength = textLength

	def scroll(self):
		doubleText = self.text + '     ' + self.text
		scrolledText = doubleText[self.position:len(doubleText)]		
		self.position = self.position + 1
		
		# We add five extra spaces between each complete text scroll.
		if self.position > len(self.text) + 4 :
			self.position = 0		

		return scrolledText

	def setNewText(self, newText):
		self.text = newText
		self.position = 0

# Initialize the LCD plate.  Should auto-detect correct I2C bus.  If not,
# pass '0' for early 256 MB Model B boards or '1' for all later versions
lcd = Adafruit_CharLCDPlate()
lcd.begin(16,2)

# Clear display and turn on backlight
lcd.clear()
lcd.backlight(lcd.ON)

# Poll buttons
btn = (lcd.LEFT, lcd.UP, lcd.DOWN, lcd.RIGHT, lcd.SELECT)

lastArtistSong = ''
scroller = TextScroller('', 16)

while True:
	# Get current status and playtime
	process = subprocess.Popen('mpc', shell=True, stdout=subprocess.PIPE)
	status = process.communicate()[0]
	statusLines = status.split('\n')
	
	# Check if mpc returns more that one line plus an extra, in that case we dont have stopped the music and can parse additional information
	if len(statusLines) > 2:		
		# Extract the songName (first line)
		songName = statusLines[0]

		# Extract play status
		playStatus = statusLines[1].split(' ',1)[0].strip()

		# Extract a string like '2:01/3:43 (54%)' from the string '[playing] #2/13   2:01/3:43 (54%)'
		time = statusLines[1].split('   ',1)[1].strip()
	else:
		songName = ''
		playStatus = '[stopped]'
		time = '0:00/0:00 (0%)'

	lcd.setCursor(0,0)
	# Without scolling of text
	#lcd.message((playStatus + ' ' + songName)[0:16] + '\n' + (time + '     ')[0:16])
	
	# with scolling text
	artistSong = (playStatus + ' ' + songName)
	if artistSong != lastArtistSong:
		scroller.setNewText(artistSong)
		lastArtistSong = artistSong

	if songName != '':		
		lcd.message(scroller.scroll()[0:16] + '\n' + (time + '     ')[0:16])
	else: 
		lcd.message((playStatus + '             ')[0:16] + '\n' + (time + '     ')[0:16])
	
	# Poll the buttons most of the sleep time, to make them responsive the plan is to 
	# poll the buttons for 400ms and then update the status on the display
	# If we sleep for 40ms between each poll time and have five buttons that equals to 200 ms
	# Two iterations of this gives us 400 ms.
#	for i in range (0, 10):
	for i in range (0, 5):
	   for b in btn:
	        if lcd.buttonPressed(b):
			if b is lcd.RIGHT:
				subprocess.Popen('mpc next', shell=True)
				sleep(0.2) # Sleep a little extra to avoid dubble registrations
			if b is lcd.LEFT:
				subprocess.Popen('mpc prev', shell=True)
				sleep(0.2) # Sleep a little extra to avoid dubble registrations
			if b is lcd.UP:
				subprocess.Popen('mpc play', shell=True)
				sleep(0.2) # Sleep a little extra to avoid dubble registrations
			if b is lcd.DOWN:
				subprocess.Popen('mpc stop', shell=True)
				sleep(0.2) # Sleep a little extra to avoid dubble registrations
			if b is lcd.SELECT:
				subprocess.Popen('mpc toggle', shell=True)
				sleep(0.2) # Sleep a little extra to avoid dubble registrations
		        break
  	   sleep(0.04)  
 


I autostart the python script by adding the following line to the root crontab (sudo crontab -e):
@reboot sudo python /home/pi/Adafruit-Raspberry-Pi-Python-Code/Adafruit_CharLCDPlate/showSongInfo.py



Video of the display in action.

Part list

  • Raspberry Pi
  • 5V power supply
  • 16 GB SD-card
  • Adafruit 16x2 LCD Plate
  • NuForce uDAC 2
  • Network Cable
  • Sennheiser HD598
  • Case (e.g. Adafruit Pi Box or similar)

Todo

That is left to do is to assemble my PiSupply kit and connect to the wireless network using an USB WIFI dongle and maybe mount all parts in some clever way.


5 kommentarer:

  1. Hi, I like very much your article !!!
    I am looking to use it and I was wondering if, possibly, you had time to make the "todo" :
    # Todo
    # * display brightness timout, i.e. turn off display when stopped or paused for a while
    #

    this would be awesome...

    Francis

    SvaraRadera
    Svar
    1. Some day I will implement the display brightness but I cannot say when it will be ready.

      Radera
  2. Hi, this is a great addition however it does not work for me. I did change crontab, since that did not give any result I edited /etc/rc.local, that returned an error: Permission denied. Any suggestion how to start Python script?

    SvaraRadera
    Svar
    1. It is strange that the crontab start didn't work. Maybe Raspbian has been updated since I wrote the guide. I have not updated the SD-card I use for this for a while.
      If you use rc.local, don't forget to runt the script with sudo...

      Radera
  3. I know this is from a few years ago, but has anyone got this to run on the current version of Volumio as I have a 40x2 display I'd like to use?

    Regards

    Andrew

    SvaraRadera