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

Raspberry PI3, 503HTA Tube DAC and USB Microphone.

 Music, Projects, Raspberry PI  Comments Off on Raspberry PI3, 503HTA Tube DAC and USB Microphone.
Oct 222016
 
Share

I have a Raspberry Pi 3 with a 503HTA Hybrid Tube Amp and a USB Microphone attached. I am running a HiFiBerry preconfigured SD Card image.

My intent is to run a MPD server accessible via tablet app; in this case MPD Control for Android. And also the Amazon Alexa sample application.

The only way I could get this to work was thru PulseAudio. Prior to doing any of this, make sure everything is up-to-date.

sudo apt-get update
sudo apt-get dist-upgrade
sudo rpi-update
sudo reboot

I have Raspbian Jessie configured for the Tube Amp with the following modifications:
1. In /boot/config.txt; comment out dtparam=audio=on. This disables the onboard audio. I added the following –

#dtparam=audio=on
dtoverlay=pi3-disable-bt
dtoverlay=hifiberry-dac

dtoverlay=pi3-disable-bt disables the onboard bluetooth which I believe is necessary for HiFiBerry DACs.

2. In /etc/modules; I have the following lines –

i2c-dev
#snd_bcm2835
snd_usb_audio
#DAC Modules
snd_soc_bcm2708
bcm2708_dmaengine
snd_soc_pcm5102a
snd_soc_hifiberry_dac

3. In /home/pi/.asoundrc; I have the following –

pcm.usb
{
    type hw
    card Device
}
pcm.external
{
    type hw
    card sndrpihifiberry
}
pcm.!default
{
    type hw
    playback.pcm
    {
        type hw
        slave.pcm "external"
    }
    capture.pcm
    {
        type plug
        slave.pcm "usb"
    }
}
ctl.!default
{
    type hw
    playback.pcm
    {
        type hw
        slave.pcm "sndrpihifiberry"
    }
    capture.pcm
    {
        type plug
        slave.pcm "usb"
    }
}
# The following lines are constantly added at boot. I have no idea which process does
# this, but it overrides the configuration above.
pcm.!default {
	type hw
	card 1
}

ctl.!default {
	type hw
	card 1
}

I don’t believe this step is needed when using PulseAudio, however, this .asoundrc config works if you are using a USB microphone and onboard audio out. You can determine the name of your DAC with ‘aplay -l’ from the command line. Replace sndrpihifiberry with ‘ALSA’

4. Installing PulseAudio – My steps:

sudo apt-get install vlc
sudo apt-get install pulseaudio
sudo apt-get install pavucontrol

5. Installing MPD

sudo apt-get install mpd

Modify the /etc/mpd.conf with:

audio_output {
#       type            "alsa"
        type            "pulse"
        name            "MPD PulseAudio Output"
        server          "127.0.0.1"
}

You will probably also need to edit this file with your custom options.

6. Modify /etc/pulse/client.conf; uncomment autospawn=yes. Delete the ‘;’ before that line.

7. Modify /etc/pulse/default.pa

### Network access (may be configured with paprefs, so leave this commented
### here if you plan to use paprefs)
#load-module module-esound-protocol-tcp
load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1
#load-module module-zeroconf-publish

Uncomment the native-protocol entry and add ‘auth-ip-acl=127.0.0.1’ at the end
Some instructions say to uncomment the zeroconf line, but I left it as-is.

8. Depending on which user runs MPD. These commands lets specific users to the PulseAudio groups.

sudo usermod -a -G pulse-access mpd
sudo usermod -a -G pulse mpd
sudo usermod -a -G pulse-access pi
sudo usermod -a -G pulse pi

9. Boot into your Raspbian Desktop GUI. Open a terminal and start PulseAudio.

pulseaudio -D

10. You should now be able to launch the PulseAudio Volume Control.
pac

You should see your DAC as an Output Device and your USB Microphone as an Input Device.

11. Start your Alexa services per the instructions on their GitHub.
Note: The original errors I was getting from the ‘wakeWordAgent -e sensory’ were:

wakeWordAgent: pcm.c:694: snd_pcm_close: assertion 'pcm' failed"
or
wakeWordAgent: pcm.c:1046: snd_pcm_prepare: Assertion `pcm' failed.

12. Verify your MPD app.

In my experience, you must always start PulseAudio first from a terminal before Alexa or the MPD server will function properly. With these instructions, I was able to have output from MPD and also have Alexa listen and respond.