close-circle
Close

Phase 2 - Prototype (software)

A project log for LighTouch - Control music with the wave of a hand

A touch-less, streaming radio/alarm clock

Thomas Clauser 08/10/2014 at 18:190 Comments

I'm by no means a developer.  I find a great deal of enjoyment in 'tinkering' with different programming languages and I prefer to learn as a go.  To me it's like visiting a new country and trying to learn the language on the fly rather than in a book.  While there's a certain appeal to that approach it can have some downfalls such as inefficient code.

My first python scripts were functional, but UGLY so I've taken the time to rewrite them but realize there's still a ways to go.  The goal is overall stability and performance.

There are two pieces of code that do all of the heavy lifting; the python script on the raspi's side and the arduino sketch.

LighTouch.py

 

import serial
import mpd
import subprocess
import alsaaudio
import os




ser = serial.Serial('/dev/ttyS0', 9600) # Change /dev/* value to match your arduino serial interface device
ser.timeout=.05 						# Timeout reduces CPU usage
mixer = alsaaudio.Mixer('PCM', 0)		# Which audio mixer to control
changecheck=" "							# variable that 'watches' to see if the song has changed
counter=0    							# variable used to delay the time to redisplay the Artist/Song after changing volume


client = mpd.MPDClient()				
client.connect("localhost", "6600")




def translate(value, leftMin, leftMax, rightMin, rightMax): # Used to translate the value of volume to a 0-100 range for accurate display
    # Figure out how 'wide' each range is
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin
    # Convert the left range into a 0-1 range (float)
    valueScaled = float(value - leftMin) / float(leftSpan)
    # Convert the 0-1 range into a value in the right range.
    return rightMin + (valueScaled * rightSpan)




while True:
	snafu=ser.readline().decode('utf-8','ignore')[:-2] 	#Read serial input
	SongInfo=client.currentsong()						#Pulls information from MPD
	artist=SongInfo['artist']							#Artist info
	song=SongInfo['title']								#Song info
	if len(song) > 16:									#Chops artist length to LCD width (16 characters)
		choppedsong = song[0:15]
	else:
		choppedsong = song[0:(len(song))]
	if len(artist) > 16:								#Chops artist length to LCD width (16 characters)
		choppedartist = artist[0:15]
	else:
		choppedartist = artist[0:(len(artist))]
	
		
	if counter==20:										#Amount of time to redisplay the Artist/Song after volume change
		changecheck=''
	
	counter=counter+1
	
	if counter>=21:
		counter=21


	if changecheck != choppedsong:						#If counter met or song changed write to LCD
		ser.write(choppedsong)
		ser.write('@')
		ser.write(choppedartist)
		ser.write('@')
		print choppedsong
		print choppedartist
		
	changecheck=choppedsong
	
	if snafu == 'Pause':								#Read serial input for Pause, Next, or Volume
		client.pause(1)
		print(snafu)
		ser.write('     Paused')						#Print to LCD
		ser.write('@')									#'@' symbol tells the arduino to move to the next row on the LCD
		counter=21;
	elif snafu == 'Next':
		client.next()
		print(snafu)
	elif snafu.isdigit():
		client.play()
		mixer.setvolume(int(snafu))						#Read serial input and set volume
		counter=0
		ser.write('Vol: ')
		vol=str(int(translate(int(snafu),70,100,0,100)))
		print(vol)
		ser.write(vol)
		ser.write('@')
		#print(snafu)


lightouch3.ino

This is the code to be uploaded to the arduino or arduino clone and is responsible for the following:

#include <SoftwareSerial.h>
#include <LiquidCrystal.h>


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  //pins for lcd


int lcd_key     = 0;  //work in progress
int adc_key_in  = 0;  //work in progress
#define btnRIGHT  0  //work in progress
#define btnUP     1  //work in progress
#define btnDOWN   2  //work in progress
#define btnLEFT   3  //work in progress
#define btnSELECT 4  //work in progress
#define btnNONE   5  //work in progress


 const int pingPinT = A4;     //Ultrasonic Trigger Pin
 //const int pingPinR = A3;   //Ultrasonic Echo Pin
 const int pingPinR = A4;     //Ultrasonic Echo Pin
 const int ledPin = 3;        //Fading LED pin
 
 const int ceiling = 80;      //Highest point of detection (cm)
 const int volumemin = 10;    //Lowest volume distance (cm) before pause
 const int volumemax = 45;    //Highest volume distance (cm) before 'dead zone'
 const int nexttrack = 70;    //Height until next track triggered (cm)
 
 int cm;                      // Distance variable
 int new_cm=54;               //Second distance variable to determine if there's been a change since last loop
 int fadevalue;               // LED brightness based on ultrasonic distance
 char buffer;                 //Serial buffer
 int old_cm;
 
 
 // WORK IN PROGRESS
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 195)  return btnUP;
 if (adc_key_in < 380)  return btnDOWN;
 if (adc_key_in < 555)  return btnLEFT;
 if (adc_key_in < 790)  return btnSELECT;  
 return btnNONE;  // when all others fail, return this...
}
 
 
void setup()
 {
    delay(5000); 
    lcd.begin(16,2);
    lcd.setCursor(0,0);
    lcd.print("All Your Base");
    lcd.setCursor(0,1);
    lcd.print("are belong to us");
    lcd.setCursor(0,0);    
    // initialize serial communication:
    Serial.begin(9600);
    Serial.flush();
    digitalWrite(10, LOW);
   }
 
void loop()
 {
   // --------------PRINT TO LCD FROM SERIAL INPUT-------------------
   int count = Serial.available();
   if (Serial.available()) {
      // wait a bit for the entire message to arrive
      lcd.clear();
      delay(100);
      lcd.setCursor(0,0);    
      while (Serial.available() > 0) {
        // display each character to the LCD
        buffer=Serial.read();
        if(buffer=='@'){  //TRIGGER VALUE TO BREAK TO NEXT LCD ROW
          lcd.setCursor(0,1);
          }
	 else if(buffer=='%'){	//ALARM CLOCK TRIGGER (WORK IN PROGRESS)
	   new_cm=volumemax;		//SET EVERYTHING TO HIGHEST (WORK IN PROGRESS)
	   }
        else
          {
          lcd.print(buffer);    //DISPLAY SERIAL DATA TO LCD
          }
      }
        
      }
      
    
    
   
     
   // --------------READ BUTTON------------------- // WORK IN PROGRESS
     lcd_key = read_LCD_buttons();  // read the buttons
       switch (lcd_key)               // depending on which button was pushed, we perform an action
       {
         case btnRIGHT:
           {
           break;
           }
         case btnLEFT:
           {
           Serial.println("Love");
           delay(500);
           break;
           }
         case btnUP:
           {
           break;
           }
         case btnDOWN:
           {
           break;
           }
         case btnSELECT:
           {
           Serial.println("Change");
           delay(500);
           break;
           }
           case btnNONE:
           {
           break;
           }
       }
       
       
   // --------------GET DISTANCE FROM ULTRASONIC SENSOR-------------------       
     long duration;


     pinMode(pingPinT, OUTPUT);
     digitalWrite(pingPinT, LOW);
     delayMicroseconds(2);
     digitalWrite(pingPinT, HIGH);
     delayMicroseconds(5);
     digitalWrite(pingPinT, LOW);
 
     pinMode(pingPinR, INPUT);
     duration = pulseIn(pingPinR, HIGH);


   // convert the time into a distance
     cm = microsecondsToCentimeters(duration);
   // --------------SET CEILING-------------------
   
   if (cm>ceiling){
      cm=new_cm;
      }


   // --------------SET/FADE LED-------------------
   if (cm<nexttrack)    {                                   
      if ((cm<volumemin) &&(cm>0)){
        analogWrite(ledPin, 0);                       //TURN LED OFF WHEN PAUSED
        cm=volumemin-1;
        }
      if ((cm>=volumemax)&&(cm<nexttrack)){
        analogWrite(ledPin, 255);                     //TURN LCD TO BRIGHTEST AT MAX VOLUME
        pinMode(10, INPUT); 
        digitalWrite(10, LOW);
        cm=volumemax;
        }
      if ((cm<volumemax)&&(cm>volumemin)){
        fadevalue = map(cm , volumemin, volumemax, 0, 125); //TRANSLATES MIN/MAX VOLUME INTO LED BRIGHTNESS VALUES
        analogWrite(ledPin, fadevalue);               //FADE LED ACCORDING TO VOLUME LEVEL
        } 
        
   }  
   else {
       analogWrite(ledPin, 0);  
       cm=nexttrack;
       }
 
   // --------------SEND COMMANDS TO PI-------------------
   if (new_cm!=cm)  {
       if ((cm < volumemin) && (cm>0)) {            
         Serial.println("Pause");                   //SEND PAUSE TO RASPI
         pinMode(10, OUTPUT);                       //TURN OFF LCD BACKLIGHT
         }
       if ((cm < volumemax) && (cm>=volumemin))  {
         Serial.println(map(cm, volumemin, volumemax, 70, 100));  //TRANSLATE MIN/MAX VOLUME TO ALSA VOLUME LEVELS AND SEND TO RASPI
	       old_cm=cm;
         pinMode(10, INPUT);                                      //TURN ON LCD BACKLIGHT
         digitalWrite(10, LOW);                                   //TURN ON LCD BACKLIGHT
         }
       if ((cm >=volumemax) && (cm<nexttrack))  {
         //mySerial.write(17);                 // Turn backlight on
         Serial.println(100);                  //SEND MAX VOLUME TO RASPI
         pinMode(10, INPUT);                                    //TURN ON LCD BACKLIGHT
         digitalWrite(10, LOW);                                   //TURN ON LCD BACKLIGHT
         }
       if (cm >=nexttrack)  {
         Serial.println("Next");        //SEND NEXT COMMAND TO RASPI
         delay(250);                    //BLINK LED AT MAX BRIGHTNESS
         analogWrite(ledPin, 255);  
	       delay(250);
         analogWrite(ledPin, 0);  
	       delay(250);
         analogWrite(ledPin, 255);  
	       delay(250);
         analogWrite(ledPin, 0);  
         cm=old_cm;
         }
      }
   new_cm=cm;
   delay(50);  // Short Delay allows for smoother fading


 }  //End Loop


 


//-------------Functions-------------------------------// 


long microsecondsToCentimeters(long microseconds)
 {
   // The speed of sound is 340 m/s or 29 microseconds per centimeter.
   // The ping travels out and back, so to find the distance of the
   // object we take half of the distance travelled.
   return microseconds / 29 / 2;
 }


 //------------End-Functions-------------------------------//

Discussions