pwm

Buzzer

Funktionsweise

Der Buzzer funktioniert wie ein Lautsprecher, jedoch mit dem Unterschied, dass dieser technisch gesehen nur einen einzigen Ton abspielen kann. Dank der Verwendung von PWM ist es jedoch möglich diesen auch so anzusteuern, dass damit entsprechende Melodien mit verschiedenen Tönen abgespielt werden können.

Die Klasse BuzzerComponent verwendet eine solche PWM-Implementation und bietet eine einfache Schnittstelle, mit welcher beliebige Töne abgespielt werden können. Um eine zuverlässige Wiedergabe der gewünschten Frequenzen zu ermöglichen, verwendet diese Komponente Hardware PWM. Mehr Informationen dazu finden sich auf der verlinkten Seite.

Um das mühsame Abtippen der einzelnen Frequenzen zu erleichtern, wird zudem eine Hilfsklasse Note bereitgestellt. Dieser Aufzählungstyp enthält alle bekannten Musiknoten von B0 zu DS8 und bildet diese auf die entsprechenden Tonfrequenzen ab, sodass diese direkt an die playTone() Methode übergeben werden können.

Voraussetzungen

DIP Switches

Für diese Komponente werden keine spezifischen DIP-Switches benötigt, sodass diese in der Standardkonfiguration belassen werden können:

ON(links)12345678ON(rechts)12345678

Verwendung

Nachfolgend wird die Verwendung der Klasse com.pi4j.crowpi.components.BuzzerComponent Javadoc beschrieben.

Konstruktoren

KonstruktorBemerkung
BuzzerComponent(com.pi4j.context.Context pi4j)Initialisiert einen Buzzer mit dem Standard-Pin für den CrowPi.
BuzzerComponent(com.pi4j.context.Context pi4j, int address)Initialisiert einen Buzzer mit einem benutzerdefinierten Pin.

Methoden

MethodeBemerkung
void playTone(int frequency)Spielt dauerhaft einen Ton mit der gewünschten Frequenz.
void playTone(int frequency, int duration)Spielt einen Ton mit der gewünschten Frequenz für die angegebene Zeit in Millisekunden.
void playSilence()Schaltet den Buzzer dauerhaft aus.
void playSilence(int duration)Schaltet den Buzzer dauerhaft aus und wartet für die angegebene Zeit in Millisekunden.

Beispielapplikation

Die nachfolgende Beispielapplikation steuert den Buzzer via PWM an, um eine bekannte Melodie abzuspielen. Hierfür wird ein Enum namens Note verwendet, welches die entsprechenden Musiknoten auf eine Frequenz [Hz] für PWM abbildet, dies klingt dann wie der gesuchte Ton. Die Applikation spielt nacheinander alle Noten ab, indem die Funktionen playTone sowie playSilence der Buzzer-Komponente verwendet werden.

Die Noten, die gespielt werden sollen, sind alle in der statischen Variable NOTES zu finden, welche durch die zusätzliche statische Variable mit dem Namen TEMPO um die Angabe der Notenlänge ergänzt wird. Die Applikation iteriert schliesslich mit einer for-Schleife über alle Noten und spielt diese mit der jeweiligen Länge ab.

Die effektive Länge in Millisekunden wird mit der Formel 1 / <tempo> berechnet, sodass ein Tempo-Wert von 4 einer Viertelnote mit 0.25 Sekunden Länge entspricht. Damit sich die Noten besser voneinander unterscheiden lassen, wird zudem jeweils noch eine kleine Pause mit dem Faktor 1.3 eingefügt.

Pfad zum Codebeispiel: src/main/java/com/pi4j/crowpi/applications/BuzzerApp.java
Auf GitHub ansehen
package com.pi4j.crowpi.applications;

import com.pi4j.context.Context;
import com.pi4j.crowpi.Application;
import com.pi4j.crowpi.components.BuzzerComponent;
import com.pi4j.crowpi.helpers.Note;

/**
 * Plays the Super Mario Overworld theme song over the builtin CrowPi buzzer.
 * Notes taken from https://www.hackster.io/jrance/super-mario-theme-song-w-piezo-buzzer-and-arduino-1cc2e4
 */
public class BuzzerApp implements Application {
    /**
     * Array of notes which should be played by this example
     */
    private static final Note[] NOTES = new Note[]{
        Note.E7, Note.E7, Note.PAUSE, Note.E7,
        Note.PAUSE, Note.C7, Note.E7, Note.PAUSE,
        Note.G7, Note.PAUSE, Note.PAUSE, Note.PAUSE,
        Note.G6, Note.PAUSE, Note.PAUSE, Note.PAUSE,

        Note.C7, Note.PAUSE, Note.PAUSE, Note.G6,
        Note.PAUSE, Note.PAUSE, Note.E6, Note.PAUSE,
        Note.PAUSE, Note.A6, Note.PAUSE, Note.B6,
        Note.PAUSE, Note.AS6, Note.A6, Note.PAUSE,

        Note.G6, Note.E7, Note.G7,
        Note.A7, Note.PAUSE, Note.F7, Note.G7,
        Note.PAUSE, Note.E7, Note.PAUSE, Note.C7,
        Note.D7, Note.B6, Note.PAUSE, Note.PAUSE,

        Note.C7, Note.PAUSE, Note.PAUSE, Note.G6,
        Note.PAUSE, Note.PAUSE, Note.E6, Note.PAUSE,
        Note.PAUSE, Note.A6, Note.PAUSE, Note.B6,
        Note.PAUSE, Note.AS6, Note.A6, Note.PAUSE,

        Note.G6, Note.E7, Note.G7,
        Note.A7, Note.PAUSE, Note.F7, Note.G7,
        Note.PAUSE, Note.E7, Note.PAUSE, Note.C7,
        Note.D7, Note.B6, Note.PAUSE, Note.PAUSE,
    };

    /**
     * Array of note types, e.g. 4 represents a quarter-note with 0.25s duration.
     * Must have the same length as the NOTES array.
     */
    private static final int[] TEMPO = new int[]{
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,

        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,

        9, 9, 9,
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,

        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,

        9, 9, 9,
        12, 12, 12, 12,
        12, 12, 12, 12,
        12, 12, 12, 12,
    };

    @Override
    public void execute(Context pi4j) {
        // Initialize buzzer component with default pin
        final var buzzer = new BuzzerComponent(pi4j);

        // Loop through all notes and play them one-by-one
        for (int i = 0; i < NOTES.length; i++) {
            // Calculate duration of note by dividing one second with tempo
            // Tempo represents the actual note type, e.g. tempo=4 -> quarter note -> 0.25s
            final var duration = 1000 / TEMPO[i];

            // Print note to standard output, play over buzzer
            System.out.print(NOTES[i] + "(" + duration + ") ");
            buzzer.playTone(NOTES[i].getFrequency(), duration);

            // Wait shortly between notes to make them more distinguishable
            buzzer.playSilence((int) (duration * 1.3));
        }
    }
}

Weitere Möglichkeiten

  • Das Beispiel kann einfach auf eine eigene Melodie angepasst werden, wofür jeweils lediglich die beiden Arrays NOTES und TEMPO angepasst werden müssen. Hier ist darauf zu achten, dass diese Arrays jeweils die gleiche Länge besitzen müssen, da jede Note auch eine dazugehörige Länge benötigt.

  • Statt dem Abspielen einer Melodie könnte auch eine Sirene mit einer for-Schleife implementiert werden. Hierfür muss die Frequenz manuell als Zahl angegeben werden statt der Verwendung der Note Klasse, welche sich beispielsweise mit dem Einsatz von for-Schleifen hoch- und runterzählen lässt.

  • Belegt man jeden Kopf der Button-Matrix mit einem eigenen Ton / Frequenz könnte man daraus seine eigene Melodie komponieren.