Introduction: Arduino Frequency Detection

As a follow up to the Arduino Audio Input tutorial that I posted last week, I wrote a sketch which 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 waveshapes. I've also turned the LED attached to pin 13 into a clipping indicator, so you know if you need to adjust your signal's amplitude as you send it into the Arduino.

Some project ideas for the code presented here include:

pitch reactive projects- change the color of RGB LEDs with pitch, or make a lock that only opens when you sing a certain pitch or melody
audio to MIDI conversion- get the Arduino to translate an incoming signal into a series of MIDI messages. See my instructable about getting the Arduino to send and receive MIDI for lots of example code to get started
audio effects- use the frequency information to reconstruct an audio signal from the tone() library or with some stored samples to make a cool effects box/synthesizer

The first step of this project is to set up the audio input circuit. I wrote a detailed Instructable about that here.

Step 1: Detection of Signal Slope

First I wanted to experiment with peak detection, so I wrote a piece of code (below) that outputs a high signal when the incoming audio signal has a positive slope, and outputs a low signal when the incoming audio signal has a negative slope.  For a simple sine wave, this will generate a pulse signal with the same frequency as the sine wave and a duty cycle of 50% (a square wave).  This way, the peaks are always located where the pulse wave toggles between its high and low states.

The important portion of the code is reproduced below.  All of this code takes place in the ADC interrupt (interrupts and runs each time a new analog in value is ready from A0, more info about what interrupts are and why we use them can be found here)

  prevData = newData;//store previous value
  newData = ADCH;//get value from A0
  if (newData > prevData){//if positive slope
    PORTB |= B00010000;//set pin 12 high
  else if (newData < prevData){if negative slope
    PORTB &= B11101111;//set pin 12 low

I should note here that in this tutorial I use direct port manipulation to turn off and on the output pin (pin 12) of the Arduino.  I did this because port manipulation is a much faster way of addressing the Arduino's pins than the digitalWrite() command.  Since I had to put all the code above inside an interrupt routine that was going off at 38.5kHz, I needed the code to be as efficient as possible.  You can read more about port manipulation on the Arduino website, or see the comments I've written above to understand what each line does.  You'll also notice in the code below that I used some unfamiliar commands in the setup() function so that I could get the Arduino's analog input to sample at a high frequency.  More info on that can be found in my Arduino Audio Input tutorial.

Fig 1 shows the pulse output in blue and the sine wave in yellow on an oscilloscope.  Notice how the pulse output toggles each time the sine wave reaches a maximum or minimum.  Fig 2 shows the pulse output in blue for an arbitrary waveshape in yellow.  Notice here how pulse wave takes on an irregular duty cycle because the incoming signal (yellow) is much more complicated than a sine wave.

Step 2: Mid Point Detection

I decided that I would get more accurate results detecting the frequency of a wave by keeping track of the times the wave crosses 2.5V instead of counting peaks.  In the last step I was essentially finding the places on the wave where the slope = 0 and counting the time between these events.  However, when the slope = 0, noise on the signal is enough to change the direction of the slope and skew my results.  When the wave is crossing 2.5V, it usually has a slope with a magnitude larger than 0, so I would not have to worry about the effects of noise as much.

The important changes to the code are reproduced below.  Since I am measuring the incoming signal from A0 with 8 bit precision (0-255), the midpoint (2.5V) will give a value of 127.  All of the following code takes place in the ADC interrupt (interrupts each time a new analog in value is ready from A0)

  prevData = newData;//store previous value
  newData = ADCH;//get value from A0
  if (prevData < 127 && newData >= 127){//if increasing and crossing midpoint
    PORTB |= B00010000;//set pin 12 high
  else if (prevData > 127 && newData <= 127){//if decreasing and crossing midpoint
    PORTB &= B11101111;//set pin 12 low

Fig 1 shows the pulse output in blue and the incoming signal to A0 in yellow.  Notice how each time the signal crosses 2.5V, the pulse output toggles.  Specifically, the output goes high when the signal crosses 2.5V with a positive slope and the signal goes low when the signal crosses 2.5V with a negative slope.  Fig 2 shows the pulse output in blue and the audio signal before it has been +2.5V DC offset in yellow.  Remember, this DC offset was necessary to get the audio signal in the 0-5V range for the Arduino's analog input pin, but normally audio signal oscillate around 0V.  In fig 2 you can see how the pulse outputs toggle corresponds to the time when the audio signal crosses 0V.  Fig 3 shows an arbitrary waveform in yellow (again before DC offset) and the pulse output in blue.  Again, the pulse toggles each time the yellow signal crosses 0V, notice how the behavior of the pulse output with the arbitrary waveform is more complex than with the sine wave.

Step 3: Sine Wave Frequency Detection

Next I measured the period of an incoming sine wave, calculated the frequency, and printed the frequency.  To do this I set up a timer in the ADC interrupt that increments each time the interrupt executes (a rate of 38462Hz).  Each time the incoming signal crosses 2.5V with a rising slope I sent the current value of the timer to a variable called "period" and reset the timer to 0.  That code is reproduced below (all takes place within the ADC interrupt).

  prevData = newData;//store previous value
  newData = ADCH;//get value from A0
  if (prevData < 127 && newData >= 127){//if increasing and crossing midpoint
    period = timer;//get period from current timer value
    timer = 0;//reset timer

 timer++;//increment timer

Then in the main loop() function, I calculated the frequency by dividing the timer rate by the period.  I used Serial.print to print these results in the Arduino serial monitor.

  frequency = 38462/period;//timer rate/period
  //print results
  Serial.println(" hz");

Fig 1 shows the signal coming into A0.  The start and end of one cycle measured by timer is indicated by the image note.  Fig 2 shows the output from the serial monitor (command/ctrl+shift+m).  This technique works great for sine waves, but when wave become more complicated (and cross 2.5V more than twice in one cycle) this technique breaks down.

Step 4: Generalized Pitch Detection

In this code I generalized my frequency detection algorithm so that it could handle waves of many (hopefully all) shapes.  When writing this code I wanted to stick with the point I made in step 1 about not using the peaks and valleys as markers measure the period of the signal (minimize error due to noise).  I also wanted to write something that was as simple as possible (needs to execute at 38.5kHz) while still being robust enough to handle lots of waveshapes.  I decided to use a technique similar to an oscilloscope trigger.

Basically what I did was choose a voltage that I always knew would be in the bounds of my wave (2.5V).  Then I looked at every time the wave crossed this level with an upward slope, let's call these "threshold events".  If this happened multiple times in one cycle I chose the threshold event with the largest slope to be the beginning of my cycle. Similar to the last step, I used a variable called "time" (incremented at a rate of 38.5kHz) to measure the time between threshold events and stored this is an array called timer[].  I also recorded the slope at each of the threshold events in an array called slope[].  Then I compared the elements of timer[] and slope[] to figure out where there was a match.  Once a match was found, I added up the elements of timer[] to determine the duration of the cycle and sent this value to a global variable called "period."  Then in the main loop() function (all of the steps I've just described happen in the ADC interrupt routine) I used the value of period to calculate the frequency and print it.  I should also add that I put a variable in the code called "noMatch" which helped me to decide that it had been too long since I had a good match and that I should just rerecord the elements of timer[] and slope[].

When writing this I thought about a lot of possible scenarios that might break the algorithm.  The trickiest wave in my mind is one which passes the 2.5V threshold many times in one cycle at similar slopes and spaced out along the cycle similarly.  I you have a wave like this, you should keep slopeTol very low (0-3) and you might find that lowering timerTol (to 5 maybe) helps track the wave correctly.  Also, if you want to measure waves with very steep slopes (like pulse waves) you should set the value of slopeTol up to 100 or even all the way up to 256 to track them better.

Generally this piece of code seems to handle lots of shapes very well, you can see some of my results in the images above.  The incoming signal is shown in yellow and the threshold event that the Arduino is tracking is indicated by a pulse of pin 12 (blue).
I also added a bit of code to stop calculating and print frequency data when the amplitude of the wave falls below a certain level.  (If there is little or no signal then the code above sometimes spits out a bunch of garbage).  Here it is:


maksman (author)2016-12-24

Hello Amanda,

Thank you very much for this very informative post!!!

I am currently working on a pitch controlled LED Christmas light and I'm using your code for the pitch detection, I'm using my own circuit, electret mic config and I am reading "inf hz" which means period is "0" but when i hold the lead alone that is connected to the a0 it reads some frequencies. I printed the "period" and it shows "0" confirming it.

Any ideas as to why period is 0?

maksman (author)maksman2017-01-06

Edit: I think I found out why, the input values I get are not "slopey" enough as they don't have a wide enough margin apart from the max and min values.

nmancha (author)maksman2017-05-08

how did you fix the slopey problem

maksman (author)nmancha2017-07-06

Hey there, my circuit was at fault. The original circuit i was using was a mic module but was for knock detection. I had to design my own circuit and it worked.

AlexV178 (author)2016-12-21

Thanks for the project!

I have constructed the audio input circuit outlined in the link at the top of this page. I'm trying to construct a device that would only light the Led attached to the Uno when a specific frequency is detected by the microphone. I'm having trouble develop code for this task and need a little guidance.

If anyone can help that will be great,



AlessioO2 (author)AlexV1782017-03-25

we are on the same boat mate :D

LouiseM67 (author)2017-01-24

hello mam . im fascinated by your code. mam im currently doing a design project that detects emergency vehicle by their siren frequency. do you think its possible with your codes as the foundation of it ? i really need some help with this

maksman made it! (author)2017-01-18

Thanks very much "uh-man-duh". I have been able to figure the issue out and it works well for my microphone circuit. Also thanks for making me understand interrupts better.

joshua.santarelli.9 (author)2015-08-11

Thanks for the great project info. I've tried it out using an electret microphone with an amplifier, but I'm not getting a clean output on the serial monitor. Basically, the frequencies it spits out are all over the map. For instance, if I play it a note around 70Hz (I'm planning on putting it on my upright bass, so I've been using that to test) it will give me a bunch of frequencies that could be from 10Hz to 7000Hz, then give me several in a row that are about 70Hz, then back to all over, etc. Even when I played a 440Hz note from a tuner it gave me a decent amount of randomness.

My guess is that I need to adjust the slopeTol and timerTol values, since the random frequencies are often (though often not) some multiple of the one I'm looking for, making me think that it's missing some crossings. Am I thinking right? I don't really know which one to change and by how much; I've messed with them both and they're currently at slopeTol = 2 and timerTol = 15. Any ideas on some ballpark ranges for these if I'm looking to measure tones from my bass? Is there some other part that I should tweak? Thanks for the help. I'd really like to use your method instead of FFT or FHT, since it seems much more efficient for my project.


See post above.


One thing I thought of: right now I'm powering the Arduino through the USB, since I'm using the serial monitor to debug. Would using the USB as a power source be adding noise to the signal? My project will be powered by a 9V battery and I have one hooked up now, just turned off. Would it help to have the 9V source on in addition to being plugged in to the USB? I have to keep it plugged in to use the serial monitor, but if that's what's causing the problems...

ValentinaM22 (author)2016-09-30

Thanks a lot for this!

although I have a question: Id like the Arduino to recognize some of the notes I play with my clarinet (so live sound), how is it possible to do this without having to use a digital track?

VaughnJ1 (author)ValentinaM222016-10-02

It's possible to do this with just the circuit and code.

I am using a variation of this sketch to read the output of a guitar pickup and display the string's frequency and intonation (an adjustment that corrects harmonics).

Since we are using instruments; we do have to consider some adverse effects of harmonics/overtones. I have included a Low Pass Filter ( 4.7K and 6.8uF - canning everything above 5kHz.) You'll want a filter that allows your highest note to come through - assuming you don't overblow or play using harmonics.

Double your highest playable note, then convert it to a frequency - insert an if statement to not display everything above it.

Change the code to divide any sampled frequency to a fundamental listed.

A 10uF coupling capacitor did not work well with this circuit for my intended application. A value of 4.7uF instead allowed me to have a quite fluid readout in my terminal display. I have also removed the 47nF capacitor to ground. All it did was hold onto my sample for too long and prevent accurate/reliable readings.

I am in the process of writing a header for musical computations which includes FHT/FFT algorithms - but all you'll need to do is use your preferred method of identifying and dealing with different number values.

double noteFrequency = 440; // Concert 'A' - 440 hz

double deviation = 4; // nobody's perfect

double hiRange, loRange;

hiRange = noteFrequency + deviation;

loRange = noteFrequency - deviation;

if(frequency >= loRange && frequency <= hiRange)


// We have detected roughly an 'A - 440hz" do something


Expand this as you will but it's a basic start.

Thanks for an excellent instructable Amanda!

MakerKen (author)ValentinaM222016-10-02

Aside from inventing an analog computer or using quantum computing, the computing devices we all use know 1's and 0's. Using a digital signal, 10-bit is referring to 2^10=1024, that is with ten on/off switches, there are 1024 combinations. That is why there are steps to the wave.

The sound that we hear is the intensity over time, how loud a sound is doesn't tell us what note it was. FFT is a 'transform' to show to intensity over frequency, then when we see intensity at a certain frequency/note we see that note was played.

I've seen libraries using the Teensy microcontroller running at 120MHz being able to sample and do a FFT to determine notes. Start looking there. The only way I could think of it being possible otherwise, is if it was a small pattern of exact notes you may be able to do it using set very specific set of bandpass filters, but that approach would have other problems.

MarianoG11 made it! (author)2016-02-09

I'm working on a personal project about audio frequency detection using Arduino. I have a question to ask you about why the last frequency detected by the software it's repeated in the serial monitor.

I mean, while I playing a sound and then I have no signal in the line, however, the serial monitor still printing the last frequency detected. Do you know how to fix that or if it is possible? I leave an attached screenshot to show you what I'm talking about.


PaulC175 (author)2016-01-26

thank you for this

behoudenhuis (author)2015-09-10

Thanks a lot for posting this very clear and helpful project.
I build it but using the soundcard output of a computer(or a similar one, like a phone or mediaplayer) as an input. Since these signals are much stronger I left out the opamp. For the rest the setup is similar: one resistor(+) and a potmeter (-) as a voltage divider before the 10uF Cap and two resistors (+&-) as a voltage divider on the other side. And the 47nF Capacitor between signal and (-).
It works fairly good but the readings show inconsistencies f.i.: many lines of an almost perfect 80,80-82,01Hz and all of a sudden a 25,94Hz (and strikingly often half the values of the signal) or say a 12820,67Hz passing by. And often similar erratic glitches pop up at the beginning and/or at the end of a signal.
For an artwork I want to steer a servo with this technique, with something moving in sinc with a video (either directly (frequency is position) or through a ssc32 (frequency is position and signallength is speed) with charstrings passed over). Therefor these jumps are quite annoying. I tried tweaking a lot with the settings in your program (ampThreshold, timingTol, slopeTol) and on the hardwareside with capacitors to get the ripples out but so far with no luck. Any suggestions?

My iduino is powered by the computer to be able to read the readings, and I don't have an oscilloscope to check the signal-output of the computer. I tried other sources though, with similar outcome.

Many thanks anyway for the instructable which already helped me a lot and many thanks in advance.


steering servos this way seems a kind of a detour but manually putting the right blocksignals in the right place and play them with the mediaplayer appeared to be even more work. Using the frequency technique makes it far more flexible and ubiquitous.

cyril.k.chan.7 (author)2015-04-07

Thank you for your sharing Amanda!! I don't have to start from the zero point on my automatic guitar tuner project!

But I have a question.Can I use a piezo to pick up the vibration or the sound instead of using a mic,which enables the tuner to work in noisy environment?

daviddein (author)cyril.k.chan.72015-06-17

Yes you can. Im using it on a violin. At the moment the algorithm is a little sporadic but if you turn down the time and slope tolerances it works a little better. I think if you restructure the code to get the full 10 bit resolution instead of 8 bits, you should be able to make this code work better. But the extra instruction cycles might hinder any other operations...


I've got a project along these lines as well, and I wondered that same thing about picking up noise from other sources (or instruments, since I plan on using it while playing). Using a piezo came to mind, glad to see someone has done it successfully. I was wondering, what did you use to stick your piezo on your instrument? I don't want do anything that would damage the wood. Did you use some sort of adhesive that's safe for string instruments?


I tried a under the bridge pick up, as well as a bridge stick on pickup; A generic rod piezo and a Shadow SH 3001 respectively. I used it on a dedicated electric so the rod piezo was appropriate and much cheaper, but there are film-type under the bridge pickups like Realist pickups that should work for you. I have found that blending both types of pickups (horizontal and veritcal) through a cheap behringer micromix mx400 gives really good results, although you'll have to contend with two wires instead of just one.


Gotcha, thanks. I'll see if I can find something like that for my upright bass. I'm planning on using it to control an LED strip, so I probably don't need the quality to be quite that high; one pickup should do it. Did you need to wire it up with the whole op amp setup from the Audio Input tutorial, or do some have amplifiers built in?


Yes, you'll need to amplify it a little bit based upon the voltage response characteristics of the element you are using. You'll have to tinker because no two piezo's are the same. There are a lot of projects for arduino out there on how to do this, just search "arduino audio input" if the above schematic isn't descriptive enough for you.

LucasP2 (author)2014-09-28

So with this arduino code I would be able to control a the light's frequency with the frequency of the sound? I want to build a lighting system for my room and car that will react with the sound, but in a way so that bass="cool colors", treble= "warm colors"(or even vice versa). I feel like that would give a better experience to the music

nkuck (author)LucasP22015-06-22

Did you manage to get your idea working? I have a similar project idea.

rien.brand (author)2015-01-05

Hi Amanda,

Thanks for the very usefull work you have done, nou I can measure the frquency from my agriculture application that use a exciter ring.

I have one qustion how can I change to another analog pin in the ide?


Rien Brand


daviddein (author)rien.brand2015-06-17

Not sure if you found the answer to your quesiton, but if you look up ADMUX register in the datasheet for your particular atmega you'll see how to change the analog to digital converter.

Here's a link to understand ADMUX

rjtsh (author)2015-04-27

We did it, finally facing problems. We used electric guitar for this purpose. Thanku Amanda. Keep up the good work !

IchbalB (author)2015-04-06

Hello Amanda thanks for your sharing. i want to ask about this one..
so i am developing an Arduino human scream detection is it possible to me detect human help scream voice using this kind of Arduino program? because i want to build up the component consist by band pass filter , gain, and rectifier circuit and then look for its pitch detection and voice duration..

Do you have any idea about the circuit that i`ll need to complete my project?
Or is it possible enough to use some Arduino library and write the speech recognition code?

성범강 (author)2015-02-24

Hi Amanda, thank you very much for this source!! very amazing!!!

but... little problem my arduino...

your source is exellent !!

but.. my arduino is not working

my arduino

with 256K Bytes


version is problem is different???

byondo (author)2015-01-29

Hi Amanda, thank you very much for this instructable! Very useful!

I prefer this simpler code version (integrated with amp treshold), because I want to make a sound analyzer , where you whistle or sing a "oooh" note and then you have the frequency. Making a portable built with a 4 digit display and there it is a perfect gadget for any sound engineer or musician, who wants to know which frequency causes microphones feedbacks.

Does it make any sense collecting a number of measurements and make a mathematical average? as I don't need a fast process, but a slower and more solid (without "garbage" measurements); I tried some "for" cycle with a simple array, but it doesn't seem to add any precision (maybe I don't know where to put it)..

renaissa (author)2014-12-03

Hi amanda...your instructables on arduino are great..however i want to know if it is possible to measure a amplitude of sine wave on arduino

shinew (author)2014-11-20

Thanks for the instruction! I just put together a circuit with an amplified electret mic signal using opamp feeding into it. However, when I'm looking at the serial output, it only seems to work for the first second or 2, then it stops working with the last line looks like attached image. Any idea what could be the cause? thanks!

Screenshot 2014-11-21 01.45.01.png
weichi.chien (author)2014-11-14

Hi, this is an excellent project. It works fine on my arduino uno. However, I need it to run on a arduino mega with ethernet shield. When adding the codes to my ethernet project, the connection to internet is brocken (I'm uploading data onto xively server). Any suggestion?

buddika123 (author)2014-11-01

How we meaured the frequency ....I want to detect frequncy range 60Hz to 100Hz....can I use this progtame thanks and quick reply from you

Derpancakes (author)2014-10-30

This is awesome! I'm looking in to a guitar-MIDI pedal, but a DIY solution is cheaper and way cooler. Props to you for this awesome bit of code!

novaninjas (author)2014-09-25

Hi, has anybody completed this project (possibly, with a diff sort of mic) and wouldn't mind sharing about it? Am a beginner and would really like to try this so.. slightly more detailed steps would be most helpful :D

pierattilio (author)2014-09-07

Hi amanda, i want to congratulate with you on this instructable, really powerful :D I have a question, that is is it possible to check the frequencies of a playing song in this way, through an aux signal from i.e. an MP3 player or the notebook audio headphones output (after the right managment of the signal like in your instructable on arduino auido input)?

Thanks a lot


aoss (author)2014-08-07

Has anyone made this project with ChipKit Uno32. I am really looking for projects like that for my Uno32 developement board because I need to read analog signal frequency coming from optical rotation sensor.

Bokononestly (author)2013-04-22

Great work! I'm trying to use this code for an automatic laser oscilloscope I am designing.

I've found that the code stops measuring frequencies as soon as I try analogRead() from a different analog pin. At the moment I don't fully understand the lower level programming of the ADC that you've done. Is there any way to read from the other analog pins without interfering with the pitch measurement?


yes, the way this is set up, all of the other analog pins are deactivated. you can read an analog value from a digital pin by using RCTime:
hope that works for what you're doing.

MirzaR1 (author)amandaghassaei2014-08-02

Thank you for the great article. I was just wondering can I use a microphone with a preamp to make the detection.

I have :

Thank you very much!

arimika (author)2014-04-23

i tried to build guitar tuner

but arduino can't read the input from my guitar.
can you help me to solve this problem ?

amandaghassaei (author)arimika2014-04-28

do you have an oscilloscope?

arimika (author)amandaghassaei2014-05-06

i don't have oscilloscope.
does become a problem if I use 100nF Cap on the DC offset ?

i see your project use 47nF Cap.

amandaghassaei (author)arimika2014-05-06

no that should be fine. You will need an oscilloscope to debug this, maybe see if there's a hackerspace or school near you where you can use one for free.

arimika (author)amandaghassaei2014-05-22

what is the main problem that the input from guitar can not reach the Arduino?

whether the project could be completed if there is oscilloscope ?

amandaghassaei (author)arimika2014-05-24

you could try using a computer running audacity as a makeshift oscilloscope, to find out where the signal is getting lost. Do you have an audio input on your computer?

arimika (author)amandaghassaei2014-05-24

yes, i have.
how to make audio output from amplifier and DC offset circuit into my computer ? there are 3 outputs "A0 , 5v and GND"

andrew95434 (author)2014-05-01

I'm new in Arduino, I want to make this but with an Electret Microphone, I think it's very different than the microphone you used. How can I make the circuit with an Electret Microphone?

About This Instructable




Bio: I'm a grad student at the Center for Bits and Atoms at MIT Media Lab. Before that I worked at Instructables, writing code for ... More »
More by amandaghassaei:OTCA Metapixel - Conway's Game of Life"9 Degrees of Freedom" IMUTwitter Controlled Pet Feeder
Add instructable to: