Created
October 24, 2016 19:16
-
-
Save dirkjanfaber/3c05c2cc8e9a8cf5e05af45585eba82e to your computer and use it in GitHub Desktop.
Catch spotify song and display it on an esp98266 OLED
This file contains 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
/* | |
Spotify catcher | |
Functions as a "man in the middle" for the Spotify Client scrobbles that | |
go to post.audioscrobbler.com (last.fm). | |
Works on an esp8266 (Arduino IDE). Connect an oled to pin 3 (SDA) and | |
pin4 (SCL) to show the currently playing song. | |
Make sure you put the IP of the esp9266 into /etc/hosts with the name | |
post.audioscrobbler.com. | |
*/ | |
#include <ESP8266WiFi.h> | |
#include <ESP8266mDNS.h> | |
#include <WiFiClient.h> | |
#include <Credentials.h> | |
#include "SSD1306.h" | |
const char* ssid = WIFI; | |
const char* password = WIFIPW; | |
// TCP server at port 80 will respond to HTTP requests | |
WiFiServer server(80); | |
// SDA on GPIO4, SDC on GPIO5 | |
SSD1306 display(0x3c, 4, 5); | |
char *req = (char *)malloc(256); | |
char *path = (char *)malloc(64); | |
void get_param(char * str, char * value, char p) { | |
int start, length; | |
for (int i=0; i<(strlen(str)-2); i++) { | |
if ( str[i] == p && str[i+1] == '=' ) { | |
start = i + 2; | |
i=strlen(str); | |
} | |
} | |
length = 0; | |
if ( start > 0 ) { | |
for ( int i=start; i<strlen(str); i++ ) { | |
if ( str[i] == '&' ) | |
break; | |
length++; | |
} | |
} | |
if ( length > 0 ) | |
memcpy(value, &str[start], length); | |
value[length] = '\0'; | |
return; | |
} | |
void update_lastfm( char *req, char *str, char *feedback ) { | |
WiFiClient client; | |
const char* host = "post.audioscrobbler.com"; | |
const int httpPort = 80; | |
if (!client.connect(host, httpPort)) { | |
Serial.println("last.fm unreachable"); | |
} | |
char *post = req; | |
// char *replace = (char *)"Host: post.audioscrobbler.com"; | |
// First find "Host:" | |
// char *rest = strstr(str, "Host:"); | |
// Determine where to start replacing | |
// int s = (int)(rest - str); | |
// The rest of the string starts after \n | |
// rest = strstr(rest, "\n"); | |
// Determine how long the replacement text is. | |
// int l = 0; | |
// while ( rest[l] != '\n' ) { l++; }; | |
// Allocate memory | |
// char *output = (char *)malloc(strlen(post)+strlen(str)+strlen(replace)-l); | |
char *output = (char *)malloc(strlen(post)+strlen(str)); | |
memcpy(output, post, strlen(post)); | |
memcpy(output+strlen(post), str, strlen(str)); | |
output[strlen(output)+strlen(post)] = '\0'; | |
// memcpy(output+strlen(post), str, s); | |
// memcpy(output+strlen(post)+s, replace, strlen(replace)); | |
// memcpy(output+strlen(post)+s+strlen(replace), rest, strlen(rest)); | |
// output[strlen(post)+s+strlen(replace)+strlen(rest)] = '\0'; | |
// Send the new output to last.fm | |
client.print(output); | |
delay(10); | |
// catch the feedback | |
int i=0; | |
while(client.available()) { | |
feedback[i++] = client.read(); | |
} | |
feedback[i] = '\0'; | |
delay(10); | |
free(output); | |
client.flush(); | |
} | |
int url_decode(char *str, char *dst) { | |
unsigned int i; | |
char *ptr = dst; | |
for (i=0; i < strlen(str); i++) { | |
if (str[i] == '+') { | |
*ptr++ = ' '; | |
continue; | |
} | |
if (str[i] != '%') { | |
*ptr++ = str[i]; | |
continue; | |
} | |
if (!isdigit(str[i+1]) || !isdigit(str[i+2])) { | |
*ptr++ = str[i]; | |
continue; | |
} | |
*ptr++ = ((str[i+1] - '0') << 4) | (str[i+2] - '0'); | |
i += 2; | |
} | |
*ptr = '\0'; | |
return 0; | |
} | |
void setup(void) | |
{ | |
Serial.begin(115200); | |
// Connect to WiFi network | |
WiFi.begin(ssid, password); | |
Serial.println(""); | |
// Wait for connection | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println(""); | |
Serial.print("Connected to "); | |
Serial.println(ssid); | |
Serial.print("IP address: "); | |
Serial.println(WiFi.localIP()); | |
// Set up mDNS responder: | |
// - first argument is the domain name, in this example | |
// the fully-qualified domain name is "esp8266.local" | |
// - second argument is the IP address to advertise | |
// we send our IP address on the WiFi network | |
if (!MDNS.begin("esp8266")) { | |
Serial.println("Error setting up MDNS responder!"); | |
while(1) { | |
delay(1000); | |
} | |
} | |
// Start TCP (HTTP) server | |
server.begin(); | |
// Add service to MDNS-SD | |
MDNS.addService("http", "tcp", 80); | |
display.init(); | |
display.clear(); | |
display.setFont(ArialMT_Plain_16); | |
display.drawString(0, 0, "Spotify catcher"); | |
display.display(); | |
} | |
void loop(void) | |
{ | |
// Check if a client has connected | |
WiFiClient client = server.available(); | |
if (!client) { | |
return; | |
} | |
Serial.println("New client"); | |
// Wait for data from client to become available | |
while(client.connected() && !client.available()){ | |
delay(1); | |
} | |
// Read the first line of HTTP request | |
client.readBytesUntil('\r', req, 255); | |
// Serial.print("req = ["); Serial.print(req); Serial.println("]"); | |
// Get the path from the request (we are only interested in responding to | |
// /?hs=tr and /np_1.2 requests) | |
path = strchr(req, ' '); | |
path++; | |
if (path == NULL) { | |
Serial.print("Invalid request: "); | |
Serial.println(req); | |
return; | |
} | |
char feedback[512]; | |
// Spotify still uses the deprecated API of last.fm... | |
if ( strncmp(path, "/np_1.2", 7) == 0 ) { | |
Serial.println("searching track"); | |
char *artist; | |
char *track; | |
char data[512]; | |
// First we need to read all of the data that is comming from the spotify | |
// client | |
int i=0; | |
while(client.available()) { | |
data[i++] = client.read(); | |
} | |
data[i] = '\0'; | |
// The interesting part is in the posted data. That contains the first = of | |
// the whole package, so search for the '='. | |
char *s; | |
s = strchr(data, '='); | |
// first url decode | |
char *d = (char*)malloc(strlen(s)); | |
url_decode(s, d); | |
artist = (char*)malloc(strlen(s)); | |
get_param(d, artist, 'a'); | |
Serial.print("artist="); | |
Serial.println(artist); | |
track = (char*)malloc(strlen(s)); | |
get_param(d, track, 't'); | |
Serial.print("track="); | |
Serial.println(track); | |
// update last.fm and get feedback | |
update_lastfm(req, data, feedback); | |
// Now output the band and track to the oled screen. | |
display.clear(); | |
display.drawString(0, 0, artist); | |
display.drawString(0,32, track); | |
display.display(); | |
} else if ( strncmp(path, "/?hs=tr", 7) == 0 ) { | |
// Serial.println("Authentication.. leave that to last.fm"); | |
char data[512]; | |
// First we need to read all of the data that is comming from the spotify | |
// client | |
int i=0; | |
while(client.available()) { | |
data[i++] = client.read(); | |
} | |
data[i] = '\0'; | |
update_lastfm(req, data, feedback); | |
} | |
client.flush(); | |
String s; | |
if ( strncmp(path, "/np_1.2", 7) == 0 ) { | |
s = feedback; | |
} | |
else if ( strncmp(path, "/?hs=tr", 7) == 0 ) { | |
s = feedback; | |
} | |
else | |
{ | |
s = "HTTP/1.1 404 Not Found\r\n\r\n"; | |
Serial.println("Sending 404"); | |
} | |
client.print(s); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment