I have a SparkFun BOB-12758 so just for fun, awhile back I made an SPL Meter. Here is the code, followed by some notes & comments:
//==============================================================================================================
// SPLmeter.ino (7-17-21)
// This is just a simple example program, not intended as a "real" SPL meter.
// It reads the peak & average SPL and "prints" the results to the Arduino Serial Monitor via USB.
// The USB has to stay connected so you can use the Serial Monitor on your computer.
// It could be modified to use an LED or LCD display.
// IT NEEDS TO BE CALIBRATED FOR THE PARTICULAR MICROPHONE BOARD, SO YOU NEED A REAL SPL METER!
// To calibrate, find the peak and average ADC readings at a known SPL level from the real meter.
// The input is assumed to be biased at Vcc/2 for a reading of ~512 with no signal (silence).
// The bias is subtracted-out to get the "real" readings.
// Since you'll never have true-silence (and there is electrical noise) you can measure and calculate an average to find the actual true-bias.
// My example does not include code for finding the true bias/average but when I tried it I was getting readings of 511 or 512 as expected.
// This works with most Arduino microphone boards that have amplified & biased outputs.
// I used a SparkFun BOB-12758 microphone board.
// Some microphone boards have an "envelope follower" output which puts-out a varying DC voltage relateive to loudness.
// A voltage-follower output isn't biased so you can take the bias subtraction out of the program.
// dB calculations are always relative so the raw dB calculation is added to dBSPLref (or effectively "subtracted" if you "add a negative" raw dB reading).
//==============================================================================================================
// Global Variables
int Bias = 512; // Nominally half of the 1023 range Adjust as necessary
int Analog; // The analog reading with bias subtracted-out and converted to absolute value (Read from A0)
int Max; // The maximum peak
int LoopTime = 1000; // Read continuously in a fast-loop for one second before finding peak & calculating average
int SPLref = 94; // An arbritary (semi-standard) reference for finding PeakRef and AvgRef. Any known SPL level will work
int PeakRef = 159; // Measured (or calculated) at SPLref
int AvgRef = 73; // Measured (or calculated) at SPLref
int n; // Number of readings in the loop (for calculatijng the average)
unsigned long Sum; // For finding average
unsigned long ReadStartTime; // millis() used for SampleTime loop
float Average; // ADC average
float dBSPLPeak; // Peak dB SPL reading.
float dBSPLAvg; // Average SPL reading
//==============================================================================================================
void setup()
{
Serial.begin(9600); // Used for serial monitor
}
// Start main loop ============================================================================================
void loop()
{
Max = 0; //Initilize/reset every time before starting while() loop
Sum = 0;
n = 0; //Number of readings (number of loops counted in 1 second)
ReadStartTime = millis(); //Save/update loop-starting time
// Find maximum & accumulate sum loop ==================================================================================
// Takes readings in a "fast loop" to find the peak & average.
while (millis() - ReadStartTime < LoopTime) // Normally 1 second
{
Analog = abs(analogRead(A0) - Bias); // Read, take out the 2.5V bias/offset, make positive.
if (Analog > Max)
Max = Analog; // Save overall maximum reading (Zero is invalid for log/dB calculation)
Sum = Sum + Analog;
n++; // Count the number of readings (to calculate average)
} // of while() loop ===================================================================================================
Average = (float)Sum/n; // Zero is invalid for log/dB calculation
//Calculate dB SPL maximum and and average and send to Serial Monitor before re-starting main loop
// Since the dB calculculaion is relative to the reference we must add it to the reference to get dB SPL. (It's OK to "add a negative")
dBSPLPeak = SPLref + 20*log10((float)Max/PeakRef);
dBSPLAvg = SPLref + 20*log10((float)Average/AvgRef);
Serial.print (" Max = "); // Take out (or comment-out) after calibration
Serial.print (Max); // Take out (or comment-out) after calibration
Serial.print (" "); // Take out (or comment-out) after calibration
Serial.print (" Average = "); // Take out (or comment-out) after calibration
Serial.print (Average); // Take out (or comment-out) after calibration
Serial.print (" "); // Take out (or comment-out) after calibration
Serial.print (dBSPLPeak,1); // Display peak dB SPL reading to one decimal place
Serial.print (" dB SPL Peak");
Serial.print (" ");
Serial.print (dBSPLAvg,1); // Display average dB SPL reading to one decimal place
Serial.println (" dB SPL Average");
} // End of main loop ==========================================================================================
The BOB 12758 microphone element is rated up to 110dB but my quick-and-dirty calculations say the built-in preamp will clip at about 96dB. If you use this board you might have to change a resistor to reduce the gain in addition to blocking some of the sound. (There are amplifier boards with adjustable gain.)
This isn't an issue with loud sounds but when USB powered I was getting quite a bit of electrical noise to the point where I couldn't measure the loudness of my TV without turning it up louder than normal. With a separate power supply it was useable at lower levels but still not useful for "quiet sounds".
SPL meter COMPLICATIONS -
YOUR METER NEEDS TO BE CALIBRATED. Generally, that means you need a real SPL meter to calibrate your homemade meter. Or you can buy an SPL calibrator that puts-out a known SPL level. A cheap one costs about $100 USD (more than a cheap SPL meter). If the microphone specs give you the sensitivity and you know the amplifier gain you can make a calculation instead of using an SPL meter but there are tolerances so it won't be as accurate. The microphone board I used has a spec of +/-2dB but my calcuated value was only off by about 1dB. The calculated average had more error and I'm not sure why... With a sine wave the peak-to-average ratio is is a known constant.
YOU CAN'T USE A HOMEMADE SPL METER FOR LEGAL OR REGULATORY PURPOSES. For this, your meter has to be calibrated/certified by an independent certified lab. These labs won't calibrate your homemade meter. Same thing if you buy a "cheap" non-certified meter.
Real SPL meters are usually A-WEIGHTED which takes-into account the fact that our ears are most-sensitive to mid-frequencies. A-weighting reduces low-frequency (bass) and high frequency readings. The weighting circuit is normally an analog filter which would be inserted between the microphone preamp and the Arduino. (I've never built a weighting filter.) Weighting could be done in software but it adds lots of software complexity and a regular Arduino would have to pause reading while doing the filter calculations, and you would miss some peaks.
A simple high-pass filter can be used to reduce the low-frequency readings and it's probably "better than nothing". A high-pass filter will filter-out the DC bias (DC is zero Hz) so you'd have to add another bias circuit (or build that as part of your filter).
There is a lot of low-frequency energy in natural sounds or noise and especially in music so a homemade meter without A-weighting will read higher than a real SPL meter. However, you can calibrate-out that error to some extent if you calibrate it with the sound you intend to measure. For example, if you want to measure the loudness of a particular machine you can make your meter match the real meter with the sound from that particular machine.
I calibrated my meter to match with a 1kHz sine wave so at 94dB, my real meter reads 94dB and with this constant tone my homemade meter reads 94dB for both the peak and the average. With music from my stereo the homemade meater reads higher (because of the bass) and of course the peaks are higher than the real meter. (My real SPL meter doesn't read peaks.)