//This code allows an app to send a URI to the ESP32 via a POST request. // The ESP32 then writes the URI to an NFC card using the MFRC522 library. // The NFC card is then read and the URI is used to start playback on Spotify. // The code uses the Spotify API to control playback on a Spotify client. #include #include #include #include #include #include char* SSID = "SSID_NAME"; const char* PASSWORD = "SSID_PASSWORD"; const char* CLIENT_ID = "SPOTIFY_CLIENT_ID"; const char* CLIENT_SECRET = "SPOTIFY_SECRET_KEY"; const char* REFRESH_TOKEN = "SPOTIFY_REFRESH_TOKEN"; //Having a refresh token is required to not login every time you start the ESP32. #define RST_PIN D6 #define SS_PIN D7 MFRC522 mfrc522(SS_PIN, RST_PIN); Spotify sp(CLIENT_ID, CLIENT_SECRET,REFRESH_TOKEN); WebServer server(80); String receivedUri = ""; // <- store latest URI bool writePending = false; // <- flag to indicate pending write void setup() { Serial.begin(115200); SPI.begin(); mfrc522.PCD_Init(); connect_to_wifi(); server.on("/uri", HTTP_POST, handleUriPost); server.begin(); Serial.println("HTTP server started"); sp.begin(); while(!sp.is_auth()){ sp.handle_client(); } Serial.println("Authenticated"); } void loop() { server.handleClient(); if (writePending) { write_nfc(); } read_nfc(); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); delay(3000); } void connect_to_wifi(){ WiFi.begin(ssid, password); Serial.print("Connecting to WiFi..."); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.printf("\nConnected to WiFi\n"); Serial.println(WiFi.localIP()); } void handleUriPost() { if (server.hasArg("uri")) { receivedUri = server.arg("uri"); writePending = true; // mark that we need to write Serial.println("Received URI: " + receivedUri); server.send(200, "text/plain", "URI received: " + receivedUri); } else { server.send(400, "text/plain", "Missing 'uri' parameter"); } } void write_nfc(){ Serial.println("Waiting for NFC card to write..."); MFRC522::MIFARE_Key key; for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // Default key if (!mfrc522.PICC_IsNewCardPresent()) return; if (!mfrc522.PICC_ReadCardSerial()) return; Serial.print(F("Card UID: ")); for (byte i = 0; i < mfrc522.uid.size; i++) { Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); Serial.print(mfrc522.uid.uidByte[i], HEX); } Serial.println(); // Prepare data byte buffer1[16]; byte buffer2[16]; const char* playlistID = receivedUri.c_str(); size_t len = strlen(playlistID); // Fill buffer1 with first 16 bytes for (byte i = 0; i < 16; i++) { buffer1[i] = (i < len) ? playlistID[i] : ' '; } // Fill buffer2 with next 16 bytes (if any) for (byte i = 0; i < 16; i++) { byte index = i + 16; buffer2[i] = (index < len) ? playlistID[index] : ' '; } // Write to block 1 byte block = 1; MFRC522::StatusCode status; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.print(F("Auth failed for block 1: ")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Write(block, buffer1, 16); if (status != MFRC522::STATUS_OK) { Serial.print(F("Write failed for block 1: ")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } Serial.println(F("Block 1 written successfully.")); // Write to block 2 block = 2; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.print(F("Auth failed for block 2: ")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } status = mfrc522.MIFARE_Write(block, buffer2, 16); if (status != MFRC522::STATUS_OK) { Serial.print(F("Write failed for block 2: ")); Serial.println(mfrc522.GetStatusCodeName(status)); return; } Serial.println(F("Block 2 written successfully.")); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); Serial.println(F("Done writing Spotify ID!")); writePending = false; // clear the flag after writing delay(3000); } void read_nfc(){ MFRC522::MIFARE_Key key; for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // Default key if (!mfrc522.PICC_IsNewCardPresent()) return; //skip everything if the card present is not new if (!mfrc522.PICC_ReadCardSerial()) return; //skip everything if the card is new but the data is same Serial.print(F("Card UID: ")); for (byte i = 0; i < mfrc522.uid.size; i++) { Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); Serial.print(mfrc522.uid.uidByte[i], HEX); } Serial.println(); byte buffer1[18]; // 16 bytes + 2 for CRC byte size = sizeof(buffer1); byte block; // Read block 1 block = 1; if (!authenticateAndRead(block, buffer1, size, key)) return; // Read block 2 block = 2; byte buffer2[18]; size = sizeof(buffer2); if (!authenticateAndRead(block, buffer2, size, key)) return; // Combine both buffers into one string char context_uri[64]; // 32 + null terminator for (int i = 0; i < 16; i++) { context_uri[i] = (char)buffer1[i]; context_uri[i + 16] = (char)buffer2[i]; } context_uri[32] = '\0'; // null terminator // Trim trailing spaces for (int i = 31; i >= 0; i--) { if (context_uri[i] == ' ') { context_uri[i] = '\0'; } else { break; } } char final_uri[80]; snprintf(final_uri, sizeof(final_uri), "spotify:%s", context_uri); //concatenating 'spotify:playlist:'+'context_uri' into 'final_uri' Serial.print(F("Context URI: ")); Serial.println(final_uri); response resp = sp.start_resume_playback(final_uri, 0, 0, nullptr); //send the put message to play the final_uri if (resp.status_code == 204 || resp.status_code == 200) { Serial.println(F("Playback started successfully!")); } else { Serial.print(F("Failed to start playback. HTTP code: ")); Serial.println(resp.status_code); } // Serial.print(F("Playlist ID: ")); // Serial.println(context_uri); } bool authenticateAndRead(byte block, byte *buffer, byte &size, MFRC522::MIFARE_Key &key) { MFRC522::StatusCode status; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.print(F("Authentication failed for block ")); Serial.print(block); Serial.print(F(": ")); Serial.println(mfrc522.GetStatusCodeName(status)); return false; } status = mfrc522.MIFARE_Read(block, buffer, &size); if (status != MFRC522::STATUS_OK) { Serial.print(F("Read failed for block ")); Serial.print(block); Serial.print(F(": ")); Serial.println(mfrc522.GetStatusCodeName(status)); return false; } return true; }