Tuesday, February 18, 2014

A simple Sound Pressure Level Meter (SPL) dB audio meter using AVR ATmega

Updated to version 02.



What is proposed here is a SPL db meter using and AVR Atmega micro.


A sound level meter or sound meter is an instrument which measures sound pressure level. Sound pressure level (SPL) or sound level is a logarithmic measure of the effective sound pressure of a sound relative to a reference value. It is measured in decibels (dB) above a standard reference level. The commonly used reference sound pressure in air is = 20 µPa (rms) which is usually considered the threshold of human hearing. Keep in mind that 1 pascal will equal an SPL of 94 dB. Because the frequency response of human hearing changes with amplitude, a weighting have been established for measuring sound pressure. Usually the A-weighting curve is used. A weighting curve is a graph of gain across the frequency range (10Hz to 20kHz).

SPL level is defined as


given p_rms as the sound pressure measured, and p_ref as the reference sound pressure.

Once we have got the RMS value of the signal (actualRMS), we can transform it to SPLdb using this formula:


given refRMS as the reference RMS value for the input board at a know refSPLdb SPLdb level.

To compute SPL measurements, the meters loop is:
  1. collects N samples
  2. do FFT for the N samples collected, the signal is now transformed in the frequency domain
  3. apply A-weighting (in freq domain)
  4. get magnitude of the signal
  5. get RMS value of the signal
  6. apply a time-weight filter to RMS value
  7. compute the SPL using the RMS value
  8. output data
Every sample is collected at a fixed time, a timer interrupt impose this timing, this is because we need to know the sampler frequency, to built filters and output signal magnitude.
Runnig @16Mhz i'm able to collect samples at almost 22050Hz.

For FFT i've used Radix-4 FFT library you can find it here http://davidegironi.blogspot.it/2013/06/avr-atmega-audio-input-rma-using-fft.html.

The method to weighting the signal proposed here just use a weight table that contains the weight of the signal in the frequency domain, this table shoud be FFT size/2, because we can retain frequencies below Nyquist rate.


A matlab script to check this filter method is used.
The picture above is the frequency reponse graph of the A-Weighting curve used.
The picture below compares the values of SPL for the unfiltered signal (blue), A-weighting filtered signal using the matlab "filter" function (green), A-weighting filtered signal using the weight table method (red).
As you can see, matlab native "filter" function and weight table method almost response in the same way.


Once we have got the A-weighted signal we can compute the RMS value for the signal.
To obtain RMS value of the AC current of the audio signal processed, from which you can get the SPL db value, we use the Parseval's theorem for FFT so for fft size of N (look at the Radix-4 FFT library link above for further information)


An SPL meter also needs the signal to be time-weighted. Time-weightings have been internationally standardised, 'S' (1 s) originally called Slow, 'F' (125 ms originally called Fast and 'I' (35 ms) originally called Impulse.
A time-weight is a RC filter, exponential averaging is equivalent to a simple RC filter


Y is the RMS signal global, and Ynew is the RMS signal computed every step.
alpha effects the filter speed, it has to be a number between 1 and 0.
We can define alpha so that it depends on the sample frequency Fs and the windows size W. A good estimation could be


In my code, i've use another estimation for alpha, it seams to me better to consider the number of cycles needed to collect the N samples, i've call this number fsc, said this, my alpha was estimated as


Now that the signal is windowed, we can compute SPL using the above definition:



The input microphone preamp board proposed here is a derivation of the John Conover WM-61A preamp. The reference is the schematic-6 found here: Using the Panasonic WM61A as a Measurement Microphone (John Conover).
This board is stated 306uV RMS at ~32db.
Using an ebay SPL meter, and a scope, i've found that my board implementation should be 315uV at ~32db (which is next to 306uV as stated in the referecence audio board schematics).


Two http://processing.org/ scripts are provided as a viewer for the data.
The screenshot below shows one of them, it also show you the spectrum of the input signal.


I'm not a DSP expert, so method proposed here can be improved, for example it is possible to implement the A-weighting filter in time domain, becuse it can be used as a IIR filter, this will prevent the use of the FFT computation for the signal, saving microcontroller resources.

Also, if you want to help me, send me feedback if you:
  • you've got any better input board circuit
  • you've found bugs in my software implementation
This project is build on an ATmega8 running at 16Mhz, compiled with avrgcc, using Eclipse IDE


ChangeLog
  • 02b: main.c strict compile error fixed, function audioget_getrmsval now called the right way (thanks to Emmanuel Pierre for reporting this bug) 
  • 02: weighting function bug fix, weighting now applied on both the real and imaginary part of the fft (thanks to Xiaofeng for reporting this bug).
  • 01: first version.

Code

Notes
  • read risk disclaimer
  • excuse my bad english

76 comments:

  1. Hi Davide, great idea. Im working in a SPL Meter too, and I found Primo Mic M173 capsule, wich is working great. Im refining preamp now. I will try your design for it. Is your code good to be run on an arduino uno r3 board?

    ReplyDelete
    Replies
    1. Hello, as far as i know Arduino UNO is built on ATmega328, so yes, it can be run on that board.
      But yo built it using the Arduino framework code may need some refactoring.
      Let me know how does it works.

      Delete
    2. Hi,davide I'm asking you?? , Audioget_visualizer software for me Please send Email me: msun.nbtelectronic@gmail.com Thank you

      Delete
    3. You can find the visualizer software in the code you can download here.

      Delete
    4. Hello,
      Do you have the same code for arduino framework?
      I mean the audioget library refactored for arduino?
      Thanks

      Delete
    5. No I'm sorry. However it should not be difficult to translate in to arduino. Because arduino too use avrgcc compiler, and it is based on a ATmega family.

      Delete
  2. Excuse, I 've downloaded the code file, But They are (PDE) format How they are setup ? Computer does not recognize their format
    Please get help !! Thank you





    ReplyDelete
    Replies
    1. Hi, mr Davide Gironi, Thanks for your help, Im download software & setup,
      Im a have a question of you ?
      circuit diagram & How The connect Micro to usb & Driver Used for computer
      Type usb adapter & IC used for adapter About this explain
      Thank you



      Delete
    2. microcontroller to pc communication is done through UART, you have to connect an RS232 USB TTL adapter to TX and RX uart pin of your microcontroller. If this is your first project, i suggest you to build an "hello world" uart example.

      Delete
  3. Hi, for programm micro atmega8 in project of HEX code in file avr_splemeter.HEX
    use ?

    ReplyDelete
    Replies
    1. you can compile the code by your own, using avrgcc as compiler, or upload the avr_splmeter\Release\avr_splmeter.hex file to your ATmega8 microscontroller, running at 16Mhz.

      Delete
  4. Hi Davide, I cannot download the zip from source forge. Would you please send me copy to my mail address? beijingcrow@gmail.com Thank you

    ReplyDelete
  5. Hi Davide, I use the ATmega1281 with 3.3V power supply. Can I use this circuit with Vcc = 3.3V power supply ?

    Thank you for answer.

    ReplyDelete
    Replies
    1. Hello,
      it depends on the opamp you use, you have to look for one with a minimum votlage below 3v. Like the TLC272C*.
      Of course you have to calibrate you microphone pre, setting up the corretct voltage ref and db ref (AUDIOGET_VOLTREF, AUDIOGET_DBREF).

      Delete
    2. Hi, i have another question. Why do you use in the "adc_emafilter" (64 - aplha) instead of (1 - alpha) and how you calculated the coefficient alpha = 30? I found a formula for EMA filter: alpha = exp (-1 / (sampling_frequency * TAU_S)) ...

      Delete
    3. I've use 64 to avoid the use of float math.
      Writing
      _ (1-alpha)*Y + alpha*Ynew _ , alpha between 1 and 0
      is pretty the same of int math
      _ ((63-63alpha)*Y + 63alpha*Ynew)>>6 _, alpha between 63 and 0
      To analize the freq response of that filter one can use the Laplace transformation, and then convert it to the Z domain because we are in discrete time. It means that the magnitude respose H[z] = alpha / (alpha*z^-1), with 0 <= alpha <= 1. Decreasing the value of alpha will lower the cutoff frequency. I've chose to an alpha of almost 0.46 it means 30.

      Delete
  6. I can't found Audio signal Generator .PDE
    so can you please send me .PDE on
    mahendrasondagar08@gmail.com

    ReplyDelete
    Replies
    1. Hello, the audio signal generator it's not a processing sketch. It's a binary windows file, you can find it here: https://code.google.com/p/audiotools/

      Delete
  7. hi dear Davide,
    I have a question, what is the program for opening the schematic file that you put above, neither altium nor proteus could open that.
    thank you very very much

    ReplyDelete
  8. Hey can you mail the connection diagram with the microcontroller. And if you have any documentation regarding the code please mail me. Thank you...!

    ReplyDelete
    Replies
    1. Hello, simply connect the analog output of the microphone preamplifier to one of the ADC of the microcontroller. If you are using the code i posted connect it to the PC3 input pin of the ATmega8. Also add a 16Mhz crystal, and set fuses in order to make the ATmega run @ 16Mhz. Unluckly the only documentation i've build for this project is the one you can find here or in the attached download.

      Delete
    2. can you tell me the specific data for fuse bits atmega8@16mhz

      Delete
    3. Hello, take a look here: http://www.engbedded.com/fusecalc/
      Usually Low Fuse to FF works. But keep in mind that if you made mistake on setting fuse, you will "brick" your micro, then you would need something like the "Atmega fusebit doctor".

      Delete
  9. Hi, Davide Gironi, can u tell me what should I change if I want to get this program with ATmega16?

    ReplyDelete
    Replies
    1. if it isnt hard to do maybe you can send it? maciek1992mac@wp.pl

      Delete
    2. Hello,
      You have to change the TIMER interrupt registers.

      Delete
    3. Thank you, I will try to do it.

      Delete
  10. hey in your program you keep the voltage reference to Aref; internal vref off. so what voltage do you apply to the aref pin.

    ReplyDelete
    Replies
    1. Hello, in this code ADC reference is set to external, and the AREF pin is connected to 5V, but you can even set ADC_REF in order to accept other reference voltage. Keep in mind that with internal 1.1V, or 2.56V mode, or with lower external voltage, you reduce the amount of signal you can compute.

      Delete
  11. Replies
    1. Hello, where's your question? I can not find it, sorry.

      Delete
    2. Hi there, I'm sorry but i think my question wasn't published... anyway, I can't load the code directly into my Arduino Uno R3 controller, could you send me the code in .txt, please?

      Delete
    3. This code is for ATmega8, compiled with avr-gcc, you could not load it into an Arduino Uno R3 with the Arduino IDE / framework. You could download the code from the above link, read the files you read from the audioget folder as an arduino library, and use that library. Or you can adjust this code in order to be compiled with avrgcc and to be run on a ATmega328p.

      Delete
  12. Dear Sir
    i can connect atmega 8 microcontroller with JP1 pin(from above diagram)with your code.then i can get a value on lcd.

    ReplyDelete
    Replies
    1. Hello, it that's a question, yes, you can connect JP1 from the analog board to an ADC of your micro, then you can output values to a LCD.

      Delete
  13. Hi Davide!
    In advance I wanna thank you for all your effort in this project is simply amazing. In the other hand I have a question for you, if I wanna run it on atmega328p what do I have to do to the code and to the hardware connections? .
    Thanks in advance.

    Mike

    ReplyDelete
    Replies
    1. The ATmega8/A should have no problem at all, but you have to try. About the ATmega328, you have to change the timer registers that you can find in the audioget_init function, the one that rules the TIMER1_OVF_vect interrupt, according to the ATmega328 registers.

      Delete
    2. hey Davide!

      I´ve been experimenting with your project, and could not find the FUSE BIT CONFIGURATION so I can not get it to work, would you be so kind to explain to me the configurations that you did to the atmega8.

      Thanks in advance

      Delete
    3. This project is tested on ATmega8 @16Mhz. To run a ATmega8 at 16Mhz you will need to connect a 16Mhz crystal between XTAL1 and XTAL2 pin, and two small cap like 22pF ceramic, between XTAL1 and ground, and XTAL2 and ground. One you have connect those, you have to set the low fuse to accept external clock source, something like 0xFF should work. For further information about fuse, and all fuse combination, you could look at the ATmega8 datasheet or take a look here http://www.engbedded.com/fusecalc/. Remember that settings your fuse the wrong way can "brick" your micro.

      Delete
  14. Hi, can the output of this device be send via bluetooth? Thankyou

    ReplyDelete
    Replies
    1. Yes, one can use a simple and cheap UART Bluetooth module as example.

      Delete
  15. Hello and first many thanks for providing the circuit and code.

    Using avr-gcc with the Release/makefile gives some errors:

    hgode@aoa150:~/Documents/adsbox/audio/avr_splmeter/Release$ make all
    Building file: ../src/uart/uart.c
    Invoking: AVR Compiler
    avr-gcc -Wall -Os -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -std=gnu99 -funsigned-char -funsigned-bitfields -mmcu=atmega8 -DF_CPU=16000000UL -MMD -MP -MF"src/uart/uart.d" -MT"src/uart/uart.d" -c -o "src/uart/uart.o" "../src/uart/uart.c"
    ../src/uart/uart.c:86:36: error: attempt to use poisoned "SIG_UART_RECV"
    #define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
    ^
    ../src/uart/uart.c:87:36: error: attempt to use poisoned "SIG_UART_DATA"
    #define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
    ^
    In file included from ../src/uart/uart.c:40:0:
    ../src/uart/uart.c: In function ‘SIG_UART_RECV’:
    ../src/uart/uart.c:86:36: warning: ‘SIG_UART_RECV’ appears to be a misspelled signal handler [enabled by default]
    #define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
    ^
    ../src/uart/uart.c:252:8: note: in expansion of macro ‘UART0_RECEIVE_INTERRUPT’
    SIGNAL(UART0_RECEIVE_INTERRUPT)
    ^
    ../src/uart/uart.c: In function ‘SIG_UART_DATA’:
    ../src/uart/uart.c:87:36: warning: ‘SIG_UART_DATA’ appears to be a misspelled signal handler [enabled by default]
    #define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
    ^
    ../src/uart/uart.c:295:8: note: in expansion of macro ‘UART0_TRANSMIT_INTERRUPT’
    SIGNAL(UART0_TRANSMIT_INTERRUPT)
    ^
    src/uart/subdir.mk:18: recipe for target 'src/uart/uart.o' failed
    make: *** [src/uart/uart.o] Error 1

    How do I fix these? (GNU Make 4.0, avr-gcc (GCC) 4.8.1)

    ~josef

    ReplyDelete
    Replies
    1. Hello. You can try to update the uart library, using the newer Peter Fleury one, the one shipped with this project could be not compatible with your avg-gcc. Let me know if it works ;)

      Delete
  16. After download and unpack the uartlib and make clean and make I got another issue:
    Building file: ../src/audioget/fftradix4.c
    Invoking: AVR Compiler
    avr-gcc -Wall -Os -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -std=gnu99 -funsigned-char -funsigned-bitfields -mmcu=atmega8 -DF_CPU=16000000UL -MMD -MP -MF"src/audioget/fftradix4.d" -MT"src/audioget/fftradix4.d" -c -o "src/audioget/fftradix4.o" "../src/audioget/fftradix4.c"
    ../src/audioget/fftradix4.c:17:1: error: unknown type name ‘prog_int16_t’
    const prog_int16_t Sinewave[NWAVE] PROGMEM = {
    ^
    src/audioget/subdir.mk:21: recipe for target 'src/audioget/fftradix4.o' failed

    This has been resolved by inserting a line into src/audioget/fftradix4.c before #include
    (NEW is now:)
    #define __PROG_TYPES_COMPAT__
    #include

    This has been taken from /usr/lib/avr/include/avr/pgmspace.h where it says for prog_int16_t:

    =========================
    ...
    \typedef prog_int16_t
    \note DEPRECATED

    This typedef is now deprecated because the usage of the __progmem__
    attribute on a type is not supported in GCC. However, the use of the
    __progmem__ attribute on a variable declaration is supported, and this is
    now the recommended usage.

    The typedef is only visible if the macro __PROG_TYPES_COMPAT__
    has been defined before including (either by a
    #define directive, or by a -D compiler option.)

    Type of an "int16_t" object located in flash ROM.
    */
    typedef int16_t PROGMEM prog_int16_t;
    ...
    =======================

    with the above added line the code then compiles.

    Thanks for the uart hint.

    ~Josef

    ReplyDelete
  17. Created Arduino compatible version at https://github.com/hjgode/avr_splmeter_arduino

    ReplyDelete
  18. Hello

    I build the TLC272 circuit, but the output is to low. What is the expected amplification?
    As I used a signal injector with a high output, audioget_visualizer.pde showd a nice graph etc. But with a microphone and the TLC272 amplifier, the signal seems to be by far too low.
    As noted for the Arduino port, I switched Vref to internal for the Atmega. Is this correct? Can you post the complete circuit with the Atmega8 and a note about which Anaolg in and Vref is used?

    Thanks

    Josef

    ReplyDelete
    Replies
    1. Hello, the gain of this preamp should be 1+R3/R9. I've proposed the complete schematics, thought you just have to connect the preamp output to any ADC input of your ATmega. You have to set the ADC_REF (adc.h file) according to your ADC reference voltage. If your Arduino has the AREF pin connected to ground by a capacitor, then your ADC_REF should be set to 1 (i.e. AVCC with external capacitor at AREF pin).

      Delete
    2. Thank you for the reply. I found my mistake: during transfer of the circuit mannually for a striped pcb, I replaced R3 100K by a 220Ohm resistor. Now the amplification is better. Using the internal AREF will be 1.1V and the SPL is down at 32 most the time, where it was about 52-55db ground noise level here with the now broken SPL meter. I will have to do some more tests the get more or less reliable SPLs.
      thanks again
      ~Josef

      Delete
  19. http://www.avrki.ru/forum/download/file.php?id=379&mode=view
    Hi . Tell me how it is possible to calculate the frequency Hz . If such a waveform . Help solve the problem .

    ReplyDelete
    Replies
    1. Hello, I'm sorry but I do not understand your question. Anyway, as long as you run FFT on the input signal, you transform the signal in his frequency domain, you now can read the signal frequencies. Take a look here at the The Scientist and Engineer's Guide to
      Digital Signal Processing here http://www.dspguide.com, it's a well written book.

      Delete
  20. how to display data on 16x2 by the LCD?

    ReplyDelete
    Replies
    1. You cna't do that much on a 16x2 LCD, you can just write out the string for the frequency observed. Take a look at the P.Fleury LCD library here: http://homepage.hispeed.ch/peterfleury/avr-software.html Thas's really a well made and simple to use piece of software.

      Delete
  21. http://s019.radikal.ru/i638/1702/51/e1ea9a5d4eb7.jpg
    how to calculate the frequency of the signal?
    atmega 32 + lcd

    ReplyDelete
    Replies
    1. Hello, i suppose you are asking me how to make a project like the one in that picture. As i tell you January 31, 2017 at 9:22 PM you may use FFT. Rude speaking: trasform your signal in the domain frequency using FFT, then you get your signal "splitted" by frequency and you may measure each frequency power. Take a look here, that's well explained http://www.dspguide.com/ch9/1.htm. Once you've got your frequency analysis you can draw it out on an LCD, there are a few library out there for LCD, it depends on what driver you are using. Sometime you have to draw pixel by pixed, making a pixel matrix of the signal you would like to draw.

      Delete
    2. One further not about frequency analysis. I completly forget that you can also take a look at my processing sketch to see how the specrum meter is coded, and the port it to your micro, and plot it on an LCD. Hope this helps.

      Delete
  22. Hi Davide,why the SPL value is showed in integer format instead of float? Thanks

    ReplyDelete
    Replies
    1. Hello, thank you for that question. That's because I wasn't interesed in decimal point. This is not a precision instrument, to me 1dB of accuracy was enought on this meter, but one can even use float.

      Delete
    2. This comment has been removed by the author.

      Delete
  23. Replies
    1. Hello, what do you mean with which? If you are speaking of the frequency spectrum analyzer, take a look at the audioget_visualizer processing sketch and the parts of code involved by the audioget_getmagnitude(&spectrum[0]) on the main sample code.

      Delete
    2. I need to get the frequency Hz and display on lcd

      Delete
    3. As i tell you you here, check the audioget_getmagnitude function and select a proper LCD library to work with. Hope this helps.

      Delete
  24. Hello again, I have seen another project similar to this, but yours is more complete. The autor uses Fast Hartley Transform (FHT). I am reading that process time is similar for FFT and for FHT due modern computers speed. But what about microcontrollers? Here: http://www.redalyc.org/articulo.oa?id=344234322004 they say that:
    "Orthogonal transformations have been very useful in thecharacterization and signal processing. In particular Hartley Transform allows for time-frequency representations and vice versa. This paper presents an algorithm for calculating the Discrete Hartley Transform in embedded systems with the objective of minimizing the computational load and storage capacity required. It exploits the similarity with the Discrete Fourier Transform to use a fast algorithm reduces the number of trigonometric functions calculated using the rotation factors (Twiddle factors). In general, the implementation can increase the size of the processing window and increase computational speed compared to direct calculation".
    What do you think about using FHT instead of FFT in order to improve your project? Thanks for your answer and
    Un saludo desde Popayán, Colombia

    ReplyDelete
    Replies
    1. Hello, thank you for that question. Yes FHT is even faster than Radix-4, this could be a good improvement, at the time I write this project I was testing and using the Radix-4, that's because of my choice, but FHT should do the job. I will try to use FHT for an improved version of this meter, testing the FHT for real. Here you can find a comparison against the most used FFT http://www.cypress.com/file/55401/download, then here ther's a good FHT implementation for AVR http://www.waitingforfriday.com/?p=53#Fast_Hartley_Transformation

      Delete
  25. Hello Devide,

    Insted of TLC2272 I'm suing NPN transistor 2N3904 as a pre-amplifier, which i got the idea from your November 2014 post

    http://davidegironi.blogspot.in/2014/11/a-npn-transistor-electret-condenser.html

    I'm using ATmega1281 with 3.3V @16Mhz
    I'm using same sampling rate, tune my ADC with 3.3V
    my amplifier output voltage is 1.44V at 32dB Noise
    now the thing is that which points that i need to change to get the proper output

    ReplyDelete
    Replies
    1. Hello, you can use any preamp, the TLC2272 should have an higher SNR, but a NPN will do the job. You have to setup your hardware in the audioget.h header file.

      Delete