Using an Arduino Uno as a Spotify Controller on Mac
Hardware:
Arduino Uno
Adafruit 1.8″ TFT Shield
Software:
Mac OS X 10.8.5 Mountain Lion
rb-appscript 0.6.1
Ruby
There are three steps to this project:
- Interact with Spotify and be able to get the artist and song as well as perform actions such as next track, previous track, play/pause, increase volume and decrease volume.
- Use the serial port through USB to send data between Arduino and Mac.
- Display song, artist and use joystick input for controls.
Step 1: Interact with Spotify
Install rb-applescript
gem install rb-applescript
require 'appscript' spot = Appscript.app('Spotify') spot.launch # Get track info artist = spot.current_track.artist.get song = spot.current_track.name.get # Toggle play/pause spot.playpause # Play next track spot.previous_track # Play next track spot.next_track # Get volume curVol = spot.sound_volume.get # Decrease volume spot.sound_volume.set(curVol - 10) # Increase volume spot.sound_volume.set(curVol + 10)
Step 2: Use Serial Port with Arduino
Ruby has a serial port gem that allows you to read/write from the serial port to your Arduino:
gem install serialport
Example:
# Gem for serial port IO require 'serialport' # Include Input stream ready? require 'io/wait' # Open serial port to your port location sp = SerialPort.new("/dev/cu.usbmodem411", 9600) # Write to serial port sp.write("hellon") # Nonblocking read from serial while true # Other actions... # Nonblocking input if sp.ready? # Get string and chomp rn from end of string str = sp.gets.chomp puts str end
The Arduino Uno can also send and receive from USB port:
// Input from serial port if(Serial.available() > 0){ String data = Serial.readString(); } // Output to serial port Serial.println("output");
Step 3: Display with Arduino and read Joystick
The 1.8″ TFT Shield I bought from Adafruit came with a graphics library for drawing shapes and text. We can use it to draw the current song and track to the screen.
void printArtist(uint16_t color) { tft.setCursor(0, 0); tft.setTextSize(2); tft.setTextColor(color); tft.setTextWrap(false); tft.print(artist); } void printSong(uint16_t color) { tft.setCursor(x, 50); tft.setTextSize(2); tft.setTextColor(color); tft.setTextWrap(false); tft.print(song); }
Since the screen is not wide enough to display a full song name, we will animate the song text by scrolling to the left. We will do this by redrawing the song name X units to the left every 0.5 seconds where X is determined by the desired scroll speed. When we redraw, we draw the song text of the previous position in the background color and then we draw the song text again in the text color shifted X units left. We do this because we want to minimize the number of pixel draws since redrawing the screen causes a flicker. When the end of the song name reaches the screen, we need to reset it back to the original position. The width of each character in text size 2 is 12 pixels and the screen width is 128 pixels so if x < -12 *song.length() + 128, we reset x.
In our loop() function:
if(time + 500 < millis()) { time = millis(); printSong(ST7735_BLACK); x -= SCROLL; if(x < (-12 * (int)song.length() + 128)){ x = SCROLL; } printSong(ST7735_BLUE); }
The joystick can be read by reading analog 3.
#define Neutral 0 #define Press 1 #define Up 2 #define Down 3 #define Right 4 #define Left 5 int CheckJoystick(){ int joystickState = analogRead(3); if (joystickState < 50) return Left; if (joystickState < 150) return Down; if (joystickState < 250) return Press; if (joystickState < 500) return Right; if (joystickState < 650) return Up; return Neutral; }
We only send the state of the joystick if it changes:
int curCmd = CheckJoystick(); if(curCmd != lastCmd){ Serial.println(curCmd); lastCmd = curCmd; }
In our ruby app, we can perform actions based on the joystick state.
Putting it all together:
https://github.com/ayoungprogrammer/arduino-spotify-controller