Skip to content

Instantly share code, notes, and snippets.

@vsefer
Created August 10, 2020 22:13
Show Gist options
  • Save vsefer/0d524343c63677cc02f6e5786cc9a8d0 to your computer and use it in GitHub Desktop.
Save vsefer/0d524343c63677cc02f6e5786cc9a8d0 to your computer and use it in GitHub Desktop.
pi@scarlett:~ $ cat mopidy_display_ili9341.py
"""
This will show some Linux Statistics on the attached display. Be sure to adjust
to the display you have connected. Be sure to check the learn guides for more
usage information.
This example is for use on (Linux) computers that are using CPython with
Adafruit Blinka to support CircuitPython libraries. CircuitPython does
not support PIL/pillow (python imaging library)!
"""
import time
import sys
import os
import subprocess
import digitalio
import board
from PIL import Image, ImageDraw, ImageFont
from socket import error as socket_error
from datetime import datetime
import adafruit_rgb_display.ili9341 as ili9341
import adafruit_rgb_display.st7789 as st7789 # pylint: disable=unused-import
import adafruit_rgb_display.hx8357 as hx8357 # pylint: disable=unused-import
import adafruit_rgb_display.st7735 as st7735 # pylint: disable=unused-import
import adafruit_rgb_display.ssd1351 as ssd1351 # pylint: disable=unused-import
import adafruit_rgb_display.ssd1331 as ssd1331 # pylint: disable=unused-import
# MPD Clint
from mpd import MPDClient, MPDError, CommandError, ConnectionError
#reload(sys)
#sys.setdefaultencoding('utf-8')
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = digitalio.DigitalInOut(board.D24)
# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000
# Setup SPI bus using hardware SPI:
spi = board.SPI()
# pylint: disable=line-too-long
# Create the display:
# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
# disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R
# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
disp = ili9341.ILI9341(
spi,
rotation=90, # 2.2", 2.4", 2.8", 3.2" ILI9341
cs=cs_pin,
dc=dc_pin,
rst=reset_pin,
baudrate=BAUDRATE,
)
# pylint: enable=line-too-long
# MPD Client
class MPDConnect(object):
def __init__(self, host='localhost', port=6600):
self._mpd_client = MPDClient()
self._mpd_client.timeout = 10
self._mpd_connected = False
self._host = host
self._port = port
def connect(self):
if not self._mpd_connected:
try:
self._mpd_client.ping()
except(socket_error, ConnectionError):
try:
self._mpd_client.connect(self._host, self._port)
self._mpd_connected = True
except(socket_error, ConnectionError, CommandError):
self._mpd_connected = False
def disconnect(self):
self._mpd_client.close()
self._mpd_client.disconnect()
def _play_pause(self):
self._mpd_client.pause()
#return False
def _next_track(self):
self._mpd_client.next()
#return False
def _prev_track(self):
self._mpd_client.previous()
#return False
def fetch(self):
# MPD current song
song_info = self._mpd_client.currentsong()
# Artist Name
if 'artist' in song_info:
artist = song_info['artist']
else:
artist = 'Unknown Artist'
# Song Name
if 'title' in song_info:
title = song_info['title']
else:
title = 'Unknown Title'
# Album Name
if 'album' in song_info:
album = song_info['album']
else:
album = 'Unknown Album'
# MPD Status
song_stats = self._mpd_client.status()
# State
state = song_stats['state']
# Song time
if 'elapsed' in song_stats:
elapsed = song_stats['elapsed']
m,s = divmod(float(elapsed), 60)
h,m = divmod(m, 60)
eltime = "%d:%02d:%02d" % (h, m, s)
else:
eltime ="0:00:00"
# Audio
if 'audio' in song_stats:
bit = song_stats['audio'].split(':')[1]
frequency = song_stats['audio'].split(':')[0]
z, f = divmod( int(frequency), 1000 )
if ( f == 0 ):
frequency = str(z)
else:
frequency = str( float(frequency) / 1000 )
bitrate = song_stats['bitrate']
audio_info = bit + "bit " + frequency + "kHz " + bitrate + "kbps"
else:
audio_info = ""
# Bitrate
if 'bitrate' in song_stats:
bitrate = song_stats['bitrate']
else:
bitrate = 'N/A'
# Volume
vol = song_stats['volume']
return({'state':state, 'artist':artist, 'title':title, 'album':album, 'eltime':eltime, 'volume':int(vol), 'audio_info':audio_info, 'bitrate':bitrate})
def main():
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
if disp.rotation % 180 == 90:
height = disp.width # we swap height/width to rotate it to landscape!
width = disp.height
else:
width = disp.width # we swap height/width to rotate it to landscape!
height = disp.height
image = Image.new("RGB", (width, height))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
disp.image(image)
# First define some constants to allow easy positioning of text.
#padding = -2
#x = 0
# Load a TTF font. Make sure the .ttf font file is in the
# same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
font_artist = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Arial-Unicode-Bold.ttf', 48)
font_title = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Arial-Unicode-Regular.ttf', 36)
font_album = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Arial-Unicode-Regular.ttf', 24)
font_info = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Verdana.ttf', 24)
font_time = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Verdana.ttf', 36)
font_time_label = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Verdana.ttf', 18)
font_timenow = ImageFont.truetype('/home/pi/MoodeAudio-OLED/Arial-Unicode-Bold.ttf', 120)
# MPD Connect
client = MPDConnect()
client.connect()
padding = 2
shape_width = 20
top = padding
bottom = height-padding
artoffset = 2
titoffset = 2
alboffset = 2
animate = 20
while True:
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=0)
# Fetch data
info = client.fetch()
state = info['state']
eltime = info['eltime']
vol = info['volume']
artist = info['artist']
title = info['title']
album = info['album']
audio = info['audio_info']
bitrate = info['bitrate']
# Position text of Artist
artwd,artz = draw.textsize(str(artist), font=font_artist)
# Artist animate
if artwd < width:
artx = (width - artwd) / 2
artoffset = padding
else:
artx = artoffset
artoffset -= animate
if (artwd - (width + abs(artx))) < -312:
artoffset = 320
# Position text of Title
titwd,titz = draw.textsize(str(title), font=font_title)
# Title animate
if titwd < width:
titx = (width - titwd) / 2
titoffset = padding
else:
titx = titoffset
titoffset -= animate
if (titwd - (width + abs(titx))) < -312:
titoffset = 320
# Position text of Album
albwd,albz = draw.textsize(str(album), font=font_album)
# Album animate
if albwd < width:
albx = (width - albwd) / 2
alboffset = padding
else:
albx = alboffset
alboffset -= animate
if (albwd - (width + abs(albx))) < -312:
alboffset = 320
# Position text of audio infomation
audiox,audioy = draw.textsize(audio, font=font_info)
if audiox < 158:
audiox,audioy = divmod((65-audiox),2)
else:
audiox = 2
if state == 'stop':
# Draw text
#draw.text((30,15), "Music Stop", font=font_title, fill="#FFFFFF")
#draw.text((padding,100), eltime, font=font_time, fill="#FFFFFF")
#draw.text((100,100), "vol: " + str(vol) , font=font_time, fill="#FFFFFF")
now = datetime.now()
current_time = now.strftime("%H:%M")
draw.text((5,15), current_time, font=font_timenow, fill="#FFFFFF")
else:
# Draw text.
draw.text((artx,1), str(artist), font=font_artist, fill="#00FFFF")
draw.text((titx,55), str(title), font=font_title, fill="#FFFFFF")
draw.text((albx,100), str(album), font=font_album, fill="YELLOW")
#draw.text((audiox,35), audio, font=font_info, fill="#FFFFFF")
draw.text((93,195), "kbit/s", font=font_time_label, fill="YELLOW")
draw.text((98,215), bitrate , font=font_info, fill="#00FFFF")
draw.text((86,140), eltime, font=font_time, fill="#FFFFFF")
draw.text((padding,215), str(vol) + "%" , font=font_info, fill="#00FFFF")
draw.text((padding,195), "input", font=font_time_label, fill="YELLOW")
#cmd = "amixer --card 1 get Headphone | grep dB | awk '{print $5}' | sort -u | tr -d '[]'" # pylint: disable=line-too-long
cmd = "amixer get Master | grep '%' | awk '{print $5}' | sort -u | tr -d '[]'" # pylint: disable=line-too-long
volume = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "cat /proc/asound/card1/pcm0p/sub0/hw_params | grep rate | awk '{print $2 / 1000}'"
freq = subprocess.check_output(cmd, shell=True).decode("utf-8")
draw.text((260,215), str(volume) , font=font_info, fill="#00FFFF")
draw.text((257,195), "output", font=font_time_label, fill="YELLOW")
draw.text((188,215), str(freq) , font=font_info, fill="#00FFFF")
draw.text((185,195), "kHz", font=font_time_label, fill="YELLOW")
#image_ratio = image.width / image.height
#screen_ratio = width / height
#if screen_ratio < image_ratio:
# scaled_width = image.width * height // image.height
# scaled_height = height
#else:
# scaled_width = width
# scaled_height = image.height * width // image.width
#image = image.resize((scaled_width, scaled_height), Image.BICUBIC)
cover = Image.open("/home/pi/pidi/current.jpg")
image = cover.resize((200, 200), Image.BICUBIC)
# Crop and center the image
#x = scaled_width // 32 - width // 32
#y = scaled_height // 32 - height // 32
#image = image.crop((x, y, x + width, y + height))
# Draw the image buffer.
disp.image(image)
#disp.display()
# Pause briefly before drawing next frame.
time.sleep(1)
# Shell scripts for system monitoring from here:
# https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load
# cmd = "hostname -I | cut -d' ' -f1"
# IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8")
# cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
# CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
# cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB %.2f%%\", $3,$2,$3*100/$2 }'"
# MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
# cmd = 'df -h | awk \'$NF=="/"{printf "Disk: %d/%d GB %s", $3,$2,$5}\''
# Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
# cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'" # pylint: disable=line-too-long
# Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment