เปียโนตัวนี้มันสามารถเล่นโน้ตที่ความถี่ระหว่างคีย์สองปุ่มได้เลยนะเว้ย! มันเป็นคีย์บอร์ดแบบสัมผัสที่ตรวจจับการแตะหลายจุดพร้อมกันได้ และวัดแรงกดได้ด้วย แต่ในบทเรียนนี้ เราจะใช้แค่การแตะจุดเดียวและความเข้มเดียวเท่านั้น การทำมันขึ้นมาจะต้องมีความรู้พื้นฐานเกี่ยวกับ Arduino นิดหน่อยสำหรับการดีบั๊ก
อยากเห็นขั้นตอนการทำแบบจบในคลิปเดียวมั้ย? ดูวิดีโอนี้เลย (สปีดแรงไปหน่อย)!
ฟีเจอร์เด็ด:
- เปียโนที่รับอินพุตจากการสัมผัส
- เล่นความถี่ระหว่างคีย์สองปุ่มได้
- ปรับช่วงความถี่ได้ตามชอบ
- การตอบสนองต้องเร็วพอ (ห้ามช้าเป็นเต่า!)
มันทำงานยังไง?
ก่อนอื่น เราแยกปัญหาออกเป็นส่วนๆ แล้วค่อยแก้ทีละจุด สำหรับการทำเปียโนของเรา งานหลักๆ มีดังนี้:
- วัด/ประมาณตำแหน่งที่สัมผัส: ตรงนี้เราไม่ได้ต้องการแค่ค่าสัมผัสเดียว แต่ต้องวัดตำแหน่งการสัมผัสหลายจุดที่เป็นค่าแบบอนาล็อกด้วย จะได้จัดการกับความถี่ระหว่างคีย์ได้ การทำงานนี้รวมถึงการรับข้อมูลจากคีย์และประมวลผลข้อมูลนั้น
- คำนวณคีย์และความถี่: หลังจากได้ข้อมูลจากคีย์ทั้งหมดแล้ว เราต้องแมปค่าเหล่านั้นให้เป็นความถี่เฉพาะ เรารู้กันดีว่าความถี่ของเปียโนเพิ่มขึ้นแบบเรขาคณิต (Geometric progression) หมายความว่าต้องคูณด้วยตัวประกอบบางตัวเพื่อให้ได้ความถี่ของคีย์ถัดไป
- สร้างเสียงและกำจัดสัญญาณรบกวน (ในข้อมูล): หลังจากคำนวณความถี่แล้ว เราต้องสร้างสัญญาณรูปคลื่นประมาณของความถี่นั้น นอกจากนี้ยังมีสัญญาณรบกวนที่อาจเกิดขึ้นเมื่อไม่ได้สัมผัส ซึ่งต้องกำจัดออกไป
หลักการทำงาน 1: การวัดตำแหน่งสัมผัส
เราจะใช้ capacitive touch สำหรับงานนี้ โดยจะใช้อาร์เรย์ของแผ่นโลหะหลายแผ่น แต่ละแผ่นจะทำหน้าที่เป็นเซ็นเซอร์สัมผัส
เซ็นเซอร์สัมผัสก็คือเซ็นเซอร์วัดความจุไฟฟ้า (capacitor sensor) นั่นแหละ เราจะวัดการเปลี่ยนแปลงของความจุไฟฟ้า (ซึ่งเกิดจากการสัมผัส) โดยใช้ Arduino เราใช้พินสองขา โดยต่อกับตัวต้านทาน (Resistor) ค่าสูง เราจะสั่งให้พินหนึ่งเป็นสถานะสูง (HIGH) และเริ่มจับเวลา เพื่อดูว่าอีกพินหนึ่งใช้เวลานานแค่ไหนถึงจะเปลี่ยนเป็นสถานะสูง เวลานี้ที่เราเรียกว่า "rise time" ขึ้นอยู่กับความจุไฟฟ้าและความต้านทาน ตรงนี้ความต้านทานคงที่ แต่ความจุไฟฟ้าจะแปรผันตามการสัมผัส
ข้อมูลเทคนิคเพิ่มเติม: โครงสร้างของ CapacitiveSensor.h
น้องจะเอาแผ่นฟอยล์อะลูมิเนียมไปเสียบตรงๆ เข้าขา Digital Input ของ Arduino ไม่ได้เด็ดขาด! มันจะ "ลอย" (float) สุดๆ แล้วอ่านค่าแบบมั่วๆ เอาได้ โปรเจคนี้ต้องใช้ขา Arduino 2 ขาสำหรับแต่ละ "คีย์" (Key) นะ: ขาส่ง (Sender Pin) และขารับ (Receiver Pin) จากนั้นก็ต่อตัวต้านทานค่าสูงๆ (เช่น 1 เมกะโอห์ม) ระหว่างสองขานั้น แล้วค่อยเอาแผ่นเทปทองแดงหรือฟอยล์อะลูมิเนียมไปต่อตรงๆ กับขารับ
หลักการฟิสิกส์เบื้องหลัง: Arduino จะยิงพัลส์ไฟฟ้า 5V ออกมาจากขาส่งด้วยความเร็วสูง แล้วรอดูว่าขารับจะตรวจจับพัลส์นั้นได้ช้าเร็วแค่ไหน พอนิ้วน้องแตะลงบนแผ่นฟอยล์ ร่างกายน้องจะทำตัวเหมือนตัวเก็บประจุ (Capacitor) ขนาดใหญ่ที่ต่อลงกราวด์ มันจะไปขโมยประจุไฟฟ้า ทำให้การถ่ายโอนพัลส์ผ่านตัวต้านทานช้าลง! ไลบรารีนี้ก็จะวัดความช้าที่เพิ่มขึ้นนี่แหละ
long fleshReading = cs_4_2.capacitiveSensor(30); // ยิงพัลส์ความเร็วสูง 30 ลูก!
if (fleshReading > 500) {
// ตรวจจับเนื้อหนังมนุษย์ได้! พัลส์ถูกทำให้ช้าลงโดยมวลชีวภาพ
tone(8, 261); // เล่นโน้ต 'C4'
}
ในรูปตัวอย่าง เราทำคีย์ไว้ประมาณ 12 คีย์ และมีการสัมผัสที่บริเวณระหว่างแผ่นที่ 6 กับ 7 เนื่องจากเราต้องการข้อมูลแบบต่อเนื่อง และไม่สนใจแค่ว่ามันอยู่ที่คีย์ที่ 6 หรือ 7 แบบตายตัว แต่เราอยากรู้ตำแหน่งที่แน่นอนของการสัมผัสระหว่าง 6-7 มากกว่า ขั้นแรกคือหาคีย์ที่ตรวจจับการสัมผัสได้สูงสุดก่อน จากนั้นก็คำนวณตำแหน่งการสัมผัสระหว่างคีย์โดยใช้คณิตศาสตร์พื้นฐาน
หลักการทำงานส่วนที่ 2: การกำหนดความถี่และการสร้างเสียงโทน
หลังจากคำนวณเลขคีย์ได้แล้ว เราก็ต้องคำนวณความถี่ที่สัมพันธ์กับคีย์นั้น ความถี่ของคีย์จะเรียงกันในรูปแบบความก้าวหน้าทางเรขาคณิต ฟังก์ชันคำนวณแสดงไว้ในรูป
ในฟังก์ชันด้านบน "n" คือเลขคีย์ ดังนั้นเมื่อใส่เลขคีย์เข้าไป เราก็จะคำนวณความถี่ได้
ตรงนี้เราจะแมปคีย์ที่ 1-18 (สมมติว่าบอร์ดเรามีแผ่นสัมผัส 18 แผ่น) ไปยังคีย์เป้าหมายใดๆ ก็ได้ที่เราต้องการ และเพราะอุปกรณ์เราเป็นแบบอนาล็อก เราไม่จำเป็นต้องจำกัดช่วงไว้แค่ 18 คีย์ แต่สามารถใช้ช่วงไหนก็ได้ ตัวอย่างเช่น ถ้าน้องอยากเล่นเสียงคีย์ที่ 40 ถึง 70 บนคีย์บอร์ดเป้าหมาย โดยแมปมันลงบนแผ่นสัมผัส 1-18 ของเรา แผ่นซ้ายสุดก็จะเล่นเสียงความถี่ของคีย์ที่ 40 และแผ่นขวาสุดจะเล่นเสียงความถี่ของคีย์ที่ 70
เราจะใช้ฟังก์ชัน tone() โดยตรงสำหรับสร้างความถี่ มันจะสร้างคลื่นสี่เหลี่ยม (square wave) ของความถี่ที่กำหนด เพื่อประสบการณ์ที่ดีขึ้น เราสามารถตั้งค่า duty cycle สำหรับควบคุมระดับเสียงตามแรงกดของการสัมผัสได้ด้วย
รายละเอียดเทคนิคเพิ่มเติม: การสร้าง Linear Synthesizer
เพราะว่าแผ่นเซ็นเซอร์ (sensing plates) เราตัดเป็นรูปร่างอะไรก็ได้ น้องเลยไม่ต้องยึดติดกับปุ่มกดแบบเดิมๆ เลย สร้างเป็นคีย์แบบสไลด์ต่อเนื่องไปเลยก็ได้โคตรเท่ โค้ดของเราก็จะถูกปรับให้แมปค่าจากแต่ละเซ็นเซอร์ไปเป็นโน้ตดนตรีเฉพาะ เช่น CapSensor1 จะเล่นความถี่ 261Hz (โน้ต C), CapSensor2 เล่น 293Hz (โน้ต D) ไปเรื่อยๆ แบบนี้เลย ผลลัพธ์ที่ได้คือซินธิไซเซอร์สุดล้ำ ที่เล่นเสียงเพลงได้ทันทีที่น้องลูบมือไปบนพื้นผิวเรียบๆ ที่มีลายวงจรโลหะซ่อนอยู่ข้างใต้!
ขั้นตอนที่ 1: เตรียมบอร์ด
เริ่มจากเอาไม้บล็อกมา แล้วติดฟอยล์อลูมิเนียมลงไป ไม่มีข้อจำกัดเรื่องขนาดนะ เอาเศษฟอยล์ส่วนเกินออก แล้วตัดให้เหลือประมาณ 30% ของความกว้างตามรูปที่ 4 พื้นที่ว่างตรงนี้แหละที่เราจะเอาไว้ติดตัวต้านทาน (Resistor)
ทีนี้ก็ทำเครื่องหมายห่างกัน 12mm ตามความยาวของบอร์ดเลย จะลองทำคีย์ให้เล็กลงกว่านี้เพื่อผลลัพธ์ที่ดีขึ้นก็ได้นะ หลังจากนั้นก็ตัดฟอยล์ตามเส้นที่ทำไว้ เราจะได้แผ่นแยกๆ กันหลายแผ่น ซึ่งจะเอาไว้ใช้เป็นคีย์สัมผัส (touch key/plates) จากนั้นก็ปิดทับฟอยล์อลูมิเนียมทั้งหมดด้วยเทปพลาสติก จะได้ไม่ต้องสัมผัสแผ่นโลหะโดยตรง
สำคัญมาก: แผ่นทุกแผ่น (ทุกคีย์) ต้องแยกวงจรไฟฟ้าออกจากกันนะจ๊ะ
ขั้นตอนที่ 2: ต่อวงจรไฟฟ้า
- ตรวจสอบให้แน่ใจว่าแผ่นทุกแผ่นแยกจากกันจริงๆ ต้องไม่มีไฟฟาต่อถึงกันนะ ตรวจสอบทุกแผ่นหลังทำทุกขั้นตอนเลย
- ต่อตัวต้านทาน 87K เข้ากับทุกแผ่น แล้วบัดกรีให้แน่นหนา
- ต่อปลายอีกข้างของตัวต้านทานทุกตัวเข้าด้วยกัน (common) แล้วต่อเข้าไปที่ขา 4 ของ Arduino จากนั้นก็ดึงสายจากทุกแผ่น (ตรงปลายอีกข้างของตัวต้านทานที่ไม่ได้ต่อ common) ออกมา
- ต่อขา 12 ของ Arduino เข้ากับลำโพง (speaker)
การเชื่อมต่อ:
- KEY 1 ไปที่ A6
- KEY 2 ไปที่ 13
- KEY 3 ไปที่ A5
- KEY 4 ไปที่ A4
- KEY 5 ไปที่ A3
- KEY 6 ไปที่ A2
- KEY 7 ไปที่ A1
- KEY 8 ไปที่ A7
- KEY 9 ไปที่ 2
- KEY 10 ไปที่ 3
- KEY 11 ไปที่ 5
- KEY 12 ไปที่ 6
- KEY 13 ไปที่ 7
- KEY 14 ไปที่ 8
- KEY 15 ไปที่ 9
- KEY 16 ไปที่ 10
- KEY 17 ไปที่ 11
น้องจะต่อขาเองก็ได้นะ แต่ต้องจัดให้ค่าของคีย์ที่ 'i' ในอาร์เรย์ตรงกับ x[i] ให้ถูกต้องด้วยล่ะ วางแผนดีๆ หน่อย!
รายละเอียดทางเทคนิคเพิ่มเติม: เมทริกซ์สถาปัตยกรรมการสัมผัส
- Arduino Uno/Nano: ความเร็วของโปรเซสเซอร์มาตรฐานทำงานได้พอดีกับคณิตศาสตร์จับเวลาพัลส์
- ตัวต้านทานค่าสูง (87K ถึง 10M Ohm): จำเป็นมากๆ เพื่อให้ค่าคงที่ RC ช้าพอให้สัญญาณนาฬิกา 16MHz จับการเปลี่ยนแปลงเล็กน้อยจากความจุไฟฟ้าของร่างกายมนุษย์ได้
- เทปทองแดงหรือฟอยล์อะลูมิเนียมหนาๆ: ทำหน้าที่เป็นแผ่นเซ็นเซอร์สัมผัสทางกายภาพ
- ออดเพียโซหรือลำโพงแบบขยายสัญญาณ: ต่อกับขา Digital สำหรับสร้างโทนเสียง
ขั้นตอนที่ 3: เข้าใจโค้ด:
เริ่มแรกเลย น้องต้องติดตั้งไลบรารี CapacitanceSensor ก่อนนะ
1. กำหนดช่วงคีย์:
float N_min=48; float N_max=70;
ตรงนี้คือการเลือกช่วงของคีย์เปียโน ตัวอย่างในโค้ดนี้คือแมปคีย์ที่ 48 ถึง 70 ลงบนแผ่นเซ็นเซอร์ของเรา
2. การทำให้เรียบ (Smoothing):
int smooth=2;
ค่านี้คือค่าการทำให้เรียบ ยิ่งค่ามากก็ยิ่งลื่นไหล แต่ก็จะตอบสนองช้าลงหน่อยนะ เอาไว้ลดการกระตุกของค่าอ่าน
3. กำหนดขาเซ็นเซอร์ความจุ:
CapacitiveSensor i20 = CapacitiveSensor(4,A6);
CapacitiveSensor i13 = CapacitiveSensor(4,13);
.
.
.
CapacitiveSensor i11 = CapacitiveSensor(4,11);
ส่วนนี้คือการกำหนดขาต่างๆ ให้เป็นอินพุตเซ็นเซอร์ โดยขา 4 เป็นขาส่งสัญญาณร่วม (common transmitter pin)
4. การแมปค่า:
N_map();
ฟังก์ชันนี้ทำหน้าที่แมปช่วงความถี่ตามค่า N_min และ N_max ลงบนแต่ละแผ่นเซ็นเซอร์
5. อ่านค่า:
ฟังก์ชัน Read_val() นี้คือโค้ดที่อ่านค่าจากแต่ละแผ่นเซ็นเซอร์
x[1] = i20.capacitiveSensor(30);
x[2] = i13.capacitiveSensor(30);
..
x[18] = i11.capacitiveSensor(30);
6. key_calc(): ฟังก์ชันนี้ทำการคำนวณหาคีย์ที่ถูกสัมผัสจากค่าต่ำสุดและสูงสุดของเซ็นเซอร์
for(int z=1;z<19;z++)
{ if(x[z]>t1){t1=x[z];t4=z;}
โค้ดส่วนนี้ตรวจจับแผ่นเซ็นเซอร์ที่มีค่าการสัมผัสสูงสุด
kt=((x[t4-1]*N[t4-1])+(x[t4]*N[t4])+(x[t4+1]*N[t4+1]))/(x[t4-1]+x[t4]+x[t4+1]);
สมการนี้เอาแผ่นที่มีค่าสูงสุดบวกกับแผ่นข้างเคียงมาคำนวณหาตำแหน่งสัมผัสที่แม่นยำและต่อเนื่อง โดยใช้ค่าเฉลี่ยถ่วงน้ำหนักของตำแหน่งและความเข้มของแต่ละแผ่น
7. freq_calc(): ทีนี้เราก็ต้องคำนวณความถี่ของคีย์นั้นๆ
float n;n=(key-49)/12;
freq=m*220*pow(2,n);
ตรงนี้ 'key' คือเลขคีย์ที่คำนวณได้ ส่วนตัวแปร m นี่มันจะกลายเป็น 1 ก็ต่อเมื่อค่าอินพุตจากการสัมผัสสูงพอ เอาไว้เป็น Noise Gate น่ะ ดังนั้นถ้าไม่มีใครแตะจริงจัง ค่าความถี่ก็จะเป็น 0
ขั้นตอนที่ 4: อัพโหลดโค้ดและดีบั๊ก
หลังจากอัพโหลดโค้ดแล้ว มันอาจจะทำงานหรือไม่ทำงานก็ได้ อย่าเพิ่งท้อนะ นี่คือปัญหาบางส่วนที่พี่เจอตอนทำโปรเจคนี้
- ปัญหาคลาสสิกเลยคือการต่อสายระหว่าง Arduino กับแผ่นเซ็นเซอร์หลวม! ถ้าต่อไม่ดี เสียงจะขาดเป็นห้วงๆ
- ถ้าแผ่นเซ็นเซอร์สองแผ่นที่อยู่ใกล้กันแตะกันเอง มันจะมีความจุไฟฟ้าเท่ากัน ทำให้อ่านค่าผิด
- ลำโพงที่ใช้อาจไม่เหมาะกับ Arduino น้องอาจต้องเปลี่ยนลำโพงหรือต่อวงจรขยายสัญญาณเพิ่ม
เปียโนนี้อาจไม่ทำงานกับแบตเตอรี่ นะ เพราะขาดกราวด์ที่ดี มันจะทำงานได้สุดๆ ถ้าใช้ไฟจาก PC เพราะมีกราวด์ที่ชัดเจน
วิธีตรวจหาปัญหา:
- เปิดฟังก์ชัน
print_raw()แล้วลองแตะทุกแผ่นพร้อมกัน ดูซิว่าแผ่นไหนไม่ตอบสนอง - อัพโหลดโค้ดแบบอื่นเพื่อทดสอบแผ่นเซ็นเซอร์แต่ละแผ่นแบบตัวต่อตัวก็ได้
มาทำเปียโนกันเถอะ: ตอนจบ...
ข้อจำกัดของเจ้านี่ก็มีอยู่นะ:
- เสียงมันฟังดูไม่หวานเท่าเปียโนจริงหรอก เพราะมันเป็นคลื่นสี่เหลี่ยม (square wave) ธรรมดา ในขณะที่เปียโนจริงๆ มันเป็นการผสมของฮาร์มอนิกหลายตัว
- กดคีย์พร้อมกันหลายปุ่มไม่ได้นะตัวนี้ (ห้ามช็อตนะ!)
- ความดังของเสียงมันปรับตามแรงกดไม่ได้ ตั้งไว้เท่าไหร่ก็เท่านั้น
ก็ลองปรับช่วงคีย์ (key range) กับค่าความลื่น (smoothness number) ตามสไตล์ตัวเองเลย แล้วก็ไปไล่หาตำแหน่งโน๊ตต่างๆ เอาไว้ จะได้เอาไปเล่นเพลงโปรดได้ สู้งานนะน้อง!