Bemerkung

Hallo und herzlich willkommen in der SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasten-Community auf Facebook! Tauche tiefer ein in Raspberry Pi, Arduino und ESP32 mit gleichgesinnten Technikbegeisterten.

Warum beitreten?

  • Expertenunterstützung: Löse Probleme nach dem Kauf und technische Herausforderungen mit Hilfe unserer Community und unseres Teams.

  • Lernen & Teilen: Tausche Tipps und Tutorials aus, um deine Fähigkeiten zu verbessern.

  • Exklusive Vorschauen: Erhalte frühzeitigen Zugang zu neuen Produktankündigungen und exklusiven Einblicken.

  • Sonderrabatte: Genieße exklusive Rabatte auf unsere neuesten Produkte.

  • Festliche Aktionen und Verlosungen: Nimm an Verlosungen und Feiertagsaktionen teil.

👉 Bereit, mit uns zu entdecken und zu kreieren? Klicke auf [hier] und tritt noch heute bei!

23. „Twinkle, Twinkle, Little Star“ spielen

In dieser Lektion tauchen wir in die faszinierende Verbindung zwischen Musik und Technologie ein. Du lernst, wie verschiedene Tonhöhen durch Frequenzänderungen erzeugt werden und wie dieses Prinzip mit einem Mikrocontroller wie Arduino genutzt werden kann, um einen Piezo-Summer zu steuern. Am Ende dieser Lektion wirst du nicht nur die Grundlagen der musikalischen Frequenzen verstehen, sondern auch in der Lage sein, ein Arduino so zu programmieren, dass es eine einfache Melodie spielt.

Am Ende dieser Lektion wirst du in der Lage sein:

  • Zu verstehen, wie musikalische Tonhöhen spezifischen Frequenzen entsprechen.

  • Arrays zu verwenden, um Noten zu speichern und zu manipulieren und dadurch das Programmieren zu vereinfachen.

  • Ein Programm zu schreiben und auszuführen, das einen passiven Piezo-Summer steuert, um „Twinkle, Twinkle, Little Star“ zu spielen.

Musikalische Frequenzen und Klangerzeugung

_images/7_sound.png

Verschiedene Musikinstrumente erzeugen unterschiedliche Tonhöhen durch Änderung der Frequenz. Zum Beispiel bringen beim Klavier die Tasten die entsprechenden Saiten zum schnellen Schwingen, wodurch spezifische Tonhöhen erzeugt werden. Wissenschaftler und Musiker haben verschiedene Methoden der Musiksstimmung und Tonhöhenstandards entwickelt, indem sie diese Schwingungsfrequenzen präzise gemessen haben.

Wenn du ein Arduino oder einen anderen Mikrocontroller steuerst, um ein elektrisches Signal an einen Piezo-Summer zu senden, schwingt die Membran des Summers je nach Frequenz des Signals schnell hin und her und erzeugt so einen Klang. Ein auf 440 Hz eingestelltes Signal erzeugt zum Beispiel die Standard-Tonhöhe „A4“, die als Referenzpunkt in der Musikstimmung dient. Wenn die Frequenz steigt oder fällt, ändert sich auch die erzeugte Tonhöhe entsprechend, sodass eine Bandbreite von tiefen bis hohen Tönen in einer musikalischen Komposition erreicht wird.

In der westlichen Musik umfasst eine Oktave 12 Tonhöhen (Halbtöne), von C bis B und dann zurück zu einem höheren C.

Zum Beispiel beträgt die Frequenz von Mittlerem C (meistens als C4 bezeichnet) etwa 261,63 Hz. Die Frequenz einer Note kann mit der folgenden Formel berechnet werden:

_images/7_music_format.png

wobei f_0 die Referenztonhöhe (in der Regel A4 mit einer Frequenz von 440 Hz) ist, und n die Anzahl der Halbtonschritte von der Referenztonhöhe bis zur Zieltonhöhe angibt (positive Zahlen bedeuten eine Erhöhung, negative Zahlen eine Absenkung). Mit dieser Formel können wir die Frequenz jeder Note berechnen.

Hier ist eine Frequenztabelle:

  • C (C4): 262 Hz (actually close to 261.63 Hz, rounded to 262)

  • D (D4): 294 Hz

  • E (E4): 330 Hz

  • F (F4): 349 Hz

  • G (G4): 392 Hz

  • A (A4): 440 Hz

  • B (B4): 494 Hz

Nun werden wir die Geheimnisse der Noten mit Arduino und einem Piezo-Summer erkunden. Lass uns den passiven Summer die ersten zwei Zeilen von „Twinkle, Twinkle, Little Star“ spielen lassen:

Bemerkung

Die Melodie von „Twinkle, Twinkle, Little Star“ basiert auf einfachen Notenkombinationen, und die Melodie dieses Liedes geht auf Variationen von „Ah vous dirai-je, Maman“ des französischen Komponisten Wolfgang Amadeus Mozart zurück, was sie für Anfänger sehr geeignet macht.

Hier ist das grundlegende Notenblatt für „Twinkle, Twinkle, Little Star“, einschließlich jeder Note:

C C G G A A G
F F E E D D C
G G F F E E D
G G F F E E D
C C G G A A G
F F E E D D C

Schaltungsaufbau

Benötigte Komponenten

1 * Arduino Uno R3

1 * Steckbrett

1 * Passiver Summer

Jumper-Kabel

list_uno_r3

list_breadboard

list_passive_buzzer

list_wire

1 * USB-Kabel

list_usb_cable

Schritt-für-Schritt Aufbau

Diese Lektion verwendet die gleiche Schaltung wie 12. Sirenensound.

_images/16_morse_code.png

Codeerstellung - Array

  1. Öffne die Arduino IDE und starte ein neues Projekt, indem du „Neue Datei“ aus dem Menü „Datei“ wählst.

  2. Speichere deinen Sketch unter dem Namen Lesson23_Array mit Strg + S oder durch Klicken auf „Speichern“.

  3. Erstelle nun zu Beginn des Codes ein Array, das die Noten von „Twinkle, Twinkle, Little Star“ speichert.

// Definiere die Frequenzen für die Noten der C-Dur-Tonleiter (Oktave beginnend mit mittlerem C)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // Hohes C

// Definiere ein Array, das die Reihenfolge der Noten in der Melodie enthält
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };

Ein Array ist eine Datenstruktur, die in der Arduino-Programmierung verwendet wird, um mehrere Elemente des gleichen Typs zu speichern. Es ist ein sehr grundlegendes und leistungsfähiges Werkzeug, das bei richtiger Verwendung die Programmier-Effizienz und -Leistung erheblich steigern kann. Arrays können Elemente wie ganze Zahlen, Gleitkommazahlen und Zeichen speichern.

Ähnlich wie bei der Erstellung von Variablen und Funktionen wird auch bei der Erstellung eines Arrays der Array-Typ und der Array-Name angegeben - int melody[].

Die Elemente innerhalb der {} Klammern werden als Array-Elemente bezeichnet, beginnend bei Index 0, sodass melody[0] dem ersten c(262) entspricht und melody[13] ebenfalls c(262) ist.

  1. Nun drucke die Elemente an den Indizes 0 und 13 des Arrays melody[] im seriellen Monitor aus.

// Definiere die Frequenzen für die Noten der C-Dur-Tonleiter (Oktave beginnend mit mittlerem C)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // Hohes C

// Definiere ein Array, das die Reihenfolge der Noten in der Melodie enthält
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };

void setup() {
  // Stelle deinen Setup-Code hier bereit, der einmal ausgeführt wird:
  Serial.begin(9600);  // Initialisiere die serielle Kommunikation mit einer Baudrate von 9600
  Serial.println(melody[0]);
  Serial.println(melody[13]);
}

void loop() {
  // Stelle deinen Hauptcode hier bereit, der wiederholt ausgeführt wird:
}
  1. Nach dem Hochladen des Codes auf das Arduino Uno R3 öffne den seriellen Monitor, und du wirst zweimal 262 sehen.

262
262
  1. Wenn du jedes Element im Array melody[] einzeln ausdrucken möchtest, musst du zuerst die Länge des Arrays kennen. Du kannst die Funktion sizeof() verwenden, um die Anzahl der Elemente im Array zu berechnen.

void setup() {
  // Stelle deinen Setup-Code hier bereit, der einmal ausgeführt wird:
  Serial.begin(9600);  // Initialisiere die serielle Kommunikation mit einer Baudrate von 9600
  int notes = sizeof(melody) / sizeof(melody[0]); // Berechne die Anzahl der Elemente
}
  • sizeof(melody) gibt die Gesamtanzahl der Bytes zurück, die alle Elemente im Array belegen.

  • sizeof(melody[0]) gibt die Anzahl der Bytes zurück, die ein einzelnes Element im Array belegt.

  • Die Division der Gesamtbytes durch die Bytes pro Element ergibt die Gesamtanzahl der Elemente im Array.

  1. Verwende dann eine for-Schleife, um durch die Elemente des Arrays melody[] zu iterieren und drucke sie mit der Funktion Serial.println() aus.

// Definiere die Frequenzen für die Noten der C-Dur-Tonleiter (Oktave beginnend mit mittlerem C)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // Hohes C

// Definiere ein Array, das die Reihenfolge der Noten in der Melodie enthält
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };

void setup() {
  // Stelle deinen Setup-Code hier bereit, der einmal ausgeführt wird:
  Serial.begin(9600);                              // Initialisiere die serielle Kommunikation mit einer Baudrate von 9600
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke die Frequenz jeder Note auf dem seriellen Monitor aus
    Serial.println(melody[i]);
  }
}

void loop() {
  // Stelle deinen Hauptcode hier bereit, der wiederholt ausgeführt wird:
}
  1. Nach dem Hochladen des Codes auf das Arduino Uno R3 öffne den seriellen Monitor, und du wirst die Elemente im Array melody[] nacheinander gedruckt sehen.

262
262
392
392
440
440
392
349
349
330
...

Fragen

Du kannst auch Operationen auf den Elementen im Array durchführen, wie zum Beispiel das Ändern zu Serial.println(melody[i] * 1.3);. Welche Daten wirst du erhalten und warum?

Codeerstellung - „Twinkle, Twinkle, Little Star“ spielen

Da wir nun ein solides Verständnis für die Erstellung von Arrays, den Zugriff auf Array-Elemente und die Berechnung ihrer Längen und Operationen haben, wollen wir dieses Wissen anwenden, um einen passiven Piezo-Summer so zu programmieren, dass er „Twinkle, Twinkle, Little Star“ mit gespeicherten Frequenzen und Intervallen spielt.

  1. Öffne den zuvor gespeicherten Sketch, Lesson23_Array. Wähle „Speichern unter…“ aus dem Menü „Datei“ und benenne es in Lesson23_Little_Star um. Klicke auf „Speichern“.

  2. Definiere zuerst den Pin für den Summer.

const int buzzerPin = 9;  // Weist dem Pin 9 die Konstante für den Summer zu
  1. Erstelle nun ein weiteres Array, um die Dauer der Noten zu speichern.

// Richte die Reihenfolge der Noten und deren Dauer in Millisekunden ein
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };
int noteDurations[] = { 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000 };
  1. Verschiebe nun einen Teil des Codes von void setup() in void loop().

void setup() {
  // Stelle deinen Setup-Code hier bereit, der einmal ausgeführt wird:
  Serial.begin(9600);                              // Initialisiere die serielle Kommunikation mit einer Baudrate von 9600
}

void loop() {
  // Stelle deinen Hauptcode hier bereit, der wiederholt ausgeführt wird:
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke die Frequenz jeder Note auf dem seriellen Monitor aus
    Serial.println(melody[i]);
  }
}
  1. Kommentiere im for-Statement den Druckbefehl aus und verwende die tone()-Funktion, um die Noten abzuspielen.

void loop() {
  // Hauptcode, der wiederholt ausgeführt wird:
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke jede Frequenz der Noten auf den seriellen Monitor
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // Spiele die Note
  }
}
  1. Um die Melodie natürlicher klingen zu lassen, füge nach jeder Note eine kurze Pause hinzu. Hier multiplizieren wir die Notendauer mit 1,30, um das Intervall zu berechnen und die Melodie weniger gehetzt wirken zu lassen.

void loop() {
  // Hauptcode, der wiederholt ausgeführt wird:
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke jede Frequenz der Noten auf den seriellen Monitor
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // Spiele die Note
    delay(noteDurations[i] * 1.30);                // Warte, bevor die nächste Note gespielt wird
  }
}
  1. Verwende die noTone()-Funktion, um die Tonausgabe am aktuellen Pin zu stoppen. Dies ist ein notwendiger Schritt, um sicherzustellen, dass jede Note klar gespielt wird, ohne in die nächste überzugehen.

void loop() {
  // Hauptcode, der wiederholt ausgeführt wird:
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke jede Frequenz der Noten auf den seriellen Monitor
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // Spiele die Note
    delay(noteDurations[i] * 1.30);                // Warte, bevor die nächste Note gespielt wird
    noTone(buzzerPin);                             // Stoppe das Spielen der Note
  }
}
  1. Dein vollständiger Code ist unten dargestellt. Sobald du den Code auf das Arduino Uno R3 hochgeladen hast, wirst du hören, wie der Summer „Twinkle Twinkle Little Star“ spielt.

int buzzerPin = 9;  // Weist den Pin 9 als Konstante für den Summer zu

// Definiere die Frequenzen für die Noten der C-Dur-Tonleiter (Oktave beginnend mit mittlerem C)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // Hohes C

// Richte die Reihenfolge der Noten und deren Dauer in Millisekunden ein
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };
int noteDurations[] = { 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000 };

void setup() {
  // Initialisiere den Setup-Code, der einmal ausgeführt wird:
  Serial.begin(9600);  // Initialisiere die serielle Kommunikation mit einer Baudrate von 9600
}

void loop() {
  // Hauptcode, der wiederholt ausgeführt wird:
  int notes = sizeof(melody) / sizeof(melody[0]);  // Berechne die Anzahl der Elemente
  // Schleife durch jede Note im Melody-Array
  for (int i = 0; i < notes; i = i + 1) {
    // Drucke jede Frequenz der Noten auf den seriellen Monitor
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // Spiele die Note
    delay(noteDurations[i] * 1.30);                // Warte, bevor die nächste Note gespielt wird
    noTone(buzzerPin);                             // Stoppe das Spielen der Note
  }
}
  1. Vergiss nicht, deinen Code zu speichern und deinen Arbeitsplatz aufzuräumen.

Frage

Wenn du den passiven Summer in der Schaltung durch einen aktiven Summer ersetzt, kannst du dann trotzdem „Twinkle Twinkle Little Star“ spielen? Warum?

Zusammenfassung

Nachdem der Unterricht nun beendet ist, haben wir in dieser Lektion gelernt, wie man Arrays verwendet, um Daten zu speichern, die Länge von Arrays zu berechnen, Elemente innerhalb eines Arrays zu indexieren und Operationen mit jedem Element durchzuführen. Indem wir Notenfrequenzen und Zeitintervalle in Arrays speicherten und sie mithilfe einer For-Schleife durchliefen, haben wir erfolgreich einen passiven Summer so programmiert, dass er „Twinkle, Twinkle, Little Star“ spielt.

Zusätzlich haben wir gelernt, wie man die Wiedergabe einer Note mit der noTone()-Funktion pausiert.

Diese Lektion hat nicht nur unser Verständnis von Array-Operationen und Kontrollstrukturen in der Programmierung vertieft, sondern auch gezeigt, wie diese Konzepte angewendet werden können, um Musik mit elektronischen Komponenten zu erzeugen. Auf unterhaltsame und ansprechende Weise verknüpften wir theoretisches Wissen mit praktischen Anwendungen.