Automatic Guitar Tuner

14,494

32

35

Two things that I really like to do are: play guitar and tinker with my Arduino. I was very interested at the thought of this project, so I decided to do it. I am new to programming, so this was a real challenge to me!

This machine works with an electric guitar. It takes the electrical signal transmitted from the guitar and analyzes the code. It all starts with the Op Amp circuit. The circuit itself was designed by amandaghassaei, and I want to thank her for helping me get a running start on my project.

Here is the list of processes in a nutshell:
1. The string is picked, and the electrical signal is sent through the stereo cable, which carries the sound waves.
2. The female stereo adapter receives the sound waves and puts it into the Op Amp circuit.
3. The Op Amp circuit takes the sound waves and increases the amplitude, so that the Arduino can read it easily. It then sends the signal into the Arduino to be sampled.
4. The Arduino samples the frequency of the sound waves, and records it on the serial port so the user can see.
5. The Arduino looks at which direction the 6-way rotary switch is in, so that it can determine which string it is tuning.
6. The position of the rotary switch determines what the desired frequency is for the string to be at (with a margin of error of 2Hz)
7. If the String is not in tune, then the motor moves accordingly.
8. This process repeats until the string is tuned, then the user moves on to the next string.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson. Participated in the
Arduino Contest

35 Discussions

The below code compiled

#include <Servo.h>

Servo Servo1; //declaration of servo object

int angleStill = 90; //stores servo position

int angle = 90; //stores servo position

int trim = 0; //trim adjustment to zero position

int servoPin = 8; //Arduino pin assigned to servo

int clockwisespeed = 120; //the speed to go in the clockwise direction

int countclockwisespeed = 60; //the speed to go in the counter clockwise direction

boolean clipping = 0; //clipping indicator variables

//data storage variables

byte newData = 0;

byte prevData = 0;

unsigned int time = 0;//keeps time and sends values to store in timer[] occasionally

int timer;//sstorage for timing of events

int slope;//storage fro slope of events

unsigned int totalTimer;//used to calculate period

unsigned int period;//storage for period of wave

byte index = 0;//current storage index

float frequency;//storage for frequency calculations

int maxSlope = 0;//used to calculate max slope as trigger point

int newSlope;//storage for incoming slope data

int minfreq; //it will be changed according to its string

int maxfreq; //it will be hanged according to to its string

int basefreq = 0; //define baseline noise to ignore any frequency less than this value

//int highstring;//says if you are tuning high string or not

//variables for decided whether you have a match

byte noMatch = 0;//counts how many non-matches you've received to reset variables if it's been too long

byte slopeTol = 3;//slope tolerance- adjust this if you need

int timerTol = 10;//timer tolerance- adjust this if you need

int notenum = 0; //set the guitar string number I want to tune

int notenumPrior = 0; //holds the previously selected guitar string

//-------------------------------------------------------------------------------------------------------------

//all Arduino programs must have a setup routine

//Starts Setup routine

void setup(){

Serial.begin(9600); //begins sampling the serial port for monitoring purposes

pinMode(13, OUTPUT); //clipping indicator

pinMode(12, OUTPUT); //wave period indicator

pinMode(11, OUTPUT); //freq too low

pinMode(10, OUTPUT); //freq spot on

pinMode(9, OUTPUT); //freq too high

//sets all three pitch indicator lights off

digitalWrite(11, LOW);

digitalWrite(10, LOW);

digitalWrite(9, LOW);

pinMode(7, INPUT);// different paths from dip switch indicating the notenum value (output)

pinMode(2, INPUT);

pinMode(3, INPUT);

pinMode(4, INPUT);

pinMode(5, INPUT);

pinMode(6, INPUT);

digitalWrite(7, HIGH);// making sure the input pins are set high, so when pressed they will be on low

digitalWrite(2, HIGH);

digitalWrite(3, HIGH);

digitalWrite(4, HIGH);

digitalWrite(5, HIGH);

digitalWrite(6, HIGH);

Servo1.attach(servoPin); //attaches servo on pin x to servo object

//ADC - Handle Analog to Digital Conversion of Guitar Signal

cli(); //disables interrupts

//set up continuous sampling of analog pin 0 at 38.5kHz

ADMUX |= (1 << REFS0); //set reference voltage

ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only

ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz

ADCSRA |= (1 << ADATE); //enabble auto trigger

ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete

sei();//enable interrupts

}

void reset()

{//clea out some variables

index = 0;//reset index

noMatch = 0;//reset match couner

maxSlope = 0;//reset slope

}

//Launch Continuous Interupt Service Routine (ISR) for Sampling Analog to Digital Conversion of Guitar Audio Input

PORTB &= B11101111;//set pin 12 low

prevData = newData;//store previous value

newData = ADCH;//get value from A0

if (prevData < 127 && newData >=127){//if increasing and crossing midpoint

newSlope = newData - prevData;//calculate slope

if (abs(newSlope-maxSlope)) //record new data and reset time

slope[index] = newSlope;

timer[index] = time;

time = 0;

if (index == 0)

{//new max slope just reset

PORTB |= B00010000;//set pin 12 high

noMatch = 0;

index++;//increment index

}

else if ( abs(timer-timer[index]) ) //sum timer values

totalTimer = 0 ;

for (byte i=0;i= totalTimer+=timer[i];)

{

period = totalTimer;//set period

//reset new zero index values to compare with

timer = timer[index];

slope = slope[index];

index = 1;//set index to 1

PORTB |= B00010000;//set pin 12 high

noMatch = 0;

}

}

else{//crossing midpoint but not match

index++;//increment index

if (index > 9)

{

reset();

}

else if (newSlope>maxSlope)

{//if new slope is much larger than max slope

maxSlope = newSlope;

time = 0;//reset clock

noMatch = 0;

index = 0;//reset index

}

else{ //slope not steep enough

noMatch++;//increment no match counter

}

}

if (noMatch>9){

reset();

}

if (newData == 0 || newData == 1023){//if clipping

PORTB |= B00100000;//set pin 13 high- turn on clipping indicator led

clipping = 1;//currently clipping

}

time++;//increment timer at rate of 38.5kHz

}

//---------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------------

void checkClipping(){//manage clipping indicator LED

if (clipping){//if currently clipping

PORTB &= B11011111;//turn off clipping indicator led

clipping = 0;

}

}

//----------------------------------------------------------------------------------------------

//Start of Main Arduino Loop

void loop() // loop

{

//highstring = 0;

frequency = 38462/float(period); //calculate frequency timer rate/period

// added for debugging and visualizing data

Serial.print(frequency);

Serial.println(" hz");

Serial.println(" ");

Serial.println("string picked: ");

Serial.print(notenum);

notenum_val(); //call function to determine value of note/string we want to tune

freq_range_identify(); //set frequency threshold ranges for string previously selected

checkClipping(); //frequency determining and led indicator loop

set_indicator_lights(); //checks value of current frequency and lights correct indicator light. Then moves servo accordingly

//Serial.println(angle);

move_motor(); //move tuning servo motor

//delay(1000); //turn on to control debouncing

notenumPrior = notenum;

} //end of main loop

//-----------------------------------------------------------------------------------------------

//Determine String selected from rotary switch and voltage values on designated arduino pins

void notenum_val()

{

int val_1 = digitalRead(2); //read the value of rotary switch pins

int val_2 = digitalRead(3);

int val_3 = digitalRead(4);

int val_4 = digitalRead(5);

int val_5 = digitalRead(6);

int val_6 = digitalRead(7);

if (val_1 == LOW && val_2 == HIGH && val_3 == HIGH && val_4 == HIGH && val_5 == HIGH && val_6 == HIGH) //switch set on pressed, then notenum = 1

{

if (notenumPrior != 1)

{

delay(5000);

notenum = 1;

}

else

{

notenum = 1;

}

}

else if (val_1 == HIGH && val_2 == LOW && val_3 == HIGH && val_4 == HIGH && val_5 == HIGH && val_6 == HIGH) //Switch 3 pressed, noenum = 2

{

if (notenumPrior != 2 )

{

delay(5000);

notenum = 2;

}

else

{

notenum = 2;

}

}

else if (val_1 == HIGH && val_2 == HIGH && val_3 == LOW && val_4 == HIGH && val_5 == HIGH && val_6 == HIGH) //switch 4 pressed, notenum = 3

{

if (notenumPrior != 3)

{

delay(5000);

notenum = 3;

}

else

{

notenum = 3;

}

}

else if (val_1 == HIGH && val_2 == HIGH && val_3 == HIGH && val_4 == LOW && val_5 == HIGH && val_6 == HIGH) //switch 5 pressed, notenum = 4

{

if (notenumPrior != 4)

{

delay(5000);

notenum = 4;

}

else

{

notenum = 4;

}

}

else if (val_1 == HIGH && val_2 == HIGH && val_3 == HIGH && val_4 == HIGH && val_5 == LOW && val_6 == HIGH) //switch 6 pressed, notenum = 5

{

if (notenumPrior != 5)

{

delay(5000);

notenum = 5;

}

else

{

notenum = 5;

}

}

else if (val_1 == HIGH && val_2 == HIGH && val_3 == HIGH && val_4 == HIGH && val_5 == HIGH && val_6 == LOW) //switch 7 pressed, notenum = 6

{

if (notenumPrior != 6)

{

delay(5000);

notenum = 6;

}

else

{

notenum = 6;

}

}

else

{

notenum == 0;

//sets all three pitch indicator lights off

digitalWrite(11, LOW);

digitalWrite(10, LOW);

digitalWrite(9, LOW);

}

}

//---------------------------------------------------------------------------------------------------

// Sets the flat, sharp and spot on frequency thresholds depending on the string we're trying to tune

void freq_range_identify()

{

switch (notenum)

{

case 1://desired frequency=80-84 -- ideal value is 82

minfreq = 80;

maxfreq = 84;

break;

case 2://desired frequency between 108-112 ideal=110

minfreq = 108;

maxfreq = 112;

break;

case 3://desired frequency between 145-149 ideal= 147

minfreq = 145;

maxfreq = 149;

break;

case 4://desired frequency between 194-198 ideal=196

minfreq = 194;

maxfreq = 198;

break;

case 5://desired frequency= 245-249 -- ideal value is 247

minfreq = 245;

maxfreq = 249;

break;

case 6: //desired frequency between 328-332 ideal=330

minfreq = 328;

maxfreq = 332;

//highstring = 1;//stating high string is one being tuned

break;

}

}

//---------------------------------------------------------------------------------------------------

//Sets the 3 appropriate pitch indicator lights and sets the direction and speed of the tuning motor

void set_indicator_lights()

{

// if (highstring = 1)

//{

// checkfreq();

//}

if (frequency <= basefreq)

{

digitalWrite (11, LOW);

digitalWrite (10, LOW);

digitalWrite (9, LOW);

angle = 90;

}

else if (frequency < minfreq && frequency > basefreq)

{

digitalWrite (11, HIGH);

digitalWrite (10, LOW);

digitalWrite (9, LOW);

angle = countclockwisespeed;

}

else if (frequency > maxfreq)

{

digitalWrite (11, LOW);

digitalWrite (10, LOW);

digitalWrite (9, HIGH);

angle = clockwisespeed;

}

else

{

digitalWrite (11, LOW);

digitalWrite (10, HIGH);

digitalWrite (9, LOW);

angle = 90;

delay(1000);

checkfreq();

}

}

//---------------------------------------------------------------------------------------------------

void checkfreq()

{

frequency = 38462/float(period); //calculate frequency timer rate/period

Serial.println("doublecheck");

Serial.print(frequency);

Serial.println(" hz");

Serial.println(" ");

if (frequency <= basefreq)

{

digitalWrite (11, LOW);

digitalWrite (10, LOW);

digitalWrite (9, LOW);

angle = 90;

}

else if (frequency < minfreq && frequency > basefreq)

{

digitalWrite (11, HIGH);

digitalWrite (10, LOW);

digitalWrite (9, LOW);

angle = countclockwisespeed;

}

else if (frequency > maxfreq)

{

digitalWrite (11, LOW);

digitalWrite (10, LOW);

digitalWrite (9, HIGH);

angle = clockwisespeed;

}

else

{

digitalWrite (11, LOW);

digitalWrite (10, HIGH);

digitalWrite (9, LOW);

angle = 90;

Serial.println("String is tuned! Please move on!");

Serial.println(" ");

delay(5000);

}

}

//Turns the tuning motor appropriately

void move_motor()

{

Servo1.write(angle+trim);

delay(50);

Servo1.write(angleStill+trim);

}