MPD Monitor [gav-pi6]
At times, with my Multimedia System playing random music by default (obviously, it can be overridden with any number of Music Player Clients), I just wasn't sure which track was playing.
So, with a spare Raspbery Pi Zero W, a 4x20 dot matrix display, an enclosure that I bought from a seller who's no longer on Ebay, and a little bit of Python, we have a fully functioning MPD monitor (that also tells me if my Internet connection is live)!
Installation was simple:
- Assemble the enclosure.
- Mount the LCD (with LCD backpack) and Raspberry Pi to the enclosure.
- Connect VCC, GND, SDA and SCL from the Pi to the backpack.
- Install Raspbian Lite to a micro SD card.
- Put the sd card in the Pi and boot up.
- Enable I2C in the configuration.
     sudo raspi-config
- Install a couple of additional python modules:
     sudo apt install python3-smbus python3-mpd mpc
- Create the Python script
     nano mpd_lcd.pywith the following contents:#!/usr/bin/python
 import smbus
 import time
 from mpd import MPDClient
 import socket
 
 # Server for MPD
 SERVER = "lounge-pi"
 client = MPDClient() # create client object
 client.timeout = 10 # network timeout in seconds (floats allowed), default: None
 client.idletimeout = None # timeout for fetching the result of the idle command is handled seperately, default: None
 
 # Host lookup for determining Internet connectivity
 INET_SERVER = "www.google.com"
 
 # Define some device parameters
 I2C_ADDR = 0x27 # I2C device address
 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
 ZERO_DISP = 3
 
 #Open I2C interface
 #bus = smbus.SMBus(0) # Rev 1 Pi uses 0
 bus = smbus.SMBus(1) # Rev 2 Pi uses 1
 
 def lcd_init():
 # Initialist 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):
 # Send string to display
 message = message.ljust(LCD_WIDTH," ")
 lcd_byte(line, LCD_CMD)
 for i in range(LCD_WIDTH):
 lcd_byte(ord(message[i]),LCD_CHR)
 
 def music_details():
 # Set defaults just in case of errors
 artist = "Cannot connect to"
 album = SERVER
 song = ""
 length = 0
 position = 0
 
 # Connect to MPD on the server
 server_up = 1
 try:
 client.connect(SERVER, 6600)
 # Catch socket errors
 except IOError:
 server_up = 0
 print("Car_Server_LCD.py: Could not connect to " + SERVER)
 # Catch Other MPD Errors
 except MPDError:
 server_up = 0
 print("Car_Server_LCD.py: Could not connect to " + SERVER)
 
 if server_up == 1:
 status = client.status()
 if status['state'] == "play":
 position = int(float(status['elapsed']))
 track = client.currentsong()
 if len(track) > 0:
 artist = track['artist']
 if len(artist) > LCD_WIDTH:
 artist += " "
 album = track['album']
 if len(album) > LCD_WIDTH:
 album += " "
 song = track['title']
 if len(song) > LCD_WIDTH:
 song += " "
 length = int(track['time'])
 else:
 artist = "Unknown"
 album = "Unknown"
 song = "Unknown"
 
 # Close and disconnect from the server
 client.close()
 client.disconnect()
 
 return (artist, album, song, length, position)
 
 def format_string(old_string, cur_string, pos):
 if old_string != cur_string:
 # We need to reset the position on a new string
 pos = 0
 if len(cur_string) > LCD_WIDTH:
 return cur_string[pos:pos + 20]
 else:
 return cur_string
 
 def string_pos(cur_string, pos, zero_count):
 return_pos = 0
 if (pos + 20) < len(cur_string):
 if (zero_count < ZERO_DISP) and (pos == 0):
 zero_count = zero_count + 1
 else:
 zero_count = 0
 return_pos = pos + 1
 return (return_pos, zero_count)
 
 def is_connected(hostname):
 try:
 # see if we can resolve the host name -- tells us if there is
 # a DNS listening
 host = socket.gethostbyname(hostname)
 # connect to the host -- tells us if the host is actually
 # reachable
 s = socket.create_connection((host, 80), 2)
 s.close()
 return True
 except:
 pass
 return False
 
 def main():
 # Main program block
 
 # Initialise display
 lcd_init()
 
 # Define old variables to speed up display updates
 old_artist = ""
 old_album = ""
 old_song = ""
 # Define old displays to speed up display updates
 disp_artist = ""
 disp_album = ""
 disp_song = ""
 disp_timer = ""
 # Define the start positions of text strings
 start_artist = 0
 start_album = 0
 start_song = 0
 # Define the number of times a scrolling message has been at pos=0
 zero_artist = 0
 zero_album = 0
 zero_song = 0
 
 # Define an internet check timer
 inet_timer = 0
 
 while True:
 
 # Query the MPD server
 the_music = music_details()
 artist = the_music[0]
 album = the_music[1]
 song = the_music[2]
 length = the_music[3]
 position = the_music[4]
 
 # Convert the times into HH:SS
 song_len = time.strftime('%M:%S', time.gmtime(length))
 song_pos = time.strftime('%M:%S', time.gmtime(position))
 
 # Work out if we need to display part of a string
 artist_string = format_string(old_artist, artist, start_artist)
 old_artist = artist
 pos_artist = string_pos(artist, start_artist, zero_artist)
 start_artist = pos_artist[0]
 zero_artist = pos_artist[1]
 
 album_string = format_string(old_album, album, start_album)
 old_album = album
 pos_album = string_pos(album, start_album, zero_album)
 start_album = pos_album[0]
 zero_album = pos_album[1]
 
 song_string = format_string(old_song, song, start_song)
 old_song = song
 pos_song = string_pos(song, start_song, zero_song)
 start_song = pos_song[0]
 zero_song = pos_song[1]
 
 # Now work out if we have Internet connection, but only
 # do it every 10 seconds to save bandwidth
 if time.time() - inet_timer >= 10:
 if is_connected(INET_SERVER):
 inet = "*"
 else:
 inet = "-"
 inet_timer = time.time()
 
 timer = song_pos + " - " + song_len + " " + inet
 
 # Send some test
 if artist_string != disp_artist:
 disp_artist = artist_string
 lcd_string(artist_string, LCD_LINE_1)
 if album_string != disp_album:
 disp_album = album_string
 lcd_string(album_string, LCD_LINE_2)
 if song_string != disp_song:
 disp_song = song_string
 lcd_string(song_string, LCD_LINE_3)
 if timer != disp_timer:
 disp_timer = timer
 lcd_string(timer, LCD_LINE_4)
 
 time.sleep(0.2)
 
 if __name__ == '__main__':
 
 try:
 main()
 except KeyboardInterrupt:
 pass
 finally:
 lcd_byte(0x01, LCD_CMD)
- Call the script from /etc/rc.local
- Reboot and hope it works (you may want to run it from the command line first of all though)!
Note: there may be a slightly later version of the script on my useful scripts page (which may be safer, just in case I've got the multiple   required to show white space delimiation for Python wrong.
Specifications
| Case: | Laser cut 20x4 LCD enclosure | ||||
| Board: | Rapsberry Pi Zero-W (version 1.1) | ||||
| SoC: | Broadcom BCM2835 
 | ||||
| RAM: | 512MiBytes SDRAM | ||||
| Storage: | Sandisk Ultra, class 10 (16GByte micro SDHC) | ||||
| Display: | RGB backlight negative LCD 20x4 + extras (RGB on black) with I2C LCD Backpack | 
 
      
      
      
      
      
      
      
      
      
    





![Validate my Atom 1.0 feed [Valid Atom 1.0]](./images/valids/valid-atom10-yellow.gif)
![Validate my RSS 2.0 feed [Valid RSS 2.0]](./images/valids/valid-rss20-yellow.gif)