MUSICAL WATER DANCING SYSTEM

Published Jan 03, 2021
 24 hours to build
 Beginner

Our project is a Musical Water fountain. It works based on the frequency ranges. Each different range will correspond to a different valve which comprises a musical water fountain operating to the beats and rhythms of the song.

display image

Components Used

Solenoid Valve 12V
Solenoids & Actuators 12V Solenoid Valve 3/4in
1
Wire
Hook-up Wire 788133 RED 100 FT
1
Red LED 5mm
Standard LEDs - Through Hole Red Round
10
GPCB
PCBs & Breadboards PadBoard-3U 2 sided with plated holes
1
Breadboard
Breadboard
1
Speaker
Speakers & Transducers 28 mm, Round Frame, 0.25 W, 32 Ohm, Neodymium Magnet, PET Cone, Speaker
1
Microphone
Microphones microphone, 4 mmx1.5mm, electret condenser, noise cancelling, solder pads, 1 Vdc
1
Copper clad PCB board
Copper Clad Boards DB SIDE COP CLAD BRD 1/16in 1 OZ COPPER
1
RESEARCHING VALVES
1
Description
HARDWARE DESIGN


INTRODUN

Our final project is a musical water fountain loosely based on the fountain. The basic idea of the project is to take an input from an iPod (or any sound source), sample the sound and break it down to different “sequencies” by Walsh Transform , then use the output to turn on various solenoid valves. We first used a two-stage low pass filter, and then performed Fast Walsh Transforms on the sound source to split the sounds into different frequency ranges.   Each different range will correspond to a different valve which comprises a musical water fountain operating to the beats and rhythms of the song.
             Currently, we have split the sound source into eight frequency ranges. The reasons for eight fountain heads are: 1- aesthetically appealing and 2- there are 8 pins for a standard I/O port on the Atmel Microcontroller. Since the purpose of this final project is to create a water fountain, we needed a pressurized water source. Originally, we had thought about using fish pumps; but then realized they did not have enough pressure.   The pressure coming from the sink is between 30 and 40 psi, and therefore we decided to attach our input source to the sink.
 

SOFTWARE 

One of the most difficult parts of our program was determining which transform we should use.   Originally, we had decided to use the Fourier Transform, because we felt we understood it the best.   However, the Fourier transform took too many cycles, and was not fast enough.   We then had to determine whether to use the Fast Fourier transform, which splits the sound waves into different frequency ranges, or the Walsh transform.   The Walsh transform is faster, and much easier to code; in fact Professor Land had already written a basic Walsh transform which we could modify for our purposes.   Unfortunately, the Walsh transform does not directly break up the sound waves into frequency ranges.   Instead, it breaks up the sound waves into sequency ranges, which has a linear relationship to frequency ranges.   Rather than sine and cosine, the ranges are broken into cal and sal equations.   The result is often thought of as "a poor-man's fast Fourier transform (FFT)" representing the conversion of a time-sampled signal into an equivalent frequency-sampled form.   Every range has roughly 150 Hz frequency range.   In the end, we chose the Walsh transform, due to its speed.

 

CIRCUIT 

            The sound source or music can be fed from any standard player (mp3, cd, walkman, pc) with the 3.5mm stereo phone jack. We used a simple 2-to-1 splitter so the music can be played through a speaker and inputted into the Analog to Digital Converter (ADC) port of the microcontroller at the same time. The sound source from a standard player (ex. mp3) can not be directly used for ADC because the output signal maximum amplitude is usually +/- 1.5 V. The input signal therefore needs to have a DC bias which eliminates negative voltages and still stay within the range of the ADC reference voltage of 5V. Even though the audible range of a human ear is 20Hz-20kHz, we decided to sample the music at 2kHz to allow for fast ADC conversion and simple breakdown of frequency ranges. At this sampling rate, we can detect tones at up to 1kHz which will cut off high frequency treble sounds but still be useful for decoding typical music. As a result, we low pass filtered the input signal through two stages for sharper cutoff at 1kHz, removed the DC component of the sound source, and then biased the signal at 2.5V.

WIRING 
OUTPUT


CONCLUSION 
 

Our project evolved numerous ways over the course of the planning, execution, construction, and testing phases. However, the main goals and concept of our musical fountain (or water fountain) stayed the same and we were able to successfully achieve the desired results. While our end result did not come any close to the famed Bellagio water fountains in Vegas, we did have great satisfaction in building a water fountain (or fall) that can play to the beats and rhythms of our music source. We realized early on that some aspects of our project were too ambitious and we made the right changes to continue progress and ultimately accomplish the main goals we set in the beginning. For example, the solenoid valves we need in this project turned out to be very difficult to get because of their cost and the lack of free samples from valve manufacturers. Fortunately we were able to secure a lot of overstocked, outdated valves on Ebay for an extremely bargain price. Overall, this project turned out to be much more challenging and time consuming than we originally planned. A large amount of time was spent in mechanical engineering and construction on top of the circuit design, testing, and programming.

#include <mega32.h> 
//#include <stdio.h> 
#include <math.h>

//I like these definitions
#define begin {
#define end   } 

#define t1 200	//Valve cycle time
#define t2 100 
#define t3 400       

//State machine state names - Debouncer
#define NoPush 1 
#define MaybePush 2
#define Pushed 3
#define MaybeNoPush 4

#define int2fix(a)   (((int)(a))<<8) 
#define fix2int(a)   ((signed char)((a)>>8))
#define float2fix(a) ((int)((a)*256.0)) 
#define fix2float(a) ((float)(a)/256.0)  

#define N_WAVE          16    /* size of FWT */
#define LOG2_N_WAVE     4     /* log2(N_WAVE) */
int fwt_t1, fwt_t2; 

//FWT State
#define Single	1	//Find max sequency - 1 valve on per cycle
#define Multi	2	//Valve on if pass threshold 

int ain1[N_WAVE], ain0[N_WAVE] ;       //double buffer inputs
char countISR, Nbuffer;   //Nbuffer keeps track of which buffer is in use  
unsigned char State;
unsigned int timer, debtime, time3;   
unsigned char PushFlag1, PushFlag2, PushFlag3;			//message indicating a button push 
unsigned char PushState1, PushState2, PushState3;			//state machine   
//reordering tables
flash char reorder16[16] = {0,15,7,8,3,12,4,11,1,14,6,9,2,13,5,10};
                 
//======================================================== 

void FWTfix(int x[])
// This routine does foward transform only
//output is in dyadic order -- see web links
//use FWTreorder() to put the transform in sequency order 
begin
    unsigned char ii, k, j, i, bb, st;
    
    bb = N_WAVE/2; st = N_WAVE; 
    //
    for (i=1; i<LOG2_N_WAVE+1; i++)
    begin
        for (j=0; j<N_WAVE; j+=st)
        begin
            for (k=0; k<bb; k++)
            begin
                ii=j+k;
                fwt_t1 = x[ii] >>1 ;         //scale to keep fixed pt in range
                fwt_t2 = x[ii+bb] >>1 ;
                x[ii] = fwt_t1 + fwt_t2 ;    //
                x[ii+bb] = fwt_t1 - fwt_t2 ;
            end
        end
        bb = bb>>1 ; st = st>>1 ;
    end
end

//========================================================  
void FWTreorder(int x[], flash char r[]) 
//converts from dyadic order to sequency order -- see references
begin
    char i,j;
    int xp[N_WAVE];
    for (i=0; i<N_WAVE; i++)
    begin
        j = r[i];
        xp[j] = x[i];
    end
    for (i=0; i<N_WAVE; i++)
    begin
        x[i] = xp[i];
    end    
end 
//========================================================
interrupt [TIM0_COMP] void getAD(void)
begin 
    //read a/d converter and update appropriate buffer 
    if(timer>0) timer--; 
    if(debtime>0) debtime--;
    if(time3>0) time3--;
    if (countISR < N_WAVE)
    begin 
        if (Nbuffer) ain1[countISR] = (ADCH *2); 
        else ain0[countISR] = (int)(ADCH *2) ;
        //start a/d converter
        ADCSR.6 = 1;  
        //update ISR counter
        countISR++; 
        //PORTB = ~PORTB; 
    end
end          
//========================================================  
void debounce1(void) 
//button for switching modes
begin
  debtime=t2;     //reset the task timer
  switch (PushState1)
  begin
     case NoPush: 
        if (~PINA.1 == 0x01) PushState1=MaybePush;
        else PushState1=NoPush;
        break;
     case MaybePush:
        if (~PINA.1 == 0x01) 
        begin
           PushState1=Pushed;   
           PushFlag1=1;
        end
        else PushState1=NoPush;
        break;
     case Pushed:  
        if (~PINA.1 == 0x01) PushState1=Pushed; 
        else PushState1=MaybeNoPush;    
        break;
     case MaybeNoPush:
        if (~PINA.1 == 0x01) PushState1=Pushed; 
        else 
        begin
           PushState1=NoPush;
           PushFlag1=0;
        end    
        break;
  end
end    
//========================================================  
void debounce2(void) 
//button for increasing threshold
begin
  switch (PushState2)
  begin
     case NoPush: 
        if (~PINA.2 == 0x01) PushState2=MaybePush;
        else PushState2=NoPush;
        break;
     case MaybePush:
        if (~PINA.2 == 0x01) 
        begin
           PushState2=Pushed;   
           PushFlag2=1;
        end
        else PushState2=NoPush;
        break;
     case Pushed:  
        if (~PINA.2 == 0x01) PushState2=Pushed; 
        else PushState2=MaybeNoPush;    
        break;
     case MaybeNoPush:
        if (~PINA.2 == 0x01) PushState2=Pushed; 
        else 
        begin
           PushState2=NoPush;
           PushFlag2=0;
        end    
        break;
  end
end
//========================================================  
void debounce3(void) 
//button for decreasing threshold
begin
  switch (PushState3)
  begin
     case NoPush: 
        if (~PINA.3 == 0x01) PushState3=MaybePush;
        else PushState3=NoPush;
        break;
     case MaybePush:
        if (~PINA.3 == 0x01) 
        begin
           PushState3=Pushed;   
           PushFlag3=1;
        end
        else PushState3=NoPush;
        break;
     case Pushed:  
        if (~PINA.3 == 0x01) PushState3=Pushed; 
        else PushState3=MaybeNoPush;    
        break;
     case MaybeNoPush:
        if (~PINA.3 == 0x01) PushState3=Pushed; 
        else 
        begin
           PushState3=NoPush;
           PushFlag3=0;
        end    
        break;
  end
end

//========================================================        
//read A/D converter
// transform
// PWM LEDs to make 8 channel analyser

void main(void)
begin
    unsigned char CurBuf, i, m ; 
    int compare[8], th;
       
    //serial setup for debugging using printf, etc.     
    UCSRB = 0x18 ;
    UBRRL = 103 ;
    //putsf("\r\nStarting...\r\n");  
    
    //set up timer0 to sample a/d at about 7800 Hz
    //turn on timer with period= 256*8 cycles  = 2048 cycles 
    //which implies 7812 samples/sec 
    TCCR0 = 0b00001011; 
    TIMSK = 2 ;       
    OCR0 = 125;
    
    timer = t1; 
    debtime = t2;     
    time3 = t3;
    //set up a/d for external Vref, channel 0
    //channel zero/ left adj /EXTERNAL Aref
   //!!!CONNECT Aref jumper!!!!
   ADMUX = 0b01100000;   
   //enable ADC and set prescaler to 1/128*16MHz=125,000
   //and clear interupt enable
   //and start a conversion
   ADCSR = 0b11000111; 
   
   //initialize state to single valve
   State = Single;
   
    //set up PORTB/D for output             
    DDRB=0xff;  
    DDRD=0xff;    
    PORTD.7=0;
    
    //initialize PortB for off (Valve Normally Open)   
    PORTB=0xff;               
    
    //initialize debounce variables
    PushFlag1=0;
    PushFlag2=0;
    PushFlag3=0;
    PushState1=NoPush;   
    PushState2=NoPush;
    PushState3=NoPush;
    
   //and start the show
    #asm("sei")
    
    //start with buffer 0
    Nbuffer = 0;
    
    //led threshold **************************************************
    th=40;     //initial value
    
    while(1)
    begin
        if(debtime==0) 
        begin  
          debtime=t2;
          debounce1();
          debounce2();
          debounce3();
        end
        
        if(time3==0)
        begin      
          time3=t3;
          if(PushFlag1) 
          begin
               if(State==Single) State=Multi;
               else if(State==Multi) State=Single;
               PushFlag1=0;
          end
        
          //threshold can be set from 10-80
          if(PushFlag2 && th<80)
          begin
               th = th + 10;
               PushFlag2=0;
          end
          if(PushFlag3 && th>10)  
          begin
               th = th-10;    
               PushFlag3=0;
          end 
        end   
          
        //when there are 16 points in buffer
        if (countISR == N_WAVE)
        begin
            
            //critical section
            #asm("cli")
            CurBuf = Nbuffer;
            //switch input buffers
            Nbuffer ^= 0x01;
            //reset the counter
            countISR = 0;
            #asm("sei")
            //end critical section
            
            //update the FWT 
            if (CurBuf) FWTfix(ain1);
            else FWTfix(ain0);
            // sequency order
            if (CurBuf) FWTreorder(ain1,reorder16);
            else FWTreorder(ain0,reorder16); 
            // combine sal and cal
            if (CurBuf)
            begin
                //omit DC in ain1[0]
                compare[0] = (abs(ain1[1])+abs(ain1[2])) ;//adds the cal and sal values for each sequency band
                compare[1] = (abs(ain1[3])+abs(ain1[4])) ;
                compare[2] = (abs(ain1[5])+abs(ain1[6])) ;
                compare[3] = (abs(ain1[7])+abs(ain1[8])) ;
                compare[4] = (abs(ain1[9])+abs(ain1[10])) ;
                compare[5] = (abs(ain1[11])+abs(ain1[12])) ;
                compare[6] = (abs(ain1[13])+abs(ain1[14])) ;
                compare[7] = (abs(ain1[15]));  //highest sequency - no sal value 
                //printf("%u\r\n",compare[7]);
            end
            else
            begin
                //omit DC
                compare[0] = (abs(ain0[1])+abs(ain0[2])) ;//adds the cal and sal values for each sequency band
                compare[1] = (abs(ain0[3])+abs(ain0[4])) ;
                compare[2] = (abs(ain0[5])+abs(ain0[6])) ;
                compare[3] = (abs(ain0[7])+abs(ain0[8])) ;
                compare[4] = (abs(ain0[9])+abs(ain0[10])) ;
                compare[5] = (abs(ain0[11])+abs(ain0[12])) ;
                compare[6] = (abs(ain0[13])+abs(ain0[14])) ;
                compare[7] = (abs(ain0[15]));  //highest sequency - no sal value 
            end                                 
                       
            if(timer == 0)
            begin
            //printf("********************************\n\r");
            /*printf("compare0 %i \n\r", compare[0]); 
            printf("compare1 %i \n\r", compare[1]); 
            printf("compare2 %i \n\r", compare[2]); 
            printf("compare3 %i \n\r", compare[3]); 
            printf("compare4 %i \n\r", compare[4]); 
            printf("compare5 %i \n\r", compare[5]); 
            printf("compare6 %i \n\r", compare[6]); 
            printf("compare7 %i \n\r", compare[7]);   */         
            
            //printf("state %i\n\r", State);  
            //printf("th %i\n\r", th);             
            //printf("m %i \n\r", m);
            
            //combine threshold and max and light leds 
            
            //find the maximum element
            m = 0;
            for (i=1; i<8; i++)
            begin
                if (compare[i]>compare[m]) m = i;    
            end   
            
            if (State == Single)
            begin
                if((m == 7) && (compare[7]>th)) PORTB.0 = 0; else PORTB.0 = 1;
                if((m == 6) && (compare[6]>th)) PORTB.1 = 0; else PORTB.1 = 1;
                if((m == 5) && (compare[5]>th)) PORTB.2 = 0; else PORTB.2 = 1;
                if((m == 4) && (compare[4]>th)) PORTB.3 = 0; else PORTB.3 = 1;
                if((m == 3) && (compare[3]>th)) PORTB.4 = 0; else PORTB.4 = 1;
                if((m == 2) && (compare[2]>th)) PORTB.5 = 0; else PORTB.5 = 1;
                if((m == 1) && (compare[1]>th)) PORTB.6 = 0; else PORTB.6 = 1;
                if((m == 0) && (compare[0]>th)) PORTB.7 = 0; else PORTB.7 = 1; 
            end
            
            if (State == Multi)
            begin
                if((compare[7]>th)) PORTB.0 = 0; else PORTB.0 = 1;
                if((compare[6]>th)) PORTB.1 = 0; else PORTB.1 = 1;
                if((compare[5]>th)) PORTB.2 = 0; else PORTB.2 = 1;
                if((compare[4]>th)) PORTB.3 = 0; else PORTB.3 = 1;
                if((compare[3]>th)) PORTB.4 = 0; else PORTB.4 = 1;
                if((compare[2]>th)) PORTB.5 = 0; else PORTB.5 = 1;
                if((compare[1]>th)) PORTB.6 = 0; else PORTB.6 = 1;
                if((compare[0]>th)) PORTB.7 = 0; else PORTB.7 = 1; 
            end
            
            timer = t1;
            end
        end       
    end   
end 


 

Codes

Downloads

CIRCUIT DIAGRAM Download

Institute / Organization

BANNARI AMMAN INSTITUTE OF TECHNOLOGY, SATHYAMANGALAM
Comments
Ad