A program to automatically tune a guitar. Written by Justin Reidhead and Steven Swenson

Dependencies:   FFT FrequencyFinder Motor NewTextLCD PinDetect mbed strings

main.cpp

Committer:
melangeaddict
Date:
2012-04-26
Revision:
13:948c7d19acb9
Parent:
12:03c189de6e2e
Child:
14:fd59e7acf2e5

File content as of revision 13:948c7d19acb9:

#include "mbed.h"
#include "Motor.h"
#include "FrequencyFinder.h"
#include "NewTextLCD.h"
#include "PinDetect.h"
#include "strings.h"
//#include "vector"

//using namespace std;
//***************************************************
//***************Globals*****************************
PinDetect string_but(p11);
PinDetect pitch_but(p12);//These are the buttons the user will interface with
PinDetect start_but(p13);
PinDetect mode_but(p14);

//DigitalOut led1(LED1);//For diagnostic purposes
//DigitalOut led2(LED2);

TextLCD lcd(p5,p6,p7,p8,p9,p10);//Our method of communication with LCD screen (rs e d4 d5 d6 d7)
Motor motor(p24,p19,p21);//Setup for the motor (enable, direction, step)
FrequencyFinder guitar(p20);//Interface to get data from guitar (input pin)
AnalogOut bias(p18);//1.6v DC offset

DigitalOut ledBlue(p27);
DigitalOut ledGreen(p28);
DigitalOut ledRed(p29);


short selected_string;//Holds on to the selected string
bool current_mode;//Tuning mode or winding mode
bool start_tuning;//Bool to tell whether to start the tuning process or not
bool wind_up;//Bool for winding mode - turn motor up
bool wind_down;//Bool for winding mode - turn motor down
short up, down;//To move the motor up or down

//vector<strings> strings_array;//Holds strings objects - basically the frequency data for each string
strings strings_array[6];


LocalFileSystem local("local");
//***************************************************
//*****************constants*************************
const bool tuning_mode=false;//For setting the mode to tuning or winding
const bool winding_mode=true;

//***************************************************
//******************prototypes***********************
void device_init();

void string_sel();//string select
void pitch_sel();//pitch select
void start();//start tuning
void stop();//stop tuning
void mode();//change mode
void do_nothing();//does nothing
void wind_up_start();//start motor winding up
void wind_down_start();//start motor winding down
void wind_up_stop();//stop motor winding up
void wind_down_stop();//stop motor winding down

void button_init();//Set sampling period etc... for buttons
void setup_buttons();//set function calls for buttons
void output_menu();//Output the main menu
void motor_calibration();//Calibrate the motor's direction
bool check_threshold(float);//check to make sure the frequency is valid (frequency to check)
void LED_initialize();//Initialization sequence for LEDs, called at start-up
void set_LED(int led, int value);//sets or clears an LED: Blue=1(in tune), Red=2(flat), Green=3(sharp). Value=1(off), 0(on);
int get_steps(float desired, float current);
//*************************************************
//*********************main************************
int main() {
    lcd.cls();//Clear the LCD
    lcd.printf("Perfect\n      Pitch");//Modify to whatever we want to name this thing
    LED_initialize();
    // wait(.5);

    device_init();//Setup buttons and set global variables


    float freq_acc[6] = {.7,.7,.6,.6,.5,.5};//Values to be between when tuning i.e., the first string is +-.7 to desired
    int state=0,next_state=0;
    int num_steps=0;
    //float old_freq=0;
    short current_direction=up;
    float new_freq=0;
    float desired_freq=0;
    strings *temp=&strings_array[0];
    wait(.5);

    output_menu();

    while (1) {
        state=next_state;

        switch (state) {
                //------------------------------------
            case 0://Stay here till the user selects the string and pitch
                if (start_tuning==true) {
                    next_state=1;
                } else {
                    next_state=0;
                    //output_menu();
                }
                break;
                //----------------------------------------
            case 1://motor calibration state
                //motor_calibration();//determine which direction is up and down for the motor
                next_state=2;
                break;
                //-----------------------------------------
            case 2://begin the actual tuning

                temp=&strings_array[selected_string];
                desired_freq=temp->get_freq();//Get the desired frequency for the string selected
                //old_freq=desired_freq;//We have to initalize it to something...

                next_state=3;
                break;
                //-----------------------------------------
            case 3://Do the dirty work of tuning
                new_freq=guitar.find_frequency();//Get the current frequency of the string

                if (desired_freq*2.2>new_freq && desired_freq*1.8<new_freq) {
                    new_freq=new_freq/2;
                }

                if (check_threshold(new_freq)) {//The check_threshold function makes sure the frequency is valid (less than 500 Hz)

                    num_steps=get_steps(desired_freq, new_freq);

                    if ((desired_freq-freq_acc[selected_string])<new_freq && (desired_freq+freq_acc[selected_string])>new_freq) {//We are within .5Hz of the desired frequency

                        new_freq=guitar.find_frequency();
                        if ((desired_freq-freq_acc[selected_string])>new_freq || (desired_freq+freq_acc[selected_string])<new_freq) {//This checks the frequency again to make sure we are close
                            next_state=3;
                            break;
                        }

                        lcd.cls();
                        lcd.printf("String %d\ntuned",selected_string+1);
                        set_LED(1,0);//blue on
                        set_LED(2,1);//red off;
                        set_LED(3,1);//green off
                        wait(1);

                        start_tuning=false;
                        output_menu();
                        setup_buttons();
                        new_freq=0;//Just to reset
                        next_state=0;
                    } else if ((desired_freq-.5)>new_freq) {//We are too low, and need to turn the string tigher
                        if (current_direction==down) {
                            num_steps+=10;//For deadband
                            current_direction=up;
                        }

                        //lcd.cls();
                        //lcd.printf("Tuning up");

                        set_LED(1,1);//blue off
                        set_LED(2,0);//red on;
                        set_LED(3,1);//green off

                        //found_frequency=0;

                        //wait(.5);

                        motor.motor_turn(up,num_steps);//TODO:Adjust # of steps
                        next_state=3;

                    } else {//We are too high, and need to loosen the string
                        if (current_direction==up) {
                            num_steps+=10;//For deadband
                            current_direction=down;
                        }

                        //lcd.cls();
                        //lcd.printf("Tuning down");
                        set_LED(1,1);//blue off
                        set_LED(2,1);//red off;
                        set_LED(3,0);//green on

                        //found_frequency=0;

                        //wait(.5);

                        motor.motor_turn(down,num_steps);
                        next_state=3;
                    }
                } else {
                    next_state=3;
                }

                if (start_tuning==false) {//If the stop button is pressed, the state machine returns to user input
                    next_state=0;
                    setup_buttons();
                    output_menu();
                }

                //TODO:Determine number of steps per frequency change

                break;
                //-----------------------------------------
            case 4://Winding mode

                if (current_mode==winding_mode) {
                    if (wind_up) {
                        motor.motor_turn(up,5);//TODO:Adjust number of turns
                    }

                    if (wind_down) {
                        motor.motor_turn(down,5);//TODO:Adjust number of turns
                    }
                    next_state=4;
                } else {
                    output_menu();
                    wind_up=false;
                    wind_down=false;
                    setup_buttons();
                    //set_LED(1,0);
                    next_state=0;
                }
                break;
            default:
                break;
        }//end switch
        wait_ms(5);

        if (start_tuning==false) {//If the stop button is pressed, the state machine returns to user input
            next_state=0;
            //setup_buttons();
            //output_menu();
        }

        if (current_mode==winding_mode) {//TODO:Debug this
            next_state=4;
        }

        if (start_tuning) {
            if (check_threshold(new_freq)) {
                lcd.cls();
                lcd.locate(9,0);
                lcd.printf("%f",new_freq);
                lcd.locate(9,1);
                lcd.printf("%f",desired_freq);

                lcd.locate(0,1);
                lcd.printf("Desired");
                lcd.locate(0,0);
                lcd.printf("Detected");
            }
        }
    }//end while

    //   return 0;
}

//***************************************************
//******************functions************************
//Display the string and pitch selection menu
void output_menu() {
    lcd.cls();
    lcd.printf("Select String: %d",selected_string+1);

    lcd.locate(0,1);
    strings *temp=&strings_array[selected_string];
    lcd.printf("Select Pitch: ");
    lcd.printf("%s",temp->get_note());
    //wait(.5);

    set_LED(1,0);//Turn blue LED on
    set_LED(2,1);
    set_LED(3,1);
}

//***************************************************
//Initialize the buttons
void button_init() {
    string_but.mode( PullDown );
    string_but.setSampleFrequency();

    pitch_but.mode( PullDown );
    pitch_but.setSampleFrequency();

    start_but.mode( PullDown );
    start_but.setSampleFrequency();

    mode_but.mode( PullDown );
    mode_but.setSampleFrequency();

    setup_buttons();
}

//***************************************************
//Depending on the current mode, the buttons do different things
void setup_buttons() {
    if (current_mode==tuning_mode) {//Tuning mode
        string_but.attach_asserted(&string_sel);
        string_but.attach_deasserted(&do_nothing);
        pitch_but.attach_asserted(&pitch_sel);
        pitch_but.attach_deasserted(&do_nothing);
        start_but.attach_asserted(&start);
        mode_but.attach_asserted(&mode);
    } else {//Winding mode
        string_but.attach_asserted(&wind_up_start);
        string_but.attach_deasserted(&wind_up_stop);
        pitch_but.attach_asserted(&wind_down_start);
        pitch_but.attach_deasserted(&wind_down_stop);
        start_but.attach_asserted(&do_nothing);
        mode_but.attach_asserted(&mode);
    }
}
//***************************************************
//Change the selected string - there are only six strings
void string_sel() {
    selected_string--;
    if (selected_string<0)
        selected_string=5;

    strings *temp=&strings_array[selected_string];
    temp->reset_index();

    output_menu();
}
//***************************************************
//Change the pitch of the selected string
void pitch_sel() {
    strings *temp=&strings_array[selected_string];
    temp->inc_index();

    output_menu();
}
//***************************************************
//Start the tuning process
void start() {
    start_tuning=true;

    string_but.attach_asserted(&do_nothing);//Disable the other buttons
    string_but.attach_deasserted(&do_nothing);
    pitch_but.attach_asserted(&do_nothing);
    pitch_but.attach_deasserted(&do_nothing);
    start_but.attach_asserted(&stop);       //except for the start/stop button
    mode_but.attach_asserted(&do_nothing);
}
//***************************************************
void mode() {
    if (current_mode==tuning_mode) {
        set_LED(2,0);//red off
        set_LED(1,0);//blue on
        set_LED(3,1);//green on
        
        current_mode=winding_mode;
        lcd.cls();
        lcd.printf("Winding Mode");
        wait(1);
        lcd.cls();
        lcd.printf("String for up\nPitch for down");
    } else {
        set_LED(2,1);//red off
        set_LED(1,0);//blue on
        set_LED(3,1);//green off
        
        current_mode=tuning_mode;
        lcd.cls();
        lcd.printf("Tuning Mode");
        wait(.5);
        output_menu();
    }
    setup_buttons();//Change the functions the buttons connect to
}
//***************************************************
void stop() {
    start_tuning=false;

    output_menu();
    setup_buttons();
}
//***************************************************
void do_nothing() {
    return;
}
//***************************************************
void wind_up_start() {
    wind_up=true;
    set_LED(2,0);//red on
    set_LED(1,1);//blue off
    set_LED(3,1);//green off
}
//***************************************************
void wind_up_stop() {
    wind_up=false;
    set_LED(2,0);//red off
    set_LED(1,0);//blue on
    set_LED(3,1);//green on
}
//***************************************************
void wind_down_start() {
    wind_down=true;
    set_LED(3,0);//green on
    set_LED(1,1);//blue off
    set_LED(2,1);// off
}
//***************************************************
void wind_down_stop() {
    wind_down=false;
    set_LED(2,0);//red off
    set_LED(1,0);//blue on
    set_LED(3,1);//green on
}
//***************************************************
void device_init() {

    strings string1(1);
    strings string2(2);
    strings string3(3);
    strings string4(4);
    strings string5(5);
    strings string6(6);

    strings_array[0]=string1;
    strings_array[1]=string2;
    strings_array[2]=string3;
    strings_array[3]=string4;
    strings_array[4]=string5;
    strings_array[5]=string6;

    selected_string=5;
    current_mode=tuning_mode;
    start_tuning=false;
    up=1;
    down=0;
    wind_up=false;
    wind_down=false;
    bias=.5;

    button_init();

    output_menu();

}
//***************************************************
void motor_calibration() {
    lcd.cls();
    lcd.printf("Calibrate Motor");
    wait(.5);

    float freq=0, freq_up=0, freq_down=0;
    bool done=false;
    lcd.cls();
    lcd.printf("Please pluck\nstring");
    wait(1);

    //motor.motor_turn(up,25)//TODO: Adjust the number of steps here
    //On second thought, we don't need to tune up and down for this, we can find the current frequency
    //and then turn the peg for the two frequencies!
    while (!done) {
        freq=guitar.find_frequency();

        if (check_threshold(freq)) {
            lcd.cls();
            freq_up=freq;
            done=true;
        }

        if (start_tuning==false)
            break;
    }
    motor.motor_turn(down,25);//TODO: Adjust the number of steps here
    done=false;
    while (!done) {
        freq=guitar.find_frequency();

        if (check_threshold(freq)) {
            lcd.cls();
            freq_down=freq;
            done=true;
        }
        if (start_tuning==false)
            break;
    }

    if (freq_up<freq_down) {
        //down=0;
        //up=1;
    } else {
        //down=0;
        //up=1;
    }

    lcd.cls();
    lcd.printf("Calibration Done");

    if (start_tuning==false) {
        output_menu();
        setup_buttons();
    }
}
//**********************************************
bool check_threshold(float freq) {
    strings *temp=&strings_array[selected_string];
    float desired_freq=temp->get_freq();//Get the desired frequency for the string selected

    int hertz=0;
    switch (selected_string) {
        case 0:
            hertz=85;
            break;

        case 1:
            hertz=66;
            break;

        case 2:
            hertz=53;
            break;

        case 3:
            hertz=42;
            break;

        case 4:
            hertz=29;
            break;

        case 5:
            hertz=22;
            break;

        default:
            break;
    }

    if (freq>(desired_freq+hertz) || freq<(desired_freq-hertz) || freq>500) {//new_freq>(desired_freq+50) || new_freq<(desired_freq-50)
        lcd.cls();
        lcd.printf("Pluck string %d\nagain",selected_string+1);

        set_LED(1,1);
        set_LED(2,1);
        set_LED(3,1);//Turn led off

        set_LED(2,0);
        set_LED(3,0);//Make the LED turn yellow

        //wait(.5);
        return false;
    } else
        return true;
}



void LED_initialize() {
    float wait_time=0.1;
    /*    ledBlue=1;//red
        ledGreen=1;
        ledRed=0;
        wait(wait_time);
        ledRed=1;//green
        ledGreen=0;
        wait(wait_time);
        ledGreen=1;
        ledBlue=0;//blue
        wait(wait_time);

        for (int a=0; a<3; a++) {
            ledRed=0;//all
            ledGreen=0;
            ledBlue=0;
            wait(wait_time);
            ledRed=1;
            ledGreen=1;
            ledBlue=1;
            wait(wait_time);

        }
        ledBlue=0;


        ledRed=1;//blue
        ledGreen=1;
    */
    set_LED(1,1);
    set_LED(2,1);
    set_LED(3,1);

    for (int b=1; b<4; b++) {
        for (int a=1; a<4; a++) {
            set_LED(a, 0);
            wait(wait_time);
            set_LED(a,1);
            wait(0.001);

        }
    }
    set_LED(1,0);
    set_LED(2,1);
    set_LED(3,1);

    return;

}


void set_LED(int led, int value) {
    //ledBlue=1;
    //ledRed=2;
    //ledGreen=3;
    //value must be an int: 0(on) or 1(off)

    if (value==0 || value==1)

        switch (led) {
            case(1):
                ledBlue=value;
                break;

            case(2):
                ledRed=value;
                break;

            case(3):
                ledGreen=value;
                break;

            default:
                break;

        }

    return;

}
//***********************************
//********get_steps*****************
int get_steps(float desired, float current) {

    float difference=abs(desired-current);

    switch (selected_string) {
        case 5:
            if (difference>10) {
                return 60;
            } else if (difference>3) {
                return 35;
            } else if (difference>2) {
                return 20;
            } else if (difference>1) {
                return 15;
            } else {
                return 10;
            }
        case 4:
            if (difference>5) {
                return 60;
            } else if (difference>3) {
                return 40;
            } else if (difference>2) {
                return 30;
            } else if (difference>1) {
                return 23;
            } else {
                return 10;
            }
        case 3:
            if (difference>5) {
                return 40;
            } else if (difference>3) {
                return 25;
            }    else if (difference>2) {
                return 20;
            } else if (difference>1) {
                return 15;
            } else {
                return 10;
            }
        case 2:
            if (difference>5) {
                return 33;
            } else if (difference>3) {
                return 23;
            } else if (difference>2) {
                return 17;
            } else if (difference>1) {
                return 12;
            } else {
                return 8;
            }
        case 1:
            if (difference>5) {
                return 35;
            } else if (difference>3) {
                return 25;
            } else if (difference>2) {
                return 20;
            } else if (difference>1) {
                return 10;
            } else {
                return 5;
            }
        case 0:
            if (difference>8) {
                return 30;
            } else if (difference>6) {
                return 20;
            } else if (difference>4) {
                return 15;
            } else if (difference>1) {
                return 10;
            } else {
                return 5;
            }


        default:
            return 0;
    }//end switch
}