ארדואינו למתחילים: קוד לנגן המוזיקה (פוסט המשך)

בפוסט הקודם חיברנו ארדואינו לרמקול, בתיווך פוטנציומטר לשליטה בעוצמת הקול. כתבנו קוד בסיסי ביותר להשמעה של צליל קצר מדי שניה, רק כדי לבדוק שהעסק עובד. בפוסט הנוכחי אציג את הקוד המלא של תוכנה, ש"יושבת" על אותה חומרה בדיוק אך מנגנת את "יונתן הקטן", "London bridge" – ובשינויים קלים, גם מנגינות נוספות.

תו יחיד

ראשית, אנחנו זקוקים לקוד שמסוגל להשמיע תווים. לצורך הפשטות, נתמקד (אם הבנתי נכון את המינוחים) בסולם דו מז'ור באוקטבה הראשונה – או בעברית, חמשת הקלידים שעליהם מנגנים את "יונתן הקטן" והשניים שמימינם.

כזכור, פקודת הארדואינו tone משמיעה צליל בתדר שאנחנו קובעים. חיפוש זריז באינטרנט יעלה את התדרים התואמים לדו, רה, מי ושאר התווים בסולם שלנו. במקום לכתוב אותם כל פעם מחדש בקוד התוכנה, יצרתי מערך שיכיל אותם. התדרים מעוגלים למספר השלם הקרוב ביותר:

int freq[7] = {440, 494, 262, 294, 330, 349, 392};

ההגדרה הזו היא גלובלית לתוכנית, ולכן נמצאת מחוץ לפונקציות setup ו-loop. "אבל רגע," יזעקו מביני עניין, "המספר הראשון תואם לתו 'לה', השני ל'סי', ואילו התדר של 'דו' מופיע שלישי!" ובכן, זה נכון – ועשיתי את זה לצורך תאימות עם הנוטציה הלועזית, שקוראת לדו C, לרה D וכן הלאה. אחרי סול (G) מגיע לה שמסומן כ-A, וסי הוא B. במערך הנוכחי, מסיבה שתובהר בהמשך, העדפתי לשמור על הסדר האלפביתי דווקא.

ועכשיו, הנה קוד לפונקציה שמשמיעה תו. הפונקציה מקבלת כפרמטר תו ASCII יחיד, מפענחת אותו לפי הנוטציה הלועזית שהזכרנו ומשמיעה את התו המוזיקלי המתאים. אם הפרמטר אינו תואם שום תו שהוגדר, הפונקציה משתיקה את הרמקול.

void playNote(char note) {
  if ((note >= 'A') && (note <= 'G'))
   tone(TONE_PIN, freq[note - 65], NOTE_LENGTH_MS);
   else {
     noTone(TONE_PIN);
     delay(NOTE_LENGTH_MS);
  }
}

הקבוע TONE_PIN הוגדר בראש התוכנית ומציין את החיבור אליו מחובר הרמקול. הקבוע NOTE_LENGTH_MS מציין את משך הצליל (או הדממה). החישוב note – 65 הוא זה שממיר את קוד ASCII למיקום במערך התווים: ב-ASCII, הקוד של האות A הוא 65, של B הוא 66 וכן הלאה. עכשיו אתם מבינים למה התחלתי את המערך ההוא ב-A?

רצף תווים

עם הפונקציה הזו, יצירה של מנגינה – רצף תווים – היא פשוטה מאד. ניקח מחרוזת (מערך תווים), נעבור עליה תו אחד כל פעם וננגן אותו, עם הפסקה קבועה בין התווים. הנה מחרוזת לדוגמה, ופונקציית הנגינה:

char LONDON[] = "GAGFEFG DEF EFG GAGFEFG D G EC";

void playMelody(char *c) {
  while (*c != 0) {
    playNote(*c++);
    delay(NOTE_INTERVAL_MS);
  }
}

יש לנו כאן קבוע חדש – NOTE_INTERVAL_MS – ומצביע (c) שמקודם בצעד אחד כל פעם לצורך ניגון התווים. במחרוזת יש פה ושם תווי רווח: בפונקציה הקודמת, כפי שראינו, תו שאינו בין A ל-G אינו מוכר ולכן יושמע כדממה, מה שייתן לנו למעשה הפסקה גדולה יותר בין תווים.

הדבר האחרון שהוספתי לתוכנית הוא קוד שבוחר בכל פעם מחדש, אקראית, איזו מנגינה לנגן. אפשר, אמנם, להשתמש בפונקציה random, אבל בשביל הכיף בחרתי במשהו אקראי באמת – קלט שמגיע מחיבור אנלוגי (בדוגמה זו: A0) שלא מחובר לכלום. הפקודה הבאה מבודדת את הביט הנמוך של הקלט, ובוחרת לפיו בין שתי המנגינות שמוגדרות בקוד:

if (analogRead(A0) & 1) playMelody(YONATAN);
 else playMelody(LONDON);

בסרטון בפוסט הקודם חיברתי ל-A0 חוט חשמל ירקרק, שישמש כ"אנטנה" וייצור קצת יותר רעש ואקראיות בחיבור.

את הקוד המלא של התוכנה אפשר להוריד מכאן. מכיוון שזה הפוסט הראשון שאני מעלה בענייני קוד, אשמח לפידבקים: זה היה ברור? מסובך מדי? פשוט מדי? הערות, שאלות והצעות לשיפורים יתקבלו בברכה בתגובות!

להרשמה
הודע לי על
14 Comments
מהכי חדשה
מהכי ישנה לפי הצבעות
Inline Feedbacks
הראה את כל התגובות

היי, הקוד הראשוני שנתת עבד והשמיע צליל במשך כל שניה, למה כשאני משתמש בקודים של השירים הם לא מתנגנים ובהעברה יש הודעת כשל?

הקובץ עם הקוד כבר לא קיים יותר

לא הבנתי בפונקציה הראשונה מהו המשתנה note שהגדרת כchar. הוא לא משתנה לאורך כל הפונקציה ולכן לא הבנתי איך כל התווים יכולים להיות מוגדרים במצב כזה?
בפונקציה השניה כנל לא הבנתי מהו המשתנה c* ולמה אי אפשר לשנות אותו (רושם שגיאה במחרוזת יהונתן)
את note אפשר לשנות.
תודה

הי עידו,
הבלוג שלך מעולה והוא פשוט כיף טהור!!

בתור אחד עם אפס ניסיון בארדואינו (מן הסתם כמו רוב קוראי הבלוג) הייתי שמח להתחיל להתעסק בעצמי עם הרכיבים ולהתחיל לעשות מה שאתה מתאר.
הבעיה היא שבכל פוסט אתה מוסיף עוד ועוד רכיבים (LED-ים, רמקול וכו'). הייתי שמח אם היית יכול לרכז בבת-אחת את כל הרכיבים שאתה מתכנן להשתמש בהם בזמן הקרוב, כך שאוכל לעשות קנייה אחת מרוכזת.

תודה

יותר נכון אי אפשר להוריד את ה* מהמצביע, למה זה?

אולי תרשום את הקוד בתוך "תיבת קוד" על מנת שהייה קצת קריא יותר. לא שהייתה לי בעיה לקרוא, אבל שאלת !!

הי עידו, לא נראה מסובך מדי, הבעיה היחידה היא המעברים בין אנגלית לעברית ורוחב השורות (או שאולי סתם טעית באיות), מה שגורם לTONE_PIN להיות כתוב פתאום כNOTE_PIN…
בקיצור, עדיף להוריד את הקוד מהלינק שלך ואז להביט בו מול הביאורים שלך.
🙂