├── README.md ├── esp32_spotify └── esp32_spotify.ino └── images ├── add.jpg ├── app.jpg ├── cookie.jpg └── edit.jpg /README.md: -------------------------------------------------------------------------------- 1 | # esp32-spotify-oled 2 | View your current playing song on ESP32 & OLED display using Spotify API 3 | 4 | ### Libraries 5 | - [`OLED_I2C`](http://www.rinkydinkelectronics.com/library.php?id=79) 6 | 7 | 8 | ### Spotify 9 | - Visit Spotify Dashboard [`https://developer.spotify.com/dashboard/login`](https://developer.spotify.com/dashboard/login) 10 | - [`Create an app`](https://developer.spotify.com/documentation/general/guides/authorization/app-settings/) 11 | - Copy Client ID to sketch 12 | - Add Redirect URI http://httpbin.org/anything & Save in `Edit Settings` 13 | 14 | ### Authentication 15 | - Authenticate the app using your browser (Chrome) 16 | - Enable Developer tools `Ctrl+Shift+I` 17 | - Add Client ID & Redirect URI (URL Encoded) -> `http%3A%2F%2Fhttpbin.org%2Fanything` 18 | - Authentication Link: `https://accounts.spotify.com/authorize?response_type=token&redirect_uri=" + redirect + "&client_id=" + client_id + "&scope=user-read-playback-state+user-read-playback-position+user-modify-playback-state&state=cryq3` 19 | - It should redirect to the specified URI with an Access Token on the URL 20 | - Copy the Cookie value to the sketch 21 | 22 | ![image](images/cookie.jpg?raw=true "image") 23 | -------------------------------------------------------------------------------- /esp32_spotify/esp32_spotify.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | char * WIFI_SSID = "__WIFI_SSID__"; 9 | char * WIFI_PASS = "__WIFI_PASSWORD__"; 10 | 11 | String client_id = "__YOUR_CLIENT_ID__"; 12 | String redirect = "http%3A%2F%2Fhttpbin.org%2Fanything"; // redirect link -> http://httpbin.org/anything 13 | String cookie = "__YOUR_COOKIE_HERE__"; 14 | 15 | long current = 0, duration = 0; 16 | int volume; 17 | String artists = "", title, album, device, type; 18 | bool isActive, playing; 19 | 20 | bool isExpired = false; 21 | String token = ""; 22 | 23 | WiFiMulti wifiMulti; 24 | OLED myOLED(21, 22); //(SDA, SCL) 25 | 26 | extern uint8_t SmallFont[], MediumNumbers[]; 27 | 28 | void setup() { 29 | Serial.begin(115200); 30 | Serial.println("Starting device"); 31 | wifiMulti.addAP(WIFI_SSID, WIFI_PASS); 32 | 33 | if (!myOLED.begin(SSD1306_128X64)) { 34 | while (1); // In case the library failed to allocate enough RAM for the display buffer... 35 | } 36 | myOLED.setFont(SmallFont); 37 | 38 | Serial.print("Waiting for WiFi to connect..."); 39 | 40 | myOLED.clrScr(); 41 | myOLED.print("Connecting...", LEFT, 12); 42 | myOLED.update(); 43 | 44 | while ((wifiMulti.run() != WL_CONNECTED)) { 45 | Serial.print("."); 46 | } 47 | Serial.println("Connected"); 48 | myOLED.clrScr(); 49 | myOLED.print("Connected", LEFT, 12); 50 | myOLED.update(); 51 | 52 | getToken(); 53 | 54 | } 55 | 56 | void loop() { 57 | 58 | if (isExpired) { 59 | getToken(); 60 | } 61 | 62 | getPlayer(); 63 | updateScreen(); 64 | 65 | } 66 | 67 | void updateScreen() { 68 | myOLED.clrScr(); 69 | myOLED.setFont(SmallFont); 70 | myOLED.print(title, LEFT, 0); 71 | myOLED.print(artists, LEFT, 12); 72 | myOLED.print(album, LEFT, 24); 73 | myOLED.print(device, LEFT, 36); 74 | myOLED.print(String(volume), RIGHT, 36); 75 | myOLED.drawRect(0, 48, 127, 52); 76 | int pr = map(current, 0, duration, 0, 127); 77 | myOLED.drawRectFill(0, 48, pr, 52); 78 | myOLED.print(playing ? ">" : "||", LEFT, 56); 79 | // int mC = current / 60000; 80 | // int sC = (current % 60000) / 1000; 81 | // myOLED.print(String(mC) + ":" + (sC < 10 ? "0" : "") + String(sC), 64, 56); 82 | int m = duration / 60000; 83 | int s = (duration % 60000) / 1000; 84 | myOLED.print(String(m) + ":" + (s < 10 ? "0" : "") + String(s), RIGHT, 56); 85 | myOLED.update(); 86 | } 87 | 88 | 89 | 90 | void getToken() { 91 | HTTPClient http; 92 | String url = "https://accounts.spotify.com/authorize?response_type=token&redirect_uri=" + redirect + "&client_id=" + client_id + "&scope=user-read-playback-state+user-read-playback-position+user-modify-playback-state&state=cryq3"; 93 | Serial.println(url); 94 | http.begin(url); 95 | http.addHeader("Cookie", cookie); 96 | int httpCode = http.GET(); 97 | 98 | String payload = http.getLocation(); 99 | token = payload.substring(payload.indexOf("access_token=") + 13, payload.indexOf("&token_type")); 100 | Serial.print("Token: "); 101 | Serial.println(token); 102 | 103 | http.end(); 104 | } 105 | 106 | 107 | void getPlayer() { 108 | HTTPClient http; 109 | http.begin("https://api.spotify.com/v1/me/player"); 110 | http.addHeader("Content-Type", "application/json"); 111 | http.addHeader("Accept", "application/json"); 112 | http.addHeader("Authorization", "Bearer " + token); 113 | int httpCode = http.GET(); 114 | String payload = http.getString(); 115 | //Serial.println(payload); 116 | if (httpCode == HTTP_CODE_OK) { 117 | DynamicJsonDocument json(20000); 118 | deserializeJson(json, payload); 119 | 120 | title = json["item"]["name"].as(); 121 | album = json["item"]["album"]["name"].as(); 122 | artists = ""; 123 | JsonArray arr = json["item"]["artists"].as(); 124 | for (JsonVariant value : arr) { 125 | artists += value["name"].as() + " "; 126 | } 127 | 128 | current = json["progress_ms"].as(); 129 | duration = json["item"]["duration_ms"].as(); 130 | playing = json["is_playing"].as(); 131 | 132 | device = json["device"]["name"].as(); 133 | isActive = json["device"]["is_active"].as(); 134 | type = json["device"]["type"].as(); 135 | volume = json["device"]["volume_percent"].as(); 136 | 137 | 138 | // Serial.print("Title: "); 139 | // Serial.println(title); 140 | // Serial.print("Artist: "); 141 | // Serial.println(artists); 142 | // Serial.print("Album: "); 143 | // Serial.println(album); 144 | // Serial.print("Duration: "); 145 | // Serial.println(duration); 146 | // Serial.print("Current: "); 147 | // Serial.println(current); 148 | // Serial.print("Playing: "); 149 | // Serial.println(playing); 150 | // Serial.print("Device: "); 151 | // Serial.println(device); 152 | // Serial.print("Type: "); 153 | // Serial.println(type); 154 | // Serial.print("Active: "); 155 | // Serial.println(isActive); 156 | // Serial.print("Volume: "); 157 | // Serial.println(volume); 158 | 159 | isExpired = false; 160 | 161 | } else { 162 | current = 0, duration = 10; 163 | volume = 0; 164 | artists = "", title = "ERROR", album = "Token expired", device = "", type = ""; 165 | isActive = false, playing = false; 166 | isExpired = true; 167 | } 168 | 169 | http.end(); 170 | 171 | } 172 | -------------------------------------------------------------------------------- /images/add.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbiego/esp32-spotify-oled/1b905740ca6ec2d3f8d88a1d3aaee19408d1dc0d/images/add.jpg -------------------------------------------------------------------------------- /images/app.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbiego/esp32-spotify-oled/1b905740ca6ec2d3f8d88a1d3aaee19408d1dc0d/images/app.jpg -------------------------------------------------------------------------------- /images/cookie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbiego/esp32-spotify-oled/1b905740ca6ec2d3f8d88a1d3aaee19408d1dc0d/images/cookie.jpg -------------------------------------------------------------------------------- /images/edit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbiego/esp32-spotify-oled/1b905740ca6ec2d3f8d88a1d3aaee19408d1dc0d/images/edit.jpg --------------------------------------------------------------------------------