之前使用tone()透過小喇叭播放周杰倫的青花瓷,現在要使用tone()來播放RTTTL(Ring Tone Transfer Language),這是一種用來儲存鈴聲的格式,譬如底下這一串是不可能的任務主題曲:
"MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"
電路圖(Fritzing格式)與程式原始碼,可到此下載。其中播放RTTTL的函式,來自於Brett Hagman。
Arduino板子腳位3,接到100 ohm電阻,電阻另一端接到揚聲器的正極。
Arduino板子的GND,接到揚聲器的負極。
#define BAUDRATE 38400
#define BUZZER_PIN 3
#define OCTAVE_OFFSET 0
int notes[] = { 0,
262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951
};
// 底下是鈴聲的RTTTL資料
const char *song1 = "Indiana..." // 印第安那瓊斯,省略RTTTL資料內容
const char *song5 = "Xfiles..." // X檔案
const char *song10 = "StarWars..." // 星際大戰
const char *song13 = "A-Team..." // 天龍特攻隊
const char *song20 = "MissionImp..." // 不可能的任務
// ...省略其他鈴聲
// 把想播放的鈴聲,放進來
#define SONGS_NUM 6
const char *songs[SONGS_NUM] = {
song1, song10, song13,
};
#define isdigit(n) (n >= '0' && n <= '9')
// 底下是播放RTTTL的函式
void play_rtttl(const char *p)
{ // 並沒有做任何錯誤偵測
byte default_dur = 4;
byte default_oct = 6;
int bpm = 63;
int num;
long wholenote;
long duration;
byte note;
byte scale;
// 格式:d=N,o=N,b=NNN:
// 跳過name,找到開頭處
while(*p != ':') // 跳過name
p++;
p++; // 跳過':'
// 取得預設duration
if(*p == 'd')
{
p++; p++; // 跳過"d="
num = 0;
while(isdigit(*p)){
num = (num * 10) + (*p++ - '0');
}
if(num > 0)
default_dur = num;
p++; // 跳過','
}
Serial.print("default_dur: ");
Serial.println(default_dur, 10);
// 取得預設octave
if(*p == 'o')
{
p++; p++; // 跳過"o="
num = *p++ - '0';
if(num >= 3 && num <=7)
default_oct = num;
p++; // 跳過','
}
Serial.print("default_oct: ");
Serial.println(default_oct, 10);
// 取得BPM(beats per minute),也就是tempo
if(*p == 'b')
{
p++; p++; // 跳過"b="
num = 0;
while(isdigit(*p)){
num = (num * 10) + (*p++ - '0');
}
bpm = num;
p++; // 跳過':'
}
Serial.print("bpm: ");
Serial.println(bpm, 10);
// BPM通常表示為每分鐘幾個quarter notes
wholenote = (60 * 1000L / bpm) * 4; // 這是全音符的時間(milliseconds)
Serial.print("wholenote: ");
Serial.println(wholenote, 10);
while(*p) // 開始播放每個音符
{
// 首先取得音符的uration,如果有的話
num = 0;
while(isdigit(*p)){
num = (num * 10) + (*p++ - '0');
}
if(num)
duration = wholenote / num;
else
duration = wholenote / default_dur; // 之後須檢查是否有附點'.'
// 取得音符
note = 0;
switch(*p){
case 'c':
note = 1;
break;
case 'd':
note = 3;
break;
case 'e':
note = 5;
break;
case 'f':
note = 6;
break;
case 'g':
note = 8;
break;
case 'a':
note = 10;
break;
case 'b':
note = 12;
break;
case 'p':
default:
note = 0;
}
p++;
// 取得選用性的'#' sharp
if(*p == '#'){
note++;
p++;
}
// 取得選用性的'.' dotted note
if(*p == '.'){
duration += duration/2;
p++;
}
// now, get scale
if(isdigit(*p)){
scale = *p - '0';
p++;
}
else{
scale = default_oct;
}
scale += OCTAVE_OFFSET;
if(*p == ',')
p++; // 跳過',',處理下個音符,或到尾巴了
// 播放音符
if(note){
Serial.print("Playing: ");
Serial.print(scale, 10);
Serial.print(' ');
Serial.print(note, 10);
Serial.print(" (");
Serial.print(notes[(scale - 4) * 12 + note], 10);
Serial.print(") ");
Serial.println(duration, 10);
tone(BUZZER_PIN, notes[(scale - 4) * 12 + note]);
delay(duration);
noTone(BUZZER_PIN);
}
else{
Serial.print("Pausing: ");
Serial.println(duration, 10);
delay(duration);
}
}
}
// setup與loop,沒什麼,就是把鈴聲資料丟給play_rtttl就對了
void setup(void)
{
Serial.begin(BAUDRATE);
}
void loop(void)
{
int i;
Serial.println("Start.");
for(i = 0; i < SONGS_NUM; i++){
play_rtttl(songs[i]);
delay(1000);
}
Serial.println("Done.");
delay(3000);
}
若想聽聽印第安那瓊斯、星際大戰、天龍特攻隊的鈴聲效果,請到YouTube聆聽,音量很小,請自行放大音量。
大大妳好,我是個對Arduino有興趣的大二生,我是想要自學,請問要入門的話買哪一塊主板比較好呢?我未來是有想考AMA的認證,麻煩大大指點一條明路
ReplyDelete入門的話建議買Arduino UNO,最廣為使用的一塊板子,很多範例與文件,也都是以UNO為準。
ReplyDelete雖然有更新的板子,譬如Leonardo,但有著諸多小差異,可能會對初學者造成困擾。
關於UNO與Leonardo的差異,可參考http://yehnan.blogspot.tw/2013/09/arduinoleonardouno.html。
關於AMA認證,您學校教授應能給出更好的建議。
葉先生,您好
ReplyDelete不好意思,我也是個arduio的新手,我有買個UNO的板子,我想試著使用arduino做個網路轉Rs232的轉換器,硬體:enc28j60+arduino uno
我收尋了很久但都沒有什麼收穫,想請教葉先生
謝謝
網路轉Rs232的轉換器?
ReplyDelete這是什麼意思?
像似
ReplyDeletehttp://en.usr.cn/RS232-serial-to-ethernet-converter-tcp-ip-module
嗯,了解,就是從RS232序列介面、轉到Ethernet。
Delete硬體部分,Ethernet由enc28j60負責,然後你需要RS232轉一般Serial(3.3V or 5V)的轉接器,負責電腦-RS232-轉接器-Serial-Arduino微控制器晶片之間的溝通。
那麼,從RS232要怎麼下網路相關指令呢?知道這部份的話,接下來就是撰寫Arduino程式,從RS232接收指令,呼叫enc28j60程式庫,傳出或接收網路封包,
關於指令的部份,或可參考ESP8266的作法:
http://www.instructables.com/id/Using-the-ESP8266-module/?ALLSTEPS
以上只是從理論上猜想,我沒有相關經驗,僅供參考。
您的RS232端的轉接器意思是用MAX232?
ReplyDelete是。
Delete恩 謝謝您
Delete我再慢慢摸索如何ethernet端封包聽取轉換封包對serial端輸出的程式碼
這部分,第一次接觸有點頭疼,要找個管道學一下