Reading it on an ESP32
An ESP32 reads in finer steps than an Uno, about 0.7 mV to the Uno's 4.9, but its readings also wander further on their own. What decides whether a sound can be told from nothing is the swing against that wander, so measure the quiet swing at start-up and count only what rises above it. Use an ADC1 pin, and VCC from 3V3.
Finer steps, more wander
An Uno's converter turns 0 to 5 V into 1024 steps, about 4.9 mV each. An ESP32's turns its range, about 0.1 to 3.1 V at the Arduino core's default setting, into 4096 steps, about 0.7 mV each. So the same swing is roughly seven times as many counts on an ESP32.
That would make an ESP32 the better ear, except that its readings wander on their own. Read a pin whose voltage is perfectly still and an Uno returns the same count, give or take one; an ESP32 commonly scatters over a few tens of counts. Tens of counts of 0.7 mV is tens of millivolts, several times the Uno's wander.
Pick a sound. Each row is one board, with VCC on its own logic supply: the bar is the swing the sound makes, the grey band how far that board's readings wander with no sound at all. Both the sound sizes and the wander are illustrative, not measured. Talking a metre away is inside every board's band; a raised voice close by is clear of all of them.
Measure the quiet swing
The cure is the same on every board, so the sketch does it on every board. For one quiet second at start-up it measures the swing window after window and keeps the biggest. That is the quiet swing: the converter's own wander plus whatever the room is doing. After that it prints each window's swing and how far it rises above the quiet one.
A threshold set this way fits the board it runs on. On an Uno it lands around ten millivolts, on an ESP32 at a few tens, and neither sketch needs to know which board it is on. A clap switch puts its threshold a fixed margin above it.
Millivolts, 3V3 and ADC1
The sketch reads millivolts. On an ESP32 analogReadMilliVolts converts the
count with calibration written into the chip at the factory; on an Uno or a
Pico the count is scaled by the converter's full scale.
VCC comes from 3V3. From 5V the resting level would be higher and the gain higher too, but a loud sound would drive SIGNAL to 5 V, over what an ESP32's pin is rated for.
And SIGNAL goes to an ADC1 pin: GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3. The classic ESP32's other converter, ADC2, is shared with the radio and fails while Wi-Fi is on.
The code
Measures the quiet swing first: the biggest peak-to-peak it sees in a second of silence. Then it prints each 50 ms window's swing and how far it rises above the quiet one. Works on every board; it matters most on an ESP32.
/*
Analog Microphone - above the quiet 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. ESP32S3 Dev Module
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;
const unsigned long WINDOW_MS = 50;
const unsigned long CALIBRATE_MS = 1000;
float quietMv;
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
}
// Highest minus lowest over one window, in mV.
float swingMilliVolts() {
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;
}
return hi - lo;
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("Measuring the quiet swing. Keep quiet...");
quietMv = 0;
unsigned long start = millis();
while (millis() - start < CALIBRATE_MS) {
float s = swingMilliVolts();
if (s > quietMv) quietMv = s; // keep the biggest
}
Serial.print("Quiet swing ");
Serial.print(quietMv, 0);
Serial.println(" mV");
}
void loop() {
float swing = swingMilliVolts();
float above = swing - quietMv; // what the room adds
Serial.print("swing ");
Serial.print(swing, 0);
Serial.print(" mV, above quiet ");
Serial.println(above > 0 ? above : 0, 0);
}Keep quiet for the first second, or the quiet swing will include your noise and the sketch will ignore it too. On an ESP32 expect a quiet swing of tens of millivolts; on an Uno, ten or so.
The quiet swing first, then each 50 ms window's swing and how far it rises above the quiet one, in MicroPython on an ESP32, an ESP32-S3 or a Pico.
"""
Analog Microphone - above the quiet 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
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
def swing_millivolts():
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)
return hi - lo
print("Measuring the quiet swing. Keep quiet...")
quiet = 0
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < CALIBRATE_MS:
quiet = max(quiet, swing_millivolts())
print("Quiet swing %.0f mV" % quiet)
while True:
swing = swing_millivolts()
above = max(0, swing - quiet) # what the room adds
print("swing %.0f mV, above quiet %.0f" % (swing, above))There is no Uno here: an Uno cannot run MicroPython. Without the atten line MicroPython's ESP32 ADC stops near 1 V, below most boards' resting level, and the swing reads 0. Stop it with Ctrl-C.
When it does not work
Typical. An ESP32's ADC wanders by a few tens of counts from reading to reading with nothing changing, and each count is under a millivolt. A USB lead that also powers other things can add hum. The sketch measures it so it can ignore it; nothing needs fixing unless it is hundreds of millivolts.
SIGNAL is on an ADC2 pin. On the classic ESP32 the radio uses ADC2 while Wi-Fi is on, and readings from it fail or return rubbish. Move SIGNAL to an ADC1 pin: GPIO 34 here, or any of GPIO 32 to 39.
It is part of Espressif's ESP32 Arduino core, in the 2.x and 3.x releases. Update the core in the Boards Manager. On an Uno or a Pico the sketch never calls it: the #if sends them down the analogRead path.
No. From 5V a loud sound takes SIGNAL up to 5 V, and the ESP32's pins are rated for 3.3 V. Use 3V3. The gain is lower than from 5V, which costs sensitivity to quiet sounds, and this block is for loud ones anyway.
Edit this page — content/books/analog-microphone/reading-it-on-an-esp32.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.