Skip to content

Instantly share code, notes, and snippets.

@espired
Created June 16, 2024 07:55
Show Gist options
  • Save espired/95cee1243aba1c2fb4649ee161706c75 to your computer and use it in GitHub Desktop.
Save espired/95cee1243aba1c2fb4649ee161706c75 to your computer and use it in GitHub Desktop.
Spotify current playing - esp32
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>
// WiFi credentials
const char* ssid = "SSID";
const char* password = "PASSWORD";
// Spotify credentials
const char* spotifyClientID = "SPOTIFY_CLIENT_ID_HERE";
const char* spotifyClientSecret = "SPOTIFY_CLIENT_SECRET_HERE";
String accessToken = "ACCESS_TOKEN"; // Hard-code the access token obtained manually
String refreshToken = "REFRESH_TOKEN";
const char* spotifyApiUrl = "https://api.spotify.com/v1/me/player";
// OLED display initialization
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/22, /* data=*/21);
// Global variables for track information
String currentTrackName = "";
String currentArtistName = "";
int currentTrackDuration = 0;
int currentTrackProgress = 0;
bool isPlaying = false;
void setup();
void loop();
void displayCurrentTrack();
void updateTrackInfo();
String renewAccessToken();
// Define LED_BUILTIN if not already defined
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
// Setup function: initializes serial communication, WiFi, display, and tasks
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB08_tr);
pinMode(LED_BUILTIN, OUTPUT); // Set the LED pin mode
// Create tasks for each core to handle different operations
xTaskCreatePinnedToCore(taskUpdateTrackInfo, "UpdateTrackInfo", 10000, NULL, 1, NULL, 0); // Task 1 on Core 0
xTaskCreatePinnedToCore(taskDisplayTrackInfo, "DisplayTrackInfo", 10000, NULL, 1, NULL, 1); // Task 2 on Core 1
}
void loop() {
// Main loop can be left empty if all work is done in tasks
}
// Task to update track information periodically
void taskUpdateTrackInfo(void * parameter) {
while (true) {
updateTrackInfo();
delay(2000); // Update every 2 seconds
}
}
// Task to display current track information and control LED based on playback status
void taskDisplayTrackInfo(void * parameter) {
while (true) {
displayCurrentTrack();
if (isPlaying) {
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(200);
}
delay(1500); // Update display every 1.5 seconds
}
}
// Function to renew Spotify access token using the refresh token
String renewAccessToken() {
HTTPClient http;
http.begin("https://accounts.spotify.com/api/token");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
String httpRequestData = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=" + spotifyClientID + "&client_secret=" + spotifyClientSecret;
http.POST(httpRequestData);
String response = http.getString();
http.end();
DynamicJsonDocument doc(1024);
deserializeJson(doc, response);
return doc["access_token"].as<String>();
}
// Function to fetch and update current track information from Spotify
void updateTrackInfo() {
HTTPClient http;
http.begin(String(spotifyApiUrl) + "/currently-playing");
http.addHeader("Authorization", "Bearer " + accessToken);
int httpResponseCode = http.GET();
if (httpResponseCode == 401) { // Token expired
accessToken = renewAccessToken();
http.begin(String(spotifyApiUrl) + "/currently-playing");
http.addHeader("Authorization", "Bearer " + accessToken);
httpResponseCode = http.GET();
}
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println(response);
DynamicJsonDocument doc(1024);
deserializeJson(doc, response);
currentTrackName = doc["item"]["name"].as<String>();
currentArtistName = doc["item"]["artists"][0]["name"].as<String>();
currentTrackDuration = doc["item"]["duration_ms"].as<int>();
currentTrackProgress = doc["progress_ms"].as<int>();
isPlaying = doc["is_playing"].as<bool>();
} else {
Serial.println("HTTP GET request failed with error: " + String(httpResponseCode));
currentTrackName = "No Data";
currentArtistName = "";
currentTrackDuration = 0;
currentTrackProgress = 0;
isPlaying = false;
}
http.end();
}
// Function to display the current track on the OLED display
void displayCurrentTrack() {
u8g2.clearBuffer(); // Clear the buffer before drawing new frame
u8g2.setDrawColor(1); // Set draw color to white
u8g2.drawBox(0, 0, 128, 16); // Draw a filled box for the inverted background
u8g2.setDrawColor(0); // Set draw color to black
u8g2.drawStr(0, 12, "Now Playing:"); // Draw the "Now Playing:" text with inverted background
u8g2.setDrawColor(1); // Reset draw color to white for the rest of the text
if (u8g2.getStrWidth(currentTrackName.c_str()) > 128) {
currentTrackName = currentTrackName.substring(0, 125) + "...";
}
u8g2.drawStr(0, 24, currentTrackName.c_str());
u8g2.drawStr(0, 36, currentArtistName.c_str());
// Draw progress bar with stroke
int progressBarWidth = map(currentTrackProgress, 0, currentTrackDuration, 0, 128);
u8g2.drawFrame(0, 54, 128, 10); // Draw the frame for the progress bar
u8g2.drawBox(0, 54, progressBarWidth, 10); // Draw filled progress bar based on track progress
u8g2.sendBuffer();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment