Created
June 16, 2024 07:55
-
-
Save espired/95cee1243aba1c2fb4649ee161706c75 to your computer and use it in GitHub Desktop.
Spotify current playing - esp32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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