Using the Pimoroni Presto to display what's playing

cc_rider

Major Contributor
Joined
Oct 20, 2022
Messages
1,225
This is a slick little device, very much ups the game for displays with embedded controllers. Out of stock at the moment, I'm guessing it was much more popular than they anticipated.


Here's my first attempt at using Micropython code for this device, with the WiiM http API, to monitor the current track and album cover. Nothing fancy, I'll try using asyncio to make it update faster, but for now, "it just works", as has done for at least a week, even though I have my routers reboot every morning. If nothing's playing, it'll display the time and date after a few seconds.

Note that it doesn't have the processing power to resize JPGs or PNGs, so those are being done using an external web service (thanks, Paul Webster!).

I haven't attempted to do anything but display at this point, though it's a touchscreen, so has the possibility to be used as a remote control for the WiiM, as well.

The Presto comes pre-loaded with the necessary binaries, so you just need to load two files (via Thonny or another IDE) to get this working:

main.py
Python:
from picovector import ANTIALIAS_FAST, PicoVector, Polygon, Transform
import machine
from presto import Presto
import time
import utime
import math
import urequests as requests
import gc
import socket
import jpegdec
import pngdec
import json
import secrets
import ntptime

# Constants
WIIM_IP = secrets.WIIM_IP
TIMEOUT = 15
TIMEZONE_OFFSET = secrets.TIMEZONE_OFFSET * 3600

# Initialize Presto and Display
presto = Presto(ambient_light=False, full_res=True)
presto.set_backlight(0.2)
display = presto.display
 

WIDTH, HEIGHT = display.get_bounds()
CX, CY = WIDTH // 2, HEIGHT // 2


# Colors
BLACK = display.create_pen(0, 0, 0)
WHITE = display.create_pen(255, 255, 255)
GRAY = display.create_pen(60, 60, 60)

months = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')

display.set_pen(BLACK)
display.clear()

# Initialize PicoVector
vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_FAST)
vector.set_font("Roboto-Medium.af", 14)

vector.set_font_letter_spacing(100)
vector.set_font_word_spacing(100)

transform = Transform()
#transform.scale(512,512)
vector.set_transform(transform)

# Initialize JPEG Decoder
jpd = jpegdec.JPEG(display)
pnd = pngdec.PNG(display)

def connect_to_wifi():
    print("Connecting to WiFi")
    status = str(presto.connect())
    print("Connection status: "+ status)
 
    # Get current time from NTP server
    try:
        ntptime.settime()
    except:
        print('Error updating time from NTP server')

def show_clock():
    display.set_pen(BLACK)
    display.clear()

    timestamp = utime.mktime(utime.localtime()) + TIMEZONE_OFFSET
    tm = utime.localtime(timestamp)

    # Format date and time
    date_str = "{:s} {:02d} {:s} {:d}".format(days[tm[6]], tm[2], months[tm[1]-1], tm[0])
    time_str = "{:02d}:{:02d}".format(tm[3], tm[4])
    display.set_pen(WHITE)
    vector.set_font_size(200)
    vector.text(time_str, 20, 240)
    vector.set_font_size(60)
    vector.text(date_str, 40, 360)
    presto.update()
 
def printtext(x,y,txt):
    display.set_pen(WHITE)
    vector.set_font_size(20)
    vector.text(txt, x, y)

def fetch_album_art(art_url):
 
    gc.collect()
    if "size=0" in art_url:
        proxy_url = str(art_url[:-1]) + "420X420"
    else:
        proxy_url = "https://wsrv.nl/?url="+art_url+"&w=420&h=420"

    response = requests.get(proxy_url, timeout=TIMEOUT)
 
    if response.status_code == 200:
        display.set_pen(BLACK)
        display.clear()
        album_art = response.content
        try:
            if ".png" in proxy_url:
                pnd.open_RAM(memoryview(album_art))
                pnd.decode(30,0)
            else:
                jpd.open_RAM(memoryview(album_art))
                ret = jpd.decode(30, 0, jpegdec.JPEG_SCALE_FULL)
             
        except Exception as e:
            print("Fetch Art Error: ",e)
        finally:
            response.close()
            gc.collect()

        ### Round corners of album cover
        try:
            rect = Polygon()
            display.set_pen(BLACK)
            rect.rectangle(20, -10, 440, 440, corners=(15,15,15,15), stroke=10)
            vector.draw(rect)
        except Exception as e:
            print("rect error:",e)
    else:
        print("Failed to fetch album art")
        display.set_pen(BLACK)
        display.clear()

def update_display(title, artist):

    display.set_pen(BLACK)
    display.rectangle(0,420,WIDTH,60)
    display.set_pen(WHITE)
    vector.set_font_size(30)
    vector.text(artist, 30, CY + 205)
    vector.set_font_size(20)
    vector.text(title, 30, CY + 225)
 
    presto.update()

def main():
    # Give enough time to breakout before wdt
    #time.sleep(10)
    # Reboot when non-responsive
    #wdt = WDT(timeout=8388)
    #wdt.feed()

    connect_to_wifi()

    show_clock()
 
    old_album = ""
    track = -1
    queue = None
    trackId = ""
    title = ""
    Title = ""
    artist = ""
    art_url = ""
    state = ""
    count = 0
 
    gc.collect()
    print("Free:", gc.mem_free())

    while True:
        try:
            response = requests.get("https://"+WIIM_IP+"/httpapi.asp?command=getPlayerStatus", timeout=TIMEOUT)
            if response.status_code == 200:
                data = response.json()
             
                if data["status"] == "play":
                    count = 0
                    try:
                        if Title != data["Title"]:
                            Title = data["Title"]
                            try:
                                response = requests.get("https://"+WIIM_IP+"/httpapi.asp?command=getMetaInfo", timeout=TIMEOUT)
                                data = response.json()["metaData"]
                                if art_url != data["albumArtURI"]:
                                    art_url = data["albumArtURI"]
                                    fetch_album_art(art_url)

                                artist = data["artist"]
                                if artist == "unknow":
                                    artist = data["subtitle"]
                                 
                                title = data["title"]
                                update_display(artist, title)
                            except Exception as e:
                                print("Error getting metadata:",e)
                    except Exception as e:
                        print(e)
                else:
                    count +=1
                    if count > 10:
                        count = 0
                        Title = ""
                        show_clock()
         
        except Exception as e:
            print("Connection error:", e)
            time.sleep(1)
            count += 1
            if count > 5:
                count = 0
                connect_to_wifi()
         
        time.sleep(1)
 
if __name__ == "__main__":
    main()

secrets.py:
Code:
WIFI_SSID = ""
WIFI_PASSWORD = ""
WIIM_IP = "192.168.68.xxx"
TIMEZONE_OFFSET = -8

The secrets.py entries are obvious; in my case, right now in California, time is -8 hours compared to GMT.


IMG_3516.jpg
IMG_3514.jpg
 

Attachments

  • IMG_3595.jpg
    IMG_3595.jpg
    279 KB · Views: 6
Last edited:
I haven't attempted to do anything but display at this point, though it's a touchscreen, so has the possibility to be used as a remote control for the WiiM, as well.

First of all, thank you @cc_rider!

I would say having only album art, title and artist has a real strength to this design and obviously works great for a near-square design.

With bringing in more info, controls etc you obviously have the functional work but also a graphical design challenge that isn't so easy to resolve.

Personally would be looking at a slightly bigger screen. But other priorities for now...
 
Looks amazing, i just did a similar thing using an esp32 display called "GUITION 4” 480x480 ESP32-S3-4848S040"
It's cheap and works great but it's my first dabble with anything simmilar to this. I only had experiance with some JS and react stuff.

I dont really know what i'm doing so i chatGPTd my way to making it fetch json and displaying the title, artist and album but never could i make it display the album art. I need to learn more and make new code from the ground up because currently it's an AI mess :D
 
Back
Top