User Tools

Site Tools


workshop:audio_ties

Audio Ties

This project was realized in Fall of 2008 by Ru Zarin, during the course Experience Prototyping taught by Camille Moussette. The main idea is to have five mobile illuminated ties that are displaying music levels at the Halloween party. You can read more about the project and read the PDF report.

search?q=2275470&btnI=lucky

Revival, November/December 2011

I, Camille, decided to revive this great project and turn it into a permanent project on display in the Interaction Workshop. I feel this project is quite easy to understand for a random person visiting the lab, but also to students once they start digging how it works.

Originally, the project was using a laptop to analyze the audio stream with Processing. The application was then sending commands over serial to a xBee base station. For this rebuild, I decided to get rid of the computer and happily found the Spectrum Analyzer Arduino Shield. Once it arrived, I tested it and it worked beautifully for what I needed for this revival.

After talking to Ru, I discovered he had no backup of the Arduino/Processing code used in this project. I helped Ru a bit to build this project back in the days, so I could remembered a bit what he did, and how. It was also good to review his project report. It gave me some leads how to review the status of the three ties we had in storage since 2008. I connected one of the tie, and tried sending test commands over wireless to see if it would respond. And it did light up after a few tries.

Once I knew the ties were working, I started rebuilding the base station with the audio analyzer shield. I managed to get something working and decided to package the whole thing in a plastic box. After a few revisions, here is the code for the Base Station, where audio is fed to.

#include <SoftwareSerial.h>
 
SoftwareSerial serialXbee(9, 10); // soft serial port for xBee radio
 
// Spectrum analyzer read values will be kept here.
int SpectrumLeft[7];
int SpectrumRight[7];
 
int onLed = 12;
int activityLed = 13;
 
int activityThreshold = 120;
 
void setup(){
  //Setup pins to drive the spectrum analyzer. It needs RESET and STROBE pins.
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(10, OUTPUT);
 
  // pins for status LEDs
  pinMode(onLed, OUTPUT);
  pinMode(activityLed, OUTPUT);
 
  Serial.begin(19200);
  serialXbee.begin(19200);
 
  //Init spectrum analyzer
  digitalWrite(4,LOW);  //pin 4 is strobe on shield
  digitalWrite(5,HIGH); //pin 5 is RESET on the shield
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
 
  // init on led
  digitalWrite(onLed, HIGH);
 
}
 
void loop(){
 
  readSpectrum();
 
// print spectrum values over serial to computer 
//  for(int i =0; i< 7; i++){
//    Serial.print(int(SpectrumLeft[1]));
//    Serial.print(" - ");
//  }
// Serial.println("");
 
  // send relevant spectrum values, in mono/left channel to radio base station for broadcasting
 
  int charVal1 = map(SpectrumLeft[1], 150, 1023, 97, 107);
  serialXbee.print('1');
  serialXbee.print(char(charVal1));
  serialXbee.print('*');
 
  int charVal2 = map(SpectrumLeft[3], 150, 1023, 97, 107);
  serialXbee.print('2');
  serialXbee.print(char(charVal2));
  serialXbee.print('*');
 
  int charVal3 = map(SpectrumLeft[5], 150, 1023, 97, 107);
  serialXbee.print('3');
  serialXbee.print(char(charVal3));
  serialXbee.print('*');
 
  serialXbee.println(' ');
 
  // activity check/led
  if( SpectrumLeft[1] > activityThreshold || SpectrumLeft[3] > activityThreshold || SpectrumLeft[5] > activityThreshold){
     digitalWrite(activityLed, HIGH); 
  }else{
    digitalWrite(activityLed, LOW); 
  }
 
  // slows things down a bit
  delay(30);
 
}
 
// Function to read 7 band equalizers
void readSpectrum()
{
  // Band 0 = Lowest Frequencies.
  byte Band;
  for(Band=0;Band <7; Band++)
  {
    SpectrumLeft[Band] = analogRead(0); //left
    SpectrumRight[Band] = analogRead(1); //right
    digitalWrite(4,HIGH);  //Strobe pin on the shield
    delay(1);
    digitalWrite(4,LOW);     
  }
}

The ties demanded a bit more attention. Since I didn't have access to the original source code, I had to rewrite all the code for the nodes/ties. In 2008, I explained to Ru how to build a small little command protocol in order to control wireless each tie. The packet structure is pretty simple: 1f* for example. The first digit is the tie ID, the letter is the sound level, and * is the end delimiter (see image below). The commands are broadcast to all ties, so each node/tie has to check if this packet is relevant to itself, if yes then chop the value and display LEDs accordingly.

I code something fairly basic and it worked adequately. The intro scan sequence is just for fun, and I think it was much nicer in the original code (which I coded according to Ru!). Anyway, here is the code for the Arduino Minis used in each tie.

/*
Audio Ties
 
 an original project by Ru Zarin, Umea Institute of Design, Fall 2008
 revived and slightly modified in December 2011 by Camille Moussette, camille@guchmu.com
 
 This is the Arduino code for the ties. Each tie is a node and receives commands from the broadcasting unit. It checks if the packet is for this particular tie, and if so it turns on LEDs depending on the sound level recieved. 
 
 Protocol/packet format is : [id][level]*
 
 [id]: node/tie id, 0-9
 [level]: value/sound level, a-k
 *: end delimiter
 
 
 */
 
const int thisTieId = 1;  // particular id for each tie
const int ledPins[] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2};
const int ledsCount = 11;
 
boolean debugOverSerial = false;
 
long lastCommandTime = 0;
int pop = 0;
 
String rawCommand = "";
 
void setup() {
  // initialize serial communication for xBee
  Serial.begin(19200);
  Serial.flush();
 
  //init pins for output  
  for(int i=0; i < ledsCount; i++){
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], HIGH);
  }
 
  delay(1000);
  powerOn();
  allOff();
 
}
 
void loop(){
  // read serial coming from wireless radio
  while (Serial.available()) {
    int c = Serial.read();
    if (c=='*'){  // * is the command end delimiter
      parseCommand(rawCommand);
      rawCommand = "";
      break; 
    }
    else{
      rawCommand += char(c);
      //Serial.println(rawCommand);
    }
  }
 
  // decay if no command for some time
  if(millis() - lastCommandTime > 2000){
    popOne();
    lastCommandTime = millis() - 1500;
  }
 
}
 
void parseCommand(String cmd){
 
  cmd.trim(); // clean it
 
  // check for command size, must be 2, else discard
  if(cmd.length() == 2){
    int tieId = cmd[0]; 
    if(tieId >= '0' && tieId <= '9')        // is c a digit?
      tieId =  tieId - '0';     // make it a digit
    char tieCommand= cmd[1];
 
    if(debugOverSerial){
      Serial.print("id: ");
      Serial.print(tieId);
      Serial.print(" --> ");
      Serial.println(tieCommand);
    }
 
    // check if this command applies to this tie, else ignore
    if(thisTieId == tieId){
 
      int level = int(tieCommand) - 97 ; // 'a' in ASCII is 97, so a is no led on
      if(level >= 0 && level < ledsCount){
        levelOn(level);
        lastCommandTime = millis();
        pop = 0;
      }
    }    
  }
}
 
void allOff(){
  for(int j=0; j < ledsCount; j++) digitalWrite(ledPins[j], LOW);
}
 
void levelOn(int pos){ // pos is from 0 to 11, refers to level
  for(int j=0; j < ledsCount; j++){
    if(pos > j){
      digitalWrite(ledPins[j], HIGH);
    } 
    else{
      digitalWrite(ledPins[j], LOW);
    }
  }
}
 
void oneOn(int pos){  // pos is from 0 to 11, refers to level  
  for(int j=0; j < ledsCount; j++){
    if(pos == j){
      digitalWrite(ledPins[j], HIGH);
    } 
    else{
      digitalWrite(ledPins[j], LOW);
    }
  }
}
 
void powerOn(){
  scan();
  delay(500);
  scan();
 
  // go up to middle
  for(int i=0; i < 7; i++){
    oneOn(i);
    delay(100);
  }
 
  // fade out
  for(int i=255; i > 0; i =  i - 5){
    analogWrite(ledPins[6], i);
    delay(20);
  }  
}
 
void scan(){
  for(int i=0; i < ledsCount; i++){
    oneOn(i);
    delay(50);
  }
 
  for(int i=ledsCount -1; i >= 0; i--){
    oneOn(i);
    delay(50);
  }
}
 
void popOne(){
  if(pop <= ledsCount){
    digitalWrite(ledPins[pop], LOW);
    pop++;
  }
}

Other than that, I configured the Xbee (802.15.4 Series 1) modules using the following.

PAN ID=3334, BAUD RATE=19200

Base Station: MYID=0, Destination Address Low=FFFF (broadcast to all)
Tie 1: MYID=1, Destination Address Low=0
Tie 2: MYID=2, Destination Address Low=0
Tie 3: MYID=3, Destination Address Low=0

It should be possible and fun to use the ties as display for other projects. Just send some valid commands over wireless and watch the lights turn on!

workshop/audio_ties.txt · Last modified: 2011/12/28 11:52 by camille