Using a raspberry pi with a microphone to hear an audio alarm using FFT in python

If your smoke alarm or, in my case, water alarm goes off you want to know right away – even if you are currently half way across the world traveling in China. I run a fish tank. I take many precautions but you really can’t be too safe. I bought a set of Honeywell water sensors which I highly recommend. Sadly, this particular alarm is not IoT enabled. In fact, last I checked all the IoT alarm systems were terribly reviewed and overpriced. Hopefully that gets fixed soon. Until then, I needed to make do with what I had.

Rather than ripping the alarms open and hard wiring in a detection, I wanted to use an audio detection method so I could listen to many devices around the house. I wanted to receive a text message if any alarm went off and my raspberry pi could hear it. I saw some commercial devices for sale but what is the fun in that, honestly? I scoured github and google for some example code, but did not find much. So, here is my quick and dirty audio alarm sensor code.

DISCLAIMER: Under no circumstances should this code be used in any safety or production setting. Using an audio mechanism to listen for safety alerts is just plain dumb and could easily malfunction causing serious injury or death. This is for education purposes only.

This code works on a set of moving windows to detect confirmed alarm beeps. My alarm beeps at 3500Hz with a regular interval. I used a fast fourier transform with numpy in python to isolate the most intense sounds. I then use quadratic interpolation to determine the frequency of the most intense sound wave. If enough “blips” fill a window to detect a “beep,” and enough “beeps” fill a larger window, then the program indicates the alarm is active. If enough non-beep detected windows pass, it will clear the alarm detection.

At some point I’ll need to learn machine learning and teach it how to automatically recognize different types of alarms! Anyone have some tips?

13 thoughts on “Using a raspberry pi with a microphone to hear an audio alarm using FFT in python”

  1. Thanks for sharing this, the code looks like exactly what I was looking for…except I want to modify it to detect when our doorbell goes off!

    What did you use as a microphone and how did you connect it up to the Pi?

    1. I use a blue yeti USB microphone connected to my raspberry pi. It works great! I have a cheaper USB input too but I have not tested it yet… it likely will require lowering the sample rate.

      1. I have found issues with the current approach of using a sound detection:

        It works pretty well – but it also detects my neighbors buzzing in too… Sadly, the wire which indicates “which button the user pressed outside” only goes to the base station which is in a common area of our building. That wire does not go to my unit where my raspberry pi is located. Instead, the base station detects the signal and broadcasts a (very loud) audio buzz to my unit. For some crazy reason, I am not seeing this signal very loud and I have not had a chance to explore why. I am debating whether to fix this issue (likely I am monitoring the wrong audio?) by using some amplitude detection, or whether to move my raspberry pi system to the common area (risky, but simple) so I can simply use the high/low signal coming from the button rather than trying to audio process it. My project has been on hold for a few months due to this limitation – would love your ideas.

  2. I had this code running on a Mac, no problem. I am trying to run it on an RPi now, and not having much success. arecord and aplay run fine, like: “arecord example.wav -D sysdefault:CARD=1”

    When I try to run your code, I get:

    {bunch of ALSA warnings not shown}

    Alarm detector working. Press CTRL-C to quit.
    freq= 42.4418160133
    Traceback (most recent call last):
    File “”, line 49, in
    _stream.get_read_available()), dtype=short)[-NUM_SAMPLES:]
    File “/usr/local/lib/python2.7/dist-packages/”, line 608, in read
    return pa.read_stream(self._stream, num_frames, exception_on_overflow)
    IOError: [Errno -9981] Input overflowed

    Maybe those ALSA warnings are significant, but overflow sounds like a memory issue. How much memory is installed on the RPi you’re using, please?

    1. What external microphone are you using and what is the sample rate it supports? I believe your memory issue may actually be that you are exceeding the sample rate of your external microphone sound card. I have a very nice usb microphone (blue yeti – supports 48khz) but many cheap sound cards may require a smaller sample rate.

      1. Oh, my mic is a piece of junk! I had no idea it could be the mic — I suppose PNP mics must have a built -in sound card. I’ll try turning the sample rate down for a test. Thanks so much!

        I do a radio show for print-disabled people, and I’ve been looking for an excuse to get a better mic, so thanks twice!


  3. Dear Benjamin,

    thank you for your code…

    Just search some code to detect 1750hz to open a hamradio Relais..

    I modify your code to this:

    #!/usr/bin/env python
    import pyaudio
    from numpy import zeros,linspace,short,fromstring,hstack,transpose,log
    from scipy import fft
    from time import sleep

    #Volume Sensitivity, 0.05: Extremely Sensitive, may give false alarms
    # 0.1: Probably Ideal volume
    # 1: Poorly sensitive, will only go off for relatively loud
    # Alarm frequencies (Hz) to detect (Use audacity to record a wave and then do Analyze->Plot Spectrum)
    TONE = 1750
    #Bandwidth for detection (i.e., detect frequencies within this margin of error of the TONE)
    BANDWIDTH = 10
    # Show the most intense frequency detected (useful for configuration)


    #Set up audio sampler –
    NUM_SAMPLES = 2048
    SAMPLING_RATE = 48000
    pa = pyaudio.PyAudio()
    _stream =,
    channels=1, rate=SAMPLING_RATE,

    print(“1750hz detector working. Press CTRL-C to quit.”)


    while True:
    while _stream.get_read_available()< NUM_SAMPLES: sleep(0.01)
    audio_data = fromstring(
    _stream.get_read_available()), dtype=short)[-NUM_SAMPLES:]
    # Each data point is a signed 16 bit number, so we can normalize by dividing 32*1024
    normalized_data = audio_data / 32768.0
    intensity = abs(fft(normalized_data))[:NUM_SAMPLES/2]
    frequencies = linspace(0.0, float(SAMPLING_RATE)/2, num=NUM_SAMPLES/2)
    if frequencyoutput:
    which = intensity[1:].argmax()+1
    # use quadratic interpolation around the max
    if which != len(intensity)-1:
    y0,y1,y2 = log(intensity[which-1:which+2:])
    x1 = (y2 – y0) * .5 / (2 * y1 – y2 – y0)
    # find the frequency and output it
    thefreq = (which+x1)*SAMPLING_RATE/NUM_SAMPLES
    thefreq = which*SAMPLING_RATE/NUM_SAMPLES
    print "freq=",thefreq
    if max(intensity[(frequencies TONE-BANDWIDTH )]) > max(intensity[(frequencies TONE-2000)]) + SENSITIVITY:
    if blipcount <= beeplength:
    if blipcount == beeplength:
    print "1750hz"


    It works great, but maybe there is something else to improve?

    Best regards Tommi

    1. This code would likely not work very well to detect an ambulance because most ambulances have a sweeping tone, where this code is trying to find a specific frequency. You could adapt the code to fit your purpose though.

    1. Sure, go for it – but unfortunately this particular algorithm will be very poor at accurately detecting vehicle sounds — it is simply listening for a particular frequency which is the loudest thing it can hear. Each vehicle makes many frequencies and since you are likely in a noisy outside environment it will be difficult to avoid false positives or especially recognize a particular car. Good luck 😉

Leave a Reply

Your email address will not be published. Required fields are marked *