Nachdem der letzte Teil die Grundlagen der Datenabfrage über die Spotify API thematisiert hat, geht es in diesem Teil um eine benutzerfreundliche Darstellung auf einem Display. Als Display eignet sich das AZ-Touch, da es eine Touch-Funktion bietet, das Mikrocontrollerboard lediglich eingesteckt werden muss und am Ende im mitgelieferten Gehäuse optisch ansprechend betrieben werden kann. Über die Touch-Funktion wird eine Steuerung über die API umgesetzt.
Dieser Teil basiert auf dem ersten Teil, die Installation des ESP32 Paket wird vorausgesetzt.
Hardware:
Board mit ESP32-Chip, zum Beispiel: Dev Kit C V2 oder Dev Kit C V4 USB-C
AZ-Touch (2.8” empfohlen)
Software:
Um das ILI9341 Display des AZ-Touch über den ESP zu betreiben, eignet sich die Bibliothek TFT_eSPI von Bodmer. Des Weiteren wird für die Darstellung des Albumcovers die TJpeg decoder Library benötigt. Für das Ermitteln der Touch-Koordinaten wird die XPT2046 Library verwendet.
Die Libraries können über den jeweiligen Link als .zip heruntergeladen und in der Arduino IDE unter Sketch>Include Library>Add .ZIP Library… hinzugefügt werden.
Falls Sie PlatformIO verwenden, kopieren Sie folgendes unter lib_deps in der platformio.ini Datei:
paulstoffregen/XPT2046_Touchscreen@0.0.0-alpha+sha.26b691b2c8
bodmer/TFT_eSPI@^2.5.43
bodmer/TJpg_Decoder@^1.1.0
bblanchon/ArduinoJson@^7.2.1
Die Treiber und Anschlussdefinition müssen im Ordner der TFT_eSPI Bibliothek ausgewählt werden. Kopieren Sie dazu folgende Zeilen in die user_setup.h Datei im Verzeichnis der Bibliothek:
#define USER_SETUP_INFO "User_Setup" #define LOAD_FONT4
|
Alternativ kann die Datei auch hier heruntergeladen werden.
1 Anzeigen der Metadaten auf dem Display
1.1 Name, Interpret, Album
Da die Daten bereits in der getCurrentPlayingTrack() Funktion, aus dem ersten Teil, ermittelt werden, müssen diese nun lediglich in entsprechenden Variablen zwischengespeichert werden.
String title, artist, album, imgUrl, key; |
Das Darstellen auf dem Display ist in der printScreen() Funktion realisiert. Hier kann ein Text und ein y-Wert angegeben werden, der Text wird automatisch in der Mitte zentriert.
Des Weiteren steht die Methode cleanString() zur Verfügung. Diese entfernt Klammern und Bindestriche, damit der Text in eine Zeile des Displays passt. Die Länge wird zusätzlich auf 27 Zeichen begrenzt.
Dieses Darstellen der Textinformationen wird im finalen Programmcode in der printCurrentData() Funktion umgesetzt.
1.2 Album Cover
Um die Benutzeroberfläche ansprechender zu gestalten, soll das Album Coverbild auf dem Display angezeigt werden. Die API liefert bereits einen Link zu diesem Bild, dafür muss in der getCurrentPlayingTrack() Funktion die folgende Zeile eingefügt werden:
const char* albumImage = doc["item"]["album"]["images"][2]["url"];
Von der API werden drei verschiedene Bilder zur Verfügung gestellt, diese unterscheiden sich jedoch nur in der Größe und Auflösung. Da das Bild später in das interne Dateisystem im Flash geladen wird und das Herunterladen der Datei byteweise verläuft, empfiehlt es sich, das kleinste Bild (Index 2; 64x64 px) zu verwenden, da das Herunterladen sonst länger dauern und die Größe des internen Dateisystems nicht mehr ausreichen würde.
Das Coverbild wird mit der Funktion downloadImage() in das interne Dateisystem geladen.
HTTPClient http; |
Zuerst wird eine Verbindung zu der URL des Bildes hergestellt. Danach wird ein Stream erstellt und die Daten aus dem Stream in den Buffer, welcher daraufhin in die entsprechende Datei geschrieben wird.
Ist die Datei heruntergeladen, wird diese in printAlbumCover() mit Hilfe der TJpgDec Library auf das Display geladen.
1.3 Fortschrittsbalken
Des Weiteren soll ein Fortschrittsbalken dargestellt werden. In der getCurrentPlayingTrack() Funktion werden bereits die Gesamtdauer und die bereits gespielte Zeit über die API abgefragt und der aktuelle Fortschritt in Prozent berechnet.
Da der Balken aber kontinuierlich aktualisiert werden soll, um eine flüssige Darstellung zu erhalten und der API-Abruf etwas Zeit in Anspruch nimmt, wird bei jeder API-Abfrage die interne Zeit zwischengespeichert. Bei der finalen Berechnung wird die Zeit, welche seit der Abfrage vergangen ist, zur bereits gespielten Zeit dazugerechnet. Als Balken wird ein abgerundetes Rechteck mit einer maximalen Länge von 300 px verwendet. Da diese Länge 100% entspricht, kann die Länge durch Fortschritt*3 berechnet werden.
Dieser Fortschrittsbalken wird in printProgress() berechnet und auf dem Display dargestellt.
if(!playing) return; |
Falls nichts abgespielt wird, kann die Methode beendet werden, ansonsten wird der Fortschritt in Prozent berechnet. Zu der aus der API abgerufenen Zeit wird noch die Differenz aus der aktuellen Zeit und der Zeit der letzten API Abfrage addiert, um den Fortschrittsbalken unabhängig von API-Abfragen aktualisieren zu können.
Das komplette Programm können Sie hier herunterladen.
2 Playback Steuerung
Um diese Funktionen zu benutzen, ist ein Spotify Premium Account erforderlich.
Des Weiteren muss die Authentifikation aus dem ersten Teil noch einmal mit zusätzlichen Berechtigungen (scopes) durchgeführt werden. Benutzen Sie dazu folgenden Link: https://accounts.spotify.com/authorize?response_type=code&client_id=YOUR_CLIENT_ID&scope=user-read-currently-playing%20user-read-playback-state%20user-modify-playback-state%20user-read-playback-state&redirect_uri=http://localhost/callback
Im setup() muss der bisher gespeicherte Authorization Token gelöscht werden. Dafür sind folgende Befehle nötig:
prefs.begin("Token"); |
Nachdem der Token gelöscht ist, entfernen Sie die Befehle wieder und laden das Programm erneut hoch. Kopieren Sie den neuen Refresh Token wie im ersten Teil in den Seriellen Monitor.
Informationen zu den API-Aufrufen finden Sie in der Dokumentation.
Um die Bedienung zu erleichtern, sollen in der untersten Zeile Symbole in Form eines doppelten Dreiecks links und rechts (vorspulen, zurückspulen) und ein Dreieck bzw. Pausenzeichen in der Mitte dargestellt werden.
Diese Symbole werden am einfachsten durch die Grafikbefehle der Display Library realisiert. Die beiden Pfeile nach links und rechts können zusammen mit den Informationen einmalig auf dem Display dargestellt werden.
Anzeigen der Doppelpfeile in der printCurrentData() Funktion:
tft.fillTriangle(30, 190, 60, 175, 60, 205, TFT_WHITE); |
Das Symbol der mittleren Start/Stopp-Taste muss je nach Zustand geändert werden. Dies wird wie bei der Funktion printCurrentData() mit einer Hilfsvariable umgesetzt, welche mit dem aktuellen Wert verglichen und die Funktion nur bei einer Änderung aufgerufen wird.
Für das Pausen/Wiedergabe-Zeichen gibt es eine extra Funktion printMidButton():
tft.fillRect(140, 165, 50, 50, TFT_BLACK); |
Zuerst wird das bisher vorhandene Symbol mit einem schwarzen Quadrat überschrieben. Im Anschluss das entsprechende Symbol gezeichnet.
Um die Benutzereingaben zu verarbeiten, müssen die Touch-Koordinaten abgefragt und zugeordnet werden. Dies passiert in der Methode touch():
if(digitalRead(27)) return; |
Wurde das Display nicht berührt (Interrupt HIGH), wird die Funktion abgebrochen. Sonst werden die Touch-Koordinaten ermittelt. Befindet sich der berührte Punkt in einem festgelegten Bereich, so wird eine Verbindung zur entsprechenden URL aufgebaut. Bei jeder Verbindung werden noch Header mit der Autorisierung und der Content Length hinzugefügt.
Überprüfen Sie mit den Ausgaben im Seriellen Monitor, ob die Koordinaten mit den Bereichen übereinstimmen und korrigieren Sie diese bei Bedarf.
Das komplette Programm können Sie hier herunterladen.
Nachdem das Projekt getestet ist, kann die Rückseite des AZ-Touch-Gehäuses an eine Wand geschraubt und mit einem Netzteil standalone betrieben werden. Hiermit ist das Projekt abgeschlossen. Gerne können Sie auch noch weitere Funktionen der Spotify API integrieren. Auch die Farben der Symbole und des Fortschrittsbalkens können beliebig geändert werden.
Viel Spaß beim Nachbauen :)
Spotify und Spotify Premium sind eingetragene Marken von Spotify AB. Dieser Blog steht in keiner Verbindung zu Spotify AB und wird weder von Spotify AB gesponsert noch unterstützt. Alle in diesem Beitrag verwendeten Markennamen, Logos und Warenzeichen gehören ihren jeweiligen Eigentümern und werden ausschließlich zur Beschreibung oder Identifikation verwendet.