โปรเจกต์ เพลง Christmas Carols บน Arduino
Sketch ที่เล่นเพลง Christmas Carols บน Arduino โดยใช้ Speaker Circuit แบบง่ายๆ
Sketch ที่เล่นเพลง Christmas Carols บน Arduino โดยใช้ Speaker Circuit แบบง่ายๆ
'Christmas Carols on Arduino' ประกอบด้วย Sketch เพียงตัวเดียวที่รวมเอา Playlist เพลงคริสต์มาสยอดนิยม 10 เพลงเข้าด้วยกัน โดย Playlist ทั้ง 10 เพลงนี้จะถูกสุ่มลำดับใหม่หลังจากเล่นจบครบทุกเพลงในแต่ละรอบ และจะเริ่มเล่นใหม่ในลำดับที่ต่างไปจากเดิม โดยเพลงที่มีมาให้ในตอนเริ่มต้น (OOTB) ได้แก่:
( Function ของแต่ละเพลงจะเล่นจนจบครบสองรอบ แต่คุณสามารถเพิ่มหรือลดจำนวนรอบได้ตามต้องการ โดยการเข้าไปแก้ไขค่าอย่างง่ายที่ ' for-loop upper bounds ' ใน Function ของเพลงนั้นๆ )
Sketch ชุด Christmas Carols on Arduino นี้เป็นแบบ 'plug and play' แต่มีสองวิธีที่คุณสามารถเลือกใช้สำหรับบทความ/โปรเจกต์นี้ได้ คือ -
1. คุณสามารถสร้างวงจรลำโพงตามที่ระบุไว้ (ดูที่ Hardware และ Schematics ด้านล่าง) จากนั้นดาวน์โหลดและรัน Sketch ตามที่เป็นอยู่ นั่งพักผ่อนพร้อมกาแฟสักถ้วย (หรือเครื่องดื่มอื่นๆ หากอยู่ในช่วงเทศกาล!) แล้วเพลิดเพลินไปกับเสียงเพลง
หรือ
2. คุณสามารถสร้างวงจรลำโพงตามที่ระบุไว้ ดาวน์โหลด Sketch ที่เกี่ยวข้อง แล้วศึกษาโครงสร้าง เนื้อหา และวิธีการเขียนโค้ด (ซึ่งมีเทคนิคที่น่าสนใจเป็นพิเศษที่อาจเป็นประโยชน์ต่อโปรเจกต์ในอนาคตของคุณ)
หากคุณเลือกวิธีแรก ก็ถือว่ายอดเยี่ยมมาก งานสำเร็จแล้ว ขอให้สนุกกับบทเพลงและเก็บ Sketch นี้ไว้ใช้ในอนาคต คุณไม่จำเป็นต้องอ่านต่อจากนี้ แต่ถ้าคุณเป็นพวกขี้สงสัยและต้องการเข้าใจว่า Sketch เพลงคริสต์มาสนี้ทำงานอย่างไร ให้อ่านต่อได้เลย
ไม่มีอะไรจะง่ายไปกว่านี้อีกแล้ว - สิ่งที่คุณต้องมีคือ Arduino Microcontroller (รุ่นไหนก็ได้), Resistor ขนาด 100 ohm, Breadboard ขนาดเล็ก, สายไฟ และถ้าจะให้ดีควรมีลำโพงขนาด 8 ohm 0.25 watt (หรือจะใช้ Buzzer ก็ได้หากไม่มีลำโพงอยู่ใกล้ตัว)
แผนผังการต่อสายจะแสดงอยู่ที่หัวข้อ Schematics ด้านล่าง ซึ่งเป็นวงจรที่ประกอบง่ายมาก Sketch นี้จะใช้ Digital Output Pin 11 ในการขับลำโพง แต่ถ้าคุณต้องการใช้ Pin อื่น ก็เลือก Pin ที่เหมาะสมและเปลี่ยนค่าที่ ' #define speaker ' ใน Sketch ตามต้องการ
ขอแนะนำว่า Microcontroller ไม่ควรเชื่อมต่อโดยตรงกับสิ่งอื่นนอกเหนือจากลำโพง/Buzzer ตามที่ระบุไว้ มิฉะนั้นคุณอาจทำให้ Microcontroller เสียหายได้
Sketch นี้ดัดแปลงมาจากบทความเดิม ' Let's Make Music ' โดยนำมาปรับปรุงเฉพาะเพื่อจัดการกับ Playlist ของโน้ตดนตรี (เพลงคริสต์มาส) การออกแบบ Sketch นั้นตรงไปตรงมาและน่าจะเข้าใจได้ง่ายสำหรับผู้ที่สนใจ อย่างไรก็ตาม มันมีเทคนิคหนึ่งที่ควรค่าแก่การสังเกตเป็นพิเศษ คือการใช้ Pointer เพื่อกำหนดและจัดการ Playlist ของเพลง (C++ Pointers)
หากคุณมีความเข้าใจเรื่อง Pointer วัตถุประสงค์ และการใช้งานของมันเป็นอย่างดีแล้ว คุณสามารถข้ามส่วนนี้ไปได้ แต่ถ้าไม่ ก็น่าจะคุ้มค่าที่จะใช้เวลาสักนิดเพื่อดูว่า Sketch นี้ใช้งานพวกมันอย่างไร
เรามาเริ่มกันที่วิธีที่การออกแบบนี้เลือกใช้ Pointer:
อย่างแรก โน้ตเพลงของแต่ละเพลงจะถูกกำหนดโดย Function ของตัวเอง โดย Function เหล่านี้จะถูกวางไว้ที่ส่วนท้ายของ Sketch เพื่อหลีกเลี่ยงไม่ให้ Sketch ดูรกและช่วยให้อ่านง่ายขึ้น
ถัดมา ในส่วนบนของ Sketch เราจะทำการประกาศ Forward References สำหรับแต่ละ Function ของเพลง เนื่องจากเราจำเป็นต้องกำหนด Function เหล่านี้ให้กับ Playlist Array (ซึ่งถูกกำหนดเป็น Array ของ Pointer) โดยทำก่อนที่จะมีการประกาศ Play List Pointer Array เพราะจำเป็นต้องทำให้มั่นใจว่า Address References ของ Function เพลงเหล่านั้นอยู่ใน Scope ที่ Compiler ต้องการใช้งาน เราอาจจะประกาศ Function เพลงทั้งหมดไว้ก่อนประกาศ Play List Pointer Array ที่ด้านบนสุดของ Sketch เลยก็ได้ แต่นั่นจะทำให้ Sketch ดูรกมาก ทั้งที่จริงแล้วมันควรจะดูสะอาดตา
หลังจากประกาศ Forward References ของ Function เพลงแล้ว Sketch จะกำหนด Playlist ( play_list ) ซึ่งเป็น Array ประเภท Pointer (*) ที่มีขนาดเท่ากับจำนวนเพลง (Function ของเพลง) ที่ประกาศไว้ใน Sketch (ในตอนเริ่มต้นจะมีขนาด 10) แต่ละ Element ของ Playlist Array จะถูกกำหนด (Preset) ด้วย Address Reference ของแต่ละ Function เพลง (ซึ่งทำได้เพราะมีการประกาศ Forward Carol Function ไว้ก่อนหน้าแล้ว) เมื่อเสร็จเรียบร้อย เราก็สามารถใช้งานหรือประมวลผล Playlist ได้ตามต้องการ เช่น การ Shuffle (สุ่มลำดับ), การไล่ดูทีละ Element และเรียกใช้ Function เพลงที่เกี่ยวข้อง เป็นต้น
เพื่อเป็นการสรุป เรามาดูโค้ดแต่ละส่วนใน Sketch ที่ช่วยให้เราใช้ Playlist แบบ Pointer ได้
Forward Reference Declarations To The Carol Functions - สังเกตรูปแบบคำสั่ง (Syntax):
// Forward references สำหรับ function โน้ตเพลง
// ซึ่งถูกกำหนดไว้ที่ส่วนท้ายของ sketch เพื่อความสะอาดตา...
void we_wish_you_a_merry_christmas();
void o_come_all_ye_faithful();
void away_in_a_manger();
void ding_dong_merrily();
void good_king_wenceslas();
void the_first_nowell();
void the_holly_and_the_ivy();
void we_three_kings();
void silent_night();
void jingle_bells();
Play List Declaration - เราประกาศ Playlist Pointer Array พร้อมกับกำหนดค่า Address ล่วงหน้าของ Function เพลง (สังเกต Syntax อีกครั้ง):
//
// ประกาศ pointer array และกำหนดค่าล่วงหน้าด้วย address
// ของแต่ละ function โน้ตเพลง
// สังเกตว่าลำดับการกำหนดค่าล่วงหน้าไม่สำคัญ
// เพราะ array play_list จะถูกสุ่มใหม่ตลอดเวลา
// โดย function shuffle_play_list
void (*play_list[num_carols])() = {
we_wish_you_a_merry_christmas,
o_come_all_ye_faithful,
away_in_a_manger,
ding_dong_merrily,
good_king_wenceslas,
the_first_nowell,
the_holly_and_the_ivy,
we_three_kings,
silent_night,
jingle_bells
};
Syntax ในการกำหนด Playlist เป็น Array ของ Pointer อาจจะดูแปลกตาไปบ้าง แต่มีสองจุดที่ควรสังเกตคือ:
1. เครื่องหมาย '*' หน้าชื่อ Array สิ่งนี้จะบอก Compiler ให้จัดการ Array 'play_list' เป็น Array ของ Pointer และ
2. เครื่องหมาย ' () ' ต่อท้าย นี่เป็นการบอก Compiler ว่า Address Reference นั้นเกี่ยวข้องกับ Function (ซึ่งก็คือ Forward Carol Function ของเรานั่นเอง) สิ่งนี้ช่วยให้เราสามารถเรียกใช้ Function ที่กำหนดโดย Element ของ Playlist ได้โดยตรง - ดูในส่วน Main Loop ด้านล่าง สังเกตด้วยว่าเราสามารถกำหนดค่าล่วงหน้าให้ Playlist Pointer Array ได้ ก็ต่อเมื่อ เราได้ประกาศ Forward References ของ Function เพลงไว้ก่อนหน้าแล้วเท่านั้น Compiler จะใส่ Address ของ Function เพลงที่ประกาศไว้ลงในแต่ละ Element ของ Playlist Array ส่วนลำดับที่ใส่นั้นไม่สำคัญ เพราะ Playlist จะถูกสุ่มลำดับใหม่เรื่อยๆ
เท่านี้เอง ตอนนี้เราก็สามารถประมวลผล Playlist ได้ตามต้องการแล้ว เรามาดูที่ Main Loop กัน:
Main Loop - จะเห็นได้ว่า Main Loop มีการออกแบบที่เรียบง่ายมาก:
void loop() {
do {
// ประมวลผล play_list โดยเลือกแต่ละ element ที่ถูกสุ่มลำดับไว้
// และเรียกใช้ function โน้ตเพลงที่เกี่ยวข้อง
for (uint8_t carol = 0; carol < num_carols; carol++) {
play_list[carol](); // เรียกใช้ ([carol]) music score function นี้
wait(3); // เว้นระยะเวลาสั้นๆ ระหว่างเพลง
}
shuffle_play_list(); // สุ่มลำดับ play_list สำหรับการเล่นรอบถัดไป
wait(3); // เว้นระยะเวลาสั้นๆ ก่อนเริ่มเล่น playlist รอบใหม่
} while (true);
}
Main Loop จะวิ่งผ่านแต่ละ Element ใน Playlist และเรียกใช้ Function เพลงที่เกี่ยวข้อง - อีกครั้ง สังเกตที่ Syntax - เราจัดการกับ Playlist เสมือนว่าเป็น Function หนึ่ง จึงมีการใช้เครื่องหมาย ' () ' ในการเรียกใช้
หลังจากจบ Playlist ในแต่ละรอบ Playlist จะถูกสุ่มใหม่เพื่อให้ได้ลำดับเพลงที่แตกต่างกันในการเล่นรอบถัดไป
ยังมีอีกหลายวิธีในการใช้งาน Pointer และหวังว่าจากการศึกษาการใช้งานใน Sketch นี้ คุณจะมองเห็นไอเดียว่าพวกมันจะมีประโยชน์ต่อการออกแบบ Sketch ของคุณเองได้อย่างไร
สำหรับบทความนี้ก็คงมีเพียงเท่านี้ และหวังว่าคุณจะได้รับความเพลิดเพลินจากบทเพลงคริสต์มาส หรืออาจจะลองเพิ่มเพลงของคุณเองลงไปใน Sketch นี้ด้วย
ทำไมไม่ลองเพิ่มเพลงของคุณเองดูล่ะ? หรือหากคุณต้องการสำรวจเรื่องดนตรีบน Arduino ให้ลึกซึ้งยิ่งขึ้น ลองเข้าไปดูบทความอื่นๆ ที่เคยเผยแพร่อีกสองบทความนี้:
Let's Make Music - Sketch ที่รองรับโน้ตดนตรีครอบคลุมหลาย Octave และรู้จักจังหวะ/เวลามาตรฐานของดนตรี ซึ่งจะมีฟีเจอร์และ Function พื้นฐานเหมือนกับ Sketch เพลงคริสต์มาสนี้ แต่มีความสามารถเพิ่มขึ้นมาอีกนิด ลองดู crib sheet สำหรับบทความนี้ซึ่งมีข้อมูล Function ต่างๆ ที่มีประโยชน์
Music & Lights Workbench - Sketch ที่ออกแบบมาเพื่อกระตุ้นผู้ที่ไม่คุ้นเคยกับการเขียนโปรแกรมคอมพิวเตอร์ ภายใต้การแนะนำเล็กน้อย ให้ได้ลองศึกษาเรื่องนี้ผ่านตรรกะและการเล่น - ทั้งการสร้างทำนองเพลงและการเปิดปิดหลอดไฟ LED
สนับสนุนเพื่อรับ Source Code หรือแอปพลิเคชันสำหรับโปรเจกต์นี้