Resting level and swing
Two measurements turn this block into a number. At start-up, average a quiet second to find the resting level. Then, every 50 ms, take the highest reading minus the lowest: the peak-to-peak swing. The swing grows with loudness, the resting level cancels out of it, and a clap reads hundreds of millivolts where talking reads a few.
Two numbers, not one
The first reading showed the problem: one reading is one instant of the wave. So the sketch measures two things instead.
The resting level, once. At start-up it averages every reading taken over one quiet second. Averaging cancels the wave, if there is one, and what is left is where SIGNAL sits in silence on this board. It is printed so you know it; nothing later depends on it.
The swing, every 50 ms. The sketch reads as fast as it can for 50 ms, keeps the highest and the lowest reading, and prints the difference. That is the peak-to-peak swing. The resting level is in both the highest and the lowest, so it cancels out of the difference, and the same sketch works on a board that rests at 1.6 V and on one that rests at 4 V.
What a sound looks like
Pick a sound. The figure draws 50 ms of SIGNAL from 5V, with the resting level dashed and the highest and lowest point marked. Talking a metre away swings it by about 15 mV, three counts on an Uno. A raised voice close by makes about 150 mV. A clap at arm's length makes well over a volt.
The sizes are a picture, not a measurement. The capsule's own sensitivity is not published anywhere we could find, so the figure borrows one from a comparable capsule of the same size. Your numbers will differ; the order will not.
Where it stops growing
Pick Clap, close. The wave wants to swing further than the board allows. Upwards SIGNAL stops at VCC, where the transistor turns fully off; downwards it stops a few tenths of a volt above 0, where it turns fully on. The tops and bottoms are cut flat and the swing stops growing. A clap close up and a clap a little further away can then read the same.
That is fine for a switch, which only has to know loud from quiet. It is why the numbers this sketch prints are not decibels: they rise with loudness, not in proportion to it, and not at all once the wave clips.
The window
Fifty milliseconds is several cycles of the lowest sound the block passes,
about 75 Hz, so the window catches the wave's real highest and lowest
points. An Uno's analogRead takes about a tenth of a millisecond, so it
reads a few hundred times in each window. Anything much shorter than 50 ms
can miss a low sound's peaks; much longer and the sketch answers late, with
a clap and its echo in the same window.
The code
Reads millivolts: calibrated on an ESP32, scaled from the count on an Uno or a Pico. setup measures the resting level over a quiet second; loop prints the swing, highest minus lowest, for every 50 ms window.
/*
Analog Microphone - resting level and swing TK27 / /p/tk27
Wiring. Count from the square pad on the TinkerBlock board, parts
up, header at the bottom:
GND -> GND
VCC -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
NC -> nothing (unconnected on the board)
SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
ESP32-S3, GP26 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. Arduino Uno
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int MIC_PIN = A0;
// Uno and Pico only: the ADC's full scale, in mV.
// Uno: 5000. Pico: 3300.
const float FULL_SCALE_MV = 5000.0;
// Several cycles of the lowest sound the block passes.
const unsigned long WINDOW_MS = 50;
const unsigned long CALIBRATE_MS = 1000;
float readMilliVolts() {
#if defined(ARDUINO_ARCH_ESP32)
return analogReadMilliVolts(MIC_PIN); // calibrated in the chip
#else
return analogRead(MIC_PIN) * FULL_SCALE_MV / 1023.0;
#endif
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("Measuring the resting level. Keep quiet...");
float sum = 0;
long n = 0;
unsigned long start = millis();
while (millis() - start < CALIBRATE_MS) {
sum += readMilliVolts();
n++;
}
Serial.print("Resting level ");
Serial.print(sum / n, 0);
Serial.println(" mV");
}
void loop() {
// The highest and lowest reading in one window.
float lo = 100000;
float hi = -1;
unsigned long start = millis();
while (millis() - start < WINDOW_MS) {
float mv = readMilliVolts();
if (mv < lo) lo = mv;
if (mv > hi) hi = mv;
}
Serial.print("swing ");
Serial.print(hi - lo, 0); // peak to peak: louder, bigger
Serial.println(" mV");
}The resting level is printed once and not used again: the swing is measured from each window's own highest and lowest reading, so a resting level that drifts as the board warms changes nothing. On an Uno, set FULL_SCALE_MV to the 5V pin's real voltage for exact millivolts.
The same two measurements in MicroPython: the resting level over a quiet second, then the swing, highest minus lowest, for every 50 ms window. read_uv on an ESP32, the scaled count on a Pico.
"""
Analog Microphone - resting level and swing, MicroPython
TK27 / /p/tk27
Wiring. Count from the square pad on the TinkerBlock board, parts
up, header at the bottom:
GND -> GND
VCC -> 3V3 (never 5V: a loud sound takes SIGNAL up to VCC)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Nothing to install: machine, sys and time are built in.
"""
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
MIC_PIN = 4
WINDOW_MS = 50 # several cycles of the lowest sound it passes
CALIBRATE_MS = 1000
adc = ADC(Pin(MIC_PIN))
ESP = sys.platform == "esp32" # ESP32 and ESP32-S3
if ESP:
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
def read_millivolts():
if ESP:
return adc.read_uv() / 1000 # calibrated in the chip
return adc.read_u16() * 3300 / 65535
print("Measuring the resting level. Keep quiet...")
total = 0
n = 0
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < CALIBRATE_MS:
total += read_millivolts()
n += 1
print("Resting level %.0f mV" % (total / n))
while True:
lo = 100000
hi = -1
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < WINDOW_MS:
mv = read_millivolts()
lo = min(lo, mv)
hi = max(hi, mv)
print("swing %.0f mV" % (hi - lo)) # louder, biggerThere is no Uno here: an Uno cannot run MicroPython. MicroPython reads more slowly than a compiled sketch, so each window holds fewer readings; 50 ms is still several cycles of the lowest sound the block hears. Stop it with Ctrl-C.
When it does not work
Every ADC's readings wander by a count or two, more on an ESP32, so the highest and lowest reading in any window differ a little. That is the quiet swing, and a sketch should measure it before it trusts any threshold. The ESP32 article does exactly that.
The sketch prints millivolts, not counts. On an Uno one count is about 4.9 mV, so a line at 600 in the plotter is about 2930 mV here. If it is further off than that, the room was not quiet while it measured: press reset and keep still for the first second.
Close up, a clap swings SIGNAL further than it can go: up to VCC on one side and down near 0 V on the other, and both are cut flat. Past that point the swing stops growing, however close you clap. This block tells loud from quiet reliably, not how loud once it clips.
The block passes sounds down to about 75 Hz, and one cycle of 75 Hz lasts over 13 ms. A 50 ms window holds several cycles of the lowest sound it hears, so the highest and lowest reading in it are the wave's real peaks. A 10 ms window can miss them.
Finer steps, more noise, and why every sketch should measure the quiet swing first.
Reading it on an ESP32 →Edit this page — content/books/analog-microphone/resting-level-and-swing.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Analog Microphone
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.