[아두이노] Maker UNO 사용기 : 피에조 부저(piezo buzzer) 사용하기.
- 취미/장난감
- 2021. 2. 20.
이전 포스팅에서 Cytron 사의 코딩 교육용 아두이노 호환 보드인 Maker UNO를 살펴봤습니다.
내장된 푸쉬 버튼을 토글 버튼으로 사용할 수 있도록 코드를 작성했습니다.
이제 toogleStatus
변수를 가지고 음악을 재생할지, 정지할지 정할 수 있겠네요.
피에조 부저 (Piezo Buzzer)
방금 만든 토글 스위치가 활성화(SET) 되었을 때 음악을 재생할 함수 playBuzzer()
을 호출합니다.
피에조 부저는 tone()
함수를 사용하여 재생할 수 있습니다.
www.arduino.cc/reference/en/language/functions/advanced-io/tone/
tone()
함수에 사용되는 파라미터는 부저가 연결된 핀 번호인 pin
, 재생할 음을 나타내는 주파수인 frequency
, 음을 재생할 시간인 duration
이 있습니다.
Maker UNO의 피에조 부저는digital 8 pin을 통해 사용 가능합니다.
먼저 말씀 드리지만, 원하는 음악을 재생하는 것은 dog 노가다입니다.
분명히 말씀드렸습니다.
www.youtube.com/watch?v=0Fzd6vQHYm0
재생할 음악과 악보는 위의 youtube 영상을 참고했습니다.
가장 먼저 곡을 연주하는 속도인 bpm이 보이네요.
1분에 4분 음표가 190회 연주되는 속도입니다.
그런데 굳이 bpm까지 계산해서 완벽하게 재생해보고 싶진 않네요.
이미 토글 스위치를 제작하는 데에서 시간을 너무 뺏겼기 때문입니다.
먼저 아두이노에서 제공하는 부저 음을 따옵니다.
#define NOTE_Ti0 31
#define NOTE_Do1 33
#define NOTE_Di1 35
#define NOTE_Re1 37
#define NOTE_Ri1 39
#define NOTE_Mi1 41
#define NOTE_Fa1 44
#define NOTE_Fi1 46
#define NOTE_Sol1 49
#define NOTE_Si1 52
#define NOTE_La1 55
#define NOTE_Li1 58
#define NOTE_Ti1 62
#define NOTE_Do2 65
#define NOTE_Di2 69
#define NOTE_Re2 73
#define NOTE_Ri2 78
#define NOTE_Mi2 82
#define NOTE_Fa2 87
#define NOTE_Fi2 93
#define NOTE_Sol2 98
#define NOTE_Si2 104
#define NOTE_La2 110
#define NOTE_Li2 117
#define NOTE_Ti2 123
#define NOTE_Do3 131
#define NOTE_Di3 139
#define NOTE_Re3 147
#define NOTE_Ri3 156
#define NOTE_Mi3 165
#define NOTE_Fa3 175
#define NOTE_Fi3 185
#define NOTE_Sol3 196
#define NOTE_Si3 208
#define NOTE_La3 220
#define NOTE_Li3 233
#define NOTE_Ti3 247
#define NOTE_Do4 262
#define NOTE_Di4 277
#define NOTE_Re4 294
#define NOTE_Ri4 311
#define NOTE_Mi4 330
#define NOTE_Fa4 349
#define NOTE_Fi4 370
#define NOTE_Sol4 392
#define NOTE_Si4 415
#define NOTE_La4 440
#define NOTE_Li4 466
#define NOTE_Ti4 494
#define NOTE_Do5 523
#define NOTE_Di5 554
#define NOTE_Re5 587
#define NOTE_Ri5 622
#define NOTE_Mi5 659
#define NOTE_Fa5 698
#define NOTE_Fi5 740
#define NOTE_Sol5 784
#define NOTE_Si5 831
#define NOTE_La5 880
#define NOTE_Li5 932
#define NOTE_Ti5 988
#define NOTE_Do6 1047
#define NOTE_Di6 1109
#define NOTE_Re6 1175
#define NOTE_Ri6 1245
#define NOTE_Mi6 1319
#define NOTE_Fa6 1397
#define NOTE_Fi6 1480
#define NOTE_Sol6 1568
#define NOTE_Si6 1661
#define NOTE_La6 1760
#define NOTE_Li6 1865
#define NOTE_Ti6 1976
#define NOTE_Do7 2093
#define NOTE_Di7 2217
#define NOTE_Re7 2349
#define NOTE_Ri7 2489
#define NOTE_Mi7 2637
#define NOTE_Fa7 2794
#define NOTE_Fi7 2960
#define NOTE_Sol7 3136
#define NOTE_Si7 3322
#define NOTE_La7 3520
#define NOTE_Li7 3729
#define NOTE_Ti7 3951
#define NOTE_Do8 4186
#define NOTE_Di8 4435
#define NOTE_Re8 4699
#define NOTE_Ri8 4978
각 음계에 대해 주파수를 미리 정의해놓은 파일이 있습니다.
제가 악보를 초등학교 저학년때 피아노 학원 이후로 본 적이 없기 때문에 제 입맛에 맞게 좀 수정했습니다.
이걸 메인 코드가 아닌 헤더 파일로 만들어 참조할 것입니다.
아두이노 IDE 우측 상단에 아래 방향 화살표 아이콘을 클릭하여 새 탭을 추가합니다.
"pitches.h"라고 저장합니다.
메인 코드 맨 윗줄에 헤더 파일로 추가할 것입니다.
아래 코드는 완성 코드이며, 이전 포스팅에서 작성한 토글 스위치 코드를 포함하고 있습니다.
#include "pitches.h"
#define pushButton 2
#define piezoBuzzer 8
int currentNote = 0;
int quarter = 250; // 1/4 : 4분 음표
int quaver = quarter/2; // 1/8 : 8분 음표
int semiquav = quaver/2; // 1/16 : 16분 음표
int REST = 0; // 쉼표는 음을 재생하지 않습니다.
int noteFreq[] = {
// (intro)
NOTE_Do4, NOTE_Do6, NOTE_Ti5, NOTE_Do6, NOTE_Sol5, NOTE_Sol5,
// (intro)
NOTE_Mi5, NOTE_Fa5, NOTE_Sol5, NOTE_Mi5, NOTE_Do5, NOTE_Do5, NOTE_Li4, NOTE_La4,
// 돌리랑 도트가
NOTE_Fa5, NOTE_Fa5, NOTE_Fa5, NOTE_Do5, NOTE_Do5, NOTE_Do5,
// 제일 좋아
NOTE_La4, NOTE_La4, NOTE_Re5, NOTE_Do5, REST,
// 돌리랑 도트가
NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5,
// 제일 좋아
NOTE_La4, NOTE_La4, NOTE_Re5, NOTE_Do5, REST,
// 마차를 끌고
NOTE_Fa4, NOTE_Fa4, NOTE_Fa4, NOTE_La4, NOTE_La4,
// 모래언덕을 지나
NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_La5, NOTE_Fa5,
// 네~ (지나
NOTE_Sol5, REST, NOTE_La5, NOTE_Fa5,
// 네~)
NOTE_Sol5, NOTE_Sol5, NOTE_Fi5, NOTE_Sol5,
// 돌리랑 도트가
NOTE_Fa5, NOTE_Fa5, NOTE_Fa5, NOTE_Do5, NOTE_Do5, NOTE_Do5,
// 제일 좋아
NOTE_La4, NOTE_La4, NOTE_Re5, NOTE_Do5, REST,
// 돌리랑 도트가
NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5, NOTE_Do5,
// 제일 좋아
NOTE_La4, NOTE_La4, NOTE_Re5, NOTE_Do5, REST,
// 이빨은 작아도
NOTE_Re5, NOTE_Re5, NOTE_Re5, NOTE_Li4, NOTE_Li4, NOTE_Li4,
// 먹는걸 좋아해
NOTE_Sol4, NOTE_Sol4, NOTE_Sol4, NOTE_Do5, NOTE_Do5, NOTE_Fa5,
// (outro)
NOTE_Fa4, NOTE_Fa5, NOTE_Mi5, NOTE_Fa5, NOTE_Do5, NOTE_Do5,
// (outro)
NOTE_La4, NOTE_Li4, NOTE_Do5, NOTE_La4, NOTE_Fa4, NOTE_Do4, NOTE_Li3, NOTE_Sol3,
// (outro)
NOTE_La3, REST
};
int noteDuration[] = {
// (intro)
quarter, quaver+semiquav, semiquav, quaver+semiquav, quarter, semiquav,
// (intro)
quaver+semiquav, semiquav, quaver+semiquav, semiquav, quaver+semiquav, semiquav, quaver+semiquav, semiquav,
// 돌리랑 도트가
quaver+semiquav, semiquav, quarter, quaver+semiquav, semiquav, quarter,
// 제일 좋아
quaver, quaver, quaver, quaver+quarter, quarter,
// 돌리랑 도트가
quaver+semiquav, semiquav, quarter, quaver+semiquav, semiquav, quarter,
// 제일 좋아
quaver, quarter, quaver, quarter, quarter,
// 마차를 끌고
quaver+semiquav, semiquav, quarter, quarter, quarter,
// 모래언덕을 지나
quaver, quaver, quaver, quaver, quarter, quaver, quaver,
// 네~ (지나
quarter+quarter, quarter, quaver, quaver,
// 네~)
quarter+quarter, quarter, quaver+semiquav, semiquav,
// 돌리랑 도트가
quaver+semiquav, semiquav, quarter, quaver+semiquav, semiquav, quarter,
// 제일 좋아
quaver, quaver, quaver, quaver+quarter, quarter,
// 돌리랑 도트가
quaver+semiquav, semiquav, quarter, quaver+semiquav, semiquav, quarter,
// 제일 좋아
quaver, quarter, quaver, quarter, quarter,
// 이빨은 작아도
quaver+semiquav, semiquav, quarter, quaver+semiquav, semiquav, quarter,
// 먹는걸 좋아해
quaver+semiquav, semiquav, quarter, quarter, quaver, quaver,
// (outro)
quarter, quaver+semiquav, semiquav, quaver+semiquav, quarter, semiquav,
// (outro)
quaver+semiquav, semiquav, quaver+semiquav, semiquav, quaver+semiquav, semiquav, quaver+semiquav, semiquav,
// (outro)
quarter, quarter+quarter+quarter
};
unsigned long prevPlayTime = 0;
unsigned long playDuration = 0;
void playBuzzer() {
int melodySize = sizeof(noteFreq)/sizeof(int);
if(millis() < prevPlayTime + playDuration) {
return;
}
noTone(piezoBuzzer);
if(currentNote >= melodySize)
currentNote = 0;
tone(piezoBuzzer, noteFreq[currentNote], noteDuration[currentNote]);
prevPlayTime = millis();
playDuration = noteDuration[currentNote] * 1.3;
currentNote ++;
}
bool globalFlag = false;
bool toggleStatus = false;
void detectButton() {
int readPin = digitalRead(pushButton);
if(readPin == LOW) {
if(globalFlag == false)
globalFlag = true;
}
else {
if(globalFlag == true) {
if(toggleStatus == true)
toggleStatus = false;
else
toggleStatus = true;
globalFlag = false;
}
}
}
void setup()
{
Serial.begin(115200);
pinMode(pushButton, INPUT_PULLUP);
pinMode(piezoBuzzer, OUTPUT);
}
void loop()
{
detectButton();
if(toggleStatus)
playBuzzer();
Serial.println(toggleStatus);
// delay(100);
}
두 가지 배열(array)이 필요합니다.
재생할 음악의 음계를 저장할 배열(noteFreq[]
)과 재생 시간을 저장할 배열(noteDuration[]
)을 각각 제작해야 합니다.
당연히 두 배열의 인덱스 갯수와 배열 길이는 같아야 합니다.
(쉼표는 음 재생이 필요 없으니 0값의 주파수를 재생합니다)
이 규칙에 맞게 본인이 재생하고 싶은 음계와 음계의 길이를 배열로 작성합니다.
음악을 재생할 함수를 살펴 보겠습니다.
// 시간 계산을 위한 변수
// 이 시간을 통해 음악이 아직 재생 중인지, 재생이 끝났는지를 계산합니다.
unsigned long prevPlayTime = 0;
unsigned long playDuration = 0;
void playBuzzer() {
int melodySize = sizeof(noteFreq)/sizeof(int); // 배열의 길이를 계산
if(millis() < prevPlayTime + playDuration) {
return;
}
noTone(piezoBuzzer);
if(currentNote >= melodySize)
currentNote = 0; // 배열 길이만큼 tone() 함수를 반복.
// 아래 세 줄이 실질적으로 음을 재생하는 부분입니다.
tone(piezoBuzzer, noteFreq[currentNote], noteDuration[currentNote]);
prevPlayTime = millis();
playDuration = noteDuration[currentNote] * 1.3;
currentNote ++;
}
이건 제가 인터넷을 보고 베꼈습니다.
다들 이렇게 하더라구요?
각 음을 재생할 때 마다 그 시간을 계산하여 현재 음악 연주가 끝났는지, 아직 재생중인지를 구분합니다.
'취미 > 장난감' 카테고리의 다른 글
로지텍 MX Master 3 마우스 무소음 스위치로 교체하기 (1) | 2023.07.10 |
---|---|
[아두이노] 초음파 거리 센서 : HC-SR04 (with 3m 연장선) (0) | 2021.05.29 |
[아두이노] 적외선 거리 센서 : GP2Y0A21 IR - C29 (with 3m 연장선) (0) | 2021.05.28 |
[아두이노] Maker UNO 사용기 : 푸쉬 버튼을 토글 스위치로 사용하기. (0) | 2021.02.20 |
[아두이노] Cytron의 "Maker UNO" 청소년 및 학생을 위한 코딩 교육용 호환 보드 리뷰 (0) | 2021.02.18 |