Raspberry Pi with an I2C enabled LCD screen for system and MPD music display

 Music, Projects, Raspberry PI  Comments Off on Raspberry Pi with an I2C enabled LCD screen for system and MPD music display
Nov 062017
 
Share

I gutted an old RCA RC970 speaker into a RPI MPD Jukebox with LCD Screen.

On boot, the LCD screen shows RPI data. If the MPD daemon is actively playing a song, the screen switches over to music data.


I am using a 20×4 Blue LCD display with I2C backpack from eBay.


Wiring Diagram:



Installation:
From Terminal –
sudo raspi-config
Select Advanced Option -> I2C -> Enable I2C ->Finish
The Pi will reboot after you click the Finish Button

After rebooting the Pi, we need to modify the module’s config file. Type the following command in terminal:
sudo nano /etc/modules
Add following two lines in modules file if they do not exist:
i2c-bcm2708
i2c-dev
Then Type Ctrl X and Yes to save the file.

Install smbus and i2c python library
sudo apt-get update
sudo apt-get install -y python-smbus i2c-tools
sudo reboot
After rebooting the system, type the following command in order to check software installation:
lsmod | grep i2c_
You should see i2c_bcm2708 in a list, this means the library has been installed successfully.

If you want to use this for MPD display:
sudo apt-get install mpd mpc

Testing Hardware
Depending on your Raspberry Pi version, please run one of following commands in terminal:
sudo i2cdetect -y 1
or
sudo i2cdetect -y 0

You should see something as follows; the value you see is for I2C_ADDR in the Python code included below.

0 1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:             — — — — — — — — — — — — —
10: — — — — — — — — — — — — — — — —
20: — — — — — — — — — — — — — — — —
30: — — — — — — — — — — — — — — — 27
40: — — — — — — — — — — — — — — — —
50: — — — — — — — — — — — — — — — —
60: — — — — — — — — — — — — — — — —
70: — — — — — — — —

Python Requirements:
sudo apt-get install python-pip
sudo pip install python-mpd2
sudo pip3 install python-mpd2
sudo pip install psutil
sudo pip3 install psutil

Start LCD Display at Boot:
sudo nano /etc/rc.local
Add the following line BEFORE the ‘exit 0’ line.
sudo python /home/pi/scripts/lcd_display.py &
You may want to store the following scripts somewhere else, so adjust the path above accordingly.

Copy and save the following as lcd_display.py

#!/usr/bin/env python3
# Project Tutorial Url:http://www.markwheeler.com
#  

import smbus
import time
import os
import glob
import subprocess
import psutil
from datetime import datetime
#import mpd
#from socket import error as SocketError
 
#client = mpd.MPDClient()

# Define some device parameters
I2C_ADDR  = 0x3f # I2C device address, if any error, change this address to 0x3f
LCD_WIDTH = 20   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1

def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = the data
  # mode = 1 for data
  #        0 for command

  bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
  bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT

  # High bits
  bus.write_byte(I2C_ADDR, bits_high)
  lcd_toggle_enable(bits_high)

  # Low bits
  bus.write_byte(I2C_ADDR, bits_low)
  lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
  # Toggle enable
  time.sleep(E_DELAY)
  bus.write_byte(I2C_ADDR, (bits | ENABLE))
  time.sleep(E_PULSE)
  bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
  time.sleep(E_DELAY)

def lcd_string(message,line,style):
  # Send string to display
  # style=1 Left justified
  # style=2 Centered
  # style=3 Right justified

  if style==1:
    message = message.ljust(LCD_WIDTH," ")
  elif style==2:
    message = message.center(LCD_WIDTH," ")
  elif style==3:
    message = message.rjust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)

# Is MPD playing a song?
def mpc_status():
    playing = (os.popen('mpc status | grep playing')).readline()
    if playing=="":
        return(0)
    elif playing != "":
	    return(1)
# Stuff to Display
def measure_temp():
    temp = os.popen('vcgencmd measure_temp').readline()
    return(temp.replace("temp=","").replace("'C\n",""))

def ip_address():
    ip = os.popen('host localhost | grep "address" | cut -d" " -f4').readline()
    return(ip.strip())

def cpu_load():
    CPU_Pct=str(round(float(os.popen('''grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage }' ''').readline()),2))
    return(str(CPU_Pct))

def ram_used():
    mem_used = (psutil.virtual_memory().used  / (1024.0 ** 2))
    return(str(round(mem_used,2)))

def main():
  # Main program block

  # Initialise display
  lcd_init()

# Display Method
  while True:
# Call MPD Display if Running
#    client.connect("localhost", 6600)
#    if client.status()['state'] == "play":
#        lcd_string("MPD is PLAYING",LCD_LINE_2,1)
#        client.disconnect()
#        os.system('/home/pi/scripts/mpd_display.py')	
#    else:
#        client.disconnect()  

# Call MPD Display if Running - Comment these next two lines if not using MPD
    if mpc_status() == 1:
        os.system('/home/pi/scripts/mpd_display.py')
		
    # Display Data
    lcd_string(datetime.now().strftime('%m/%d/%Y %H:%M'),LCD_LINE_1,2)
    lcd_string(ip_address(),LCD_LINE_2,2)
    lcd_string("CPU: "+measure_temp()+"c "+cpu_load()+"%",LCD_LINE_3,2)
#    lcd_string(" ",LCD_LINE_3,1)
    lcd_string("RAM FREE: "+ram_used()+"Mgb",LCD_LINE_4,2)
    time.sleep(1)

if __name__ == '__main__':

  try:
    main()
  except KeyboardInterrupt:
    pass
  finally:
    lcd_byte(0x01, LCD_CMD)

Copy and save the following as mpd_display.py

#!/usr/bin/env python3
# Project Tutorial Url:http://www.markwheeler.com
#  
import smbus
import time
import os
import glob
import subprocess
#import mpd
#from socket import error as SocketError
from datetime import datetime
 
#client = mpd.MPDClient() 
# Define some device parameters
I2C_ADDR  = 0x3f # I2C device address, if any error, change this address to 0x3f
LCD_WIDTH = 20   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1

def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = the data
  # mode = 1 for data
  #        0 for command

  bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
  bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT

  # High bits
  bus.write_byte(I2C_ADDR, bits_high)
  lcd_toggle_enable(bits_high)

  # Low bits
  bus.write_byte(I2C_ADDR, bits_low)
  lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
  # Toggle enable
  time.sleep(E_DELAY)
  bus.write_byte(I2C_ADDR, (bits | ENABLE))
  time.sleep(E_PULSE)
  bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
  time.sleep(E_DELAY)

def lcd_string(message,line,style):
  # Send string to display
  # style=1 Left justified
  # style=2 Centred
  # style=3 Right justified

  if style==1:
    message = message.ljust(LCD_WIDTH)
  elif style==2:
    message = message.center(LCD_WIDTH)
  elif style==3:
    message = message.rjust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)

# Is MPD playing a song?
def mpc_status():
    playing = (os.popen('mpc status | grep playing')).readline()
    if playing=="":
        return(0)
    else:
	    return(1)
		
# Stuff to Display
def current_song():
    song = os.popen('mpc -f %title%').readline()
    return(song.strip())

def current_artist():
    title = os.popen('mpc -f %artist%').readline()
    return(title.strip())

def current_time():
    stime = os.popen('mpc -f %time%').readline()
    return(stime.strip())

def current_album():
    album = os.popen('mpc -f %album%').readline()
    return(album.strip())
	
# MPD connection times out
#def song_elapsed():
#    elapsed = client.status()['elapsed']
#    m, s = divmod(int(float(elapsed)), 60)
#    h, m = divmod(m, 60)
#    elapsed_min = ("%02d:%02d" % (m, s))
#    return(elapsed_min)

def song_elapsed():
    elapsed = os.popen('mpc status | awk \'NR==2 { split($3, a, "/"); print a[1]}\'').readline()
    return(elapsed.strip())

#This scrolls the song title if it exceeds 20 characters. Not implemented in display here.
def long_song():
    str_song = " " * 20
    long_song = str_song + current_song()
    while True:
        for i in range (0, len(long_song)):
            lcd_text = long_song[i:(i+20)]
            lcd_string(lcd_text,LCD_LINE_3,1)
            time.sleep(0.4)
            lcd_string(str_song,LCD_LINE_3,1)
            continue

def main():
  # Main program block

  # Initialise display
  lcd_init()

 
# Display Method
while True:
# Call Default Display if Stopped
#    client.connect("localhost", 6600)
#    if client.status()['state'] != "play":
#        lcd_string("MPD is STOPPED",LCD_LINE_2,1)
#        client.disconnect()
#        os.system('/home/pi/scripts/lcd_display.py')
#    else:
#	client.disconnect()

# Call Default Display if Stopped
    if mpc_status() == 0:
        os.system('/home/pi/scripts/lcd_display.py')

# Display Data
#    lcd_string(datetime.now().strftime('%m/%d/%Y %H:%M'),LCD_LINE_1,2)
    lcd_string(current_artist(),LCD_LINE_1,1)
    lcd_string(current_album(),LCD_LINE_2,1)
    lcd_string(current_song(),LCD_LINE_3,1)
    lcd_string(song_elapsed()+"/"+current_time(),LCD_LINE_4,3)
    time.sleep(0.1)


if __name__ == '__main__':

  try:
    main()
  except KeyboardInterrupt:
    pass
  finally:
    lcd_byte(0x01, LCD_CMD)

I’ve left some commented code in these files with other things I was experimenting with.

 Posted by at 2:53 pm