One day, entirely without active pleading on my part, I was handed an Amazon Echo Dot device. I wasn’t aware of the lab next door buying such devices, but apparently they had one to spare, so they kindly asked if I wanted it. It’s a rather engaging little thing, has a nice voice when it answers you, and of course, it reports everything that is said in the 3D + Robo Lab to Jeff Bezos.
But it has a good sound, and as it is able to take verbal commands, it’s quite useful. No matter where I am in the Lab, I can just say, “Alexa – play Kraftwerk”, and sure enough, Kraftwerk starts to emanate from the speakers. However, when a playlist runs out, the system keeps playing other songs in the genre. If I am not familiar with the song that plays, I need to go to my computer, open Spotify (Alexa plays independently from my computer’s Spotify), and then check the song and band. Or simply ask Alexa and concede defeat in not knowing the band, but that’s of course not possible due to issues of pride.
My son donated to me his subwoofer / loudspeaker combo, after he bought infinitely better ones and a juvenile delinquent Dachshund puppy had a go at the subwoofer box. The visual damage is negligible, and luckily the sound is not affected at all by the needle-like teeth. I then wired the extra speakers via the Echo Dot so as to get more oooomph in the base department.
So, rather soon after acquiring the Dot, the idea came to me to see if someone has created an ESP32 library that would enable me to build a Spotify song display. A quick google run will show you Brian Lough’s excellent library for doing just that on GitHub. You need to create a Spotify API key for yourself, get some identifier strings from there, and then run one INO file that Brian supplied on Github page to receive a key to match your ESP32 to your Spotify account, but it’s not a complicated process.
Setting up ESP32 and Spotify
First you must go to the Spotify Web API page to get your account set up. This will give you access to the Developer Dashboard where you can create test apps for your web pages and get to know the interface. When you create the account, you will receive a client ID, which is a long string of characters. Note it down, you will need it. You will also see a link for a Client Secret, which you will also need, but never disclose either of these to anyone, because that could compromise your account.
Next, use Brian’s file called getrefreshToken.ino. This file, available as all the files on the GitHub, needs to be run on your ESP32. When you run it, its Serial Monitor will return to you the last piece of identification that you need to successfully access your Spotify with ESP32.
So, download the file, install the libraries that are required, and update the following before running the file:
char ssid[] = "SSID"; // your network SSID (name)
char password[] = "password"; // your network password
char clientId[] = "56t4373258u3405u43u543"; // Your client ID of your spotify APP
char clientSecret[] = "56t4373258u3405u43u543"; // Your client Secret of your spotify APP (Do Not share this!)
When you then run the code, it will show the refresher code in the Serial Monitor. Note it down and keep it secure.
After this you can move to the other files in the examples folder on GitHub, such as GetCurrentlyPlaying.ino. The relevant parts in it are these:
//------- Replace the following! ------
char ssid[] = "SSID"; // your network SSID (name)
char password[] = "password"; // your network password
char clientId[] = "56t4373258u3405u43u543"; // Your client ID of your spotify APP
char clientSecret[] = "56t4373258u3405u43u543"; // Your client Secret of your spotify APP (Do Not share this!)
// Country code, including this is advisable
#define SPOTIFY_MARKET "FI" //market code is your country's two letter country code
#define SPOTIFY_REFRESH_TOKEN "AAAAAAAAAABBBBBBBBBBBCCCCCCCCCCCDDDDDDDDDDD" //this is again a no-share piece of data which you get when you run the Spotify
Add your own, and your ESP32 then has access to your Spotify account. In this example, ESP32 goes to Spotify every 60 seconds to check what is playing, and then shows the data to you on the Serial Monitor. Which is all fine and dandy, when you want to move to the next level, ie. install a display to show whatever is showing and not have to use the Serial Monitor for that.
Installing and using an e-Paper display
The e-Paper displays I bought for the Lab are Waveshare 2.9″ black and white displays, which are clear to read. They come with a bunch of libraries, which you must install, and the setup is rather complex, but when you install the libraries, you get the examples into the File / Examples menu, so you can just try them out first and get used to them.
The good thing is that you get various fonts too:
#include <GxEPD.h>
#include <GxGDEH029A1/GxGDEH029A1.h> // 2.9" b/w
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoBold24pt7b.h>// this font is referred to below with the name maxfontmono1
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <Fonts/FreeSansBoldOblique9pt7b.h>
#include <Fonts/FreeSansBoldOblique12pt7b.h>
#include <Fonts/FreeSansBoldOblique18pt7b.h>
#include <Fonts/FreeSansBoldOblique24pt7b.h>
const char* maxfontmono1 = "FreeMonoBold24pt7b"; // << see? maxfontmono1
const GFXfont* maxfmono = &FreeMonoBold24pt7b;
const char* midfontmono2 = "FreeMonoBold18pt7b";
const GFXfont* midfmono = &FreeMonoBold18pt7b;
const char* smallfontmono3 = "FreeMonoBold12pt7b";
const GFXfont* smallfmono = &FreeMonoBold12pt7b;
const char* tinyfontmono4 = "FreeMonoBold9pt7b";
const GFXfont* tinyfmono = &FreeMonoBold9pt7b;
const char* maxfontsans1 = "FreeSans24pt7b";
const GFXfont* maxfsans = &FreeSans24pt7b;
const char* midfontsans2 = "FreeSans18pt7b";
const GFXfont* midfsans = &FreeSans18pt7b;
const char* smallfontsans3 = "FreeSans12pt7b";
const GFXfont* smallfsans = &FreeSans12pt7b;
const char* tinyfontsans4 = "FreeSans9pt7b";
const GFXfont* tinyfsans = &FreeSans9pt7b;
const char* maxfontsansbold1 = "FreeSansBold24pt7b";
const GFXfont* maxfsansbold = &FreeSansBold24pt7b;
const char* midfontsansbold2 = "FreeSansBold18pt7b";
const GFXfont* midfsansbold = &FreeSansBold18pt7b;
const char* smallfontsansbold3 = "FreeSansBold12pt7b";
const GFXfont* smallfsansbold = &FreeSansBold12pt7b;
const char* tinyfontsansbold4 = "FreeSansBold9pt7b";
const GFXfont* tinyfsansbold = &FreeSansBold9pt7b;
const char* maxfontsansboldOblique1 = "FreeSansBoldOblique24pt7b";
const GFXfont* maxfsansboldOblique = &FreeSansBoldOblique24pt7b;
const char* midfontsansboldOblique2 = "FreeSansBoldOblique18pt7b";
const GFXfont* midfsansboldOblique = &FreeSansBoldOblique18pt7b;
const char* smallfontsansboldOblique3 = "FreeSansBoldOblique12pt7b";
const GFXfont* smallfsansboldOblique = &FreeSansBoldOblique12pt7b;
const char* tinyfontsansboldOblique4 = "FreeSansBoldOblique9pt7b";
const GFXfont* tinyfsansboldOblique = &FreeSansBoldOblique9pt7b;
This may look complicated, but all you do is install font libraries, then create names for the fonts to use when you refer to them. You’ll see in a moment.
This is how it looks when you run ine of the demo files:
ESP32 pins to e-paper
There are eight pins you need to hook up in the ESP32:
e-Paper | ESP32 |
Vcc | 3.3V |
GND | GND |
DIN | GPIO23 |
CLK | GPIO18 |
CS | GPIO5 |
DC | GPIO17 |
RST | GPIO16 |
BUSY | GPIO4 |
Optional – if you want to have a reset button, solder it between GND and EN on ESP32 |
After that, code like this will initialize the screen and allow you to write whatever you want.
display.init();
display.setRotation(1);
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setFont(smallfsansbold);
display.setCursor(30, 30);
display.println("Spotify Song Display");
display.println();
display.setCursor(60, 70);
display.setFont(tinyfsans);
display.println("Entering network and");
display.setCursor(60, 90);
display.println("getting Spotify data...");
display.update();
SetRotation(1) makes the display horizontal. FillScreen and setTextColor are rather self-explanatory, as is setFont. The name you use here is the one you set in the setup part of the program. The setCursor function is used to place the text on the screen, and it takes some practice to get it right. This part shows the text while the system enters the WLAN and connects to Spotify.
The box
This is a really rough 20 minute modeling job, so don’t expect to be printing out anything that would win the Scandinavian Design Award any time soon. There is a box with a hole for the display, a curved stand for the Echo Dot, a hole to lead the old mobile phone charger wire through to the ESP32, and a bottom plate with screws.
But hey, it works.
The actual Spotify data pickup
In the main loop of the program, a function call asks Spotify for the song that is currently playing.
int status = spotify.getCurrentlyPlaying(printCurrentlyPlayingToSerial, SPOTIFY_MARKET);
The reason it goes into an int variable called status is that if the request returns 200, everything is fine and we can go on to show the data. Otherwise, if it’s 204, Spotify isn’t playing anything, and if it returns anything else, we have an error, and it is carried in the payload of the status.
if (status == 200)
{
Serial.println("Successfully got currently playing");
}
else if (status == 204)
{
Serial.println("Doesn't seem to be anything playing");
}
else
{
Serial.print("Error: ");
Serial.println(status);
}
Going to the spotify.getCurrentlyPlaying() function, we have all the data in an object called currentlyPlaying:
Serial.println(currentlyPlaying.trackName);
mySong = currentlyPlaying.trackName;
Serial.println("Artists: ");
for (int i = 0; i < currentlyPlaying.numArtists; i++)
{
Serial.print("Name: ");
Serial.println(currentlyPlaying.artists[i].artistName);
}
myArtist = currentlyPlaying.artists[0].artistName;
Serial.print("Album: ");
Serial.println(currentlyPlaying.albumName);
myAlbum = currentlyPlaying.albumName;
long progress = currentlyPlaying.progressMs; // duration passed in the song
long duration = currentlyPlaying.durationMs; // Length of Song
Serial.print("Elapsed time of song (ms): ");
Serial.print(progress);
Serial.print(" of ");
Serial.println(duration);
Serial.println();
The variables mySong, my Artist, and myAlbum are then used in the function called drawSpotifyData:
void drawSpotifyData()
{
display.setFont(tinyfsans);
display.setCursor(0, 20);
display.println("Song: " + mySong);
display.println();
display.println("Artist: " + myArtist);
display.println("Album: " + myAlbum);
}
And there you have it. Every three seconds, the loop runs, and goes to check whether mySong is the same as myOldSong. If yes, it doesn’t update the screen, but if th esong name has changed, the screen is redrawn with new song data.
I had it first set up so that since we know the length of each song, I used that information as the length of the delay, but if you call out “Alexa – next!” , the screen wouldn’t update until the old song would have run its course. Therefore I opted for a 3 second delay.
That’s basically it. My code is available on GitHub, and Brian’s fantastic libraries are there too. The STL files for printing the box and the Echo Dot frame are there too.
If you build this device, it’d be cool to hear from you!