กลับไปหน้ารวมไฟล์
christmas-carols-on-arduino-457131.md

สรุปสั้นๆ ง่ายๆ

โปรเจค 'Christmas Carols on Arduino' นี้ มันก็คือสเก็ตช์เดียวที่บรรจุเพลงคริสต์มาสฮิตๆ ไว้ 10 เพลงเลยจ้า รายการเพลงทั้ง 10 จะถูกสุ่มลำดับใหม่ทุกครั้งที่เล่นจบรอบ งานนี้จัดมาให้แบบครบเซ็ตเลย ประกอบด้วย:

  1. We Wish You A Merry Christmas
  2. O Come All Ye Faithful
  3. Away In A Manger
  4. Ding Dong Merrily (On High)
  5. Good King Wenceslas
  6. The First Nowell
  7. The Holly And The Ivy
  8. We Three Kings
  9. Silent Night
  10. Jingle Bells

( แต่ละฟังก์ชันเพลงจะเล่นวน 2 รอบเต็มๆ นะ แต่ถ้าน้องอยากให้เล่นมากกว่านี้หรือน้อยลง ก็แค่ไปปรับค่า upper bound ใน for-loop ของฟังก์ชันเพลงนั้นๆ ง่ายๆ เอง )

สเก็ตช์นี้เป็นแบบ Plug and Play เลย แต่น้องจะมีสองทางเลือกในการเล่นงานโปรเจคนี้ -

1. ต่อวงจรลำโพงตามที่บอกไว้ด้านล่าง (ดูหัวข้อ Hardware and Schematics) ดาวน์โหลดสเก็ตช์มาแล้วอัพโหลดลงบอร์ดเลย จิบกาแฟ (หรือถ้าช่วงเทศกาลก็อาจเป็นเครื่องดื่มอื่นๆ!) แล้วนั่งฟังเพลงสบายๆ ไปเลย

หรือ

2. ต่อวงจรลำโพงเหมือนเดิม แต่ดาวน์โหลดสเก็ตช์มาแล้วลองแกะโค้ดดู โครงสร้าง เนื้อหา และเมธอดต่างๆ มันมีเทคนิคที่น่าสนใจมากๆ ตัวนึงที่อาจเอาไปใช้ในโปรเจคของน้องเองได้ในอนาคต

ถ้าน้องเลือกทางแรก ก็เยี่ยมไป งานเสร็จแล้ว จัดไปวัยรุ่น! ไม่ต้องอ่านต่อก็ได้ แต่ถ้าน้องเป็นสายชอบเจาะลึก อยากรู้ว่าโค้ดมันทำงานยังไง ตามมาดูกันต่อเลย

ส่วนฮาร์ดแวร์

ง่ายมากกก – สิ่งที่ต้องมีก็แค่ Arduino ไมโครคอนโทรลเลอร์ (รุ่นไหนก็ได้), ตัวต้านทาน (Resistor) 100 โอห์ม, บอร์ดทดลองเล็กๆ, สายจัมเปอร์ และที่ขาดไม่ได้คือลำโพง 8 โอห์ม 0.25 วัตต์ (หรือจะใช้บัซเซอร์แทนถ้ายังไม่มีลำโพงก็ได้)

วงจรต่อตามไดอะแกรมด้านล่างในหัวข้อ Schematics เลย ต่อง่ายมาก สเก็ตช์ตั้งค่าให้ใช้ Digital Output Pin 11 ในการขับลำโพง แต่ถ้าน้องอยากใช้พินอื่น ก็เลือกพินที่เหมาะสมแล้วไปเปลี่ยนค่าการ define '#define speaker' ในสเก็ตช์ตามต้องการได้เลย

ขอเตือนนิดนึงว่า อย่าเชื่อมต่อไมโครคอนโทรลเลอร์เข้ากับอะไรนอกจากลำโพง/บัซเซอร์ตามที่บอกไว้นี้โดยตรง เดี๋ยวจะพังเอาง่ายๆ นะตัวนี้

หลักการต่อวงจร: โปรแกรมตั้งค่าให้ใช้ Digital Pin 11 ในการขับลำโพงผ่านตัวต้านทาน 100 โอห์ม ซึ่งทำหน้าที่เป็น ตัวต้านทานจำกัดกระแส (Current Limiting Resistor) เพื่อป้องกันพอร์ต I/O ของ Arduino รับโหลดเกิน ขั้วบวกของลำโพงต่อเข้ากับตัวต้านทาน ส่วนขั้วลบต่อลง Ground (GND)

คำเตือนจากรุ่นพี่สายช่าง: ห้ามต่อลำโพงเข้ากับพิน Arduino โดยตรงโดยไม่มีตัวต้านทานเด็ดขาด! อิมพีแดนซ์ต่ำของลำโพงอาจทำให้กระแสไหลเกินขีดจำกัดของไมโครคอนโทรลเลอร์ (ปกติไม่เกิน 40mA ต่อพิน) ซึ่งอาจทำให้ชิพเสียหายถาวรได้ สู้งานนะน้อง

สเก็ตช์ – จุดที่ควรสังเกต

สเก็ตช์นี้พัฒนามาจากบทความ/โปรเจคเก่าที่ชื่อ 'Let's Make Music' โดยดัดแปลงเฉพาะเพื่อจัดการกับเพลย์ลิสต์ของโน้ตเพลง (เพลงคริสต์มาส) โครงสร้างสเก็ตช์ตรงไปตรงมา น่าตามอ่านได้ไม่ยากสำหรับคนที่สนใจ แต่มันมีเทคนิคหนึ่งที่เด็ดมากและน่าจดจำเป็นพิเศษ นั่นคือการใช้พอยน์เตอร์ (Pointer) ในการกำหนดและจัดการเพลย์ลิสต์ (พอยน์เตอร์ภาษา C++)

ถ้าน้องเชี่ยวชาญเรื่องพอยน์เตอร์ รู้จุดประสงค์และวิธีใช้แล้ว ก็ข้ามส่วนนี้ไปได้เลย แต่ถ้ายังไม่แน่ใจ ลองสละเวลาสักนิดมาเจาะลึกดูว่าสเก็ตช์นี้ใช้มันยังไง

มาเริ่มกันที่การออกแบบที่ใช้พอยน์เตอร์:

อย่างแรกเลย โน้ตเพลงแต่ละเพลงถูกกำหนดด้วยฟังก์ชันของมันเอง โดยฟังก์ชันเหล่านี้จะถูกวางไว้ท้ายสุดของสเก็ตช์ เพื่อไม่ให้โค้ดส่วนหลักรกและช่วยให้อ่านง่ายขึ้น

ต่อไป ในส่วนบนสุดของสเก็ตช์ เราจะประกาศ "forward references" สำหรับฟังก์ชันเพลงคริสต์มาสแต่ละอัน เพราะเราต้องการเอามาใส่ลงในอาร์เรย์เพลย์ลิสต์ของเรา (ซึ่งประกาศเป็นอาร์เรย์ของพอยน์เตอร์) เราทำขั้นตอนนี้ ก่อน การประกาศอาร์เรย์พอยน์เตอร์เพลย์ลิสต์ เพื่อให้แน่ใจว่าคอมไพเลอร์รู้จักที่อยู่ (address) ของฟังก์ชันเพลงตอนที่มันต้องการใช้แล้ว

จริงๆ เราก็ประกาศฟังก์ชันเพลงทั้งหมดไว้ข้างบนสุดเลยก็ได้นะ แต่แบบนั้นโค้ดส่วนบนจะรกตายห่า เลยใช้วิธีประกาศ forward reference แทน สะอาดตาดี

หลังจากประกาศ forward references ของฟังก์ชันเพลงแล้ว สเก็ตช์ก็จะประกาศเพลย์ลิสต์ (play_list) ซึ่งเป็นอาร์เรย์ของพอยน์เตอร์ (*) ขนาดเท่ากับจำนวนเพลง (ฟังก์ชันเพลง) ที่มีในสเก็ตช์ (ถ้าใช้ของที่มากับบอร์ดเลย จะมี 10 เพลง) จากนั้นก็กำหนดค่า (preset) แต่ละตำแหน่งในอาร์เรย์เพลย์ลิสต์ด้วยที่อยู่ของฟังก์ชันเพลงแต่ละอัน (ทำได้เพราะเราประกาศ forward reference ไว้แล้ว)

เสร็จแล้วเราก็สามารถเอาเพลย์ลิสต์ไปใช้งานได้ตามต้องการเลย เช่น สับเปลี่ยนลำดับ (สุ่ม), ไล่เรียกฟังก์ชันเพลงทีละอันจากในลิสต์ เป็นต้น

สรุปสั้นๆ มาไล่ดูโค้ดแต่ละส่วนในสเก็ตช์ที่ทำให้เราใช้เพลย์ลิสต์แบบพอยน์เตอร์ได้กัน

1. Forward References

การประกาศ Forward Reference ไปยังฟังก์ชันเพลง - ดู syntax ให้ดีนะน้อง

// Forward references for music score functions
// which are defined at end of the sketch to avoid clutter...
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();

ใน C++ นะ "Forward Reference" คือการบอกคอมไพเลอร์ล่วงหน้าว่า "เฮ้ย ฟังก์ชันพวกนี้มีอยู่จริงนะ แต่อยู่ข้างหลังโค้ด" ทำให้ส่วนหัวของโค้ดเราไม่รกด้วยโค้ดโน้ตเพลงยาวเฟื้อย

2. การสร้างอาร์เรย์ของฟังก์ชันพอยน์เตอร์

การประกาศเพลย์ลิสต์ - เราประกาศอาร์เรย์พอยน์เตอร์เพลย์ลิสต์ พร้อมกำหนดค่าเริ่มต้นด้วยที่อยู่ของฟังก์ชันเพลง (ดู syntax อีกรอบ)

//
// Declare pointer array and preset with addresses
// of each of the carol score functions.
// Note that the oder of the presets is not important
// as the play_list array is constantly randomised
// by the shuffle_play_list function.
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
};

ไวยากรณ์ในการประกาศอาร์เรย์ของพอยน์เตอร์อาจดูแปลกๆ หน่อย แต่มีสองจุดสำคัญที่ต้องจับตา:

  1. ตัวอักษร '*' ที่อยู่หน้าชื่ออาร์เรย์ บอกคอมไพเลอร์ว่าอาร์เรย์ 'play_list' นี้เป็นอาร์เรย์ของพอยน์เตอร์
  2. ตัวอักษร '()' ที่ต่อท้าย บอกคอมไพเลอร์อีกทีว่าที่อยู่อ้างอิงพวกนี้เป็นฟังก์ชัน (ก็ forward reference ของเพลงเรานั่นแหละ) ทำให้เราสามารถเรียกใช้ฟังก์ชันที่ถูกกำหนดโดยสมาชิกในเพลย์ลิสต์ได้โดยตรง - ดูใน Main Loop ด้านล่าง

สังเกตด้วยว่าเราสามารถกำหนดค่าเริ่มต้นให้อาร์เรย์พอยน์เตอร์เพลย์ลิสต์ได้ ก็เพราะว่า เราได้ประกาศ forward references ไปยังฟังก์ชันเพลงไว้ก่อนแล้ว คอมไพเลอร์จะกำหนดค่าแต่ละตำแหน่งในอาร์เรย์เพลย์ลิสต์ด้วยที่อยู่ของฟังก์ชันเพลงที่ประกาศไว้ ลำดับที่เรากำหนดค่าเริ่มต้นไม่สำคัญ เพราะเพลย์ลิสต์จะถูกสับเปลี่ยนลำดับแบบสุ่มอยู่ตลอดเวลา

ทำไมต้องใช้โครงสร้างแบบนี้? นี่คือกุญแจสำคัญของความยืดหยุ่น อาร์เรย์ play_list เก็บ "ที่อยู่" ของฟังก์ชันเพลงต่างๆ ไว้ การทำแบบนี้ทำให้เราสามารถ "สับเปลี่ยน" ข้อมูลในอาร์เรย์ได้ง่ายๆ เหมือนสับเปลี่ยนตัวเลขธรรมดา แต่ผลลัพธ์ที่ได้คือลำดับการเล่นเพลงที่เปลี่ยนไปจริงๆ งานเข้าสินะน้อง!

3. การทำงานของลูปหลัก

เรียบร้อย! ตอนนี้เราจัดการเพลย์ลิสต์ได้ตามใจแล้ว มาดูส่วนลูปหลักกันดีกว่า:

ลูปหลัก - ออกแบบมาให้เรียบง่ายสุดๆ จนแทบไม่น่าเชื่อ:

void loop() {
     do {
       // Process the play_list by selecting each of its preset and randomsised
       // elements, and execute each respective music score funcion.
       for (uint8_t carol = 0; carol < num_carols; carol++) {
         play_list[carol](); // execute this ([carol]) music score function
         wait(3);            // wait a short time between carols
       }
       shuffle_play_list();  // randomise the play_list for the next play_list pass
       wait(3);              // wait a short time before playing the new playlist
     } while (true);
}

ลูปหลักก็แค่ไล่เรียกฟังก์ชันเพลงแต่ละอันในเพลย์ลิสต์ — จำ syntax นี่ไว้นะตัว! เราใช้เพลย์ลิสต์เหมือนเป็นฟังก์ชันหลอกๆ เลยต้องมี ' () ' ตอนเรียก

หลังจากเล่นเพลงครบทุกอันในลิสต์แล้ว เราจะสับเปลี่ยนลำดับเพลย์ลิสต์แบบสุ่มด้วย `shuffle_play_list()` ทำให้รอบต่อไปลำดับเพลงเปลี่ยนไปเลย

ในบรรทัด play_list[carol](); โปรแกรมจะกระโดดไปที่ตำแหน่งหน่วยความจำที่เก็บโค้ดเพลงนั้น แล้วเริ่มเล่นทันที พอจบหนึ่งรอบ ฟังก์ชัน shuffle_play_list() จะสลับตำแหน่ง Address ภายในอาร์เรย์ใหม่หมด ทำให้ลำดับเพลงในรอบต่อไปเปลี่ยนไปโดยสิ้นเชิง

พอยน์เตอร์มีประโยชน์ได้อีกหลายแบบ หวังว่าจากตัวอย่างในสเก็ตช์นี้ น้องจะเห็นภาพและเอาไปประยุกต์ใช้ในงานของตัวเองได้นะ

เอาล่ะ จบเรื่องนี้แค่นี้ก่อน หวังว่าน้องจะสนุกกับเพลงคริสต์มาส และอาจจะลองเพิ่มเพลงของตัวเองลงไปในสเก็ตช์ด้วยก็ดี!

แล้วต่อยังไงดี?

ลองเพิ่มเพลงของตัวเองดูสิ! หรือถ้าอยากลุยเรื่องดนตรีบน Arduino ต่อ ลองดูบทความอื่นๆ ที่พี่เขียนไว้:

Let's Make Music - สเก็ตช์ที่รองรับโน้ตดนตรีหลายอ็อกเทฟ และเข้าใจค่าความยาวโน้ตมาตรฐาน มีฟีเจอร์ทุกอย่างเหมือนในสเก็ตช์เพลงคริสต์มาสนี้ บวกเพิ่มเติมอีกนิดหน่อย ลองดูเอกสารสรุปฟังก์ชัน (crib sheet) ของบทความนี้ ซึ่งเป็นเหมือนดัชนีค้นหาฟังก์ชันใช้ง่ายๆ

Music & Lights Workbench - สเก็ตช์ที่ออกแบบมาเพื่อชวนคนที่ไม่เคยเขียนโปรแกรมมาก่อน ให้มาลองเล่นลองเรียนรู้ผ่านการสร้างเมโลดี้และจุดไฟ LED แบบเป็นขั้นเป็นตอน

ข้อมูล Frontmatter ดั้งเดิม

apps:
  - "1x Arduino IDE"
author: "ronbentley1"
category: "Audio & Sound"
components:
  - "1x Arduino UNO"
  - "1x Resistor 100 ohm"
  - "1x Speaker: 0.25W, 8 ohms"
  - "2x Jumper wires (generic)"
description: "โค้ดเทพๆ ที่ทำให้ Arduino ร้องเพลงคริสมาสต์ได้เลยจ้า ใช้อุปกรณ์ง่ายๆแค่สปีกเกอร์วงจรเบสิค งานง่ายแต่หล่อ วัยรุ่นต้องลองจัดไป!"
difficulty: "Easy"
documentationLinks: []
downloadableFiles:
  - "https://projects.arduinocontent.cc/72c35e69-3503-4b3c-9794-18afdd6e8133.ino"
encryptedPayload: "U2FsdGVkX1+SYetUfeR4wIhu4sJhNOwLZg3rU1EF3+DUvCna/rrIFkteDvwLNokAhwFvJN2gOFI8dTmGG09kDqJSzU5YxFjRWm+LhYAcj/E="
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/christmas-carols-on-arduino-457131_cover.jpg"
lang: "en"
likes: 1
passwordHash: "c796cccb37ae425c8780d129839bb136f2ef81695a7d573fbd0a1b4b18e6c5b3"
price: 245
seoDescription: "Play festive Christmas carols on Arduino with a simple speaker circuit. Easy Arduino project with provided sketch."
tags:
  - "music"
  - "tones"
  - "carols"
  - "christmas"
  - "xmas"
  - "scores"
title: "งานคริสต์มาสตึงๆ กับ Arduino มาปล่อยเพลงฮิตกัน!"
tools: []
videoLinks: []
views: 4892