Using an Arduino Uno as a Spotify Controller on Mac

Categories Uncategorized
I recently bough an Arduino Uno with a 1.8″ TFT Arduino Shield and I thought I would have some fun with it by using it as a Spotify controller.

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:

  1. 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.
  2. Use the serial port through USB to send data between Arduino and Mac.
  3. Display song, artist and use joystick input for controls.

Step 1: Interact with Spotify

The Mac version of Spotify supports Applescript so we can use that to perform the actions we need. However, I wanted to keep all the app code in the same language (Ruby) and in the same script so I found a gem (rb-applescript) that executes Applescript with Ruby.

Install rb-applescript
gem install rb-applescript

For example:
 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