Датчик звука для Arduino (KY-038): включение хлопком и уровень звука
Как работает датчик звука KY-038/KY-037 для Arduino? Настройка потенциометра, размах сигнала, лампа на двойной хлопок, шкала из светодиодов, код для ESP32 и Pico и правда о децибелах.
Когда говорят о датчике звука для Arduino, обычно имеют в виду KY-038: дешёвый модуль, на одной плате которого стоят маленький электретный микрофон, компаратор LM393 и синий потенциометр чувствительности. Его «брат» с большим микрофоном, KY-037, работает так же. Библиотека не нужна: пин DO меняет состояние, когда звук превышает порог, и читается через digitalRead(), а пин AO выдаёт сырой сигнал микрофона и читается через analogRead(). Лампы, которые включаются хлопком, сигнализации шума и светильники, реагирующие на звук, собирают именно на этом модуле.
Но у этого датчика есть важное ограничение, о котором многие уроки молчат: KY-038 определяет только есть звук / нет звука. Он не записывает звук, не распознаёт речь и не умеет измерять децибелы. В этой статье разберёмся, как устроена схема модуля, как правильно настроить потенциометр, почему аналоговое значение часто будто бы вообще не меняется и как правильно измерять громкость методом размаха (peak-to-peak). Затем соберём лампу, которая переключается двойным хлопком, и шкалу уровня звука из 8 светодиодов, сделаем то же самое на ESP32 и Raspberry Pi Pico и посмотрим, на какие модули переходить, когда нужно настоящее измерение звука.

Что такое датчик звука и как он работает?
Серебристый цилиндр на краю платы — это электретный конденсаторный микрофон. Внутри тонкая мембрана расположена напротив пластины с постоянным электрическим зарядом, и вместе они образуют крошечный конденсатор. Звук — это волна давления в воздухе; когда она толкает мембрану вперёд и назад, ёмкость меняется и появляется очень маленькое изменение напряжения. Крошечный полевой транзистор (JFET) внутри капсулы буферизует этот сигнал, поэтому электретный микрофон питается через резистор и имеет полярность. В итоге на микрофоне получается звуковая волна размахом всего несколько милливольт, наложенная на постоянное напряжение.
В 8-выводной микросхеме LM393 в середине платы два компаратора, модуль использует один из них. Компаратор сравнивает два напряжения: сигнал с микрофона и порог, заданный потенциометром. Как только сигнал пересекает порог, выход меняет состояние, и на это реагируют пин DO и второй светодиод на плате. Очень важная деталь: компаратор не усилитель. Он не делает сигнал больше, а только отвечает на вопрос «выше порога или ниже?». На пин AO идёт сырой, неусиленный сигнал микрофона, и именно поэтому аналоговое значение такое слабое.
| Параметр | Значение |
|---|---|
| Микрофон | Электретная конденсаторная капсула, всенаправленная |
| Компаратор | LM393 (сдвоенный компаратор, используется один) |
| Питание | 3,3–5 V |
| Ток | Несколько mA (около 5–10 mA вместе со светодиодами) |
| Выходы | AO: сырой сигнал микрофона, DO: пороговый выход |
| Регулировка чувствительности | Синий многооборотный подстроечный резистор (обычно типа 3296) |
| Светодиоды | Один для питания, один для состояния DO |
| Порядок пинов | AO, G, +, DO |
KY-038 и KY-037
У KY-038 и KY-037 одна и та же схема, тот же LM393 и тот же порядок пинов, поэтому все скетчи из этой статьи работают на обоих без изменений. Разница в капсуле микрофона: у KY-037 капсула больше, и его продают как «датчик звука высокой чувствительности», а у KY-038 капсула меньше, и именно он чаще всего встречается в наборах датчиков. На практике оба хорошо слышат громкие звуки рядом, но ни один не услышит человека, который говорит на другом конце комнаты. Встречаются и 3-пиновые звуковые модули на LM393 только с цифровым выходом; у большинства из них выход при звуке уходит в LOW.
| Модель | Пины | Микрофон | Примечание |
|---|---|---|---|
| KY-038 | AO, G, +, DO | Маленькая капсула | Самая распространённая модель в наборах датчиков |
| KY-037 | AO, G, +, DO | Большая капсула | Та же схема и тот же код; обычно чуть чувствительнее |
| 3-пиновый звуковой модуль на LM393 | VCC, GND, OUT | Маленькая капсула | Только цифровой выход; OUT при звуке обычно уходит в LOW |
Подключение датчика звука: Arduino Uno, ESP32 и Pico
| Пин модуля | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| + | 5V | 3V3 | 3V3(OUT), пин 36 |
| G | GND | GND | GND, пин 38 |
| AO | A0 | GPIO 34 (ADC1) | GP26 (ADC0), пин 31 |
| DO | D2 | GPIO 27 | GP15, пин 20 |
Цифровой выход (DO) и настройка потенциометра
Пин DO даёт ответ «да/нет». На многих платах KY-038 DO переходит в HIGH и загорается второй светодиод, когда звук превышает порог; на некоторых клонах и на 3-пиновых модулях всё наоборот. Поэтому в скетчах уровень, который означает «есть звук», хранится в константе SOUND_ACTIVE. Второй важный момент: DO не выдаёт один длинный импульс на хлопок. Звуковая волна сотни раз в секунду поднимается выше порога и опускается ниже, поэтому выход включается и выключается так же быстро; один хлопок даёт пачку коротких импульсов длительностью несколько десятков миллисекунд. У компаратора на большинстве плат нет гистерезиса, поэтому при звуках около порога выход ещё и дребезжит. Так что читай DO быстрым циклом без delay() или через прерывание и считай близко идущие импульсы одним событием.
- Подключи модуль и поставь датчик туда, где он будет работать, в момент с обычным фоновым шумом; настраивай всегда в реальной обстановке.
- Посмотри на светодиод DO на плате. Если он горит в тишине, порог слишком чувствительный; крути потенциометр маленькой отвёрткой, пока светодиод не погаснет.
- Если светодиод вообще не загорается, крути в другую сторону и найди точку, где он начинает загораться.
- От этой точки поверни на пол-оборота или на целый оборот обратно, в сторону, где светодиод не горит. Тогда постоянные шумы вроде холодильника или вентилятора не будут вызывать срабатывание.
- Хлопни примерно с метра: светодиод должен коротко мигнуть. Если он загорается и от обычной речи, слегка поверни в сторону меньшей чувствительности; если не загорается от хлопка, слегка поверни в сторону большей.
1// KY-038 DO testi: sesi algıla ve dahili LED'i yak / KY-038 DO test: detect sound and light the built-in LED
2const int SOUND_DO = 2; // modülün DO pini / module DO pin
3const int SOUND_ACTIVE = HIGH; // ses gelince HIGH; kartın tersse LOW / HIGH on sound; LOW if your board is inverted
4const unsigned long HOLD_MS = 200; // yeni olay için gereken sessizlik / silence needed for a new event
5
6unsigned long lastSound = 0;
7unsigned long events = 0;
8
9void setup() {
10 Serial.begin(9600);
11 pinMode(SOUND_DO, INPUT);
12 pinMode(LED_BUILTIN, OUTPUT);
13}
14
15void loop() {
16 unsigned long now = millis();
17 if (digitalRead(SOUND_DO) == SOUND_ACTIVE) {
18 if (now - lastSound > HOLD_MS) { // sessizlikten sonra ilk darbe / first pulse after silence
19 events++;
20 Serial.print(F("Ses algilandi / Sound detected #"));
21 Serial.println(events);
22 }
23 lastSound = now; // darbe yağmuru süreyi uzatır / the pulse burst extends the time
24 }
25 digitalWrite(LED_BUILTIN, now - lastSound < HOLD_MS ? HIGH : LOW); // LED en az 200 ms yanar / LED stays on at least 200 ms
26}Скетч читает пин DO в каждом проходе цикла без delay(). Каждый раз, когда DO активен, обновляется время lastSound; новое событие засчитывается только после 200 мс тишины, поэтому пачка импульсов от хлопка выглядит в мониторе порта как одна строка. Встроенный светодиод тоже остаётся включённым минимум 200 мс, иначе миллисекундные импульсы глазом не заметить. Если счётчик растёт в тишине, значит, либо порог слишком чувствительный, либо выход DO на твоей плате инвертирован; во втором случае задай SOUND_ACTIVE значение LOW.
Почему аналоговое значение (AO) такое слабое?
Большинство новичков подключают AO к A0, выводят значение analogRead() и видят число около 500, которое почти не меняется от звука. Датчик не сломан, дело в масштабе. 10-битный АЦП Arduino Uno делит 0–5 V на 1024 шага, то есть один шаг — около 4,9 mV. При обычных звуках сигнал микрофона намного меньше этого шага:
| Звук | Примерный уровень | Выход микрофона (размах) | Шагов АЦП на Uno |
|---|---|---|---|
| Шёпот | 30 dB SPL | ~0,01 mV | 0 |
| Обычная речь (1 м) | 60 dB SPL | ~0,4 mV | 0 (не видно) |
| Громкий телевизор, улица | 80 dB SPL | ~3,6 mV | 0–1 |
| Очень громкий звук (1 Па) | 94 dB SPL | ~18 mV | 3–4 |
| Резкий хлопок или автомобильный гудок вблизи | 110 dB SPL | ~110 mV | ~23 |
Таблица рассчитана для чувствительности типичной электретной капсулы −44 dBV/Pa и синусоидального звука. В зависимости от резистора смещения микрофона на твоей плате реальные значения могут быть в несколько раз больше, но вывод не меняется: AO отчётливо видит только близкие громкие звуки, а обычная речь и шёпот оказываются ниже разрешения Uno. Подробнее о том, как работает АЦП, читай в статье Что такое АЦП?.
Вторая причина — сама природа звука. Звук — это волна, которая колеблется вверх и вниз вокруг постоянного уровня. Одиночный вызов analogRead() ловит случайный момент этой волны: он может попасть на пик, на провал или ровно на середину. Поэтому величина одного отсчёта ничего не говорит о громкости. Третья причина — скорость: на одно аналоговое чтение Uno тратит около 100 микросекунд, то есть успевает сделать не больше примерно 10 000 отсчётов в секунду. Этого хватает, чтобы следить за уровнем звука, но мало, чтобы записывать звук или анализировать частоты. Наконец, шум питания 5 V от USB проникает прямо на AO. Первые две проблемы решаются правильным методом измерения.
Измерение размаха сигнала (peak-to-peak): правильное чтение громкости
Правильный метод — взять как можно больше отсчётов за короткое окно времени и найти наибольшее и наименьшее значения. Разница между ними — это размах сигнала (peak-to-peak), и он показывает силу звука независимо от постоянного уровня. Хорошее окно — 50 мс: один период частоты 20 Hz, самой низкой, которую слышит человеческое ухо, равен ровно 50 мс, так что даже самый низкий звук укладывает в окно одну полную волну. За это время Uno успевает сделать около 450–500 отсчётов.
1// Tepe-tepe genlik ölçümü (Seri Çizici için) / Peak-to-peak amplitude (for the Serial Plotter)
2const int SOUND_AO = A0; // modülün AO pini / module AO pin
3const unsigned long WINDOW_MS = 50; // 50 ms = 20 Hz'in bir periyodu / one period of 20 Hz
4
5void setup() {
6 Serial.begin(115200);
7}
8
9void loop() {
10 int minV = 1023, maxV = 0;
11 long sum = 0;
12 int count = 0;
13 unsigned long start = millis();
14 while (millis() - start < WINDOW_MS) { // yaklaşık 450-500 örnek / about 450-500 samples
15 int v = analogRead(SOUND_AO);
16 if (v < minV) minV = v;
17 if (v > maxV) maxV = v;
18 sum += v;
19 count++;
20 }
21 int p2p = maxV - minV; // tepe-tepe genlik / peak-to-peak amplitude
22 float mv = p2p * 5000.0 / 1024.0; // milivolt karşılığı / in millivolts
23
24 Serial.print(F("p2p:"));
25 Serial.print(p2p);
26 Serial.print(F(" ortalama_avg:")); // AO'nun DC seviyesi; istemezsen bu iki satırı sil / DC level of AO; delete these two lines if not needed
27 Serial.print(sum / count);
28 Serial.print(F(" mV:"));
29 Serial.println(mv, 1);
30}После загрузки скетча открой «Плоттер по последовательному соединению» (Serial Plotter) в меню «Инструменты» и выбери скорость 115200. В тихой комнате p2p обычно держится между 0 и 3; если говорить рядом с микрофоном, он вырастает на несколько единиц, а от хлопка вблизи — на десятки. Значение ortalama_avg — это постоянный уровень AO; оно растягивает масштаб графика, поэтому, чтобы удобно следить за p2p, две строки с ortalama_avg можно удалить. На некоторых платах потенциометр сдвигает и этот постоянный уровень; если среднее застряло на 0 или 1023, сигнал упирается в край (клиппинг), поэтому крути потенциометр, пока значение не окажется около середины. Если плоттер для тебя в новинку, загляни в статью Монитор порта Arduino.
Пошагово: включение лампы двойным хлопком
Лампа на хлопок — самый известный проект с датчиком звука, но схема, которая срабатывает от одного хлопка, реагирует и на хлопнувшую дверь, упавшую книгу или кашель, и лампа то и дело мигает. Решение — искать ритм: скетч ниже реагирует только на ровно два хлопка с интервалом от 0,12 до 0,7 секунды. Одиночный звук или три и больше звуков подряд (музыка, аплодисменты, лай собаки) игнорируются.
- Подключи пин + модуля KY-038 к 5V, G к GND, а DO к D2.
- Подключи пин VCC модуля реле к 5V, GND к GND, а IN к D7. Как работает реле и как подключить сторону нагрузки, рассказано в статье Что такое модуль реле?; первые тесты делай со светодиодом самого реле, а не с лампой.
- Загрузи скетч и открой монитор порта на скорости 9600 бод.
- Хлопни дважды с расстояния в метр-два. На каждый хлопок должна появляться только одна строка «Alkis / Clap»; если один хлопок засчитывается несколько раз, слегка поверни потенциометр в сторону меньшей чувствительности.
- Лампа должна переключиться примерно через 0,7 секунды после второго хлопка.
1// Çift alkışla lamba (röle) / Double-clap lamp (relay)
2const int SOUND_DO = 2; // KY-038 DO pini / KY-038 DO pin
3const int RELAY = 7; // röle modülü IN / relay module IN
4const int SOUND_ACTIVE = HIGH; // kartın tersse LOW / LOW if your board is inverted
5const int RELAY_ON = LOW; // aktif LOW röle modülü / active-LOW relay module
6const int RELAY_OFF = HIGH;
7const unsigned long QUIET_GAP_MS = 120; // yeni alkış için gereken sessizlik / silence needed before a new clap
8const unsigned long CLAP_WINDOW_MS = 700; // dizinin bittiğine karar verme süresi / time to decide the series is over
9const unsigned long LOCKOUT_MS = 1000; // röle tıkırtısını yok say / ignore the relay click
10
11int clapCount = 0;
12unsigned long lastPulse = 0; // DO'nun son aktif olduğu an / last time DO was active
13unsigned long lastToggle = 0; // lambanın son değiştiği an / last time the lamp changed
14bool lampOn = false;
15
16void setup() {
17 Serial.begin(9600);
18 pinMode(SOUND_DO, INPUT);
19 pinMode(RELAY, OUTPUT);
20 digitalWrite(RELAY, RELAY_OFF); // lamba kapalı başlar / lamp starts off
21}
22
23void loop() {
24 unsigned long now = millis();
25
26 if (digitalRead(SOUND_DO) == SOUND_ACTIVE) {
27 bool locked = now - lastToggle < LOCKOUT_MS;
28 if (!locked && now - lastPulse >= QUIET_GAP_MS) { // sessizlikten sonra ilk darbe = yeni alkış / first pulse after silence = new clap
29 clapCount++;
30 Serial.print(F("Alkis / Clap: "));
31 Serial.println(clapCount);
32 }
33 lastPulse = now; // aynı alkışın darbeleri süreyi uzatır / pulses of the same clap extend the time
34 }
35
36 if (clapCount > 0 && now - lastPulse > CLAP_WINDOW_MS) { // dizi bitti / the series is over
37 if (clapCount == 2) { // yalnız tam iki alkış / exactly two claps only
38 lampOn = !lampOn;
39 digitalWrite(RELAY, lampOn ? RELAY_ON : RELAY_OFF);
40 lastToggle = now;
41 if (lampOn) Serial.println(F("Lamba ACIK / Lamp ON"));
42 else Serial.println(F("Lamba KAPALI / Lamp OFF"));
43 }
44 clapCount = 0; // 1, 3 ve fazlası yok sayılır / 1, 3 or more are ignored
45 }
46}| Константа | Значение | Назначение |
|---|---|---|
| SOUND_ACTIVE | HIGH | Уровень DO при звуке; если плата инвертирована, задай LOW |
| QUIET_GAP_MS | 120 мс | Тишина, нужная перед тем, как засчитать новый хлопок; пачка импульсов и эхо от хлопка считаются одним хлопком |
| CLAP_WINDOW_MS | 700 мс | Если после последнего звука прошло столько тишины, серия считается законченной и хлопки оцениваются |
| LOCKOUT_MS | 1000 мс | После переключения лампы звуки игнорируются, чтобы щелчок реле не приняли за новый хлопок |
| RELAY_ON / RELAY_OFF | LOW / HIGH | Для модуля реле с активным LOW; на плате с активным HIGH поменяй их местами |
В скетче нет delay(); всё отмеряется через millis(), поэтому моменты хлопков никогда не теряются. Этот подход подробно разобран в статье millis и delay. То, что лампа переключается через 0,7 секунды после второго хлопка, а не сразу, — осознанный выбор: скетч ждёт, не будет ли третьего хлопка. Если нужен более быстрый отклик, можно уменьшить CLAP_WINDOW_MS до 400–500 мс, но тогда хлопать придётся быстрее. Если в комнате сильное эхо и один хлопок засчитывается дважды, увеличь QUIET_GAP_MS до 150–200 мс.
Шкала уровня звука: VU-метр на 8 светодиодах
Если подавать значение размаха прямо на светодиоды, всё работает, но выглядит неестественно, потому что ухо воспринимает звук логарифмически: звук с вдвое большей амплитудой не кажется вдвое громче. Поэтому звук измеряют в децибелах (dB). Для амплитуды формула такая: dB = 20 × log10(p2p / опорное значение); удвоение амплитуды — это +6 dB, увеличение в десять раз — +20 dB. Скетч ниже при запуске одну секунду измеряет фоновый шум комнаты и берёт его за опорное значение; каждый светодиод соответствует 3 dB выше этого фона. Подключи светодиоды к пинам D3–D10, каждый через резистор 220 Ω; для светодиодов других цветов подходящий резистор подскажет калькулятор резистора для светодиода.
1// 8 LED'li ses seviyesi göstergesi (VU metre) / 8-LED sound level bar (VU meter)
2const int SOUND_AO = A0; // modülün AO pini / module AO pin
3const int LED_PINS[8] = {3, 4, 5, 6, 7, 8, 9, 10}; // her LED 220 Ω ile / each LED with 220 Ω
4const float DB_PER_LED = 3.0; // her LED 3 dB / each LED is 3 dB
5const unsigned long FALL_MS = 80; // LED'ler bu aralıkla birer birer söner / LEDs drop one by one at this rate
6
7float noiseFloor = 1.0; // sessiz ortamın p2p değeri / quiet-room p2p
8int shown = 0; // yanan LED sayısı / number of lit LEDs
9unsigned long lastFall = 0;
10
11int readPeakToPeak() {
12 int minV = 1023, maxV = 0;
13 unsigned long start = millis();
14 while (millis() - start < 50) { // 50 ms pencere / 50 ms window
15 int v = analogRead(SOUND_AO);
16 if (v < minV) minV = v;
17 if (v > maxV) maxV = v;
18 }
19 return maxV - minV;
20}
21
22void setup() {
23 Serial.begin(115200);
24 for (int i = 0; i < 8; i++) pinMode(LED_PINS[i], OUTPUT);
25 long sum = 0;
26 for (int i = 0; i < 20; i++) sum += readPeakToPeak(); // 1 sn sessizlik ölç / measure 1 s of silence
27 noiseFloor = sum / 20.0;
28 if (noiseFloor < 1.0) noiseFloor = 1.0;
29}
30
31void loop() {
32 int p2p = readPeakToPeak();
33 if (p2p < 1) p2p = 1;
34 float db = 20.0 * log10(p2p / noiseFloor); // tabana göre göreceli dB / relative dB above the floor
35 int level = constrain((int)(db / DB_PER_LED), 0, 8);
36
37 unsigned long now = millis();
38 if (level >= shown) { // yükselişte anında / rise instantly
39 shown = level;
40 lastFall = now;
41 } else if (now - lastFall >= FALL_MS) { // inişte yavaşça / fall slowly
42 shown--;
43 lastFall = now;
44 }
45 for (int i = 0; i < 8; i++) digitalWrite(LED_PINS[i], i < shown ? HIGH : LOW);
46
47 Serial.print(F("dB:"));
48 Serial.println(db, 1);
49}При запуске секунду не шуми; если в это время говорить, фон измерится слишком высоким и шкала будет реагировать слабее. Шкала поднимается мгновенно, а опускается медленно, по одному светодиоду каждые 80 мс; именно поэтому настоящие VU-метры выглядят плавно, а не дёргано. Аналоговый сигнал KY-038 слабый, поэтому ему подходят 2–3 dB на светодиод; если использовать этот же скетч с усиленным микрофоном вроде MAX4466, DB_PER_LED можно поднять до 4–6 dB. Для цветной индикации вместо светодиодов подойдёт кольцо NeoPixel; подробности в статье Светодиоды WS2812B NeoPixel.
Датчик звука с ESP32: определение хлопков через прерывание
АЦП у ESP32 12-битный и выдаёт значения от 0 до 4095; на диапазоне 3,3 V один шаг — около 0,8 mV, примерно в 6 раз точнее, чем у Uno. С другой стороны, АЦП ESP32 шумнее, поэтому в тихой комнате p2p тоже получается выше; относительный расчёт в dB сам это уравновешивает. В скетче ниже пин DO подключён к прерыванию: импульсы хлопков не теряются, даже пока loop() 50 мс занят измерением уровня звука на AO. Функция прерывания размещена в ОЗУ через IRAM_ATTR и сделана короткой, а переменные, которые используют и прерывание, и loop(), объявлены как volatile и защищены критической секцией.
1// ESP32: kesmeyle çift alkış + ses seviyesi / ESP32: double clap via interrupt + sound level
2const int SOUND_AO = 34; // ADC1 pini, Wi-Fi açıkken de çalışır / ADC1 pin, works with Wi-Fi on
3const int SOUND_DO = 27; // modülün DO pini / module DO pin
4const int RELAY = 26; // röle modülü IN / relay module IN
5const int SOUND_ACTIVE = HIGH; // kartın tersse LOW / LOW if your board is inverted
6const int RELAY_ON = LOW; // aktif LOW röle / active-LOW relay
7const int RELAY_OFF = HIGH;
8const unsigned long QUIET_GAP_MS = 120; // yeni alkış için sessizlik / silence before a new clap
9const unsigned long CLAP_WINDOW_MS = 700; // dizinin bitmesi / end of the series
10const unsigned long LOCKOUT_MS = 1000; // röle tıkırtısını yok say / ignore the relay click
11
12portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; // kesme ile loop arasında kilit / lock between ISR and loop
13volatile int clapCount = 0;
14volatile unsigned long lastPulse = 0;
15volatile unsigned long lastToggle = 0;
16bool lampOn = false;
17float noiseFloor = 1.0;
18
19void IRAM_ATTR onSound() { // kısa tutulur, RAM'den çalışır / kept short, runs from RAM
20 unsigned long now = millis();
21 portENTER_CRITICAL_ISR(&mux);
22 if (now - lastToggle >= LOCKOUT_MS && now - lastPulse >= QUIET_GAP_MS) clapCount++;
23 lastPulse = now;
24 portEXIT_CRITICAL_ISR(&mux);
25}
26
27int readPeakToPeak() {
28 int minV = 4095, maxV = 0;
29 unsigned long start = millis();
30 while (millis() - start < 50) { // 50 ms pencere / 50 ms window
31 int v = analogRead(SOUND_AO); // 0-4095 (12 bit)
32 if (v < minV) minV = v;
33 if (v > maxV) maxV = v;
34 }
35 return maxV - minV;
36}
37
38void setup() {
39 Serial.begin(115200);
40 pinMode(SOUND_DO, INPUT);
41 pinMode(RELAY, OUTPUT);
42 digitalWrite(RELAY, RELAY_OFF);
43 long sum = 0;
44 for (int i = 0; i < 20; i++) sum += readPeakToPeak(); // 1 sn sessiz ortam / 1 s of quiet room
45 noiseFloor = sum / 20.0;
46 if (noiseFloor < 1.0) noiseFloor = 1.0;
47 attachInterrupt(digitalPinToInterrupt(SOUND_DO), onSound, SOUND_ACTIVE == HIGH ? RISING : FALLING);
48}
49
50void loop() {
51 int p2p = readPeakToPeak(); // bu sırada alkışları kesme yakalar / the interrupt catches claps meanwhile
52 float db = 20.0 * log10((p2p < 1 ? 1 : p2p) / noiseFloor);
53 Serial.printf("p2p:%d dB:%.1f\n", p2p, db);
54
55 portENTER_CRITICAL(&mux);
56 unsigned long now = millis();
57 int claps = clapCount;
58 bool finished = claps > 0 && now - lastPulse > CLAP_WINDOW_MS;
59 if (finished) clapCount = 0; // dizi bitti, sayacı sıfırla / series over, reset the counter
60 portEXIT_CRITICAL(&mux);
61
62 if (finished && claps == 2) { // yalnız tam iki alkış / exactly two claps only
63 lampOn = !lampOn;
64 digitalWrite(RELAY, lampOn ? RELAY_ON : RELAY_OFF);
65 portENTER_CRITICAL(&mux);
66 lastToggle = millis();
67 portEXIT_CRITICAL(&mux);
68 Serial.println(lampOn ? "Lamba ACIK / Lamp ON" : "Lamba KAPALI / Lamp OFF");
69 }
70}Скетч работает по той же логике двойного хлопка, что и на Uno, только внутри прерывания, и примерно каждые 50 мс выводит уровень звука в dB. Подключи пин IN модуля реле к GPIO 26; некоторые модули реле с активным LOW и питанием 5 V не отпускают полностью при уровне HIGH 3,3 V от ESP32, поэтому в таком случае выбирай модуль, которым можно управлять от 3,3 V. Настоящая сила ESP32 — это Wi-Fi: если уровень звука несколько секунд держится выше порога (например, плачет ребёнок или лает собака), можно отправить уведомление на телефон. Для этого используй способ из статьи Telegram-бот на ESP32, а для подключения к системе умного дома — ESP32, MQTT и Home Assistant.
Датчик звука с Raspberry Pi Pico и MicroPython
На Raspberry Pi Pico питай модуль от пина 3V3(OUT), подключи AO к GP26, а DO к GP15; вместо лампы поставь на GP14 светодиод с резистором 220 Ω. АЦП у Pico 12-битный, но в MicroPython read_u16() масштабирует значение до 0–65535; поэтому числа p2p выглядят намного больше, чем на Uno, а относительный расчёт в dB остаётся тем же. Прерывание на пине DO настраивается через Pin.irq().
1# Raspberry Pi Pico: çift alkış + ses seviyesi / double clap + sound level
2from machine import Pin, ADC
3import time, math
4
5sound_ao = ADC(26) # GP26 = ADC0, modülün AO pini / module AO pin
6sound_do = Pin(15, Pin.IN) # modülün DO pini / module DO pin
7lamp = Pin(14, Pin.OUT, value=0) # LED (220 Ω ile) / LED (with 220 Ω)
8
9QUIET_GAP_MS = 120 # yeni alkış için sessizlik / silence before a new clap
10CLAP_WINDOW_MS = 700 # dizinin bitmesi / end of the series
11LOCKOUT_MS = 1000 # değişimden sonra sesleri yok say / ignore sounds after a toggle
12
13claps = 0
14last_pulse = time.ticks_ms()
15last_toggle = time.ticks_ms()
16
17def on_sound(pin): # kesme fonksiyonu / interrupt handler
18 global claps, last_pulse
19 now = time.ticks_ms()
20 if (time.ticks_diff(now, last_toggle) >= LOCKOUT_MS
21 and time.ticks_diff(now, last_pulse) >= QUIET_GAP_MS):
22 claps += 1
23 last_pulse = now
24
25# DO sesle LOW oluyorsa IRQ_FALLING yaz / use IRQ_FALLING if DO goes LOW on sound
26sound_do.irq(trigger=Pin.IRQ_RISING, handler=on_sound)
27
28def peak_to_peak(window_ms=50): # 50 ms boyunca en büyük - en küçük / max - min over 50 ms
29 lo, hi = 65535, 0
30 start = time.ticks_ms()
31 while time.ticks_diff(time.ticks_ms(), start) < window_ms:
32 v = sound_ao.read_u16() # 0-65535
33 if v < lo:
34 lo = v
35 if v > hi:
36 hi = v
37 return hi - lo
38
39noise_floor = max(1, sum(peak_to_peak() for _ in range(20)) / 20) # 1 sn sessizlik / 1 s of silence
40
41while True:
42 p2p = peak_to_peak()
43 db = 20 * math.log10(max(p2p, 1) / noise_floor) # göreceli dB / relative dB
44 print("p2p:", p2p, "dB:", round(db, 1))
45
46 now = time.ticks_ms()
47 if claps and time.ticks_diff(now, last_pulse) > CLAP_WINDOW_MS:
48 if claps == 2: # yalnız tam iki alkış / exactly two claps only
49 lamp.toggle()
50 last_toggle = now
51 print("Lamba / Lamp:", "ON" if lamp.value() else "OFF")
52 claps = 0Когда запустишь код в Thonny, значения p2p и dB появятся в окне Shell; чтобы следить за ними на графике, открой плоттер в меню «Вид» (View). Если выход DO на твоей плате при звуке уходит в LOW, напиши IRQ_FALLING вместо IRQ_RISING. Чтобы программа запускалась сама при подаче питания на Pico, сохрани файл на Pico под именем main.py. Если MicroPython для тебя в новинку, хорошая отправная точка — Основы MicroPython.
Может ли датчик звука измерять децибелы?
Коротко: нет. В интернете полно скетчей, которые через map() превращают размах сигнала KY-038 в диапазон вроде 49–90 dB; это догадки, а не измерения. Чтобы измерить настоящий уровень звукового давления (dB SPL), нужны микрофон с известной чувствительностью и система, откалиброванная калибратором на 94 dB, линейное усиление сигнала, среднеквадратичное значение (RMS), вычисленное за заданное время (125 мс в режиме fast, 1 с в режиме slow), и фильтр частотной коррекции A (A-weighting), повторяющий чувствительность уха. Поэтому шумомеры выпускают по стандарту IEC 61672.
KY-038 не выполняет ни одного из этих условий: усиление платы неизвестно, чувствительность капсул микрофонов отличается от экземпляра к экземпляру на несколько dB, потенциометр и напряжение питания меняют уровень сигнала, а на Uno все обычные звуки втиснуты в несколько шагов АЦП. Самое честное, что можно сделать, — это относительное измерение: сказать «сейчас на 12 dB громче, чем в тихой комнате». Даже этого более чем достаточно для сигнализации шума, «светофора шума» в классе или VU-метра. Грубую калибровку можно сделать, сравнив показания с приложением на телефоне в одной точке, но при других уровнях звука и на другой плате такая калибровка теряет смысл.
Альтернативы: MAX4466, MAX9814 и INMP441
| Модуль | Выход | Усиление | Измерение децибел | Подходящие платы |
|---|---|---|---|---|
| KY-038 / KY-037 | AO (сырой) + DO (порог) | Нет | Нет | Все |
| MAX4466 | Аналоговый, с центром на VCC/2 | 25–125×, регулируемое | Только относительное | Все |
| MAX9814 | Аналоговый, с центром на 1,25 V, макс. 2 Vpp | 40 / 50 / 60 dB + автоматическая регулировка усиления (AGC) | Нет, AGC сжимает уровень | Все |
| INMP441 | Цифровой I2S, 24 бита | Не нужно | Приблизительно в dB SPL | ESP32, Pico (не Uno) |
| Готовый модуль уровня звука (например, DFRobot SEN0232) | Аналоговый, сразу в dBA | Откалиброван на заводе | Да, 30–130 dBA, ±1,5 dB | Все |
MAX4466 сочетает такой же электретный микрофон с операционным усилителем с регулируемым усилением. В тишине его выход находится на половине напряжения питания, а при обычной речи рядом даёт сигнал в сотни милливольт; иначе говоря, сигнал KY-038 в несколько шагов АЦП превращается в десятки и даже сотни шагов. Скетчи размаха и VU-метра из этой статьи работают с MAX4466 без изменений. Как рассчитывается усиление, смотри в калькуляторе усиления ОУ и в статье Что такое операционный усилитель?. Если питать модуль от более чистого пина 3.3V на Uno, шума будет меньше. MAX9814, напротив, с помощью автоматической регулировки усиления подтягивает до рабочего уровня даже далёкую речь; он отлично подходит для записи и обнаружения голоса, но, поскольку сближает тихую и громкую обстановку, для измерения уровня не годится.
INMP441 вообще не аналоговый, а цифровой MEMS-микрофон: он сам преобразует звук в 24-битные числа и передаёт их по шине I2S, поэтому шум на проводах в измерение не попадает. Его главное преимущество в том, что производитель указывает чувствительность с узким допуском: звук 94 dB SPL читается как −26 dBFS. Значит, если перевести измеренное среднеквадратичное значение (RMS) в dBFS и прибавить 120, получится приблизительное значение в dB SPL. Для I2S нужна ESP32 или Pico; у Arduino Uno нет аппаратного I2S. Любительские модули INMP441 дешёвые и легко находятся, а ICS-43434 и SPH0645 — похожие I2S-микрофоны. Если действительно нужно точное измерение шума, проще всего взять готовый модуль уровня звука с калиброванным выходом с коррекцией A; например, если умножить выходное напряжение DFRobot SEN0232 на 50, сразу получится значение в dBA.
Идеи проектов с датчиком звука
- Лампа на хлопок: добавь к скетчу двойного хлопка выше модуль реле, чтобы управлять настольной лампой или светодиодной лентой, а три хлопка назначь второму устройству.
- «Светофор шума» для класса: индикатор шума, который по относительному значению в dB зажигает зелёный, жёлтый и красный светодиоды; учителя его обожают.
- Радионяня и наблюдение за питомцем: если звук остаётся громким дольше 5 секунд, пусть ESP32 отправит сообщение в Telegram на телефон.
- Светильники, реагирующие на звук: кольцо или лента NeoPixel, которые пульсируют в ритм музыки.
- Датчик стука в дверь: устройство, которое зуммером или светодиодом сообщает, что кто-то стучит; чтобы ловить стук как вибрацию, а не как звук, хорошо подойдёт и датчик вибрации SW-420.
- Сценарии умного дома: объедини звук с датчиками движения и освещённости, чтобы выключать свет, когда в комнате никого нет; идеи ищи в статье Проект умного дома на Arduino.
Частые ошибки и как их исправить
| Симптом | Вероятная причина | Решение |
|---|---|---|
| Светодиод DO горит постоянно | Порог слишком чувствительный или есть постоянный шум (вентилятор, холодильник) | В тишине крути потенциометр, пока светодиод не погаснет, и отодвинь датчик от источника шума |
| От хлопка ничего не происходит | Порог слишком высокий или DO подключён не к тому пину | Сначала проверь, мигает ли светодиод на плате от хлопка, затем проверь провод и номер пина в коде |
| Счётчик растёт в тишине и останавливается при звуке | Выход DO на плате с активным LOW | Задай SOUND_ACTIVE значение LOW; на Pico используй IRQ_FALLING |
| analogRead держится около 500 и меняется от звука на 1–2 | AO не усилен, и берутся только одиночные отсчёты | Используй метод размаха, поднеси источник звука ближе или перейди на MAX4466 |
| AO всегда 0 или 1023 | Перепутаны AO и DO или постоянный уровень ушёл к краю | Проверь подключение и потенциометром выведи среднее к середине |
| Один хлопок засчитывается дважды | В комнате эхо или QUIET_GAP_MS слишком мал | Задай QUIET_GAP_MS 150–200 мс и сделай порог чуть менее чувствительным |
| Лампа сама включается и выключается | Щелчок реле, хлопающие двери или музыка | Увеличь LOCKOUT_MS, отодвинь датчик от реле и оставь правило двойного хлопка |
| На ESP32 AO перестаёт читаться, когда включён Wi-Fi | AO подключён к пину ADC2 | Перенеси AO на пин ADC1 между GPIO 32 и 39 |
С парой проводов и хорошо написанным кодом KY-038 учит сразу многим основам электроники: компараторам, аналого-цифровому преобразованию, прерываниям, работе со временем и математике децибел. Начни с обнаружения хлопков через DO, измерь уровень звука методом размаха и переходи на MAX4466 или INMP441, когда понадобится настоящее измерение звука. В приложении BlueGrays есть проекты лампы на реле, бегущих огней, умного освещения на ESP32 и ленты NeoPixel со схемами и пошаговым кодом; добавить к ним датчик звука — самый простой способ превратить их в системы, которые реагируют на хлопки или звук.
Частые вопросы
Как работает датчик звука для Arduino?
Датчик звука с помощью компаратора LM393 сравнивает маленькое напряжение с электретного микрофона с порогом, заданным потенциометром. Когда звук превышает порог, пин DO меняет состояние и загорается светодиод на плате. Пин AO выдаёт сырой, неусиленный сигнал микрофона, который читается через analogRead().
Чем KY-038 отличается от KY-037?
Оба используют одну и ту же схему на LM393 и одинаковый порядок пинов AO, G, +, DO, код тоже одинаковый. У KY-037 капсула микрофона больше, и обычно он чуть чувствительнее; KY-038 меньше и чаще встречается в наборах датчиков.
Как настроить чувствительность датчика звука?
В тишине поверни синий потенциометр до точки, где светодиод DO начинает мерцать, затем поверни на пол-оборота или целый оборот обратно, в сторону, где светодиод не горит. Потенциометр многооборотный, поэтому до заметного изменения может понадобиться несколько оборотов. Настраивай всегда там, где датчик будет работать.
Может ли датчик звука измерять децибелы?
KY-038 и KY-037 не могут измерять настоящие децибелы (dB SPL): усиление платы и чувствительность микрофона неизвестны, нет ни калибровки, ни коррекции A. Можно рассчитать только относительные dB по сравнению с тихой комнатой. Для настоящих измерений используй I2S-микрофон с известной чувствительностью, например INMP441, или откалиброванный модуль уровня звука.
Почему аналоговое значение датчика звука не меняется?
Выход AO — это неусиленный сигнал микрофона, и при обычных звуках он меньше одного шага АЦП Uno в 4,9 mV. К тому же одиночный analogRead() читает случайный момент звуковой волны. Делай отсчёты в течение 50 мс и вычисляй разницу между наибольшим и наименьшим значениями (размах, peak-to-peak) — тогда изменение будет хорошо видно.
Работает ли датчик звука с ESP32 и Raspberry Pi Pico?
Да, модуль работает и от 3,3 V; на ESP32 и Pico питай его от пина 3V3, чтобы выход DO никогда не поднимался выше 3,3 V. На ESP32 подключай AO к пину ADC1 (GPIO 32–39), который не конфликтует с Wi-Fi, а на Pico — к GP26, GP27 или GP28.
Что собрать после прочтения
Все проекты
Включение и выключение светодиода кнопкой (вкл/выкл)
Нажми кнопку один раз — свет загорится, нажми ещё раз — погаснет, совсем как выключатель в твоей комнате. Arduino замечает каждое нажатие и переводит светодиод в противоположное состояние, выжидая кор

Управление светодиодом кнопкой на ESP32 (переключение)
При каждом нажатии кнопки состояние светодиода меняется: если горел — гаснет, если не горел — загорается. Классический шаблон переключения. Антидребезг убирает механический шум кнопки, чтобы одно нажатие давало одно переключение.

Бегущие огни на светодиодах
Огни, которые загораются друг за другом, будто бегут. Arduino зажигает шесть светодиодов по одному; каждый светится короткое мгновение и гаснет, так что свет проходит от начала до конца и начинает сно

Включение лампы 220V кнопкой (вкл/выкл)
Хочешь включать и выключать настоящую бытовую лампу крошечной кнопкой? При каждом нажатии кнопки Arduino переключает реле, и лампа загорается; нажмёшь снова — гаснет. Реле — посредник, который позволя

Переключение светодиода кнопкой на Pico
Кнопка + светодиод — следующий уровень! В отличие от базового проекта с кнопкой, держать кнопку не нужно. Нажми и отпусти → светодиод горит. Нажми и отпусти ещё раз → гаснет (переключение). Схема та же (светодиод GP14, кнопка GP2), но код сильно отличается. Изучи определение фронта (ловлю момента спада 1→0), отслеживание состояния и антидребезг (фильтр механического дребезга). Метод led.toggle() в MicroPython меняет состояние одной строкой.

Умное освещение на ESP32 (PIR + LDR + реле + OLED)
Сделай умное освещение для коридора, ванной или кладовки на ESP32! С PIR-датчиком движения, датчиком освещённости LDR и модулем реле собирается классический сценарий «свет включается при движении в темноте»: если вокруг уже светло, движение свет НЕ включает, и днём энергия экономится. Свет горит 30 секунд; если движение продолжается, таймер сбрасывается, и свет горит дольше. OLED SSD1306 крупно показывает состояние (ON / DAYTIME / WAITING) и обратный отсчёт.

Парктроник на ESP32 (HC-SR04 + шкала из 5 светодиодов + зуммер + OLED)
ESP32-версия автомобильного парктроника! Ультразвуковой датчик HC-SR04 измеряет расстояние до предмета перед ним. По мере уменьшения расстояния шкала из 5 светодиодов заполняется (зелёный → жёлтый → красный), а интервал сигналов зуммера сокращается. Когда предмет совсем близко (меньше 10 см), звучит непрерывная тревога и горят все светодиоды. OLED SSD1306 показывает расстояние в см крупным шрифтом и сообщение состояния (STOP / CAUTION / CLOSE / STOP! / DANGER).

Управление лентой WS2812B NeoPixel на ESP32
Адресуемые светодиоды WS2812B (NeoPixel) — умные RGB-светодиоды, которыми управляют цепочкой через один пин данных. В этом проекте лента WS2812B из 8 светодиодов управляется библиотекой Adafruit_NeoPixel и показывает плавную радужную анимацию. Логика 3,3V у ESP32 работает с большинством WS2812B, но на случай нестабильности показано решение с последовательным резистором 470Ω и конденсатором 1000µF. Анимация получается сдвигом значения оттенка.
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

