Wir bringen Ihnen die Flötentöne bei

Der Titel klingt etwas provokativ, denn der Spruch hat bekanntermaßen eine doppelte Bedeutung. Aber die ist hier ausdrücklich nicht gemeint. Wir wollen Ihnen zeigen, wie Sie Ihrem Micro Controller (AVR) Töne entlocken können. Die Idee zu dem Blog entstand, weil ich nach vielen Jahren, die ich mich mit Raspberry Pis und Micro Controllern beschäftige, gelernt habe, dass es aktive und passive Buzzer (Summer) gibt. Bislang habe ich offensichtlich nur zufällig alles richtig gemacht, wenn meine Schüler bis an die Schmerzgrenze meiner Ohren kleine Programme, z.B. ein Reaktionsspiel, programmiert und ausprobiert haben.

Verwendete Hardware

Beliebiger

Arduino kompatibler Micro Controller (AVR)

1

KY-012 Buzzer Modul aktiv

1

KY-006 Passiver Piezo Buzzer Alarm Modul

alternativ

35 in 1 Arduino Sensorenkit Modulkit und Zubehörkit für Arduino und andere Mikrocontroller


Bei dem Reaktionsspiel für zwei Spieler geht es darum, so schnell wie möglich seinen Taster zu drücken, wenn der Buzzer nach einer zufällig gewählten Zeit anfängt zu summen. Jetzt weiß ich: Dafür benötigt man einen aktiven Buzzer, wie er z.B. auch bei unserer Waschmaschine oder dem Wäschetrockner verbaut ist. Der aktive Buzzer hat eine eingebaute Schwingkreis-Elektronik (Oszillator), die bei Anlegen der Spannung von 3,3V oder 5V den Summton erzeugt. Bauartbedingt muss man hier deshalb auf die Polarität des Buzzers achten. Bei Auslieferung haben diese Buzzer meist einen kleinen Aufkleber mit dem Pluszeichen und „REMOVE SEAL AFTER WASHING“. Auch auf dem Bauteil selbst befindet sich ein +. Um das längere Beinchen zu erkennen, muss man zweimal hinschauen. Oder man nimmt gleich ein kleines Breakoutboard, wie es auch in dem 35 in 1 Sensor-Kit vorhanden ist; hier sind die Kontakte mit S (=Signal) und – (Minus) gekennzeichnet. Der dritte (mittlere) Anschluss ist nicht verbunden. Ausprobieren kann man den Buzzer mit einem einfachen Sketch wie Blink, bei dem der (fast) beliebige Pin mit digitalWrite() jeweils eine Sekunde auf HIGH und eine Sekunde auf LOW geschaltet wird.

Der passive Buzzer hat keinen eingebauten Oszillator; deshalb muss der Micro Controller diese Funktion übernehmen. Wenn man den obigen Sketch an einem passiven Buzzer ausprobiert, hört man im Sekundentakt ein leises Klicken, mehr nicht. Wenn man jedoch die Pausenzeit extrem verkürzt (delay(1) oder delay(2)), kann man einen Ton hören, dessen Frequenz wir leicht berechnen können: jeweils eine Millisekunde HIGH bzw. LOW bedeutet (unter Vernachlässigung der Ausführungszeit der weiteren Befehle) rund 500 Zyklen on/off je Sekunde, also 500Hz.

Mit folgendem kleinen Schnipsel Code kann man einen kurzen Warnton an dem (vorher definierten) Pin für den Buzzer erzeugen:

 int  var = 0;
 while (var < 100) {
   // do something repetitive 100 times
   digitalWrite(Buzzer, HIGH);
   delay(1);
   digitalWrite(Buzzer, LOW);
   delay(1);
   var++;
 }

Noch besser klappt die Tonerzeugung mit der Funktion tone(), welche jedoch immer nur ein Tonsignal (eine Frequenz) auf einem Pin erzeugen kann. Die folgenden Ausführungen gelten für den passiven Buzzer oder auch einen kleinen Lautsprecher, der über einen 100 Ohm-Widerstand an dem entsprechenden Pin angeschlossen wird.

Die Syntax lautet:

tone(pin, frequency)   oder
tone(pin, frequency, duration)

Die verwendeten Parameter sind:

pin: Der Arduino-Pin, auf dem der Ton generiert werden soll.
frequency: Die Frequenz des Tons in Hertz. Erlaubte Datentypen: unsigned int.
duration: Die Dauer des Tons in Millisekunden (optional). Erlaubte Datentypen: unsigned long.

Sofern kein dritter Parameter für duration angegeben wird, wird die Frequenz auf dem passiven Buzzer (oder alternativ einem kleinen Lautsprecher) ausgegeben, bis entweder eine neue Frequenzzuweisung erfolgt oder die Funktion noTone(pin) aufgerufen wird.

Wenn man den Parameter duration angibt, bedeutet das jedoch nicht (!), dass man auf diese Weise eine Tonleiter spielen kann. Die folgenden Töne „gehen unter“, sofern man nicht zusätzlich eine Pause mit delay() im Sketch einfügt. Dann aber kann man duration beim Spielen einer Melodie auch weglassen. Also: duration nur verwenden, wenn ein einzelner (Warn-) Ton für eine kurze Dauer ausgegeben werden soll.

Bezüglich der Frequenzen bleibt uns ein Ausflug in Musik bzw. Physik nicht erspart. Das Wenige, an das ich mich erinnere, sind Kammerton A = 440Hz sowie Halbierung bzw. Verdoppelung der Frequenz je Oktave. Es empfiehlt sich für die Variablennamen die amerikanische Schreibweise der Noten, also A4=440Hz, A3=220Hz, A5=880Hz. Mit den Zahlen sind die die jeweiligen Oktaven z.B. auf dem Klavier benannt. Für die übrigen Noten ergeben sich leider nicht immer ganzzahlige Frequenzen (zur Erinnerung: geforderter Datentyp= unsigned int); die Kakophonie ist also systembedingt unvermeidbar, aber da ja stets nur eine Frequenz gespielt wird, bleibt es erträglich und die Melodie bleibt erkennbar.

Am besten definiert man die Noten am Beginn des Sketches, z.B. für die vierte Oktave mit dem Kammerton A:

 #define NOTE_C4  262
 #define NOTE_CS4 277
 #define NOTE_D4 294
 #define NOTE_DS4 311
 #define NOTE_E4 330
 #define NOTE_F4 349
 #define NOTE_FS4 370
 #define NOTE_G4 392
 #define NOTE_GS4 415
 #define NOTE_A4 440
 #define NOTE_AS4 466
 #define NOTE_B4 494
 #define NOTE_C5 523

Oder man übernimmt aus dem Beispielsketch toneKeyboard die Datei pitches.h, in der alle Noten von 31 Hz bis 4,978 KHz definiert sind.

Beispiel toneKeyboard

Die Dateien für das Beispiel befinden sich im Programmordner für die Arduino IDE im Unterverzeichnis „examples“:

pitches.h

Kopieren Sie diese Datei und speichern Sie diese in Ihrem Sketch-Ordner; im Sketch dann mit #include "pitches.h" einbinden.

Im Internet hatte ich einen Sketch mit „Alle meine Entchen …“ gesehen, der sich fürchterlich anhörte, weil keine Pausen zwischen den einzelnen Noten eingefügt waren; das stört insbesondere, wenn eine Note wiederholt wird. Wer also wie bei einem Klavier einzelne Töne einer Melodie hören möchte, sollte nach dem ersten delay() die Klangausgabe mit noTone() und einer weiteren Pause stoppen. Beide Pausen zusammen bestimmen die Länge einer Note; deshalb sollte man diese Werte mit einer Variablen z.B. für die Anzahl Millisekunden einer Viertelnote festlegen.

Im folgenden Sketch habe ich „Alle meine Entchen …“ verschlimmbessert, die Dauer des hörbaren Tons mit int DELAY = 400; und der Pause mit int PAUSE = 100; festgelegt. Die Länge einer Viertelnote ist also 500ms. Wie oben beschrieben habe ich die Datei pitches.h im Sketch-Ordner abgelegt.

 /* Alle meine Entchen ... einmal abgespielt
  * Die Datei pitches.h muss sich im gleichen Dateiordner befinden
  */
 
 #include "pitches.h"
 
 int DELAY = 400;
 int PAUSE = 100;
 
 void setup() {
   tone(7, NOTE_C5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_D5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_E5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_F5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_G5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);
   tone(7, NOTE_G5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);  
   tone(7, NOTE_A5);
   delay(DELAY);  
   noTone(7);
   delay(PAUSE);
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_A5);
   delay(DELAY);  
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_G5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);    
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_A5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);  
   tone(7, NOTE_G5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);    
   tone(7, NOTE_F5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_F5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_F5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_F5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_E5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);    
   tone(7, NOTE_E5);
   delay(2*DELAY);
   noTone(7);
   delay(2*PAUSE);    
   tone(7, NOTE_G5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_G5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_G5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_G5);
   delay(DELAY);
   noTone(7);
   delay(PAUSE);    
   tone(7, NOTE_C5);
   delay(2*DELAY);
   noTone(7);
 }
 
 
 void loop() {
   //keine zu wiederholende Aktion
 }
Wie man sieht, kein Meisterstück der Programmierkunst. Aber sicherlich gut zu verwenden, wenn man eine Zweiton-Sirene wie in einem Rettungsfahrzeug simulieren möchte.

Für längere Melodien eignet sich die Vorgehensweise mit zwei Arrays besser, wie in unserem eBook beschrieben. Hier wird die im Internet veröffentlichte Melodie von Fluch der Karibik verlinkt:
https://github.com/xitangg/-Pirates-of-the-Caribbean-Theme-Song

Im ersten Array int notes[] sind die Noten aufgeführt, im zweiten int durations[] die jeweilige Tondauer. Wie bei anderen Beispielen wird die Melodie nur einmal gespielt, der Programmcode für die Ausführung befindet sich also in der void setup(); die void loop() bleibt leer.

Hier die Erklärungen zum Sketch:

Um die for-Schleife korrekt für alle Noten zu wiederholen, wird zunächst die Anzahl der Noten totalNotes mit der Funktion sizeof() ermittelt. Da diese Funktion die Anzahl der Bytes im Array liefert, muss noch durch die Anzahl der Bytes je Integer-Zahl, also durch 2, geteilt werden.

Die jeweiligen Werte aus den Arrays werden in den Variablen currentNote und wait zwischengespeichert. Bei Pausen, die im Array mit 0 gekennzeichnet sind, wird die Funktion noTone() aufgerufen.

Hier nur der Ausschnitt void setup() des Sketches:

 void setup()
 {
   const int totalNotes = sizeof(notes) / sizeof(int);
     
   // Loop through each note
   for (int i = 0; i < totalNotes; i++)
  {
     const int currentNote = notes[i];
     float wait = durations[i] / songSpeed;
     // Play tone if currentNote is not 0 frequency, otherwise pause (noTone)
     if (currentNote != 0)
    {
       tone(buzzer, notes[i], wait); // tone(pin, frequency, duration)
    }
     else
    {
       noTone(buzzer);
    }
     // delay is used to wait for tone to finish playing before moving to next loop
     delay(wait);
  }
 }

Es gibt weitere Programmbibliotheken für die ATmega328P/ATmega16U2 Mikrocontroller Boards (AVR) für die Ausgabe von Tönen, z.B. um zwei Töne gleichzeitig an verschiedenen Pins auszugeben. Aber die sind teilweise sehr komplex und im Ergebnis genauso ernüchternd wie die einfache Ausgabe von Tönen mit tone(). Unser vielseitiger Micro Controller eignet sich für viele Anwendungen, aber sicherlich nicht zum Synthesizer.

GPIO Zero Reaction Game für Raspberry Pi

Hier verlinke ich Ihnen noch ein kleines Spiel für den Raspberry Pi, für das neben dem Buzzer zusätzlich Taster und LEDs (nebst Widerständen) verwendet werden. Nähere Beschreibung finden Sie im Quellcode.

Download gpiozero_ReactionGame_noCheating.py

Fazit

Wer einen kurzen Warnton ausgeben möchte, sollte einen aktiven Buzzer verwenden, der wie eine LED (oder parallel dazu) ein- und ausgeschaltet wird. Dabei ist auf die Polarität zu achten.

Wer eine Zweiton-Sirene (die man deutlicher hört) oder eine kurze Erkennungsmelodie (Jingle) programmieren möchte, sollte zum passiven Buzzer oder einem kleinen Lautsprecher greifen.

Für arduinoProjekte für anfängerRaspberry pi

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert

Aanbevolen blog berichten

  1. Installeer ESP32 nu van de raad van bestuur
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP Programmeren via Wi-Fi