Мелодия на зуммере: функция tone, ноты и длительности
Зуммер умеет не только пищать. Если он пассивный, частоту звука задаёт плата — а значит, из него можно извлечь любую ноту и сыграть мелодию. Всё, что для этого нужно, уже встроено в Arduino: функции tone и noTone, никаких библиотек.
Сразу важная оговорка: на активном зуммере мелодия не получится никогда. У него внутри собственный генератор с одной фиксированной частотой, и tone на неё не влияет — он будет пищать одинаково на любой ноте. Как отличить один от другого, написано на странице про подключение зуммера.
Как работает tone
Функция принимает номер вывода и частоту в герцах: tone(8, 440) — это нота ля. Плата начинает дёргать вывод с нужной частотой в фоне и сразу возвращает управление программе: звук идёт сам по себе, пока его не остановят.
Остановить можно двумя способами: вызвать noTone(8) или заранее указать третьим аргументом длительность — tone(8, 440, 500) отыграет полсекунды и замолчит сам.
const int PIN_BUZZER = 8;
void setup() {
pinMode(PIN_BUZZER, OUTPUT);
}
void loop() {
tone(PIN_BUZZER, 440); // ля первой октавы
delay(500);
noTone(PIN_BUZZER); // тишина
delay(500);
}Частоты нот
Ноты — это просто частоты. Вот первая октава, с неё начинают почти все мелодии; каждая следующая октава — умножение частоты на два.
| нота | обозначение | частота, Гц |
|---|---|---|
| до | C4 | 262 |
| ре | D4 | 294 |
| ми | E4 | 330 |
| фа | F4 | 349 |
| соль | G4 | 392 |
| ля | A4 | 440 |
| си | B4 | 494 |
| до второй октавы | C5 | 523 |
В чужих примерах эти же числа часто лежат в отдельном файле pitches.h с именами вроде NOTE_C4. Это то же самое: константы с частотами, ничего волшебного в файле нет.
Мелодия из массива нот
Мелодия — два массива одинаковой длины: в первом ноты, во втором длительности. Дальше цикл проходит по ним и отдаёт каждую ноту в tone.
const int PIN_BUZZER = 8;
// ноты первой октавы, частоты в герцах
const int DO = 262, RE = 294, MI = 330, FA = 349, SOL = 392;
// сама мелодия и длительность каждой ноты в миллисекундах
int melody[] = { MI, RE, DO, RE, MI, MI, MI };
int durations[] = { 300, 300, 300, 300, 300, 300, 600 };
void setup() {
pinMode(PIN_BUZZER, OUTPUT);
}
void loop() {
for (int i = 0; i < 7; i++) {
tone(PIN_BUZZER, melody[i], durations[i]);
// пауза чуть длиннее самой ноты, иначе ноты сольются:
// tone не задерживает программу, и следующая начнётся поверх предыдущей
delay(durations[i] * 1.3);
}
noTone(PIN_BUZZER);
delay(2000);
}Скетч собирается arduino-cli под Arduino UNO. Коэффициент 1,3 в паузе — не магия: это зазор между нотами, без него мелодия звучит слитно.
Почему ноты сливаются в один непрерывный писк
tone не задерживает программу: вызвали — звук пошёл, а выполнение сразу идёт дальше. Если в цикле нет паузы или она короче самой ноты, следующая нота начинается поверх предыдущей, и мелодия превращается в один писк. Пауза должна быть чуть длиннее длительности ноты.Пауза внутри мелодии
Ноль в массиве удобно держать как метку паузы, но передавать его в toneнельзя — нулевой частоты не бывает. Ноль обрабатывают сами: видим его — вместо ноты вызываем noTone и просто выжидаем нужное время.
// пауза внутри мелодии — нота с частотой 0 не годится,
// нужен просто delay без tone
int melody[] = { MI, RE, 0, DO, RE };
int durations[] = { 300, 300, 200, 300, 300 };
for (int i = 0; i < 5; i++) {
if (melody[i] == 0) {
noTone(PIN_BUZZER); // пауза
} else {
tone(PIN_BUZZER, melody[i], durations[i]);
}
delay(durations[i] * 1.3);
}После tone перестал работать analogWrite на пинах 3 и 11
Сирена: плавно меняем частоту
Мелодия — не единственное, что умеет пассивный зуммер. Если менять частоту не ступенями, а в цикле с маленьким шагом, получается сирена: звук плавно ползёт вверх и обратно вниз. Это самый заметный сигнал тревоги, который можно сделать без единой дополнительной детали.
const int PIN_BUZZER = 8;
void setup() {
pinMode(PIN_BUZZER, OUTPUT);
}
void loop() {
// вверх: от 400 до 1200 Гц шагом по 10
for (int f = 400; f <= 1200; f += 10) {
tone(PIN_BUZZER, f);
delay(5);
}
// и обратно вниз
for (int f = 1200; f >= 400; f -= 10) {
tone(PIN_BUZZER, f);
delay(5);
}
}Шаг и задержка задают характер: шаг побольше и задержка поменьше — резкая «полицейская» сирена, мелкий шаг и delay(15) — тягучий вой.
Высота звука с потенциометра
Ещё один способ понять, что tone принимает любое число: скормить ему показания потенциометра. Получается инструмент вроде терменвокса — крутите ручку, и звук едет за ней.
const int PIN_BUZZER = 8;
const int PIN_POT = A0;
void setup() {
pinMode(PIN_BUZZER, OUTPUT);
}
void loop() {
int raw = analogRead(PIN_POT); // 0…1023 с потенциометра
int freq = map(raw, 0, 1023, 200, 2000); // в частоту 200…2000 Гц
tone(PIN_BUZZER, freq);
delay(20);
}Функция map переводит диапазон потенциометра (от нуля до 1023) в диапазон частот. Подробнее про сам потенциометр — на его странице.
Файл pitches.h: откуда его берут
В чужих примерах ноты почти всегда подключают из файла pitches.h, а сам файл при этом никто не показывает — и новичок застревает на ошибке No such file or directory. Скачивать его неоткуда: это просто список констант, который каждый носит с собой. Вот он — создайте в скетче вкладку с таким именем и вставьте.
// первая октава
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
// вторая октава — те же ноты, частоты вдвое выше
#define NOTE_C5 523
#define NOTE_D5 587
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_G5 784
#define NOTE_A5 880
#define NOTE_B5 988После этого в мелодии можно писать NOTE_E4 вместо числа 330 — читается гораздо понятнее. Диез обозначается буквой S: NOTE_CS4 — это до-диез.
Чего tone не умеет
Одновременно звучит только одна нота: второй вызов tone на другом выводе просто перебьёт первый. Аккорд или двухголосие встроенными средствами не сыграть — для этого берут отдельные библиотеки, которые формируют сигнал иначе.
И ещё: пьезоизлучатель — не динамик. Он честно воспроизводит частоту, но тембра у него нет, поэтому любая мелодия звучит как писк из старой игрушки. Это не ошибка схемы, а физика: для приличного звука нужен динамик с усилителем.
Что дальше
- Подключение зуммера: схема и скетчи
- Активный или пассивный: какой выбрать
- Программирование Arduino: массивы и циклы
Мелодия — частный случай работы с массивами: если цикл в примере выше кажется непонятным, начните со страницы про программирование.