[아두이노] [강좌] 25. tone()/noTone() 함수 (2) - 부저 실습

이번 시간엔 tone() 함수와 noTone() 함수를 이용해서 부저로 멜로디를 연주하는 실습을 해보자.

 

부저에는 굉장히 여러 가지가 있는데연결 핀이 2개인 것도 있고 3개인 것도 있다. 3개인 경우 Vcc, GND, Data 핀으로 이루어져 있을 것이고, 2개인 경우 GND, Data 핀으로 이루어진다.


위의 센서는 핀이 3개네표시된 대로 Vcc 핀은 5V, GND 핀은 GND SIG라고 표시된 핀은 8번에 꽂자. 8번 맘에 안 들면 아무데나 꽂아도 됨아래 그림의 부저와 본인이 가진 부저가 다르겠지만 핀 연결은 대부분 비슷하니까 참고만 하자. (fritzing 프로그램에 부저 모듈이 저거밖에 없어서.ㅜㅜ)

9f91fc4d3abbddc78de8c00c80747b35.png


그리고 스케치 툴의 파일→예제→02.DigitaltoneMelody” 예제 열기.



 toneMelody.ino

  

 #include "pitches.h"

 

 int melody[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4};

 int noteDurations[] = {4, 8, 8, 4, 4, 4, 4, 4};

 

 void setup() {:

   for (int thisNote = 0; thisNote < 8; thisNote++) {

int noteDuration = 1000 / noteDurations[thisNote];

     tone(8, melody[thisNote], noteDuration);

 

     int pauseBetweenNotes = noteDuration * 1.30;

     delay(pauseBetweenNotes);

 

     noTone(8);

   }

 }

 

 void loop() {

 }




“#include” 구문은 지정한 헤더 파일을 컴파일 하기 전 소스에 추가하는 역할을 한다그럼 pitches.h 파일은 어디에 있나스케치 툴의 아래 부분을 보면 하나의 탭이 더 있는 것을 볼 수 있다.


803bbae21cd53eb9b666b89ff665dacf.png


"#include “pitches.h”" 구문을 사용했기 때문에 탭이 생긴 건 아니고, toneMelody.ino 파일과 같은 폴더 내에 pitches.h 파일이 있기 때문에 탭이 추가되어 나타난 것이다. #include 구문으로 추가할 수 있는 헤더 파일은 소스 파일(.ino)과 같은 위치에 있거나 아두이노에서 특별히 지정된 경로(libraries 폴더나 core 폴더 등)에 존재하는 파일이어야 하며만일 다른 경로에 있는 파일을 추가하고 싶다면 직접 경로를 적어줘야 에러가 발생하지 않는다.

 

헤더 파일(.h)에는 주로 클래스가 정의되어 있거나 함수가 선언되어 있는데다른 파일에 정의된 함수나 클래스를 사용하기 위해서는 반드시 그 함수나 클래스가 선언된 헤더 파일을 “#include” 구문으로 추가해줘야 한다. (digitalWrite() 함수 등 기본 함수는 내부적으로 이미 추가되어 있으므로 따로 헤더 파일을 찾지 않아도 된다.)

 


pitches.h 탭을 클릭 해보자


뭔가 “#define” 해서 엄청 많이 있다. “#define” 구문도 처음 나왔나ㅜㅜ?


 

 

“#define” 구문은 키워드를 지정하는 구문이라고 보면 된다하나만 살펴 볼까?



 #define NOTE_C4  262 



위 구문은 262이라는 값을 “NOTE_C4”라는 키워드로 사용하겠다는 뜻이다. 소스 상에서 “NOTE_C4”라는 키워드를 사용하면 그 값은 실제로 262를 의미한다. 예제 소스에서 “NOTE_C4” 키워드가 사용된 부분을 찾아보자.



 int melody[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4};



melody 배열은 int 형의 배열이다. 배열의 첫 번째 값으로 ‘NOTE_C4’ 키워드가 사용되어 실제로는 262가 저장된다. 즉 위 배열의 의미를 풀어 쓰면 다음과 같다.



 int melody[] = {262, 196, 196, 220, 196, 0, 247, 262};



아니이렇게 알아보기 쉬운 숫자를 놔두고 왜 키워드를 사용해!?

 

 

이유는 두 가지이다첫째이 값이 무엇을 의미하는지 코드 상에서 직관적으로 알 수 있게 하기 위함이고둘째똑 같은 값(변하지 않는 상수 값이어야 함)이 여러 번 사용될 경우 나중에 값이 수정되어야 할 때 쉽게 수정할 수 있게 하기 위함이다. #define 구문에 정의된 값만 고치면 되니까.

 

 




다시 소스로 돌아와서업로드 해보자.

 

 

띠↗디↘디↘디↗디↘ (쉬고띠↗디↗  ♪♬~sally_special-3

 

 

굉장히 익숙하지만 제목을 들어본 기억이 없는 멜로디가 흘러나온다.

 

setup() 함수에서 tone() 함수를 사용하고 loop() 함수에서는 아무 동작도 하지 않으므로 전원을 넣은 후 한 번만 연주되고 더 이상 연주되지 않는다아쉽구로.

 

 

소스를 살펴보자.



 int melody[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4};

int noteDurations[] = {4, 8, 8, 4, 4, 4, 4, 4};



NOTE_C4 키워드에 저장된 262라는 값은 사실 4옥타브 ’ 음계의 주파수 값이다.

 

부저 센서는 주파수에 따라 음계가 다른 소리가 출력되는데, pitches.h 파일에 각 음계에 대한 주파수 값을 키워드로 지정해둔 것.

 

그래서 melody[] 배열에 출력할 음계의 주파수 값을 순서대로 저장했다그리고 noteDurations[] 배열에는 몇 분 음표인가를 저장하고 있네. 4분 음표, 8분 음표.

 

 

그리고 setup() 함수에서 for() 문을 이용해 각 음계를 차례로 출력한다.



 for (int thisNote = 0; thisNote < 8; thisNote++) {

    int noteDuration = 1000 / noteDurations[thisNote];

    tone(8, melody[thisNote], noteDuration);


    int pauseBetweenNotes = noteDuration * 1.30;

    delay(pauseBetweenNotes);


    noTone(8);

 }



noteDurations[] 배열에는 4분 음표, 8분 음표가 저장되어 있었고정수형 변수 noteDuration 값에는 실제로 출력될 시간이 계산되어 저장된다그리고는 tone() 함수를 이용해서 8번 핀에 thisNote 번째의 주파수(음계)를 계산된 시간만큼 출력.

 

음계 사이에 약간의 딜레이를 주기 위해 noteDuration 1.3배만큼 delay() 하고, noTone() 함수를 호출한다.

 

사실 tone() 함수에서 설정한 시간보다 delay()에서 지연된 시간이 길기 때문에 자동으로 출력이 중단되어 noTone() 함수를 다시 호출할 필요는 없는데예제에서는 호출했네?

 

 

음계를 출력하기 위한 시간과 중간 딜레이 시간을 계산하기 위해 변수가 사용되고계산식이 사용되긴 했지만 사실 내용은 아주 쉬운 예제 소스다순서대로 주파수를 변경해가며 파형을 출력하는 예제!

 

 

brown_and_cony-80

 

하지만 이거만 하고 끝내기는 아쉽고, toneMultiple 예제나 toneKeyboard 예제는 다른 필요한 애들이 많으니까우리는 스위치 하나만 추가해서 스위치가 눌리면 멜로디가 계속 반복해서 나오고한번 더 누르면 꺼지는 예제를 만들어 볼까.

 

 

toneMelody 예제와 지난 강좌에서 배웠던 인터럽트를 활용해보자.

 

아두이노 보드에 스위치 추가!


e03b79a3bc67b7756075d5386c535c23.png


위치만 바뀌었을 뿐 부저의 연결은 동일하며스위치가 추가되었다.

 

소스를 수정해볼까.

 

toneMelody 예제에서 melody[] 배열과 noteDurations[] 배열은 그대로 사용하고스위치를 위한 핀 번호와 멜로디 On/Off 상태를 저장하기 위한 변수를 추가하자.



 int swPin = 2;

 boolean bMelodyOn = false;



그리고 setup() 함수에서 멜로디가 출력되는 부분을 loop()로 옮기고 swPin 모드와 인터럽트를 설정하자인터럽트를 사용할 테니까.



 void setup() {

   pinMode(swPin, INPUT_PULLUP);

   attachInterrupt(0, swInterrupt, FALLING);

 }

 


그리고 swInterrupt() 함수를 정의해줘야겠지채터링 방지를 위해 delayMicroseconds() 구문을 추가했다. 2밀리초 정도니까 괜찮을거야.. 아마도..;;;



 void swInterrupt() {

   delayMicroseconds(2000);

   if(digitalRead(swPin) != LOW) return ;

  

   if(bMelodyOn) bMelodyOn = false;

   else bMelodyOn = true;

 }



bMelodyOn 변수의 값을 확인해서 true false, false true로 바꿔주고 있다멜로디가 나오는 중이면 멜로디를 끌 거고안 나오는 중이면 켤 거니까.

 

그리고 loop() 함수. setup()에 있었던 for() 구문을 loop() 함수로 옮기는데단 조건이 붙어야 한다. bMelodyOn 변수가 true일 경우에만 동작하도록.



 void loop() {

   if(bMelodyOn) {

     for (int thisNote = 0; thisNote < 8; thisNote++) {

       int noteDuration = 1000 / noteDurations[thisNote];

       tone(8, melody[thisNote], noteDuration);

  

       int pauseBetweenNotes = noteDuration * 1.30;

       delay(pauseBetweenNotes);

  

       if(bMelodyOn == false) break;

     }

   } 

 }

 



for() 반복문의 마지막에 bMelodyOn 값을 확인해서 false일 경우 bMelodyOn true인 상태에서 스위치가 한번 더 눌려 false로 바뀐 경우 “break;” 하고 있다.

 

“break” break를 포함하고 있는 가장 가까운 반복문을 종료하는 명령어이다유의할 점은 반복문이 중첩되어 사용 중인 경우 break 구문과 가장 가까운 반복문만 종료된다는 점.

 

아무튼그래서 스위치가 한번 더 눌리면 반복문을 종료하고 멜로디 출력을 중단한다.

 

 

업로드 한 후 실행해보자.

 

 

아무 소리도 안 난다당연하지스위치를 눌러보자.

 

띠↗디↘디↘디↗디↘ (쉬고띠↗디↗  ♪♬띠↗디↘디↘디↗디↘ (쉬고띠↗디↗  ♪♬~  띠↗디↘디↘디↗디↘ (쉬고띠↗디↗  ♪♬띠↗디↘디↘디↗디↘ (쉬고띠↗디↗  ♪♬~



sally_special-13


 

계속 나온다스위치를 한번 더 눌러보자멈춘다.

 

 

 

짝짝짝~



이 것이 멜로디 박스의 간단한 버전이다여기에 멜로디 배열을 여러 개 더 추가하고(물론 다른 멜로디로), 스위치를 추가하거나 아니면 멜로디 번호를 저장하는 변수를 추가하여 더 많은 멜로디가 나오는 멜로디 박스를 만들 수 있을 것이다.

 


이건 여러분의 몫으로 남겨두면서오늘 실습은 여기서 끝~!



진짜 끝.

0
0
이 글을 페이스북으로 퍼가기 이 글을 트위터로 퍼가기 이 글을 카카오스토리로 퍼가기 이 글을 밴드로 퍼가기

임베디드 보드

번호 제목 글쓴이 날짜 조회수
118 아두이노 ESP32 Analog Inputs (ADC) +4 icon 양재동메이커 02-12 16,139
117 아두이노 TIP : Serial의 Port가 Open 시점 확인 icon 양재동메이커 01-21 12,992
116 아두이노 ESP32 Boot Mode icon 양재동메이커 12-28 12,835
115 아두이노 아두이노 에러 리스트(Arduino Error list) icon 양재동메이커 11-24 17,818
114 아두이노 ESP32 main.cpp +1 icon 양재동메이커 11-19 13,181
113 아두이노 ESP32 EEPROM 와 IR Remote icon 양재동메이커 08-06 13,074
112 아두이노 Learn ESP32 icon 양재동메이커 06-25 12,655
111 라즈베리 파이 라즈베리 파이 (Raspberry Pi) 기초 icon 양재동메이커 06-19 14,150
110 라즈베리 파이 (동영상 강의) 라즈베리 파이 강좌 Link icon 양재동메이커 06-17 13,249
109 STM32 / MBED [MED] Switch debouncing icon HellMaker 12-30 15,226
108 기타 [타이젠] 아두이노의 16x2 LCD Display라이브러리 LiquidCrystal_I2C의 타이젠 포팅 icon 양재동메이커 09-15 14,803
107 기타 [타이젠] GPIO의 디지탈 출력과 입력 인터럽트의 C++ Class 제작 icon 양재동메이커 09-12 14,280
106 마이크로비트 서보 모터 icon HellMaker 09-03 14,098
105 마이크로비트 아날로그 온도센서 (마이크로 비트 센서 활용) icon HellMaker 09-01 14,606
104 마이크로비트 터치센서 (마이크로 비트 센서 활용) icon HellMaker 09-01 13,720
103 마이크로비트 디지털 홀 센서 (마이크로 비트 센서 활용) icon HellMaker 08-29 12,900
102 마이크로비트 리니어 홀 센서 (마이크로 비트 센서 활용) icon HellMaker 08-29 12,455
101 마이크로비트 불꽃 감지 센서 (마이크로 비트 센서 활용) icon HellMaker 08-26 12,794
100 마이크로비트 로터리 엔코더 (마이크로 비트 센서 활용) icon HellMaker 08-25 12,900
99 마이크로비트 2컬러 LED(3mm) (센서 활용) icon HellMaker 08-22 12,901