320x240 TFT, IR control, SABRE ES9023 DAC build.

A few weeks ago I found myself with a spare Pi and a few HD44780 LCDs and sort of stumbled across Volumio while looking for something to do with them - I wanted to make a quick and dirty music streaming box for at work.

Things sort of spiraled from there - I had a spare aluminium case from an amp build a few years ago, so I decided to make a decent project of things and build a proper thing, with a decent DAC, a TFT LCD screen and IR remote control. I wanted an excuse to spend some time learning python and actually using the GPIOs of a Pi. I’ve got about 8 of them but they are all used for either Kodi, as security cameras or torrent boxes, and all of them just using off the shelf images. The furthest I had delved was setting up a cron job to reboot the cameras and torrent box every 24 hours!

You’ll have to excuse the quality of some of the photos, they were taken on my phone before I had decided to do a write up.

I had initially wanted to use a HD44780 LCD, mainly because I am familiar with them and I had a drawer full. However the dimensions of the case meant that I couldn’t really fit a sizeable one in there - I was limited to two rows. I tried a 40x2 display (surprisingly hard to get hold of compared to a few years ago when there were sellers on ebay offering every size and colour of screen for pennies) but it was too wide to allow me to fit a power button.
So, I ended up with a 320x240 colour LCD, 2.2". It was hilariously cheap, at about £3 delivered. I wasn’t expecting it to be really usable, but when it arrived I was pleasantly surprised! The viewing angle is not fantastic, but other than that it’s fine.

Fairly quickly I got it bodged up on my desk with some horribly cludgy python code doing MPC commandline calls and got it to give some stuff on the screen:

I ended up using the Adafruit ili9341 libraries to make it work without major modifications to the kernel - I just used a normal Volumio image and installed python etc etc.

Once I was happy that the screen was a goer, I milled out the aperture for it with the el cheapo “machine mart” milling machine we have at work thta has sat unused since purchase. You’ll notice that the hole is wider than the screen, this wasn’t intentional. The milling machine is so wobbly that the vibration of steadily milling one of the sides made the y axis wind itself out by about 5mm. If I had personally spent over £1k on the machine I would be devastated, it’s absolutely terrible! I managed to salvage it by taking another shallow pass and locking the “dead” axis out.

On close inspection it actually turned out quite well, with a good finish on the cutting edge. I left the protective film is still on the display here, but after realising the price I’ve just ordered another 4 as spares, in case it doesn’t last too long.

With the screen hole milled without completely ruining the case I invested a bit more time and money, and machined the other side of the case to take a power button and IR reciever.

By now I had decided I wanted to use a dual 12v toroidal transformer I had left over from a previous project, and a pair of linear supplies. Partly to keep the DAC isolated from the PI, and partly because the transformer had a pair of output windings so I might as well use them both.

The decision to use linear supplies also helped me decide how I would lay the system out - rather than drill and tap the bottom of the case for all the different modules, I used a 5mm Aluminium sheet as a base and a heatsink to mount everything to, which I would fix down flush to the bottom of the case with some thermal grease to transfer the heat outside.

By now I had found a 500gb 2.5" hdd that I wanted to use, but seeing as this thing will sit 80cm from my head I had to make an isolated mount.

Here it is: I use m3x12 machine screws with silicone tubing over them, slotted into ever so slightly oversize holes in some aluminium box section:

On top of this I used a bit of scrap perspex to mount the Pi (Which is a B+ for now, but I may swap it for a Pi 2 before the project is done)

And with the plate sat in the case, things are coming together:

Only about 3 weeks ago I designed the PCBs for the power supplies. They are just designed around LM7805s and any old stuff really. I wanted to put both supplies on one board but the PCB supplier does a minimum of three copies so I slimmed it down and used a pair of separate boards. One will do the DAC alone and the other the PI, screen and HDD.

They are really nothing high tech at all, just a fuse, a bridge rectifier and a 7805 with some decoupling. - I could have bought similar boards fully populated for much less than the PCBs alone cost me (£18 for three) but where’s the fun in that?

They arrived this morning, which I suppose is reasonable timing

I populated them with essentially whatever we had lying around at work - The capacitors are way overkill and I messed up the footprints (I was going to change to a “universal” footprint which would take all kinds of them, but I forgot to do it before I sent the board off)

here’s the layout:

There’s about 8w peak sank into the base plate, more like half that with normal use.

Here’s the latest picture:

It’s more or less there electrically, I need to find some 3.5mm pitch 2 pin connectors from work on monday to get the power end wired up, and mill the rear panel out for the ethernet and USB extensions.

I’m not sure what to do about the power switch though - It’s supposedly rated for 3A 250V, but the pins are so close to the 5V input and it is a £2 cheapo one. I’d like to do a power control board with a couple of relays that talks to the PI GPIO to stop me just turning the thing off without waiting for a proper shutdown, but I’d have to wait another 25 days for the PCBs to arrive, so I’m reluctant.

Looks great. Where did you get the case? It looks expensive though…

The case was from eBay, I bought the pair a couple of years ago for about $60, shipped from china.

ebay.co.uk/itm/Aluminum-chas … 1742485329

This looks like it’s the same one. They are nice solid cases especially for the price.

Damn you cobblers, that just looks too nice.
I just went and bought that case (why are simple enclosures so expensive?)

…and an led switch in blue:
ebay.co.uk/itm/Angel-Eye-Met … fe=sidebar

Now I just need a friend with a milling machine to help me cut a hole for this:
ebay.co.uk/itm/281270914670? … EBIDX%3AIT

Hi cobblers, this is a great setup and I’d like to copycat some of it if you don’t mind.
I do have a few questions if you’d be kind enough to answer:

  1. Is there an advantage to using a Toroidal transformer over this http://www.ebay.co.uk/itm/291087849273?_trksid=p2059210.m2749.l2649&var=590244847087&ssPageName=STRK%3AMEBIDX%3AIT

  2. I see you build your own power supply boards but I will be using 2 of these http://www.ebay.co.uk/itm/261512603010?_trksid=p2059210.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT to step down to 5v from the above mentioned PSU to power the PI and DAC separately

  3. Could you share your ping wiring diagram of the lcd to the pi and maybe the code for LCD to work with volumio?

  4. I am planning to use this as a power button http://www.ebay.co.uk/itm/261510932507?_trksid=p2059210.m2749.l2649&var=560389193619&ssPageName=STRK%3AMEBIDX%3AIT
    Are you aware of a way to use it as a nice shutdown rather than simply cutting power to the pi?

I realise I have a lot of questions and would have been better to message you privately but the forum won’t let me.

Muchly appreciated!

Hi Coconuts - I don’t think you’ll fit that 20x4 LCD in the case, there isn’t enough room vertically to fit any of the ones I had, I was limited to two rows. I tried a 40x2 LCD but it was too wide to leave space for a power button.

Theoretically, using a linear power supply will leave less noise on the 5v rail. Cheap switch mode ones like the above are bound to be a bit noisy - whether the noise makes it through the DAC to your amp and speakers is another thing. I used the toroid because I had it lying around from another abandoned project.

If I hadn’t, I would probably have used an external power brick (8-12v DC) and then used linear supplies internally, however using a decent 5v switchmode supply should cause no issues. You can quite happily run the DAC and PI from the same power supply, if you like. I’m only really running them separate because there was a minimum order of three copies of the PCB, and my transformer has two independant 12v output windings, so I did it “just because”.

I wired my LCD along the lines of this:
learn.adafruit.com/user-space-s … li9341-2-8
and I wrote some very hacky python code to drive the display. Once I have the whole thing built and tidy I’ll improve the code, but I’m far from an expert and my code is really messy and scattered. I’ll post it up but I can’t really give you any support with it, you’re on your own. I used the adafruit ili9341 libraries to drive the display and python-mpd2 to get track info from volumio/mpc. If you use a HD44780 display then things are WAY simpler, just follow this guide and you’ll have it running in about ten minutes:

As for having an elegant way to shut down the PI, that’s my current task.
Basically, I have the power button wired up to control a relay which controls the 5v power rail.
When I press the switch it turns the relay on, and once the Pi is booted up I run a Python script which turns a GPIO pin on. This drives the relay via a couple of transistors and a diode, and holds it on, effectively bypassing the power switch.
When the Pi has been shut down properly, the GPIO pin turns off and the relay turns off soon after.
I’ve also added an output from the power switch to an Input on the GPIO pin, so that once the Pi is booted it monitors the power switch. If the switch is turned off, it runs a “sudo halt” command and gracefully powers off the Pi.
I’ve had to include a 4 minute delay to the relay switching off, so that when the Pi reboots or I quickly turn the button on and off the thing doesn’t turn off before the pi has booted and taken back over control of the relay.

It’s loosely based on the theory here:
raspberry-pi-geek.com/Archiv … Pi/(offset/2
but it was created with whatever relays/transistors etc we had at work, and rushed together on prototyping board over my lunchbreak. I have a (literal) back of an envelope schematic if you are interested. Again it’s a bit of a kludge so I’m neither proud of it nor can I really support it.

Here’s the prototype:

And here’s the final board:

I built it today at work, I just need to add a trimpot to adjust the delay and dummy it in the case to sort out wire lengths before adding connectors and installing it.

Your are perfectly right, there’s no way to fit the 20x4 screen in this case. the PCB behind the actual screen is just too tall.
I got the same tft u have on order now so will try it with that. I don’t suppose you drew an exact diagram of how you wired that to the GPIO? I just don’t want to burn anything, as you probably gathered I’m quite new at all this.
Your code and rough instructions, as hacked as it is is very welcome, looking at the images you posted the screen shows pretty much what I’d want in terms of info and also I’d learn by deconstructing it.

I also picked up a two way 12v toroidal transformer off ebay and I’ll use that, thank you very much for explaining the difference.

I’m also a bit nervous as to weather i can accomodate both the DAC and the screen from the GPIO (shared pins etc.
This is my DAC: http://iqaudio.com/

Digging around for a power/shutdown solution now too and will update with my findings.


Hi, can you share your python code for this display?
Connected it and got it running but I need to write python code to show mpd info.

Here is my awful but roughly functional code that pulls from MPD and writes to the screen, and also handles power functions on my system. Comment these out or your pi will shutdown as soon as you run the program. You will need to put any old TTF font in /home/pi/display (which is where I run this script from on boot, via rc.local

I have wired my screen as per learn.adafruit.com/user-space-s … 2-8/wiring But I am using GPIO 12 for the backlight switch instead.

import Image
import ImageDraw
import ImageFont
import time
import subprocess
import os
import glob
import socket
import Adafruit_ILI9341 as TFT
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.SPI as SPI
import gc
import mpd
client = mpd.MPDClient(use_unicode=True)
client.timeout = 10
client.connect("localhost", 6600)

#setup to monitor pin for shutdown on power stich off
gpio = GPIO.get_platform_gpio()

gpio.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # monitor power switch

gpio.setup(21, GPIO.OUT) # Take control of Keep Alive signal to relay board
gpio.setup(16, GPIO.OUT) # Takecontrol of Power LED
gpio.setup(12, GPIO.OUT) # Take control of LCD Backlight

gpio.output(21, True) # Turn on Keep Alive
gpio.output(16, True) # Turn on Power LED
gpio.output(12, True) # Turn on LCD backlight

# Raspberry Pi pin configuration for screen
DC = 18
RST = 23

# Create TFT LCD display class.
disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=64000000))

# Initialize display.

# Clear the Display

# Get a PIL Draw object to start drawing on the display buffer.
draw = disp.draw()

# load a TTF font
# I chose fixed width for now, to make it easier to center things

font = ImageFont.truetype('/home/pi/display/unispace.ttf', 20)
smallfont = ImageFont.truetype('/home/pi/display/unispace.ttf', 14)
bigfont = ImageFont.truetype('/home/pi/display/unispace.ttf', 60)

orig_time = time.time() # Initialise timer for track timer

# Try to connect to gmail, then note which IP address resolves to the internet to identify the correct
# network interface to print on the screen in idle mode
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Return CPU temperature as a character string                                      
def getCPUtemperature():
    res = os.popen('vcgencmd measure_temp').readline()

#The screen is usually setup portrait, so ghere i used some adafruit code to draw text at 90 degrees:

def draw_rotated_text(image, text, position, angle, font, fill=(255,255,255)):
	# Get rendered font width and height.
	draw = ImageDraw.Draw(image)
	width, height = draw.textsize(text, font=font)
	# Create a new image with transparent background to store the text.
	textimage = Image.new('RGBA', (width, height), (0,0,0,0))
	# Render the text.
	textdraw = ImageDraw.Draw(textimage)
	textdraw.text((0,0), text, font=font, fill=fill)
	# Rotate the text image.
	rotated = textimage.rotate(angle, expand=1)
	# Paste the text into the image, using it as a mask for transparency.
	image.paste(rotated, position)

#Initial MPC queries. !!!"Always use try/except !!!!! otherwise the program crashes out when one is blank!
        artist = client.currentsong()['artist']
        artist = "No Artist"
        title = client.currentsong()['title']
        title = "No Title"

        album = client.currentsong()['album']
        album = "No Album"
start_time = time.time()

# initial setup of variables and lines
oldtitle = title
artist2 = artist[28:56].center(27)
artist = artist[0:28].center(27)
title2 = title[28:56].center(27)
title = title[0:28].center(27)
album2  = album[28:56].center(34)
album = album[0:28].center(34)

#inital draw of screen

disp.clear((0, 0, 0))
draw_rotated_text(disp.buffer, artist, (20, 0), 90, font, fill=(255,255,255))
draw_rotated_text(disp.buffer, artist2, (40, 0), 90, font, fill=(255,255,255))        
draw_rotated_text(disp.buffer, title, (70, 0), 90, font, fill=(255,255,255))
draw_rotated_text(disp.buffer, title2, (90, 0), 90, font, fill=(255,255,255))
draw_rotated_text(disp.buffer, album, (150, 0), 90, smallfont, fill=(255,255,255))
draw_rotated_text(disp.buffer, album2, (180, 0), 90,smallfont, fill=(255,255,255))

oldstate="Random nonzero"


        if ( gpio.input(20) == False ):    ##I have this pin wired to the power switch on the front of the case. If the input goes low, the pi shuts down! Remove this if you don't want it to happen
                print "shit down"
                disp.clear((0, 0, 0))
                draw_rotated_text(disp.buffer, "Shutdown", (70, 10), 90, bigfont, fill=(255,255,255))
                os.system('sudo shutdown -h now')
                state = client.status()['state']
                state = "fail"
        if (state != oldstate): disp.clear((0, 0, 0))
        ## only clear when we have to, disp.clear has a memory leak!!!

        if (state != "play") and (state != "pause"):  # If device idle, show current time/date, IP and CPU temp

                draw_rotated_text(disp.buffer, ipaddress, (20, 10), 90, smallfont, fill=(255,255,255))
                draw_rotated_text(disp.buffer, getCPUtemperature(), (20, 280), 90, smallfont, fill=(255,255,255))
                localtime = time.asctime( time.localtime(time.time()) )
                draw_rotated_text(disp.buffer, localtime[0:10], (70, 10), 90, font, fill=(255,255,255))
                draw_rotated_text(disp.buffer, localtime[11:19], (90, 10), 90, bigfont, fill=(255,255,255))

                time.sleep(0.1)    # YOu can remove this to make display update quicker, but it becomes even more of a CPU hog
                oldtitle = "Random Nonsense placeholder"
                print state    # just for debugging

        else:   # If Playing or Paused
                print state   ## just for debugging
                draw_time = time.time()  ## also debug
                        newtitle = client.currentsong()['title'] 
                        newtitle = "failed to get title"

                if (newtitle == oldtitle):     # Mark time on track change

                        seconds = time.time() - start_time
                        m, s = divmod(seconds, 60)
                        h, m = divmod(m, 60)      
                        draw_rotated_text(disp.buffer,"%d:%02d:%02d" % (h, m, s) , (195, 5), 90, font, fill=(255,255,255))
                        start_time = time.time()
                        try:                            # ALWAYS TRY /EXCEPT ON MPC CALLS
                                artist = client.currentsong()['artist']
                                artist = "No Artist"
                                title = client.currentsong()['title']
                                title = "No Title"

                                album = client.currentsong()['album']
                                album = "No Album"

                         #Format display strings
                        oldtitle = title              
                        artist2 = artist[28:56].center(27)
                        artist = artist[0:28].center(27)
                        title2 = title[28:56].center(27)
                        title = title[0:28].center(27)
                        album2  = album[28:56].center(34)
                        album = album[0:28].center(34)

                        disp.clear((0, 0, 0))
                        draw_rotated_text(disp.buffer, artist, (20, 0), 90, font, fill=(255,255,255))
                        draw_rotated_text(disp.buffer, artist2, (40, 0), 90, font, fill=(255,255,255))        
                        draw_rotated_text(disp.buffer, title, (70, 0), 90, font, fill=(255,255,255))
                        draw_rotated_text(disp.buffer, title2, (90, 0), 90, font, fill=(255,255,255))
                        draw_rotated_text(disp.buffer, album, (150, 0), 90, smallfont, fill=(255,255,255))
                        draw_rotated_text(disp.buffer, album2, (180, 0), 90,smallfont, fill=(255,255,255))

                time.sleep(0.1)   # Can reduce this delay for snappyer display, but it will hog much more CPU for little benefit.


Here’s a video of it working, changing tracks and powering off.

It’s reasonably responsive, but other than running a custom kernel to output directly from the video driver to the screen (which I couldn’t comfortably get to work with volumio) I couldn’t find a way to speed it up.


I took a video of boot up but it wasn;t all that interesting. It takes just over two minutes from pressing the power switch, to the display powering up and showing the current time, like this:

Hi, got it working, was easy with your python code. Thanks a lot.

I love it!!

Has anyone thought about doing this project with a bigger lcd something like a stand up unit with a big lcd screen to view song track and volume level ?

Hi Cobblers.

Please help me out from my troubles. How you wired power to your DAC from separate power supply? If I wire 5V and GND directly (black and red cable on separate connector) to Raspberry Pi +5V and GND pin it is playing well, but when I wire it to separate power supply, it doesn’t play. I have this Audiophonics DAC Sabre ES9023 I2S: http://www.ebay.com/itm/331535439744

Here is my connection diagram when it is playing:

I2S Connector
DAC  -> Raspberry
MCLK -> Not connected
GND  -> GND (pin39)
DATA -> GPIO21 (pin40)
LRCK -> GPIO19 (pin35)
BCK  -> GPIO18 (pin12)

Power connector
DAC  -> Raspberry
+5V  -> +5V (pin02)
GND  -> GND (pin14)



hi, can you share the schematic of the power button pcb? thanks :smiley: awesome project

What do you mean for “comment” the script? As you say if i run it the only thing i see is “shutdown” and the pi powers off. Thanks :smiley:

Hi, the youtube link seems to be unavailable, can you please check? Nice build!!