Компоненты · 14 мин чтения

Датчик звука для 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 и посмотрим, на какие модули переходить, когда нужно настоящее измерение звука.

Модуль датчика звука KY-038 (электретный микрофон и LM393)
Датчик звука KY-038: справа электретный микрофон, в середине компаратор LM393 и синий потенциометр чувствительности, слева пины AO, G, + и DO.

Что такое датчик звука и как он работает?

Серебристый цилиндр на краю платы — это электретный конденсаторный микрофон. Внутри тонкая мембрана расположена напротив пластины с постоянным электрическим зарядом, и вместе они образуют крошечный конденсатор. Звук — это волна давления в воздухе; когда она толкает мембрану вперёд и назад, ёмкость меняется и появляется очень маленькое изменение напряжения. Крошечный полевой транзистор (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-038AO, G, +, DOМаленькая капсулаСамая распространённая модель в наборах датчиков
KY-037AO, G, +, DOБольшая капсулаТа же схема и тот же код; обычно чуть чувствительнее
3-пиновый звуковой модуль на LM393VCC, GND, OUTМаленькая капсулаТолько цифровой выход; OUT при звуке обычно уходит в LOW

Подключение датчика звука: Arduino Uno, ESP32 и Pico

Пин модуляArduino UnoESP32Raspberry Pi Pico
+5V3V33V3(OUT), пин 36
GGNDGNDGND, пин 38
AOA0GPIO 34 (ADC1)GP26 (ADC0), пин 31
DOD2GPIO 27GP15, пин 20
Пины ESP32 и Pico рассчитаны на 3,3 V. На этих платах питай датчик звука от пина 3V3, а не от 5V, VIN или VBUS; иначе выход DO поднимется до 5 V и может повредить входной пин. На ESP32 подключай AO к одному из пинов ADC1 (GPIO 32–39): пины ADC2 нельзя читать через analogRead(), пока включён Wi-Fi.

Цифровой выход (DO) и настройка потенциометра

Пин DO даёт ответ «да/нет». На многих платах KY-038 DO переходит в HIGH и загорается второй светодиод, когда звук превышает порог; на некоторых клонах и на 3-пиновых модулях всё наоборот. Поэтому в скетчах уровень, который означает «есть звук», хранится в константе SOUND_ACTIVE. Второй важный момент: DO не выдаёт один длинный импульс на хлопок. Звуковая волна сотни раз в секунду поднимается выше порога и опускается ниже, поэтому выход включается и выключается так же быстро; один хлопок даёт пачку коротких импульсов длительностью несколько десятков миллисекунд. У компаратора на большинстве плат нет гистерезиса, поэтому при звуках около порога выход ещё и дребезжит. Так что читай DO быстрым циклом без delay() или через прерывание и считай близко идущие импульсы одним событием.

  1. Подключи модуль и поставь датчик туда, где он будет работать, в момент с обычным фоновым шумом; настраивай всегда в реальной обстановке.
  2. Посмотри на светодиод DO на плате. Если он горит в тишине, порог слишком чувствительный; крути потенциометр маленькой отвёрткой, пока светодиод не погаснет.
  3. Если светодиод вообще не загорается, крути в другую сторону и найди точку, где он начинает загораться.
  4. От этой точки поверни на пол-оборота или на целый оборот обратно, в сторону, где светодиод не горит. Тогда постоянные шумы вроде холодильника или вентилятора не будут вызывать срабатывание.
  5. Хлопни примерно с метра: светодиод должен коротко мигнуть. Если он загорается и от обычной речи, слегка поверни в сторону меньшей чувствительности; если не загорается от хлопка, слегка поверни в сторону большей.
Синий потенциометр на этих платах обычно многооборотный подстроечный резистор типа 3296: от одного края до другого нужно около 25 оборотов. Если после нескольких оборотов светодиод никак не меняется, потенциометр не сломан; просто нужно крутить дальше. В конце хода винт начинает проскальзывать, так что прилагать силу не нужно.
C++ (Arduino IDE)
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 mV0
Обычная речь (1 м)60 dB SPL~0,4 mV0 (не видно)
Громкий телевизор, улица80 dB SPL~3,6 mV0–1
Очень громкий звук (1 Па)94 dB SPL~18 mV3–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 отсчётов.

C++ (Arduino IDE)
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 секунды. Одиночный звук или три и больше звуков подряд (музыка, аплодисменты, лай собаки) игнорируются.

  1. Подключи пин + модуля KY-038 к 5V, G к GND, а DO к D2.
  2. Подключи пин VCC модуля реле к 5V, GND к GND, а IN к D7. Как работает реле и как подключить сторону нагрузки, рассказано в статье Что такое модуль реле?; первые тесты делай со светодиодом самого реле, а не с лампой.
  3. Загрузи скетч и открой монитор порта на скорости 9600 бод.
  4. Хлопни дважды с расстояния в метр-два. На каждый хлопок должна появляться только одна строка «Alkis / Clap»; если один хлопок засчитывается несколько раз, слегка поверни потенциометр в сторону меньшей чувствительности.
  5. Лампа должна переключиться примерно через 0,7 секунды после второго хлопка.
C++ (Arduino IDE)
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_ACTIVEHIGHУровень DO при звуке; если плата инвертирована, задай LOW
QUIET_GAP_MS120 мсТишина, нужная перед тем, как засчитать новый хлопок; пачка импульсов и эхо от хлопка считаются одним хлопком
CLAP_WINDOW_MS700 мсЕсли после последнего звука прошло столько тишины, серия считается законченной и хлопки оцениваются
LOCKOUT_MS1000 мсПосле переключения лампы звуки игнорируются, чтобы щелчок реле не приняли за новый хлопок
RELAY_ON / RELAY_OFFLOW / HIGHДля модуля реле с активным LOW; на плате с активным HIGH поменяй их местами

В скетче нет delay(); всё отмеряется через millis(), поэтому моменты хлопков никогда не теряются. Этот подход подробно разобран в статье millis и delay. То, что лампа переключается через 0,7 секунды после второго хлопка, а не сразу, — осознанный выбор: скетч ждёт, не будет ли третьего хлопка. Если нужен более быстрый отклик, можно уменьшить CLAP_WINDOW_MS до 400–500 мс, но тогда хлопать придётся быстрее. Если в комнате сильное эхо и один хлопок засчитывается дважды, увеличь QUIET_GAP_MS до 150–200 мс.

Коммутация сетевого напряжения (110–230 V) через реле может убить. Делай подключения при вынутой из розетки вилке, не оставляй оголённых концов проводов и помещай схему в изолированный закрытый корпус; если есть сомнения, сначала попробуй светодиодную ленту на 12 V. Кроме того, держи датчик подальше от реле: если щелчок реле раздаётся совсем рядом с микрофоном, он может мешать даже несмотря на время блокировки.

Шкала уровня звука: VU-метр на 8 светодиодах

Если подавать значение размаха прямо на светодиоды, всё работает, но выглядит неестественно, потому что ухо воспринимает звук логарифмически: звук с вдвое большей амплитудой не кажется вдвое громче. Поэтому звук измеряют в децибелах (dB). Для амплитуды формула такая: dB = 20 × log10(p2p / опорное значение); удвоение амплитуды — это +6 dB, увеличение в десять раз — +20 dB. Скетч ниже при запуске одну секунду измеряет фоновый шум комнаты и берёт его за опорное значение; каждый светодиод соответствует 3 dB выше этого фона. Подключи светодиоды к пинам D3–D10, каждый через резистор 220 Ω; для светодиодов других цветов подходящий резистор подскажет калькулятор резистора для светодиода.

C++ (Arduino IDE)
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 и защищены критической секцией.

C++ (Arduino IDE)
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().

MicroPython
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-037AO (сырой) + DO (порог)НетНетВсе
MAX4466Аналоговый, с центром на VCC/225–125×, регулируемоеТолько относительноеВсе
MAX9814Аналоговый, с центром на 1,25 V, макс. 2 Vpp40 / 50 / 60 dB + автоматическая регулировка усиления (AGC)Нет, AGC сжимает уровеньВсе
INMP441Цифровой I2S, 24 битаНе нужноПриблизительно в dB SPLESP32, 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–2AO не усилен, и берутся только одиночные отсчётыИспользуй метод размаха, поднеси источник звука ближе или перейди на MAX4466
AO всегда 0 или 1023Перепутаны AO и DO или постоянный уровень ушёл к краюПроверь подключение и потенциометром выведи среднее к середине
Один хлопок засчитывается дваждыВ комнате эхо или QUIET_GAP_MS слишком малЗадай QUIET_GAP_MS 150–200 мс и сделай порог чуть менее чувствительным
Лампа сама включается и выключаетсяЩелчок реле, хлопающие двери или музыкаУвеличь LOCKOUT_MS, отодвинь датчик от реле и оставь правило двойного хлопка
На ESP32 AO перестаёт читаться, когда включён Wi-FiAO подключён к пину ADC2Перенеси AO на пин ADC1 между GPIO 32 и 39
Удобный способ проверить датчик звука — приложение-генератор тона на телефоне. Включи ровный тон 1 kHz и держи телефон в 10, 20 и 40 см от микрофона: в открытом пространстве при каждом удвоении расстояния звуковое давление падает вдвое, поэтому p2p должен уменьшиться примерно наполовину, а значение в dB — примерно на 6 dB. Этот простой опыт проверяет и подключение, и код.

С парой проводов и хорошо написанным кодом 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.

Все проекты
Включение и выключение светодиода кнопкой (вкл/выкл)
#10Новичок

Включение и выключение светодиода кнопкой (вкл/выкл)

Нажми кнопку один раз — свет загорится, нажми ещё раз — погаснет, совсем как выключатель в твоей комнате. Arduino замечает каждое нажатие и переводит светодиод в противоположное состояние, выжидая кор

4.8(100)
167
Управление светодиодом кнопкой на ESP32 (переключение)
#105Новичок

Управление светодиодом кнопкой на ESP32 (переключение)

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

4.6(39)
57
Бегущие огни на светодиодах
#5Средний

Бегущие огни на светодиодах

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

4.6(85)
207
Включение лампы 220V кнопкой (вкл/выкл)
#20Продвинутый
Premium

Включение лампы 220V кнопкой (вкл/выкл)

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

4.5(33)
44
Переключение светодиода кнопкой на Pico
#156Новичок

Переключение светодиода кнопкой на Pico

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

0.0(0)
9
Умное освещение на ESP32 (PIR + LDR + реле + OLED)
#141Продвинутый
Premium

Умное освещение на ESP32 (PIR + LDR + реле + OLED)

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

4.3(6)
11
Парктроник на ESP32 (HC-SR04 + шкала из 5 светодиодов + зуммер + OLED)
#142Продвинутый
Premium

Парктроник на ESP32 (HC-SR04 + шкала из 5 светодиодов + зуммер + OLED)

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

0.0(0)
14
Управление лентой WS2812B NeoPixel на ESP32
#126Средний
Premium

Управление лентой WS2812B NeoPixel на ESP32

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

4.9(19)
30