You are on page 1of 50

Arik Yavilevich's blog

“Code IT”, my thoughts on technology

 About me

1.
2. Arduino
3. Arduino sound level meter and spectrum analyzer

« Why use – Fosphor, a GNU Radio real-time spectrum analyzer?


Capturing and reversing wireless keyboard signal »

Arduino sound level meter and spectrum


analyzer 104
27 Aug 2016 | Arduino · Code

Tags: FHT · microphone · sound

Recently I have been doing some projects with the Arduino electronics platform. One of the
projects involved benchmarking certain motors and required me to measure noise levels. I will
cover that project in more detail in one of the future articles, but now I would like to write about
the process and best practices of measuring sound levels and analyzing frequencies with an
Arduino. I will talk about sound, microphones, sampling, FFT and more. This article is going to
be targeted towards beginners, who are neither signal processing experts not electronics experts
and it will be fairly high-level with links for more thorough reading.

Sound theory

Sound is a wave that moves in space and when it is stored (in digital or analog form) it is
represented by a Waveform, which is the amplitude of the wave measured at each point in time
at a certain point in space. You can think of this as sound passing through a microphone where it
is being measured constantly and the measurements form the waveform. Because we can only
measure a finite number of times per time unit, this process of measuring is called sampling and
it generates a discrete signal. Furthermore, each sample in time is also made discrete during this
process as computers and integrated circuits have finite accuracy and storage.
Sampling of a waveform

Arduino capability for measuring signals and converting them to logic that the micro-controller
(MCU) can process is provided by the Analog-to-Digital-Converter (ADC) that is pat of the
MCU. Therefore, for an Arduino implementation, this process translates to connecting a
measuring device (microphone for sound) to the MCU and sampling the value at the device by
the ADC at a constant rate. The Arduino ADC senses voltage levels (typically in the range of 0-
5V) and converts them to values in the range of 0 to 1024 (10 bit).

Depending on what we are measuring, sound levels may be very quiet or very loud. On one
hand, a microphone on its own is usually not able to provide enough voltage for the Arduino to
sense a change. The ADC needs a 5V/1024=4.8mV change to increase the digital value by 1, but
a typical “electret microphone” might not provide this amount of voltage change for quiet
sounds. For this reason a microphone is often used with an amplifier. On the other hand, a very
loud noise and a high-gain amplifier can bring a signal to the max 5V, “over-exposing” or
“clipping” it and again bringing us to a situation where the sampling is useless. Therefore it is
important to match the device and the amplification level (gain) to each use-case scenario.

Microphone selection

When choosing a microphone for your Arduino you can get one of the available “microphone
modules” that combine a microphone with an amplifier or some other logic on a tiny PCB. You
can also make your own module, which has the added advantages of being able to control all the
different aspects of the mic and amplification. I chose to get a module that is available of-the-
shelf because it is easier and faster than making your own.

If your goal is to record sound and get a fixed sound level even in unpredictable situations you
will want to get a module with automatic gain like this one based on MAX9814 from Adafruit.
Such a module will “normalize” the sound to a set level. This will be the right solution for a
scenario where you want to record voice for playback or to run a frequency analysis. Naturally,
this isn’t the right choice for measuring sound volume. To measure sound volume and to be able
to compare different measurements one to another you need to use a module where the gain is
predictable. It doesn’t mean that the gain is fixed, it just means that the gain is configurable by
you and does not change automatically.

I have evaluated 3 such modules. It is worth noting that a specific design might be on the market
under different names as different manufactures make their own versions of the design with their
own model numbers. Look at the board layout and note the main chip so you can identify the
device.

MAX4466 based module


I got mine from the Far East but it looks like it is based on an Adafuit design. This module has an
adjustable gain that you control with a tiny 1-turn potentiometer. There is a Vcc pin, a ground
pin and an analog out pin. The analog pin emits a waveform where “0” is Vcc/2 and the
amplitude depends on the gain and the volume of the sound. The MAX4466 chip is an op amp
that is specifically optimized for use as a microphone amplifier, which makes this module a great
performer and my eventual choice for the project.

“HXJ-17” / “Keyes” module based on an LM393


I got this module from a local electronics store. Not sure who designed it, but it has a multi-turn
potentiometer, no amplifier and a LM393 comparator chip. There is a Vcc pin, a ground pin, an
analog out pin and a digital out pin. Since this module lacks an amplifier, it is only good for
sensing loud sounds, such as claps and knocks. The presence of the LM393 allows you to
configure a threshold so the board can generate a digital output when the sound level is above the
threshold. The only advantage that I can think of that this would have over implementing a
threshold in code is that either 1) the comparator is more sensitive that the ADC of the MCU or
2) you don’t have an MCU in the first place and are wiring this board directly to a relay or a
similar IC. Some sellers advertise this module as having an LM393 amplifier, but detailed
analysis shows that it is not the case and that the analog out pin is not amplified.

“Sound detection” module based on an LM393


I got this one from the same local shop. This one is similar to the HXJ-17, but it is even simpler.
It has a one turn potentiometer and no analog output. Leaving this useful for knowing if there is
or isn’t a loud sound.

Analyzing analog input

As the first step, I would suggest you take some time to analyze the analog output of your
module to see the baseline and amplitude. I have used the following Arduino function to gather
data:

#define MicSamples (1024*2)


#define MicPin A0

// measure basic properties of the input signal


// determine if analog or digital, determine range and average.
void MeasureAnalog()
{
long signalAvg = 0, signalMax = 0, signalMin = 1024, t0 = millis();
for (int i = 0; i < MicSamples; i++)
{
int k = analogRead(MicPin);
signalMin = min(signalMin, k);
signalMax = max(signalMax, k);
signalAvg += k;
}
signalAvg /= MicSamples;

// print
Serial.print("Time: " + String(millis() - t0));
Serial.print(" Min: " + String(signalMin));
Serial.print(" Max: " + String(signalMax));
Serial.print(" Avg: " + String(signalAvg));
Serial.print(" Span: " + String(signalMax - signalMin));
Serial.print(", " + String(signalMax - signalAvg));
Serial.print(", " + String(signalAvg - signalMin));
Serial.println("");
}

You can then make some sounds at different volume levels and see how your average, min, max
and span values respond. Looking at the result you might see that you need to adjust the gain
potentiometer such that you utilize the max span for your sound levels while not overdoing it so
not to clip your signal.

Implementing accurate sampling with 3.3V reference and free running

The analogRead function of Arduino makes it simple to get a digital value of an analog pin. It
was implemented with single sample collection in mind. When sampling sound it is important to
take our samples at a constant rate and to take each sample accurately. To achieve these two
properties we will change a couple of things.

First, we will configure the ADC to use 3.3V as the analog reference voltage. The reason for this
is that the 3.3V is usually more stable than the 5V. The 5V can fluctuate up and down especially
when the Arduino is getting its power from the USB connection. The 3.3V is coming from a
linear regulator on the Arduino board and can be connected to the ARef pin of the Arduino. This
calibrates our ADC to map the 0 to 3.3V range of the analog input to the 0 to 1024 range of the
digital values. For this to happen on the electronics level you need to feed your module with
3.3V and to connect the Arduino ARef pin to 3.3V. Make sure that your module is capable of
operating at this voltage.

Use the following code to configure this mode:

analogReference(EXTERNAL); // 3.3V to AREF

Second, we will configure the ADC to work in “free-running” mode and read the sample values
directly from internal registers, bypassing analogRead. As mentioned, analogRead is designed to
read one value at a time and will perform initialization of the ADC for each read, something that
we better eliminate. This will allow us to get a sampling rate that is more predictable.

Setup “free-running” mode with this code:


// register explanation: http://maxembedded.com/2011/06/the-adc-of-the-avr/
// 7 =&gt; switch to divider=128, default 9.6khz sampling
ADCSRA = 0xe0+7; // "ADC Enable", "ADC Start Conversion", "ADC Auto Trigger
Enable" and divider.
ADMUX = 0x0; // Use adc0 (hardcoded, doesn't use MicPin). Use ARef pin for
analog reference (same as analogReference(EXTERNAL)).
#ifndef Use3.3
ADMUX |= 0x40; // Use Vcc for analog reference.
#endif
DIDR0 = 0x01; // turn off the digital input for adc0

Read a batch of samples with this code:

for (int i = 0; i < MicSamples; i++)


{
while (!(ADCSRA & /*0x10*/_BV(ADIF))); // wait for adc to be ready (ADIF)
sbi(ADCSRA, ADIF); // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = ((int)j << 8) | m; // form into an int
// work with k
}

Third, you can also adjust the speed of the ADC. By default the ADC is running at 1:128 of the
MCU speed (mode #7). Each sample takes the ADC about 13 clock cycles to get processed. So
by default we get 16Mhz/128/13=9846Hz sampling. If we want to sample at double the rate we
can change the divider to be 64 instead.

Here is an example of how to set divider to 32 (mode #5) which equals a sampling rate of
16Mhz/32/13~=38Khz:

// macros
// http://yaab-arduino.blogspot.co.il/2015/02/fast-sampling-from-analog-
input.html
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &amp;= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// 1 0 1 = mode 5 = divider 32 = 38.4Khz


sbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);

You can see all three of these code snippets implemented together in the source code of the
project at github.

With this logic in place we can get decent waveform data for the Arduino to process.

Sound level measurement

Theory
The sound level is defined as the amplitude of the waveform and can be measured per set of
samples which represent a part of a signal.

Peak Envelopes

For an ideal sine signal, the amplitude would be the max sample, but in practice it is easy for
some samples to be outliers and to affect the max value significantly. For this reason it is more
practical to use a metric that takes all the sample values into account. You can use an average,
but it is more common to use Root Mean Square (RMS) which will give more “weight”
to higher values.

The relationship between amplitude and RMS for sine waves is known and is amplitude=
sqrt(2)*RMS. If we assume that a sound waveform is similar to a sine waveform we can use this
relationship to estimate a stable amplitude based on an RMS value that we calculate.
Waveform with an RMS envelope

The values we are dealing with are relative and not absolute. After all we are using some value
of gain to tune the volume levels to our digital 10bit range. It is very common to work with
relative values when processing sound waveforms. The volume is then measured as a ratio from
some other “reference point” value. A common unit used to express the ratio is decibel (dB).
Resulting a formula:

dB=10*log10(v/v0)

Where dB is the level is dB units, v is the sample value and v0 is the reference value.

Since sound pressure is a field quantity a ratio of squares is used and the value “2” in the log
becomes “20” [due to log(a^b)=b*log(a)] :

dB=20*log10(v/v0)

I was trying to achieve relative measurements, I chose v0 as the max possible amplitude (1024/2
for a 10bit ADC). This yields dB measurements which are specific to a certain gain and my
device, but as long as the gain remains fixed, I can take several measurements and make valid
comparisons between them. If you are looking to measure absolute sound levels you would need
to calculate your level relative to a standard agreed sound pressure baseline value of 20
micropascals, which is the typical threshold of perception of an average human. Practically, this
is typically done by attaching a special calibration device to the microphone. The device
generates sound at a fixed dB level and you can adjust your calculations such that your dB
measurement matches the dB value of the calibration device.

When using a reference value that is higher than your samples (max amplitude), your dB values
would be negative and smaller as you approach the max.

When using a reference value that is lower than your samples (threshold of perception), your dB
values would be positive and larger as you approach the max.

To make this even more complex, several factors affect measurement in practice. First, the
human ear is not equally sensitive to all frequencies. It is typical to apply different weights to
different frequency ranges. One such a unit of measurement is called dBA, but there are others
with slightly different weights. Second, your microphone might not have equal sensitivity to all
frequencies. Third, your speakers might not have equal ability to reproduce all frequencies at the
same exact level. These complexities require very accurate and expensive equipment together
with special calibration procedures to be able to measure sound levels correctly per standards.
You need to understand that your ability to measure sound level with the setup described here is
pretty rudimentary and suitable for rough relative measurements only.

Implementation

Let’s recap that our values are 0 to 1024 which stand for [-max,max] with 1024/2=512 being “0”.
We will retrieve and process sample for some time, where the standard defines 1 second as
“Slow” and 125ms as “Fast”. For each sample, we will measure the distance from the “0” to the
sample value, which is the amplitude of that sample. Then we can do simple calculations for
max, average and RMS. The values on our scale can be “normalized” to percentage of max
amplitude or using dB or both. Here is a relevant code sample:

// consts
#define AmpMax (1024 / 2)
#define MicSamples (1024*2) // Three of these 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.

// modes
#define ADCFlow // read data from adc with free-run (not interupt). much
better data, dc low. hardcoded for A0.

// calculate volume level of the signal and print to serial and LCD
void MeasureVolume()
{
long soundVolAvg = 0, soundVolMax = 0, soundVolRMS = 0, t0 = millis();
for (int i = 0; i < MicSamples; i++)
{
#ifdef ADCFlow
while (!(ADCSRA & /*0x10*/_BV(ADIF))); // wait for adc to be ready
(ADIF)
sbi(ADCSRA, ADIF); // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = ((int)j << 8) | m; // form into an int
#else
int k = analogRead(MicPin);
#endif
int amp = abs(k - AmpMax);
amp <<= VolumeGainFactorBits;
soundVolMax = max(soundVolMax, amp);
soundVolAvg += amp;
soundVolRMS += ((long)amp*amp);
}
soundVolAvg /= MicSamples;
soundVolRMS /= MicSamples;
float soundVolRMSflt = sqrt(soundVolRMS);
float dB = 20.0*log10(soundVolRMSflt/AmpMax);
// convert from 0 to 100
soundVolAvg = 100 * soundVolAvg / AmpMax;
soundVolMax = 100 * soundVolMax / AmpMax;
soundVolRMSflt = 100 * soundVolRMSflt / AmpMax;
soundVolRMS = 10 * soundVolRMSflt / 7; // RMS to estimate peak (RMS is
0.7 of the peak in sin)

// print
Serial.print("Time: " + String(millis() - t0));
Serial.print(" Amp: Max: " + String(soundVolMax));
Serial.print("% Avg: " + String(soundVolAvg));
Serial.print("% RMS: " + String(soundVolRMS));
Serial.println("% dB: " + String(dB,3));
}

So now with proper module and calibration you can measure sound level of different events or
devices and compare them one to the other.

Frequency analysis with FHT

What if you want to “break” the sound into individual frequencies and measure or visualize each
individual frequency? Can this be done with Arduino? The answer is that it can be done
relatively easily thanks to some existing libraries. To turn signals from a time domain to a
frequency domain you would generally use a Fourier transform. Such transforms are used for
signals of different types, sound, images, radio transmissions, etc. Each signal type has its own
properties and the transform that best suits a sound signal is the Discrete Hartley Transform
(DHT). DHT will work with discrete, real values which form our waveform. To implement DHT
we will use Fast Hartley Transform (FHT) and specifically the ArduinoFHT library.

The Arduino FHT library works with vectors of 16 to 256 samples. This size is denoted as N. In
this project I will be using N=256 to achieve maximum resolution, but you may use smaller
values if you are short on memory or processing power.

First, the algorithm takes N real numbers and results in N/2 complex numbers. Then we can pass
the data to another function to calculate the magnitude of the complex numbers to get N/2 bins.
In the end we get N/2 bins, each covering a frequency range of sampling_rate/N Hz. The highest
value of the last bin will be sampling_rate/2 . The reasons for this relate to signal processing
theory, specifically aliasing and Nyquist law. In practice, if you want to avoid any strange
effects, such as higher frequencies “folding” over lower frequencies, you will need to make sure
to use a sampling rate that is twice the highest frequency you expect to have in the sound signal.
Otherwise you are not sampling fast enough. You should also not over sample, as it will result in
low ADC accuracy and wasting of FHT bins on ranges that don’t appear in the signal. I found
the value of 20Khz to be a good upper frequency based on the range of my microphone and on
the range of typical human hearing. As a result the, sampling at 38.4Khz (divider=32) seemed
optimal.

So for N=256 and sampling_rate=38.4Khz we get 128 150hz bins with the first been holding the
magnitude value of 0-150hz and the last bin holding the magnitude value of 19050-19200hz. We
can now focus on specific bins that interest us, send the values of all the bins over serial
connection, store the values, display them in some way, etc.

One of the fun ways to use the data, especially when troubleshooting and developing is to
visualize with an analyser. Load the following FHT example code to the Arduino or adapt it to
your needs. It gets the samples, runs FHT on the data and sends it in binary form over serial.
Your Arduino should be connected to a computer running Processing development environment.
In Processing, load the “FHT 128 channel analyser” project. I had to make a change to the
project to make it compatible with Processing 3.0 . To do so, move the call to “size” function
from within the “setup” function to a new function called “settings”.

Analyzer

Another way to analyze the data is for the Arduino to send it over serial in textual form, let it run
for some time, then copy it from the serial monitor and paste it in a spreadsheet. For example
using a code that is similar to this:

void MeasureFHT()
{
long t0 = micros();
for (int i = 0; i < FHT_N; i++) { // save 256 samples
while (!(ADCSRA & /*0x10*/_BV(ADIF))); // wait for adc to be ready
(ADIF)
sbi(ADCSRA, ADIF); // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = ((int)j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fht_input[i] = k; // put real data into bins
}
long dt = micros() - t0;
fht_window(); // window the data for better frequency response
fht_reorder(); // reorder the data before doing the fht
fht_run(); // process the data in the fht
fht_mag_log();

// print as text
for (int i = 0; i < FHT_N / 2; i++)
{
Serial.print(FreqOutData[i]);
Serial.print(',');
}
long sample_rate = FHT_N * 1000000l / dt;
Serial.print(dt);
Serial.print(',');
Serial.println(sample_rate);
}

Then you can format the data in a spreadsheet, such as Excel, as a “3-D Surface” mesh graph.
For example, see a graph of a Frequency Sweep from 1hz to 5000hz as captured and analyzed by
the Arduino and FHT:
Mesh of FHT frequency sweep

Summary

My code for this project can be found at github for you to experiment with.
The Arduino can be used for relative sound level measurement and for frequency
analysis/visualization. One just needs a microphone to match the use case, an Arduino, some
coding and optionally the FHT library. Have fun and let me know in the comments if you make
something nice using such a setup.

104 thoughts on “Arduino sound level meter and spectrum


analyzer”

1. Reply Finn Sep 24,2016 7:55 am

Hello,

Did I need the FHT library for the MeasureVolume?


I don’t run.

Greets
Finn

o Reply Arik Yavilevich Sep 24,2016 9:54 am

Hi Finn,

You don’t need FHT for the MeasureVolume part. What didn’t run? Do note that
the code snippet in the article is just a snippet, and not a full Arduino program.
See https://github.com/ayavilevich/ArduinoSoundLevelMeter for the full code.

2. Reply Max W. Nov 17,2016 10:34 am

Maybe I missed something in my first read of this lovely blog post, or else it’s a little
cryptic.

But I’m wondering what would make the best module for measuring sound pressure
levels (between some 50-60 dB to +100 dB) for music played (at I believe 432Hz)
through a speaker? Also, if it’s possible I would like to be able to make the PBC and
solder it myself, even if it has to be a much larger scale.

o Reply Max W. Nov 17,2016 10:48 am


Actually nevermind the 432Hz frequency, I seem to have confused it with
something else. I mean the range between 20Hz to 16K Hz which covers every
instrument and vocal.

o Reply Arik Yavilevich Nov 17,2016 12:04 pm

Hi Max, signal processing is a bit difficult to explain and understand. Happy to try
and clarify.

With regards to the dB range, dBs are relative, so you will not be able to measure
proper dB values unless you can calibrate your setup with some specially
designed equipment. As the system is described above, it can only compare sound
levels (tell how much sound A is higher or lower than sound B).

With regards to a good module to measure sound levels, you should first try a
MAX 4466 based module with an adjustable gain. For your purpose avoid
modules with no gain or modules with auto-gain.

With regards to the frequency range, to process signals up to 16Khz you will need
to sample at least double than that, at 32Khz. See above on how to set your ADC
divider to 32 (mode #5) and achieve sampling of about 38Khz.

In the end, if you are interested in frequency analysis, you could pass the sampled
data to FHT and get 128 bins back. Each bin will be 148hz wide.

3. Reply DPic Nov 18,2016 3:09 pm

Just replying to thank you for your brief and yet very useful guide.

o Reply Arik Yavilevich Nov 18,2016 7:44 pm

Nice to hear, you are welcome.

4. Pingback: HM-10 or CC41-A module? Automatic Arduino BLE module identification |


Arik Yavilevich's blog
5. Pingback: Sound level / Geiger counter with sigfox network | Projects

6. Reply Jim B Dec 13,2016 1:27 pm

Arik, thank you so much. A very concise and instructive note. Also thank you for
explaining fht. Has significantly simplified a project for me. Jim
7. Reply mondlos Jan 7,2017 5:20 pm

Thanks for this very helpfull article. But in the implementation of MeasureVolume() you
do abs(k-AmpMax) and you say, that you get the amplitude by this. But wouldn’t it be
half the amplitude?

o Reply Arik Yavilevich Jan 8,2017 7:34 am

Hi, this is probably due to confusing terms. Let me try and clarify.
First, I think it depends on what kind of amplitude you are talking about. What I
refer to as amplitude here is the distance referenced to zero, whose max is “Peak
amplitude”. If you are thinking of the distance referenced to “Peak to peak
amplitude” then it would be different. See
https://en.wikipedia.org/wiki/Amplitude for more details.
Other than that, analogRead values are: 0 => minAmp, 512 => “0” and 1023 =>
maxAmp. “Shifting down” by 512 we get: -512 => minAmp, 0 => “0” and 511
=> maxAmp and make the signal correctly biased relative to its zero reference.
Does it make sense now or am I missing something?

8. Reply Jose Jan 22,2017 11:37 am

So what is this
amp <<= VolumeGainFactorBits;

o Reply Arik Yavilevich Jan 22,2017 7:58 pm

This is (software) Gain and it is optional [


https://en.wikipedia.org/wiki/Gain_(electronics) ]. Used here to increase the
values for signals that are too low even with “hardware” Gain at max.
Applied here as a bit shift operation, so gain can be 0, 2, 4, 8 or 2^n times.

9. Reply Matthew Jan 25,2017 12:01 pm

Hello Arik,
Thank you very much for sharing this work. I am excited to try this out, but receive the
compile error below. I suspect I need to outcomment or uncomment somewhere, but
can’t figure it out. I appreciate any help! ERROR:
ArduinoSoundLevelMeter.ino: In function ‘void MeasureVolume()’:
ArduinoSoundLevelMeter.ino:175:39: error: call of overloaded ‘String(float&, int)’ is
ambiguous
ArduinoSoundLevelMeter.ino:175:39: note: candidates are:
In file included from /usr/share/arduino/hardware/arduino/cores/arduino/Print.h:26:0,
from /usr/share/arduino/libraries/LiquidCrystal/LiquidCrystal.h:5,
from ArduinoSoundLevelMeter.ino:1:
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:70:11: note:
String::String(long unsigned int, unsigned char)
explicit String(unsigned long, unsigned char base=10);
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:69:11: note:
String::String(long int, unsigned char)
explicit String(long, unsigned char base=10);
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:68:11: note:
String::String(unsigned int, unsigned char)
explicit String(unsigned int, unsigned char base=10);
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:67:11: note:
String::String(int, unsigned char)
explicit String(int, unsigned char base=10);
^
/usr/share/arduino/hardware/arduino/cores/arduino/WString.h:66:11: note:
String::String(unsigned char, unsigned char)
explicit String(unsigned char, unsigned char base=10);

o Reply Arik Yavilevich Jan 25,2017 12:31 pm

Hi Matthew, glad you liked the article.


The error is because the compiler can’t find a constructor variant for class String
that will accept a float value. It can find a bunch of other constructor variants but
not this one. I assume the issue is because your Arduino version is too old. Try
version 1.5 or latest. On this documentation page you can see that String::String is
supposed to have a float variant. Let us know if you manage to resolve this issue.
Regards, Arik.

10. Reply Boibelo Feb 22,2017 10:13 pm

Hello Arik,

your article is very instructive and i am eager to test this sound detection. Do you see any
possibility to get it from 2 or more mics simultaneously (with only one arduino uno)?
Regards,
Bb.

o Reply Arik Yavilevich Feb 23,2017 7:37 am

Hi Bb,

I believe you could connect several mics, one on each analog port of the arduino.
However, you only have one ADC in an Arduino, so sampling N mics will reduce
your individual sampling rate by N and require you to switch ports every sample
(so might not be able to use free-running mode). You could work with lower
sampling rate or you can increase ADC speed to compensate (loosing accuracy). I
would say it depends on your requirements for sampling rate and sample
resolution.
Another option is to add an external ADC chip to your circuit. For example see
this MCP3008 (https://www.adafruit.com/products/856). This is a bit more
difficult path.

11. Reply Boibelo Feb 28,2017 3:15 pm

thank you very much for this !! I’ll play around and see the accuracy I can acve then.

12. Reply siva Mar 1,2017 7:40 am

can you please tell me the source code of this project by using pic16f877a

o Reply Arik Yavilevich Mar 2,2017 1:30 pm

Hi siva, sorry, not aware if something like that exists.

13. Reply Pierre Mar 2,2017 9:03 am

Hello Arik,

I have read with interest your explanations regarding the sound level measure.
First, I test with an LCD to read the values in dB on the first line and a bargraph on the
second one.
For testing, I use a low frequency generator with a 2.5V offset and after I will do a
microphone amplifier.
I’m a beginner in Arduino and a programming problem : I have a maximum reading of 60
dB when I have on input a 1000 Hz sinusoidal voltage of 4.5V peak-to-peak and 0 dB
when this signal is Zero (but centered on 2.5V by the offset of the generator). From 0 to
4.5V p to p, the scale in dB is respected (6 dB for a doubled voltage).
I think that these 60 dB maximum correspond to the limitation of 1023 of the 10 bits
analog input. But I would like to have a larger scale, eg 90 or 100 dB for a 4.5V input.
Ideally, it’s be interesting to have access to a variable that would then allow entering a
calibration value.
Do you have an idea for this and I thank you in advance?

Regards,
Pierre

o Reply Arik Yavilevich Mar 2,2017 1:55 pm

Hi Pierre,

First, please note that in this article the dB are negative with dB=0 standing for
max volume and negative dB values standing for lower volumes. I guess you have
made some changes to the code while experimenting so your mileage may vary
depending on your reference value (v0).
With regards to your question, you are right. The resolution of the reading
determines the max range of the resulting dB values. Arduino ADC is 12 bit so
we have 512 values of range between min and max readings.
dB(range)=20*log(512)=~54 which is what you can get with the setup described
above.
You can add “gain” to scale the dB but your accuracy will not increase and your
actual measuring range will remain the same. To add gain just scale the “amp”
variable after calculating the absolute value. See “VolumeGainFactorBits” in the
article for how it can be done.
If you would have been processing a 16bit sound signal you would have
dB(range)=20*log(2^15)=~90, which is what you might be familiar with when
doing sound processing on a PC. A gain of 2^(16-12) should take you to that
range. That gain is the same as shifting the value by 4 bits.

Regards, Arik

14. Reply Pierre Mar 2,2017 6:06 pm

Hi Arik,
Many thanks for this quick and clear reply.
I will experiment with these suggestions and I will come back to you if, I have other
questions.
Thanks again and regards,

Pierre

15. Reply Vignesh Jeyanthan Mar 3,2017 12:57 pm

I need only to find db of detecting sound waves. Can someone please guide me on that
regarding program. Which part will I have to concentrate from the above program.

16. Reply Arik Yavilevich Mar 10,2017 6:24 pm

Dear readers, if this article was relevant to you, then you might also like to read the
article “Saved by the Bel — Understanding Decibels” at
http://hackaday.com/2017/03/07/saved-by-the-bel-understanding-decibels/

17. Reply Ciro Apr 11,2017 6:00 pm

Arik Hello, Your project is very interesting. I’d appreciate if you could post some
pictures on the circuit, perhaps with the practical scheme assembly and shield yourself
used. In addition, you could add an example of the data processing carried out with
excel?

o Reply Arik Yavilevich Apr 11,2017 7:11 pm

Hi Ciro, happy that you found the project interesting. The circuit is too simple to
create a schematic for it. Gnd goes to ground, 5V/Vcc/+ to Vcc, and Out goes to
A0. With regards to excel, there was no use of it for data processing, just for
visualization. The serial data was pasted in excel and a “3d mesh” graph was
created. LMK where you are having difficulties and I will try to provide more
information.

Update: the code in github is using an external analog reference. This provides
more accurate sampling using a 3.3V source. To use with the code unchanged,
connect as following: Gnd goes to ground, 3.3V on Arduino to Vcc/+ on the
module, Out on the module to A0 on Arduino, 3.3V on the Arduino to Aref on the
Arduino. You will need to connect two points in the circuit to the 3.3V header, so
use a breadboard or some connector to do that. Whatever works for you.

18. Reply toki Apr 26,2017 2:59 pm

Hi, which sensor did you end up using? I’m plannin on making a sound direction detector
but I’ve read online that the KY-038/LM393 only works for loud sounds and doesn’t
provide much sensitivity (low analog range). How does the MAX4466 one behave? Did
you only have to conmect the mic to Analog Input or did ou use some other components
aswell?

o Reply Arik Yavilevich Apr 26,2017 4:29 pm

Hi toki, I have used the MAX4466 based sensor for sound level measurement. It
is the only one out of those I tried that is an amplifier and not a mere level
detector. You can connect the OUT of the module directly to an analog input pin.
I didn’t use additional components, but you can add an amplifier if you need
further analog gain or just for the experiment.
I have not tried using an additional amplifier. If you find a good resource on that
topic, please share it here.

19. Reply Faraz G May 2,2017 9:06 pm

Hi Arik,

Thanks for your interesting article. Unfortunately, I cannot read any values with the
provided functions. I changed the microphone and the Arduino, but still reading zeros.
Any suggestions?
PS: I don’t get any uploading errors.

Cheers!
Faraz

o Reply Arik Yavilevich May 3,2017 7:46 am

Hi Faraz, please post your schematics and parts used. What functions did you
use? Are you getting non-zero values with analogRead? Have you tried exposing
the mic to loud sounds?
 Reply Faraz G May 11,2017 11:20 pm

Hi Arik,

Thank you for the reply. I have the code running now, the microphone was
damaged I guess. It’s working with a new microphone now.

I have MeasureAnalog() and MeasureVolume() working fine, however,


MeasureFHT() does not work. Here is the screenshot of the serial monitor
readings:

https://static1.squarespace.com/static/57eac67bf5e2316869418ea4/t/5914f
13537c581044515ec8c/1494544694098/Screen+Shot+2017-05-
11+at+4.11.23+PM.png

Any suggestions?

Thanks in advance!

Faraz

 Reply Arik Yavilevich May 12,2017 6:10 am

Hi Faraz, by default the MeasureFHT() function sends binary data


over serial. That binary data is meant to be consumed by the
visualizing analyzer that is written in “Processing”. If you want to
see textual output of the serial data, comment out this line “#define
FreqSerialBinary”.

20. Reply Steve Law May 10,2017 4:22 pm

Hi Arik,

I’m a newbie trying this out for myself. I have some Arduino scripting experience but
have never used Processing.

I am trying to use the FHT_128_channel_analyser in Processing but I am getting the error


“Please fix the size() line to continue”. The fix you describe is a bit low in detail, I do not
know how to “move the call to ‘size’ function from within the “setup” function to a new
function called ‘settings'”.
Can you advise? I have only a vague idea of how it would be done in Arduino script. If
you could just post your function that should work for me.

Thanks

Can you advise?

21. Reply Steve Law May 10,2017 4:43 pm

Hi Arik,

Figured it out. Found a Processing that advised that Processing likes real numbers in the
size window command. The FHT_128_channel_analyser.pde defines it as:

size(X_WINDOW, Y_WINDOW);

These are all defined in the first part of the script, so you just doa bit of basic maths:

X_WINDOW = X_MAX + 2*X_OFFSET


X_MAX = 129 x 6 = 774
= 774 + 2*X_OFFSET
X_OFFSET = 40)
= 774 + 80
X_WINDOW = 854

Y_WINDOW = Y_MAX+BOT_DIST+Y_OFFSET
Y_MAX = 256; BOT_DIST = 80; Y_OFFSET = 60;
Y_WINDOW = 256 + 80 + 60
Y_WINDOW = 396

My size command is now:

size(854, 396);

Works now!

22. Reply Steve Law May 10,2017 4:46 pm

Just so its clear for any other newbie out there, those definitions are all at the start of the
script:
final int X_OFFSET = 40; // x-distance to left upper corner of window
final int Y_OFFSET = 60; // y-distance to left upper corner of window
final int BOT_DIST = 80; // distance to bottom line of window
final int X_MAX = (128+1)*X_DIST+1; // x-axis length
final int Y_MAX = 256; // y-axis length
final int X_WINDOW = X_MAX + 2*X_OFFSET; // window width
final int Y_WINDOW = Y_MAX+BOT_DIST+Y_OFFSET; // window height
final int X_ENUM = 10;

o Reply Arik Yavilevich May 10,2017 7:38 pm

Hi Steve, it is awesome that you were able to find a workaround yourself. What I
briefly mentioned in the article is another solution. Instead of just a “setup”
function I am using:

void setup()
{
noStroke();
fontGraph = loadFont(“ArialUnicodeMS-48.vlw”);
textFont(fontGraph, 12);
println(Serial.list()); // show available COM-ports
//port = new Serial(this, “COM7”, 115200);
port = new Serial(this, “COM4”, 9600);
port.buffer(129); // 1 start-byte + 128 data-bytes
fontA = loadFont(“ArialUnicodeMS-48.vlw”);
textFont(fontA, 16);
}

void settings()
{
size(X_WINDOW, Y_WINDOW); // size of window
}

With the “size” function being called from a new function “settings” that is not
there in the original example.
Mind that your setup function might have other parameter values, such as serial
port, etc.

23. Reply Steve Law May 10,2017 8:18 pm

Ok thanks will have a go with that too!


24. Reply Reinhard May 12,2017 9:09 pm

Hi Arik,
I’m working on a noise level meter and therefor I’m most interested in frequencies below
8kHz. I tried to reduce the sampling rate to get a maximum of 8kHz within 8 (or 16)
channels. I tried to change ADCSRA (division factor) to 0xE6, but this doesn’t change
anything.
I did my tests with the example code from the FHT library.

I hope you got any idea.


Reinhard

25. Reply Reinhard May 12,2017 9:16 pm

I found the second definition of ADCSRA setting in the loop. I changed


ADCSRA = 0xf5; // restart adc
to
ADCSRA = 0xf6; // restart adc
Now it works.

o Reply Arik Yavilevich May 13,2017 5:23 pm

Hi Reinhard, happy that you managed, but I am not sure I follow you.
The ADCSRA is initialized in “setup” to 0xe0+7 and then the divider is further
adjusted to 32 in the “ADCReClock” block.
In the loop, only the ADIF bit is affected by executing “sbi(ADCSRA, ADIF); //
restart adc”. The divider is not changed in the loop, instead the ADC process is
restarted.
Please take a look at the documentation of the ADCSRA register, it is responsible
for several independent functions of the ADC.

 Reply Reinhard May 16,2017 8:00 pm

Hi Arik, you are right referencing to your code.


I was using the example code from the Arduino FHTLibrary where the
divider is set again in the loop.
Thank you.
26. Reply Tobias May 15,2017 4:08 pm

hi,

thanks for presenting your interesting work!


Similar to Max W. I would like to measure dBA levels from a PA-Soundsystem between
~80 – 110dB. There is no need for an absolute accuracy of the measurement. I use the
MAX 4466 based module. There is a handheld dBA-Meter, I could use to compare the
readings and calibrate the circuit.
With the help of your blog I was able to run it. Now it´s reading values from -6 to 0 dB.
Unfortunately mathematics is not my specialist area 😀
Wouldn´t a slight variation of the code print out the desired dBA readings?

o Reply Arik Yavilevich May 16,2017 7:10 am

Hi Tobias,
You can use another meter to calibrate your build. Assuming you have a meter
that is calibrated for a “standard agreed sound pressure baseline”, as described
above, then the delta between the two measurements should be a constant value
(in dB). Measure different sound levels and check the deltas. If the difference is
not constant then you are probably hitting sounds that are outside of the supported
range of either of the devices.
The constant is a function of a ratio between the standard reference and the
specific reference of your build. The ratio is in the log and becomes a constant
due to log rules (http://www.rapidtables.com/math/algebra/Ln.htm#product-rule)
i.e. hard math.
For example, if your devices shows -10dB and the standard meter shows 30dB,
then it means you can change your code to add 40dB to the value and that would
calibrate your device to the standard reference. LMK how that works for you.

27. Reply Tobias May 17,2017 5:29 pm

hi! Thanks, I measured the different sound levels. Now I try to understand the math to
calculate the constant value. If I do not get any further, I´ll reply.

o Reply Arik Yavilevich May 18,2017 6:25 am

Hi Tobias, just to clarify. You measure the constant offset by making an


experiment. You don’t calculate it. Once you measured the offset, you can use
math to calculate how much “louder” your build’s reference point is from the
“standard agreed sound pressure baseline”, but it is not required.

28. Reply Tobias May 18,2017 3:58 pm

hi! yes, I understand so far. I used a buzzer as sound source and took the readings from
the meter and the arduino, for three different distances.
Distance 50cm: Meter 100dBA / Arduino -4,9dB
Distance 10cm: Meter 110dBA / Arduino -1,1dB
Distance 2cm: Meter 120dBA / Arduino -0,25dB
I know, just to add the difference in dB to get the dBA, but it´s not that simple..

o Reply Arik Yavilevich May 18,2017 7:50 pm

Hi Tobias, you are hitting the volume limit of your build. 0dB is the absolute max
volume that your build can detect and you are too close to it. Move farther away,
reduce gain or reduce buzzer volume. Please post data for 3 new distances such
that Arduino measurement is below -5dB. Cheers.

29. Reply Tobias May 19,2017 4:16 am

hi! The poti on the MAX 4466 Module was in middle position. Even if I turn it
completely down, the reading is not below -6dB in calm environment.
I didn´t change something on the gain factor bits in the original code.

o Reply Arik Yavilevich May 19,2017 12:07 pm

Well, that is no good. 😀


AFAIR, dB for quiet room should be below -20dB.
By default there is no software gain, but double check that VolumeGainFactorBits
is defined as 0.
Try to debug the code and the circuit. Using “MeasureAnalog” is useful to see the
raw readings, which should be around 512.

 Reply Tobias May 21,2017 8:13 am

hi! Independently of Arduino, I viewed the output signal of the MAX


4466 Module with an oszilloscope. There is a DC offset on the output of
about +2,5V. Is this the way it should be?
Unfortunately I don´t get the “Analyzing analog input” sketch to work:
… cc3fuV8c.ltrans0.ltrans.o: In function `main’:

… arduino/main.cpp:43: undefined reference to `setup’

… arduino/main.cpp:46: undefined reference to `loop’

collect2.exe: error: ld returned 1 exit status

 Reply Arik Yavilevich May 21,2017 11:14 am

Hi, it is normal for the DC offset to be half of Vcc. So if you are


seeing 2.5V then I assume you are powering the module with 5V.
Is it possible that you are powering the module with 5V but are
using the “#define Use3.3” option in the code? Please check
compatibility. Note that by default the code is meant to be used
with 3.3V analog reference, so you would need to power the
module with 3.3V. If there is a mismatch, it would look like there
is “signal” even when the room is quiet, as you are experiencing.
The information for 3.3V and 5V operation is described above in
the article and comments. Take another look.

 Reply Tobias May 21,2017 4:32 pm

hi! I read the article very carfully, but for some reason I´ve
mistaken “module” with “arduino board”.. I wondered how
to drive Arduino with 3,3V, so I powered all components
with 5V, sorry 😀 Now it works fine, in quiet room 0%
soundVolRMS and -54dB. It is very sensitive to the
orientation of the buzzer as sound source. For reproducible
results, I´ll mount the sound source, meter and module-mic
on a rail.

 Reply Arik Yavilevich May 21,2017 4:40 pm

Great news! Happy to see that it works well for you now.

It is interesting about the orientation of the buzzer. What


about the orientation of the mic? It looks unidirectional
unlike a typical sound meter.

 Reply Tobias May 23,2017 3:56 am


hi!
I made new measurements with a mechanically more stable
construction.
Silence: Meter 42,5dBA / Arduino -54,4dB
Distance 142cm: Meter 92dBA / Arduino -26dB
Distance 100cm: Meter 97dBA / Arduino -22dB
Distance 50cm: Meter 100dBA / Arduino -14dB
How to change the code to adjust the readings? 😀
But I´m not satisfied with the quality of the buzzer, also the
sound spread is unfavorable.
I´ll mount a small speaker and the Meter or Arduino in a
fixed distance together. Then I drive the speaker with a
frequency generator at some defined output voltages. That
will give more reproducible results.

 Reply Tobias May 23,2017 3:59 am

..sorry, correct is “Silence: Meter 42,5dBA / Arduino -


54,1dB”

 Reply Tobias May 23,2017 10:30 am

…ok, I recognize the scheme. I just have to add the


difference of the arduino reading. With the wrong hardware
wiring, it seemed to be more difficult to calculate, now its
clear 😀 Just add around 114 – 119dB to the reading. I´ll try
to make a better test setup.

 Reply Arik Yavilevich May 23,2017 3:52 pm

Hey, yes, exactly. You can see it visually in this graph:


http://imgur.com/a/pHH20
Mind that the delta for “silence” measurement is different.
This can be for two reasons. A) noise sensitivity differences
B) Weighting differences.
“A” means that your Arduino module is probably more
sensitive to noise than the commercial sound level meter.
So it will report a higher level in a silent room. That level is
due to “noise” (inaccuracy in the circuit, not real noise).
“B” is due to differences in weighting the different
frequencies. You wrote that your sound level meter is dBA,
this means that it weights different frequencies differently
(more info in the article above). The Arduino is in “simple”
dB values, it is possible to implement dBA measurement in
Arduino, but it won’t be trivial. If you know the frequency
of your buzzer then you can look at the A-weighting
function ( https://en.wikipedia.org/wiki/A-
weighting#/media/File:Acoustic_weighting_curves_(1).svg
) and figure out the specific weight, maybe understand what
is going on at a deeper level.

 Reply Tobias May 25,2017 3:45 am

hi!
Thank you for your great support 😀
I just started with Arduino, my specialty is electrical
engineering, I´m into the technology of past days – such as
the construction of tube amplifiers for audio playback. I
find it hard with silicon-based electronics and informatics
😀 The Arduino board laid a long time unused in my
workshop..
But I have to say this project brings me fun, I´m working
and learning with it 😀
Also thanks for your good project documentation.
Today, I tested it on the PA music system, which it is
intended for. Basically, the dBA readings of the
commercial meter and of the Arduino seem to fit together.
But it also reacts quite differently with real music – instead
of the buzzer as sound source.
– The heights have more effect on the meter, in contrast to
Arduino, which is more sensible to bass frequencies. The
readings differ partially significantly. One might allow a
compromise.
– But there is still another problem:
The PA sound system is very loud, the meter reads peaks
around 114-115dBA. Mic module seems not to clip. The
arduino sketch produces -54 – 0dB; my correction factor is
+100. The result is, it can just read 110dBA in the max.!
But if I change the correction factor to around +110, the
readings are simply wrong 😀
– The reading of the commercial meter is slow, the
measured / displayed values change around 0,5s. The
printout of Arduino is fast, too fast for a good readoff a
LCD. Is there a way to slow it down?
The meter reacts smooth, bass impulses from the sound
system don´t produce great magnitudes – this is what I
would also wish for Arduino. It shows great jumps in the
serial reading and plotter when the music plays.
– Is it possible, to make an average reading over time,
around 0,5s, like the commercial meter? Then the readings
of meter and Arduino would be much more similar.
Some pics from the test today:
http://abload.de/image.php?img=arduinodbamessungkurztq
otj.jpg
http://abload.de/image.php?img=arduinodbamessungkurzj3
q5u.jpg
Some pics of the test with the buzzer, mounted on a rail for
several fixed distances. A nice experiment, but not
comparable with real music sounds.
http://abload.de/image.php?img=arduinodbakalibrierune8q
0a.jpg
http://abload.de/image.php?img=arduinodbakalibrierunfhr2
n.jpg
http://abload.de/image.php?img=arduinodbakalibrierunp2pt
l.jpg

 Reply Arik Yavilevich May 25,2017 9:18 am

Hi Tobias,
Great to see you are enjoying your project. Thanks for
posting the photos, nice build.

It is probable that the mic on the module is not as accurate


as the commercial sound meter. It is quite a cheap
mic/module. There could be non-linearity with volume or
with frequency response.
Still, you can change the time-weighting for Arduino and
try to make it the same as your commercial meter. Note that
the sketch prints the interval time in ms on every pass. By
default it is probably around 50ms.
Increase the define “MicSamples” to increase the time of
each interval. I recall that “Slow” is defined as 1s, so you
would define MicSamples as 38000 to get to about that.
Also, mind what frequency you used to calibrate. If you
calibrate with basses, it might be wrong for higher
frequencies. See A-weighting graph.

30. Reply Davor Stefok May 19,2017 5:46 pm

Hi Arik,
first i would like to thank you for this detailed paper. I would refer to a question that was
already asked by Ciro, the circuit. Im using arduino uno and when you say “5V/Vcc/+ to
Vcc” do you mean 3.3V on the arduino by the second “Vcc”. And can you please show
the part of the circuit containing ARef.

o Reply Arik Yavilevich May 20,2017 12:24 pm

Hi Davor, when I answered Ciro’s question, I described the wiring for the, trivial,
5V use of the module. If you are looking to do the more accurate 3.3V wiring then
it is a bit different. I will update my answer to Ciro above. Thanks for pointing
this out.

31. Reply Rizqia May 29,2017 7:56 am

Hi Arik, I found formula

Prms=(Vrms/gain)/mic Sensitivity

dB = 20xlog(Prms/Pref)

I got problem, I do sampling data from 50ms and then I calculate Vrms form that data.
But Vrms from sensor GYMax4466 is still higher than I expected.

Can you give me advise for sampling data from arduino uno?

o Reply Arik Yavilevich May 29,2017 12:57 pm

Hi Rizqia,

Not sure what Prms is vs Vrms. You should provide context.


What happens if you use the code in this article?

 Reply Rizqia May 29,2017 6:05 pm

I try to calculate SPL using that formula. And then I lil bit imitate your
code

Here’s my code
void loop()
{
unsigned long t0, t; // Start of sample window

unsigned int sampleAvg;


unsigned int sampleRms;
t0 = millis();
for (int i=0; i<50; i++) {
sample = analogRead(0);
sample1 = abs(sample-512);
sampleAvg += sample1;
sampleRms += ((long)sample1*sample1);
}
t = millis() – t0;
sampleRms /= 50;
sampleAvg /= 50;
float dataRms = sqrt(sampleRms);

float vAvg = (sampleAvg * 3.3)/1024; // convert to volts


float vRms = (dataRms * 3.3)/1024; // convert to volts
float dBAvg = 20*log10(((vAvg/77.15)/0.0063)/0.00002);
float dB = 20*log10(((vRms/77.15)/0.0063)/0.00002);

Serial.print("Time: " + String(t));


Serial.print(" vRms: " + String(vRms));
Serial.print(" dB: " + String(dBAvg));
Serial.println(" dB: " + String(dB));

the problem is dB value is still the same (measure 69-72 dB) despite I take
this sensor to noisy place (it should be more than 90 dB).

Can you notice what's wrong in my code?

 Reply Arik Yavilevich May 30,2017 4:45 pm

Hi Rizqia,

Here are some comments that I have about the code.

You are making a calculation using 50 samples, not 50ms. Why is


that? Seems to little IMO. What does “Time: ” print?

Not sure why you are converting to volts or where the various
constants in your code are coming from. How did you decide on
those values (Pref, sensitivity, gain, etc)? Please link to the formula
you found.

 Reply Rizqia May 30,2017 5:59 pm

Hi Arik,

Sorry to make you confuse, on first comment I tried to take


samples data for 50ms, but then I changed the code to
sampling 50 samples..

I print “time” to know how long it takes to get 50 samples..

I try to get SPL value (decibel unit) using this sensor. I got
the formula from this jurnal
http://www.sciencedirect.com/science/article/pii/S2468067
216300293

– Pref: 20 micro Pascal (which is referenced to the lowest


thresholds of human hearing)
– mic sensitivity: -44dB/Volts/Pascal (I got from mic
datasheet) or it’s similar with 0.0063 Volts/Pascal

I need to convert the samples in RMS to Volts, so I can get


the Value of SPL using that formula..

 Reply Arik Yavilevich Jun 1,2017 6:59 pm

Hi Rizqia,

With the additional information it starts to make sense. I


understand that: 0.0063 is the mic sensitivity [V/Pa],
0.00002 is the reference SPL level [Pa] and 77.15 is the
gain.
How did you calculate the gain to be this specific value?
Based on your algorithm and constants I see no reason why
you shouldn’t be getting 100dB results. Please change your
algorithm to make 2000 samples (instead of 50) and then
post your Serial.print* output from when you are
measuring a noisy place.

 Reply Rizqia Jun 1,2017 11:11 pm


Hi Arik,

The problem is solved. I changed the way to calculate the


rms value. Thank for your articel, it helps.

For gain, I measured the pot and other resistors, then I


calculate by my self using formula gain for non-inverting
amp.

For samples, I did take 50, 500, and 1000 samples, and the
results are still good.

It can measure from 60-104 dB at particular places. And I


use apps decibel 10th from IOS to compare the value. It has
span 4-6 dB. But that’s ok, since I didnt use the real SPL
meter.

 Reply Arik Yavilevich Jun 2,2017 6:29 am

Hi Rizqia,

Glad to see you figured it out! Thanks for the interesting


comments you provided.

Based on the Adafruit schematic of the MAX4466 module


I see that the gain is (22K+PotValue)/1K .

For the benefit of the other readers, can you explain the
change in the RMS calculation that you made? Was there a
bug?

For accurate calibration you will have to use a calibrated


reference device. A calculation based on spec values will
not be enough, because each parameter has some error
tolerance. For example the sensitivity of the mic is +-2dB.

With regards to the number of samples. If you only take 50


samples, you risk not capturing the entire wave length of
lower frequencies. So at 50 samples and 40Khz sampling
rate you might miss on frequencies below 800hz. Not sure
if this is equally true for sounds of a single frequency as it
is for natural sounds that are a mix of frequencies but still
something to take in mind.
 Reply Ivan Nov 11,2017 1:33 am

Hi, using your code cant get more than 80dB in the serial
plotter even when my SPL metter shows 100dB. I tryed to
adjust the gain but doesnt work. Can you post your how
you calculate now the rms value? thanks

32. Reply Rizqia Jun 2,2017 9:00 am

Hi Arik,

For gain, yes I found on adafruit schematic too.

For rms calculation, There was a bug. So I turned (long)sample1*sample1 into


sq(sample1) to fix it.

I guess you’re right about sampling. Thanks for your feedback.

33. Reply Guilherme Aug 8,2017 1:58 pm

Hello Arik,

Thanks for the extremely useful guide!


I’ve been running trough some problems trying to imitate your setup.
When I try to calibrate my sensor using the code displayed at section “Analyzing analog
input”, I get no difference in response whatsoever when I try to change the module gain.
When connected to 3.3V in a quiet environment, the module outputs min and max close
to 330, these values switch to min: 0 and max: 660 when I clap loudly. At 5V reference
level, the min and max change to around 500~510.
Is this normal behavior? BTW, I’m using a MAX4466 based module.

Also running the final code, I get fixed rms: 140% and the db measure ranges between
0,0000~0,0010

Thanks!

o Reply Arik Yavilevich Aug 8,2017 6:07 pm

Hi Guilherme,
Changing the gain will not affect the “base values” if there is no sound. For this
module, the base is always module Vcc/2 . The values of 500~510 match Vcc/2
for Vcc=5V.
Values of 330 match Vcc/3 for Vcc=5V which is 3.3V/2. I believe you are
powering the module with 3.3V but using an 5V analog reference.
Follow the article to switch to 3.3V analog reference and try again. Change gain
while there is sound and see if your analog reading range changes in sync. Good
luck.

34. Reply Mick Aug 11,2017 4:52 am

Searched all the articles about spectrums and found the most useful one here
Thanks a lot!
But i am having some trouble with “free running”
I was doing good with analogRead but when changed it to “free running”, the “k” i got
seemed to always be 1023
(the micro i used is HXJ-17)

o Reply Arik Yavilevich Aug 11,2017 10:05 am

What pin do you use on Arduino for the mic and what pin on the HXJ-17 do you
connect to Arduino for the analog data?
Please include a clear photo of your setup.
What values were you getting with analogRead before “free running” mode?

 Reply Mick Aug 12,2017 12:50 am

Hi Arik!
the setup is just that HXJ-17’s “A0″is connected to Arduino UNO’s “A0″
the values i got from”analogRead” are between 0 and 1024, which i think
is right.
But know i doubt that there is something wrong about my setup, cuz i
can’t understand some explanation you put.I’m working on them now.

Mick

35. Reply Paul Aug 12,2017 2:05 am

Hi Arik!
Thanks for your useful article!
I’ve got all the data correctly.But I am wondering why the value i get from
FreqOutData[] is up to 200, even in silence?Aren’t the spectrums supposed to be show
nothing when it is silent?
Thanks again!
Paul

o Reply Arik Yavilevich Aug 12,2017 6:36 am

Hi,

Have you managed to overcome the “free running” issue? For the benefit of the
other readers, please let us know how to fix that.

FreqOutData should not go that high for silence. See my “FHT in time” 3D graph
above for an example. If it is going that high for you then you are not doing
something right. Check your fht_input values to see if the issue is with FHT or
earlier.

36. Reply Branko Pecar Aug 16,2017 3:27 pm

Hi Arik,

Absolutely first class! Really enjoyed it, to the point where I am now waiting to get all
the components to replicate your project. I have a question. If you wanted to measure that
the “pitch” of the noise that you are monitoring has changed (it went from lower to higher
frequencies), what modifications/additions to your code would you make?

Many thanks,

Branko

o Reply Arik Yavilevich Aug 17,2017 3:14 pm

Hi Branko,

How do you define the “pitch of the noise”?

Didn’t do something like this myself, but maybe other readers can contribute.

37. Reply Branko Pecar Aug 17,2017 3:40 pm


Hi Arik,

Thanks for coming back so quickly. I used a very non-technical term. What I really
meant is that the spectrum changes towards higher frequencies, possibly even into the
ultrasonic range. If anyone has any ideas on how to measure this, I would welcome the
thoughts. Thanks.

Branko

38. Reply tiger.hanfeng Sep 1,2017 8:13 am

Hello Arik,

Thanks for your helpful info really. i compile source file ( ArduinoSoundLevelMeter.ino
in according your advise ( include mic gnd connect arduino uno gnd , arduino uno aref
connect arduino uno 3.3v and mic vcc , mic ao connect arduino uno ao .

But i found dB is -15.00 normally , if we speak loudly , the dB is -6.00 normally ,

Maybe it will be error value for us , if possible , please give me some advisement .

Time: 53 Amp: Max: 21% Avg: 17% RMS: 25% dB: -15.001
Time: 53 Amp: Max: 21% Avg: 17% RMS: 25% dB: -14.963
Time: 54 Amp: Max: 21% Avg: 17% RMS: 25% dB: -14.880
Time: 53 Amp: Max: 22% Avg: 17% RMS: 25% dB: -14.915
Time: 53 Amp: Max: 21% Avg: 17% RMS: 25% dB: -14.967
Time: 54 Amp: Max: 22% Avg: 17% RMS: 25% dB: -14.955
Time: 53 Amp: Max: 22% Avg: 17% RMS: 25% dB: -14.878
Time: 53 Amp: Max: 22% Avg: 17% RMS: 25% dB: -14.936
Time: 53 Amp: Max: 21% Avg: 17% RMS: 25% dB: -14.979

o Reply Arik Yavilevich Sep 2,2017 11:08 am

Hi Tiger,

Doesn’t sound like your values are bad. They could be correct. What kind of
microphone module are you using?
If you make louder sounds, does it go higher than -6?

If you want to try another way of looking at it, try undefining FreqLog and
watching the result values. Those won’t be in dB but in 8bit magnitude numbers.
39. Reply tiger.hanfeng Sep 3,2017 3:20 pm

Hello Dear Arik:

Thanks for your advisement , Whether i need adjust AmpMax ( #define AmpMax (1024 /
8) ) for realizing positive number ? as your saying , dB value is Relative value 。

float dB = 20.0*log10(soundVolRMSflt/AmpMax);

this is my sound sensor , maybe i will replace lm388 with MAX4466 later.

https://item.taobao.com/item.htm?spm=a1z09.2.0.0.2ed6e797n6R44y&id=36836608948
&_u=38pbglf105f
ic is lm388

Best Regards

Tiger

o Reply Arik Yavilevich Sep 3,2017 6:22 pm

Hi Tiger,

I would not change AmpMax unless the max of your microphone behaves in a
special way. If you want to add gain in software, use VolumeGainFactorBits.

dB is a relative metric! It is common to define max volume as 0 and have the


measurements as negative numbers relative to that. However, other reference
points can be set. See a discussion above about calibrating the measurements to a
standard reference of “20 micropascals”.
Of course you can just add some constant value (for example 20) to your dB
reading to make it “look” positive.

I have not used it personally, but an LM388 should do the job just fine.

40. Reply Max Mustermann Sep 6,2017 10:16 am

Hello,
thank you for Publishing your awesome Project.
It’s probably obvious but where does the factor 10000001 in this line
“long sample_rate = FHT_N * 1000000l / dt;”
come from?

Best Regards

o Reply Arik Yavilevich Sep 6,2017 10:57 am

Hi Max,

It is “1,000,000 L”, where L stands for the type “long”. Therefore, the value is 1M
not 10M and 1. The factor 1M is for the number if ms in one second.

Cheers,
Arik.

41. Reply tiger hanfeng Sep 6,2017 11:08 am

thanks, i updated VolumeGainFactorBits value to 10, but db value is NAN

o Reply Arik Yavilevich Sep 6,2017 11:15 am

Hi Tiger,

A value of 10 for VolumeGainFactorBits means a gain of 2^10 .


I have not tried with such a high gain. It is possible that for such a scaling the
values overflow at some point.
I suggest you try smaller gain first and debug the code to find out where it fails
for VolumeGainFactorBits=10

Regards,
Arik.

42. Reply tiger hanfeng Sep 6,2017 2:33 pm

ok,and i replace perious part with max4466 just now,db value was -10.5 normally, if i
speak loudly,and db value will be -2.49
43. Reply Francisco Sep 12,2017 12:23 am

Hi, first of all congratulations for this excellent project. Im really looking forward
finishing my noise meter, however im trying to get as much accuracy as possible. Have
you tried using this module
(https://www.amazon.com/gp/product/B00SOY52CU/ref=oh_aui_detailpage_o00_s00?ie
=UTF8&psc=1) for getting a 16 bits signal?

Thanks for all

o Reply Arik Yavilevich Sep 12,2017 5:29 am

Hi Francisco,

What you linked to is a DAC. I have not used it. It has no microphone so you will
need to connect one properly. Another issue to mind is that it has a sample rate of
860 samples/second. This is relatively low for audio. You will only be able to
identify frequencies of about 400hz and lower.

Arik.

44. Reply Soliman Sep 19,2017 9:38 pm

Hi Yavilevich,

Thank you so much for sharing such valuable experience. I really appreciate it and will
all the success in your project.

I found Grove – Loudness Sensor and I was wondering if you please tell me if what do
you think about it in term of its capability of measuring noise level?
Do you think it possible to use your code for this specific sensor?

Thank you in advance.,

45. Reply Soliman Sep 19,2017 10:11 pm

Grove – Loudness Sensor it uses chip LM2904


o Reply Arik Yavilevich Sep 20,2017 7:16 am

Hi Soliman,

I have no experience with this module. It looks ok and will probably do fine for
sound level measurement.
As it does filtering and returns a positive envelop it will not work as a spectrum
analyzer.

Regards,
Arik.

46. Reply Steve Oct 3,2017 10:03 pm

Hi, thank you for your great work and nice theory explanation.

I would like to build a sound meter for tube guitar amps comparison. You can see in this
video:

https://www.youtube.com/watch?v=xaSQBleWD2M&t=15s

It does not matter where arduino feeds. Electret mic (I have HXJ-17) or line level signal
from speaker output.

I need to display it. With 7 segment led display like in video, may be.

Do you have any suggestion to me?

Thank you.

o Reply Arik Yavilevich Oct 4,2017 11:32 am

Hi Steve,

Just follow the instructions in the article and show the result on a display instead
of sending it via a serial connection.

47. Reply Naiemah Oct 12,2017 12:49 am

Hi, what if I want to use 5V module. is there any suitable coding?


o Reply Naiemah Oct 12,2017 12:51 am

I’m use this module for my robot. I have problem with my 3.3 V input module.
hence I need to use 5V.

o Reply Arik Yavilevich Oct 12,2017 2:42 pm

Hi Naiemah,

Not sure what you are asking. Which module are you talking about?

48. Reply Sandy Radja Oct 15,2017 2:52 pm

hi arik
thanks for your very useful article as a reference for my final assignment ..
I have a project for my final project using analog sound sensor v2.
sorry before .. i’m still very beginner for arduino.o programming i want to calculate the
LEQ value of lunch (Ls), night LEQ (Lm) and LEQ daytime (Lsm) ..
I am still confused by how to calculate the output value .. because I know that the output
of the analog value of the sensor sound v2 is still worth the bit.
is there any suggestion suggestion for me ?? so i can fix the error?
o yah .. i will attach the program code ..
void setup()
{
Serial.begin(9600); // open serial port, set the baud rate to 9600 bps
}
void loop()
{
int val;
val=analogRead(0); //connect mic sensor to Analog 0
Serial.println(val,DEC);//print the sound value to serial
delay(100);
}

o Reply Arik Yavilevich Oct 15,2017 4:48 pm

Hi Sandy,

I suggest you first implement the code in the article and see that you can calculate
the dB value of the sound. From there adapt it to what you need to calculate
(which I am not familiar with).
Leave a Reply

Author (required)

Email (will not be published)(required)

Website

Subscribe to comment

« Why use – Fosphor, a GNU Radio real-time spectrum analyzer?


Capturing and reversing wireless keyboard signal »

Links

 LinkedIn
 Facebook
 Fochica

Recent Posts

 40 cent DIY pressure sensor based on a capacitive principle


 Fochica™ – Forgotten Child in Car Alert
 Fixing a bad STATE pin on an MLT-BT05 BLE module
 Efficient DC 12V to 5V conversion for low-power electronics, evaluation of six modules
 MLT-BT05 BLE module – a clone of a clone??

Archives

 October 2017
 June 2017
 April 2017
 March 2017
 December 2016
 September 2016
 August 2016
 May 2016
 March 2016
 January 2016
 December 2015
 October 2015
 May 2015
 April 2015
 February 2015
 December 2014
 October 2014
 August 2014
 April 2014
 November 2013
 September 2013
 August 2013

Categories

 Arduino
 Cloud
 Code
 Data
 Electronics
 General
 GNURadio
 Home
 Immersive Computing
 IT
 Languages
 Meta
 Optimization
 SDR
 Security
 Storage
 Virtual Reality
 Virtualization
 Web
 Why use?

Meta

 Log in
 Entries RSS
 Comments RSS
 WordPress.org

© 2017 Arik Yavilevich's blog — All Rights Reserved.

You might also like