บทนำ:
การรับสัญญาณเสียงแล้วแปลงมันให้เป็นปฏิกิริยาทางภาพหรือกลไกเนี่ย มันสนุกโคตรๆ เลยนะน้อง ในโปรเจคนี้เราจะใช้ Arduino Mega มาต่อกับตัววิเคราะห์สเปกตรัม MSGEQ7 ที่รับสัญญาณเสียงเข้าไป แล้วทำการกรองแบบแบ่งแถบความถี่ (band pass filtering) เพื่อแบ่งสัญญาณออกเป็น 7 แถบความถี่หลัก จากนั้น Arduino จะวิเคราะห์สัญญาณอนาล็อกของแต่ละแถบความถี่ แล้วสร้างการตอบสนองขึ้นมา

วัตถุประสงค์:
โปรเจคนี้เราจะคุยกันถึง 3 โหมดการทำงาน:
- ต่อ LED เข้ากับขา PWM เพื่อให้มันขยับตามแถบความถี่
- ต่อ LED เข้ากับขาดิจิตอลธรรมดาเพื่อให้มันขยับตามแถบความถี่
- ต่อปั๊มน้ำเข้ากับ Arduino Mega ผ่านมอเตอร์ไดรเวอร์ แล้วให้มันขยับตามแถบความถี่ (จัดไปวัยรุ่น!)
ทฤษฎี:
ถ้าพูดถึงไอซี MSGEQ7 เนี่ย มันมีตัวกรองแบบแบ่งแถบความถี่ (band pass filter) อยู่ข้างใน 7 ตัวเลย มันจะแบ่งสัญญาณเสียงที่ป้อนเข้าไปออกเป็น 7 แถบหลัก: 63 Hz, 160 Hz, 400 Hz, 1 kHz, 2.5 kHz, 6.25 kHz และ 16 kHz

สัญญาณเอาต์พุตจากตัวกรองแต่ละตัวจะถูกเลือกให้ออกมาทางขาเอาต์พุตของไอซีผ่านทางมัลติเพล็กเซอร์ (multiplexer) มัลติเพล็กเซอร์ตัวนี้มีสายเลือก (selector lines) ที่ถูกควบคุมโดยตัวนับไบนารี (binary counter) ข้างใน ดังนั้นเราก็บอกได้ว่า ตัวนับควรนับจาก 0 ถึง 6 (000 ถึง 110) เพื่อให้ปล่อยสัญญาณผ่านมาแถบละครั้ง นั่นเลยทำให้ชัดเจนว่าโค้ดใน Arduino ของเราต้องสามารถรีเซ็ตตัวนับได้ทันทีที่มันนับถึงเลข 7

ถ้าเราดูที่วงจรของ MSGEQ7 เราจะเห็นว่าเราใช้ RC ในการปรับความถี่ (frequency tuner) เพื่อควบคุมนาฬิกาภายในของออสซิลเลเตอร์ แล้วก็ใช้องค์ประกอบ RC ในการกรองที่พอร์ตสัญญาณเสียงขาเข้า
ข้อได้เปรียบทางฮาร์ดแวร์: MSGEQ7
การคำนวณ Fast Fourier Transform (FFT) บน Arduino Uno นั้นช้าและกินแรมจนเกลี้ยงเลยจ้า ไอซี MSGE7 ตัวนี้แหละที่มาแก้ปัญหานี้ได้อย่างสวยงาม
- มันเป็นฮาร์ดแวร์อนาล็อกล้วนๆ น้องสามารถเสียบแจ็คหูฟังเข้าไปตรงๆ เลย
- มันจะแยกสัญญาณเสียงออกเป็น 7 แถบความถี่ (63Hz, 160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz, 16kHz) แบบเรียลไทม์
- ขา Strobe: Arduino จะส่งพัลส์ไปที่ขา Strobe ของ MSGEQ7 ทุกครั้งที่พัลส์มา ไอซีจะส่งแรงดันไฟตรง (0-5V) ออกมาทางขา
OUTซึ่งค่าก็จะสอดคล้องกับความดังของ แถบความถี่นั้นๆ พอดี - Arduino ของเราแค่ใช้
analogRead()เร็วๆ ในลูปforก็ดึงค่าความดังของทั้ง 7 แถบมาเก็บได้ในพริบตาแล้ว! สบายๆ
ขั้นตอนการทำ:
จากแหล่งข้อมูล เราจะเห็นว่าโค้ดต้นทางจัดการกับเอาต์พุตเป็นสัญญาณ PWM ที่ทำงานซ้ำๆ เราสามารถปรับเปลี่ยนโค้ดบางบรรทัดให้เหมาะกับเป้าหมายของเราได้
ขั้นแรก ต่อไอซี MSGEQ7 ลงบนเบรดบอร์ดเล็กตามแผนภาพวงจรนี้เลย:

สังเกตไว้นะว่า ถ้าเราใช้แจ็คสเตอริโอ เราสามารถเพิ่มตัวต้านทาน (Resistor) และตัวเก็บประจุ (Capacitor) สำหรับช่องสัญญาณที่สองได้
เราให้ไฟเลี้ยง MSGEQ7 จาก VCC (5 โวลต์) และ GND ของ Arduino แล้วก็ต่อ MSGEQ7 เข้ากับบอร์ด Arduino เองชอบใช้ Arduino Mega เพราะมันมีขา PWM เยอะ เหมาะกับโปรเจคนี้ เอาต์พุตของไอซี MSGEQ7 ต่อกับขาอนาล็อก A0, ขา STROBE ต่อกับขา 2 ของ Arduino Mega และขา RESET ต่อกับขา 3 ห้ามช็อตนะตัวนี้ ต่อให้ถูก! สู้งานนะน้อง
สั่งน้ำให้เต้นรำ!
หลังจากที่ MSGEQ7 คำนวณเลขให้เสร็จสรรพ ก็ถึงเวลาลงมือสร้างของจริงแล้วล่ะ หลักการมันง่ายนิดเดียว:
if (band63Hz > 800): เบสหนักมาแล้วจ้า! Arduino จะสั่งงานรีเลย์ 5V หรือ MOSFET ให้ทำงาน- รีเลย์หรือ MOSFET ก็จะไปเปิด ปั๊มน้ำจุ่ม 12V ให้ทำงาน
- น้ำก็จะพุ่งเป็นฟองตั้งฉีดขึ้นมาจากท่อ PVC ที่เราต่อเอง!
- ปั๊มน้ำอีก 6 ตัวที่เหลือ ก็จะถูกแมปให้ทำงานตามแถบความถี่อีก 6 แถบที่เหลือ
โหมดการทำงาน:
LEDs as PWM digital outputs: จากโค้ดตัวอย่าง เราสามารถต่อ LED เอาต์พุตเข้ากับขา 4 ถึง 10 ได้เลย
constintLED_pins[7] ={4,5,6,7,8,9,10};
แล้วเราก็จะเห็น LED สว่างจ้า-หรี่ลง ตามความแรงของแต่ละแถบความถี่ งานนี้จัดไปวัยรุ่น!

โค้ดตัวอย่างที่แนะนำก็ประมาณนี้:
#define msg7RESET 3
#define msg7Strobe 2
#define msg7DCout 0
const int LEDpins[7] = {4,5,6,7,8,9,10};
void setup()
{
for (int x=0; x<7; x++)
{
pinMode(LEDpins[x], OUTPUT);
}
pinMode(msg7RESET, OUTPUT);
pinMode(msg7Strobe, OUTPUT);
}
void
{
digitalWrite(msg7RESET, HIGH);
delay(5);
digitalWrite(msg7RESET, LOW);
for (int x = 0 ; x < 7 ; x++)
{
digitalWrite(msg7Strobe, LOW);
delayMicroseconds(35);
int spectrumRead = analogRead(msg7DCout);
int PWMvalue = map(spectrumRead, 0, 1024, 0, 255);
if (PWMvalue < 50)
PWMvalue = PWMvalue / 2;
analogWrite(LEDpins[x], PWMvalue);
digitalWrite(msg7Strobe, HIGH);
}
}
LEDs as digital outputs:
เราสามารถต่อ LED เอาต์พุตเข้ากับขาดิจิตอลขาไหนก็ได้ตามสะดวกเลยน้อง
constintLED_pins[7] ={40,42,44,46,48,50,52};
แล้วเราก็จะเห็นไฟ LED กะพริบติ๊กต๊อก ตามความแรงของแต่ละแถบความถี่ ห้ามช็อตนะตัวนี้!

ผลลัพธ์ของการต่อแบบนี้ดูได้จากวิดีโอด้านล่างเลย:
โค้ดตัวอย่างที่แนะนำก็ประมาณนี้ สู้งานนะน้อง:
#define msg7RESET 3
#define msg7Strobe 2
#define msg7DCout 0
const int LEDpins[7] = {40,42,44,46,48,50,52};
void setup()
{
for (int x=0; x<7; x++)
{
pinMode(LEDpins[x], OUTPUT);
}
pinMode(msg7RESET, OUTPUT);
pinMode(msg7Strobe, OUTPUT);
}
void
{
digitalWrite(msg7RESET, HIGH);
delay(5);
digitalWrite(msg7RESET, LOW);
for (int x = 0 ; x < 7 ; x++)
{
digitalWrite(msg7Strobe, LOW);
delayMicroseconds(35);
int spectrumRead = analogRead(msg7DCout);
int PWMvalue = map(spectrumRead, 0, 1024, 0, 255);
if (PWMvalue < 50)
PWMvalue = PWMvalue / 2;
analogWrite(LEDpins[x], PWMvalue);
digitalWrite(msg7Strobe, HIGH);
}
}
ปั๊มน้ำในฐานะเอาต์พุตแบบดิจิทัล (Pumps as Digital outputs):
ในโหมดสุดท้ายนี้ เราจะเชื่อมต่อโมดูลขับมอเตอร์ L298N เข้ากับขาเอาต์พุตของ Arduino กัน วิธีนี้ทำให้เราสามารถควบคุมการทำงานของปั๊มน้ำได้ตามสัญญาณที่ได้จากตัววิเคราะห์สเปกตรัม MSGEQ7 เลยจ้า

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

ถ้าน้องรันโค้ดแบบดิบๆ ปั๊มอาจจะทำงานไม่เต็มที่นะ เนื่องจากสัญญาณ PWM ที่ได้อาจจะต่ำเกินไปสำหรับตัวขับมอเตอร์ มันจะไม่สามารถขับมอเตอร์หรือปั๊มและจ่ายกระแสไฟที่เหมาะสมได้ พี่เลยแนะนำให้เพิ่มค่า PWM โดยการคูณค่าที่อ่านได้จาก A0 ด้วยตัวคูณที่มากกว่า 1.3 ซักหน่อย จะได้ช่วยให้การแมปค่าพอดีกับตัวขับมอเตอร์มากขึ้น พี่แนะนำให้ใช้ประมาณ 1.4 ถึง 1.6 ครับ หรืออีกวิธีคือเราสามารถรีแมปค่า PWM ให้อยู่ในช่วง 50 ถึง 255 เพื่อให้แน่ใจว่าค่า PWM จะใช้ได้ดี
โค้ดที่พี่แนะนำจะเป็นประมาณนี้:
#define msg7RESET 3
#define msg7Strobe 2
#define msg7DCout 0
const int LEDpins[7] = {40,42,44,46,48,50,52};
const int Pumppins[7] = {4,5,6,7,8,9,10};
void setup()
{
for (int x=0; x<7; x++)
{
pinMode(LEDpins[x], OUTPUT);
}
pinMode(msg7RESET, OUTPUT);
pinMode(msg7Strobe, OUTPUT);
}
void
{
digitalWrite(msg7RESET, HIGH);
delay(5);
digitalWrite(msg7RESET, LOW);
for (int x = 0 ; x < 7 ; x++)
{
digitalWrite(msg7Strobe, LOW);
delayMicroseconds(35);
int spectrumRead = analogRead(msg7DCout*1.45);
if (spectrumRead >= 1023)
{
spectrumRead = 1023;
}
int PWMvalue = map(spectrumRead, 0, 1024, 50, 255);
if (PWMvalue < 75)
PWMvalue = PWMvalue / 2;
analogWrite(Pumppins[x], PWMvalue);
analogWrite(LEDpins[x], PWMvalue);
digitalWrite(msg7Strobe, HIGH);
}
}
เราสามารถต่อ LED ไปกับขาเอาต์พุตของตัวขับมอเตอร์ได้เหมือนกัน แต่ว่า LED อาจจะกระพริบไม่สวยเท่าเดิม เพราะค่า PWM ถูกปรับให้สูงขึ้นแล้ว พี่เลยแนะนำให้ต่อ LED ไว้กับขาดิจิทัลหมายเลข 40 ถึง 52 ไปเลยดีกว่า จะได้ไม่ต้องมานั่งงง
อุปกรณ์ที่ต้องใช้ (จัดไปวัยรุ่น!)
ถ้าอยากสร้างระบบน้ำพุดนตรี (audio-reactive fountain) ให้สมบูรณ์แบบ น้องต้องมีของพวกนี้ครบๆ นะ:
- Arduino Uno/Nano/Mega: หัวใจหลักของระบบ ตัวควบคุมเลย
- ไอซี MSGEQ7 + ตัวเก็บประจุและตัวต้านทาน (Capacitors/Resistors) ที่จำเป็น
- ปั๊มน้ำขนาดเล็ก 12V แบบจุ่มได้ (Mini Submersible Water Pumps) (x7)
- MOSFETs หรือ Relay Boards หรือ L298N Motor Drivers: ไว้จัดการกับแรงดันไฟของปั๊มให้ปลอดภัย ห้ามช็อตนะตัวนี้!
- อ่างพลาสติก, ท่อ PVC, และสายยาง: สำหรับสร้างตัวน้ำพุดนตรี สู้งานนะน้อง!