ARDUINO DIGITAL GUITAR TUNER
Arduino Code
This project uses a modified version of Amanda Ghassaei's code for Arduino Frequency Detection in order to get frequency readings using the Arduino. Before we explain more about how the code works, please keep in mind the changes we made to Amanda's original code:
-
Changing of the LED Arduino pins to accommodate for having only 7 LEDs - this is for a common anode display, all bit values should be inverted for a common cathode display.
-
Assigning the SSEG display pins so that the note of the guitar string played is displayed. When looking at the code and Segments table above, please note that this configuration is for a common anode display, so all bit values should be inverted for a common cathode display.

Code Explanation
Overall, the Arduino code analyzes a signal coming into the Arduino's analog input and determines the frequency. The code uses a sampling rate of 38.5kHz and is generalized for arbitrary wave shapes. Follow through the explanation of each method below for a better idea of how this code works.
​
The first method, setup(), assigns all of the pins we are using on the Arduino and clear ADCSRA and ADCSRB registers.
​
The second method, ISR(ADC_vect), calculates the new signal slope, midpoint, and period whenever the new ADC value is ready. By doing so, we make sure the peaks are always located where the pulse wave toggles between its high and low states. Notice that since we are measuring the incoming signal from A0 with 8 bit precision (0-255), the midpoint (2.5V) will give a value of 127. All of this method's code takes place in the ADC interrupt (interrupts each time a new analog in value is ready from A0).
​
Next, we have reset() that just cleans out the index, noMatch, and maxSlope variables.
​
Then, there's stringCheck(). Here, we determine the correct frequency and light up the appropriate SSEG letter according to which string is being played. Note the ranges allowed for each string to be played:
A - 70-90
B - 100-120
C - 135-155
D - 186-205
E - 235-255
F - 320-340
​
Similarly, there's also frequencyCheck() that determines how far off the string is form the ideal frequency value of that note. Base on this, we light up the appropriate LED's to show how sharp or flat the note is.
​
There's also allLEDsOff() that simply turns all the LEDs and the SSEG display off.
​
Finally, there's loop(), which is where all the magic happens. Here, we start by turning all the LEDs off. Then, we can the calculate frequency timer rate/period so we can call stringCheck() and frequencyCheck() to display the tuner. This function occurs every time a string is played on the guitar.
Code Repository
To test out the code:
-
Make sure to have Arduino Software (IDE) downloaded first.
-
Then, copy and paste this code into the software package OR downloadn/clone the software from the public github repository.
-
Once the code compiles, you can connect the Arduino to your computer to program the Arduino with software.
​
/*
Group 4- Delta Force
Guitar Tuner Code
This code was originally from amandaghassaei ‘s method for Frequency Detection on Instructables.
Our team made a few changes including adding a SSEG display to display the note, which replaced 6 of
the LED’s. For more information, please read our how-to website at
https://deltaforce4.wixsite.com/howtodigitaltuner
*/
//data storage variables
byte newData = 0;
byte prevData = 0;
unsigned int time = 0; //keeps time and sends vales to store in timer[] occasionally
int timer[10]; //storage for timing of events
int slope[10]; //storage for 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
//variables for deciding 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
//variables for amp detection
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 30; //raise if you have a very noisy signal
//variables for tuning
int correctFrequency; //the correct frequency for the string being played
void setup()
{
Serial.begin(9600);
//Set all LED pins and SSEG pins as outputs
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
pinMode(3, OUTPUT);
pinMode(2, OUTPUT);
pinMode(A3, OUTPUT);
pinMode(A4, OUTPUT);
pinMode(A5, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
//Beginning LED sequence
digitalWrite(7, 1);
digitalWrite(6, 1);
digitalWrite(5, 1);
digitalWrite(4, 1);
digitalWrite(3, 1);
digitalWrite(2, 1);
digitalWrite(8, 1);
analogWrite(A1, 255);
delay(500);
digitalWrite(9, 1);
analogWrite(A2, 255);
delay(500);
digitalWrite(A5, 255);
analogWrite(A3, 255);
delay(500);
analogWrite(A4, 255);
delay(500);
cli(); //disable interrupts
//set up continuous sampling of analog pin 0 at 38.5kHz
//clear ADCSRA and ADCSRB registers
ADCSRA = 0;
ADCSRB = 0;
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
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements
sei(); //enable interrupts
} // end of setup()
ISR(ADC_vect)
{
//when new ADC value ready
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) < slopeTol) //if slopes are ==
{
//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[0] - timer[index]) < timerTol && abs(slope[0] - newSlope) < slopeTol) //if timer duration and slopes match
{
//sum timer values
totalTimer = 0;
for (byte i = 0; i < index; i++)
{
totalTimer += timer[i];
}
period = totalTimer; //set period
//reset new zero index values to compare with
timer[0] = timer[index];
slope[0] = 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();
}
}
}
time++; //increment timer at rate of 38.5kHz
ampTimer++; //increment amplitude timer
if (abs(127 - ADCH) > maxAmp)
{
maxAmp = abs(127 - ADCH);
}
if (ampTimer == 1000)
{
ampTimer = 0;
checkMaxAmp = maxAmp;
maxAmp = 0;
}
}
void reset() //clean out some variables
{
index = 0; //reset index
noMatch = 0; //reset match couner
maxSlope = 0; //reset slope
} // end of reset()
//Determine the correct frequency and light up
//the appropriate LED for the string being played
void stringCheck()
{
if (frequency > 70 & frequency < 90)
{
digitalWrite(2, 1); //Every digitalWrite in the stringCheck()
digitalWrite(3, 0); //function was added
digitalWrite(4, 0);
digitalWrite(5, 0);
digitalWrite(6, 0);
digitalWrite(7, 1);
correctFrequency = 82.4;
}
if (frequency > 100 & frequency < 120)
{
digitalWrite(2, 1);
digitalWrite(3, 0);
digitalWrite(4, 0);
digitalWrite(5, 0);
digitalWrite(6, 1);
digitalWrite(7, 0);
correctFrequency = 110;
}
if (frequency > 135 & frequency < 155)
{
digitalWrite(2, 1);
digitalWrite(3, 0);
digitalWrite(4, 1);
digitalWrite(5, 0);
digitalWrite(6, 0);
digitalWrite(7, 0);
correctFrequency = 146.8;
}
if (frequency > 186 & frequency < 205)
{
digitalWrite(2, 1);
digitalWrite(3, 0);
digitalWrite(4, 0);
digitalWrite(5, 1);
digitalWrite(6, 0);
digitalWrite(7, 0);
correctFrequency = 196;
}
if (frequency > 235 & frequency < 255)
{
digitalWrite(2, 1);
digitalWrite(3, 0);
digitalWrite(4, 0);
digitalWrite(5, 0);
digitalWrite(6, 0);
digitalWrite(7, 0);
correctFrequency = 246.9;
}
if (frequency > 320 & frequency < 340)
{
digitalWrite(2, 1);
digitalWrite(3, 0);
digitalWrite(4, 0);
digitalWrite(5, 0);
digitalWrite(6, 0);
digitalWrite(7, 1);
correctFrequency = 329.6;
}
}
//Compare the frequency input to the correct
//frequency and light up the appropriate LEDS
void frequencyCheck()
{
if (frequency > correctFrequency + 1)
{
analogWrite(A3, 255);
}
if (frequency > correctFrequency + 4)
{
analogWrite(A2, 255);
}
if (frequency > correctFrequency + 6)
{
analogWrite(A1, 255);
}
if (frequency < correctFrequency - 1)
{
analogWrite(A5, 255);
}
if (frequency < correctFrequency - 4)
{
digitalWrite(9, 1);
}
if (frequency < correctFrequency - 6)
{
digitalWrite(8, 1);
}
if (frequency > correctFrequency - 1 & frequency < correctFrequency + 1)
{
analogWrite(A4, 255);
}
} // end of frequencyCheck()
void allLEDsOff()
{
digitalWrite(2, 1); // digitalWrite for pins 2-7 are additions to original code
digitalWrite(3, 1);
digitalWrite(4, 1);
digitalWrite(5, 1);
digitalWrite(6, 1);
digitalWrite(7, 1);
digitalWrite(8, 0);
digitalWrite(9, 0);
analogWrite(A1, 0);
analogWrite(A2, 0);
analogWrite(A3, 0);
analogWrite(A4, 0);
analogWrite(A5, 0);
} // end of all LEDsOff()
void loop()
{
allLEDsOff();
if (checkMaxAmp > ampThreshold)
{
frequency = 38462 / float(period); //calculate frequency timer rate/period
}
stringCheck();
frequencyCheck();
delay(100);
} // end of loop()