Введение
Пьезоэлектрический зуммер (2 штуки в комплекте) — простой способ добавить звуковую обратную связь устройству: сигнал тревоги, подтверждение действия, простая мелодия. В статье разберём управление зуммером через tone()/noTone() и воспроизведение простых мелодий, основанных на нотах.
Концепция
Активный зуммер просто включается/выключается цифровым сигналом и звучит на собственной фиксированной частоте; пассивный зуммер (более вероятный для данного типа набора, дающий больше возможностей) управляется через встроенную функцию Arduino tone(pin, frequency, duration), генерирующую прямоугольный сигнал заданной частоты — изменяя частоту, можно воспроизводить разные музыкальные ноты, а последовательность нот с заданными длительностями составляет простую мелодию.
Пример кода
// firmware/src/main.cpp — зуммер: сигналы и простая мелодия
#define BUZZER_PIN 5
// Частоты нот в герцах (упрощённая таблица для одной октавы)
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println("READY");
}
void loop()
{
static String inputBuffer = "";
while (Serial.available() > 0) {
char c = Serial.read();
if (c == 'n') {
processCommand(inputBuffer);
inputBuffer = "";
} else if (inputBuffer.length() < 64) {
inputBuffer += c;
}
}
}
void processCommand(String command)
{
command.trim();
if (command == "BUZZER:BEEP") {
tone(BUZZER_PIN, 1000, 200); // короткий сигнал подтверждения, неблокирующий благодаря duration
Serial.println("ACK:BUZZER:BEEP");
} else if (command == "BUZZER:ALARM") {
playAlarmPattern();
Serial.println("ACK:BUZZER:ALARM");
} else if (command == "BUZZER:MELODY") {
playSimpleMelody();
Serial.println("ACK:BUZZER:MELODY");
}
}
void playAlarmPattern()
{
// Несколько коротких, резких сигналов с паузами — типичный паттерн "тревоги"
for (int i = 0; i < 3; i++) {
tone(BUZZER_PIN, 2000, 100);
delay(150);
}
}
void playSimpleMelody()
{
const int melodyNotes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4};
const int noteDurations[] = {300, 300, 300, 300, 500};
for (int i = 0; i < 5; i++) {
tone(BUZZER_PIN, melodyNotes[i], noteDurations[i]);
delay(noteDurations[i] + 50); // небольшая пауза между нотами для разделения звучания
}
}
Пояснения к коду
tone(BUZZER_PIN, 1000, 200) с указанным третьим параметром (длительность в миллисекундах) — неблокирующий вызов: сама функция возвращает управление немедленно, а звук продолжает играть в фоне ещё 200 мс, генерируемый аппаратным таймером Arduino, что отличается от наивного предположения, что tone() «ждёт» окончания звука. playAlarmPattern()/playSimpleMelody(), однако, сами по себе блокирующие на уровне всей функции, поскольку используют delay() между отдельными нотами — для последовательности из нескольких нот это пока не критично (мелодия короткая), но при необходимости проигрывать мелодию параллельно с обработкой других задач нужна более сложная, неблокирующая реализация на основе millis(), аналогично остальным паттернам цикла.
Таблица частот нот (NOTE_C4 и аналоги) — стандартный для Arduino-сообщества способ задания музыкальных частот через понятные именованные константы вместо «магических чисел» герц непосредственно в коде, что делает код мелодии значительно более читаемым (сразу видно, что воспроизводится последовательность нот до-ре-ми-фа-соль, а не произвольный набор чисел).
Подводные камни
tone()использует один из встроенных аппаратных таймеров Arduino, который может конфликтовать с другими функциями, также используемыми тем же таймером (в частности,analogWrite()на некоторых конкретных пинах, зависящих от того же таймера, что и используемый для генерации тона) — при объединении зуммера с ШИМ-управлением других модулей (RGB-светодиод, моторы) в одном сложном проекте стоит явно проверить документацию по используемым таймерам конкретной платы, чтобы избежать неожиданных взаимных помех между, казалось бы, не связанными друг с другом частями схемы.- Блокирующие
delay()внутриplayAlarmPattern()/playSimpleMelody()останавливают обработку входящих команд на время всего звукового паттерна — для коротких сигналов (как в примере) это обычно некритично, но для длинных мелодий или частого воспроизведения сигналов стоит реализовать неблокирующую версию воспроизведения, аналогично остальным неблокирующим паттернам прошивки в этом цикле. - Одновременный вызов
tone()для двух разных зуммеров на разных пинах одновременно может не работать ожидаемым образом, поскольку большинство реализацийtone()на Arduino способны генерировать только один тон одновременно через используемый аппаратный таймер — для проектов, предполагающих действительно одновременное звучание двух независимых зуммеров (а не просто использование запасного второго зуммера на случай выхода из строя первого), может потребоваться более специализированная библиотека, поддерживающая несколько одновременных тонов. - Субъективное восприятие громкости и тона пьезозуммера сильно зависит от конкретной частоты — некоторые частоты звучат значительно громче или приятнее других на конкретном физическом зуммере из-за резонансных характеристик самого пьезоэлемента; при проектировании сигналов оповещения стоит эмпирически подобрать частоты, которые действительно хорошо слышны и не вызывают неприятных ощущений на конкретном используемом оборудовании, а не полагаться на теоретически «стандартные» значения нот без проверки на слух.