3.2 Wählscheibentelefon steuert DFPlayer Mini (🚧)
Synopsis: Exit-Room Wählscheiben-Telefon mit Arduino Uno und DFPlayer Mini
Wählscheibentelefone mit 📖 Impulswahl sind eine alte Technik.
Im Bild ist ein ausgemustertes Amtsstubentelefon meines Vaters zu sehen. Eine Wählscheibe erzeugt entsprechend der Ziffer Impulse, die mit einer recht einfachen Schaltung ausgewertet für verschiedene Anwendungen verwendbar sind z.B.
- Exit-Room
- Geo-Caching
- Rätsel
Die Audioausgabe geschieht über einen 🔗DFPlayer Mini.

🚨 Tastentelefone funktionieren mit dieser Schaltung nicht! Auch nicht wenn sie auf Impulswahl umgestellt wurden. Sie benötigen eine höhere Spannung als 5V.
Auswerteschaltung für Wählscheiben-Telefone mit Impulswahl
Das Prinzip der alten Technik wird hier kurz angerissen:
- Mit zwei Leitungen (a/b) wird das Telefon angeschlossen. Bei Kabel ohne Stecker findet man oft die Leitungsfarben Weiß a, Braun b, gelb E und grün bzw. blau. für W. Nimm die weiße und braune Leitung als a und b.
- Liegt der Hörer drauf wartet das Telefon auf eine Wechselspannung zum Klingeln.
- Wird der Hörer abgenommen fließt ein Sprechstrom über die Leitungen a/b.
- Funktion der Wählscheibe siehe 📖Nummernschalter

Ein Wählscheibentelefon an Telefon a/b anschließen, dabei ist es egal ob La mit lila bzw. schwarz und Lb mit schwarz bzw. lila verbunden wird.
Wenn kein Stecker und eine Dose verwendet wird nimm die weiße und braune Leitung.
Bei einem 📖 TAE-Stecker an La und Lb in der Dose verkabeln.
| Nummer | Bezeichnung | Beschreibung | Farbe |
|---|---|---|---|
| 1 | La oder a1 | a-Leitungsader | weiß |
| 2 | Lb oder b1 | b-Leitungsader | braun |
| 3 | W | Externer Wecker/Klingel | grün oder blau |
| 4 | E | Erde für Nebenstelle | gelb |
Wie die Software die gewählte Ziffer zu erkennen versucht

Mit einem Oszilloskop aufgezeichnet:
So sieht das Signal bei der Wahl der Ziffer 3 beim alten Amtsstubentelefon aus.
Beim Loslassen und Zurückdrehen der Wählscheibe wird die Verbindung der Leitungen a und b drei mal unterbrochen, danach wird auf den Sprechstrom geschaltet, siehe 📖 Kabeltelefone.
Alte Telefone haben teilweise schlechte Kontakte, ein simulierter Tiefpass glättet die Messungen. Der kurze 0-Impuls am Ende hätte 4 statt 3 ergeben. Deshalb verwende ich zur Auswertung den Analog-Digital-Wandler des Arduino und glätte die Messwerte.

Das Telefon bekommt über den Widerstand R3 mit 330Ω 5V Spannung. Wenn der Hörer abgenommen ist sinkt die Spannung auf den Wert für das Sprechen. Beim Wählen wechselt die Spannung zwischen 0V und 5V. Ein AD-Wandler digitalisiert die Spannung auf den Wertebereich 0..1023. Eine zusätzliche mathematische Glättung filtert Kontaktstörungen aus.
Messwerte nach der Glättung.
#define ADC_ABGEHOBEN 900 // wenn unter diesem Wert, ist Hoerer abgehoben
#define ADC_WAEHLSCHEIBE 200 // wenn unter diesem Wert, ist Wahlkontakt geschlossen
Library für DFPlayer Mini in Arduino laden und SD-Karten bespielen
Über den Arduino Bibliotheksverwalter die DFPlayerMini_Fast Library laden.
Details: DFPlayer Mini und Library DFPlayer Mini Fast
Tondateien für Beispielcode auf SD-Karte spielen:


Software für Exit-Room
Alle 2ms digitalisiert der AD-Wandler die Spannung an Pin A0. Verrechnet mit den letzten Messwerten in adcWert gespeichert.
adcAkt = analogRead(P_TELEFON); // AD-Wert einlesen
adcWert = (adcWert*3+adcAkt)/4; // und glaetten ca. 120µs
Nach dem Abheben des Hörers ertönt ein Wählton (MP3_WAEHLTON).
Ein Array nummer[20] speichert die gewählten Ziffern.
Wird 2s (WAHL_TIMEOUT) keine Ziffer mehr gewählt, wird die Nummer ausgeben und in der Variable wahl gespeichert.
Im Abschnitt NUMMER_AUSWERTEN kann dann entweder der Wert von wahl oder das Feld nummer[] (hier bleiben führende Nullen erhalten) ausgewertet werden.
/* ExitRoomTelefon V2.4 (cc-by-sa) Oliver Mezger 27.10.2025
* die gewaehlte Nummer wird in char-Array nummer[20], als uint-Wert wahl (geht nur bis 65535)
* und als String textNummer gespeichert. Sollte die Auswertung erleichtern..
*/
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
//#define SERIAL_PLOTTER // Signalverlauf auf Serial Plotter ausgeben bei Problemtelefonen
#define P_TELEFON A0 // Arduino Pin für Telefon
#define P_BOARD_LED 13 // Board LED zum Testen
#define ADC_ABGEHOBEN 900 // wenn unter diesem Wert, ist Hoerer ABGEHOBEN
#define ADC_WAEHLSCHEIBE 200 // wenn unter diesem Wert, ist Wahlkontakt geschlossen
#define ZIFFER_TIMEOUT 200 // nach dieser Zeit kommt kein weiterer Impuls mehr
#define WAHL_TIMEOUT 2000 // nach dieser Zeit wird keine Ziffer mehr gewaehlt
#define MP3_WAEHLTON 12 // Das MP3 fuer den Waehlton, spielt 0012.mp3
#define MP3_RICHTIG 13 // Das MP3 fuer richtige Loesung
#define MP3_FALSCH 14 // Das MP3 fuer falsche Loesung
SoftwareSerial mySerial(10, 11); // RX, TX, fuer DF-Player
DFPlayerMini_Fast myMP3;
unsigned char nummerLaenge=0; // Laenge der gewaehlten Nummer
unsigned char nummer[20]; // gewaehlte Nummer
String textNummer=""; // Nummer als String
String loesung="123123"; // richtige Nummer
void spieleMP3(int n){
myMP3.playFromMP3Folder(n); // spiele MP3
}
void setup() {
Serial.begin(115200); // Ausgabe ueber serieller Monitor
mySerial.begin(9600); // Anbindung des DF-Player
myMP3.begin(mySerial);
#ifndef SERIAL_PLOTTER
Serial.println(F("Setting volume to 20"));
#endif
myMP3.volume(20); // 0..30
delay(20);
pinMode(P_BOARD_LED,OUTPUT);
}
typedef enum {RUHE,ABGEHOBEN,IMPULS_0,IMPULS_1,NAECHSTE_ZIFFER,NUMMER_AUSWERTEN,NUMMER_VORLESEN} wZustandtyp;
wZustandtyp wZustand = RUHE;
void loop() {
static unsigned char wImpulse = 0; // Impulszaehler
static unsigned long wMillis; // Zeitmarken fuer TimeOuts
static unsigned int adcAkt; // ADC-Wert aktuell
static unsigned int adcWert=1023; // ADC-Wert an P_TELEFON geglättet
static unsigned int wahl=0; // Nummer als Integer
static unsigned char i=0; // fuer Zaehlvorgaenge
delay(2); // alle 2 ms ADC-Abtastung
//digitalWrite(P_BOARD_LED,HIGH); // Testen wie lange Wandlung dauert
adcAkt = analogRead(P_TELEFON); // AD-Wert einlesen
adcWert = (adcWert*3+adcAkt)/4; // und glaetten ca. 120µs
#ifdef SERIAL_PLOTTER
static unsigned int plott=0;
static bool messen=false;
if(messen){ // Signalverlauf ausgeben
if(plott++%5==0){ // weniger Messungen übertragen
Serial.print(F("adc:"));
Serial.println(adcAkt);
}
}
#endif
//digitalWrite(P_BOARD_LED,LOW);
switch (wZustand){ // ZustandsAutomat
case RUHE: // Warten auf Hoerer abnehmen
if(adcWert < ADC_ABGEHOBEN){
wZustand=ABGEHOBEN;
#ifndef SERIAL_PLOTTER
Serial.print(F("\nAbgehoben-ADCWert: "));
Serial.print(adcAkt);
Serial.print(F(" = "));
Serial.print(adcAkt*5000UL/1024); // umrechnen in mV
Serial.println(F(" mV"));
#endif
spieleMP3(MP3_WAEHLTON); // spiele Waehlton
}
break;
case ABGEHOBEN: // Waehlton spielen und auf Impulse warten
if(adcWert >= ADC_ABGEHOBEN+50){// wieder aufgelegt?
myMP3.pause();
wZustand = RUHE;
#ifndef SERIAL_PLOTTER
Serial.println(F("Aufgelegt"));
Serial.print(F("RUHE:"));
Serial.println(adcAkt);
#endif
}
if(adcWert < ADC_WAEHLSCHEIBE){ // Waehlscheibe betaetigt?
wZustand = IMPULS_0;
wImpulse = 0;
nummerLaenge=0;
myMP3.pause();
}
break;
case IMPULS_0: // Waehlscheibenkontakt geschlossen
if(adcWert > ADC_WAEHLSCHEIBE){ // Impuls Waehlscheibenkontakt geoeffnet?
wMillis=millis(); // Zeitmarke setzen
wImpulse++; // Impuls zaehlen
wZustand = IMPULS_1;
#ifdef SERIAL_PLOTTER
messen=true;
#endif
}
break;
case IMPULS_1: // Waehlscheibenkontakt offen
if(adcWert < ADC_WAEHLSCHEIBE){ // Kontakt wieder geschlossen?
wZustand = IMPULS_0;
}
if(millis()-wMillis > ZIFFER_TIMEOUT){ // kein weiterer Impuls
wMillis=millis(); // Zeitmarke setzen
wZustand = NAECHSTE_ZIFFER;
nummer[nummerLaenge++]=wImpulse%10; // 10 Impulse -> Ziffer 0
#ifdef SERIAL_PLOTTER
messen=false;
#else
Serial.print(F("Ziffer: "));
Serial.println(wImpulse);
Serial.print(F("NAECHSTE_ZIFFER, ADC-Wert:"));
Serial.println(adcAkt);
#endif
}
break;
case NAECHSTE_ZIFFER:
if(adcWert < ADC_WAEHLSCHEIBE){ // Waehlscheibe betaetigt?
wZustand = IMPULS_0;
wImpulse = 0;
}
if(millis()-wMillis > WAHL_TIMEOUT){ // kein weitere Ziffer
wZustand=NUMMER_AUSWERTEN;
wahl=0;
textNummer="";
for(i=0;i<nummerLaenge;i++){
wahl=wahl*10 + nummer[i];
textNummer+= (char)('0'+nummer[i]); // Werte in ASCII-Zeichen umwandeln
}
#ifndef SERIAL_PLOTTER
Serial.print(F("\nGewaehlte Nummer: "));
for(i=0;i<nummerLaenge;i++){
Serial.print(nummer[i]);
Serial.println();
}
#endif
}
break;
case NUMMER_AUSWERTEN: // Aktion mit der gewaehlten Nummer
/* Beispiel fuer einfach nur das gewaehlte Stueck spielen
myMP3.playFromMP3Folder(wahl); // einfach Stueck spielen
wZustand=ABGEHOBEN;
*/
// Bei einstelligen Nummern wird Stueck 0 bis 9 gespielt, sonst wird mit loesung verglichen
if (nummerLaenge == 1){ // Nummer ist einstellig?
spieleMP3(wahl); // einfach Stueck spielen, bei 0 -> 0000.mp3
}
else if(textNummer==loesung){
#ifdef SERIAL_PLOTTER
Serial.println(F("Richtig"));
#endif
spieleMP3(MP3_RICHTIG); // Richtig
}
else {
#ifdef SERIAL_PLOTTER
Serial.println(F("Falsch"));
#endif
spieleMP3(MP3_FALSCH); // Falsch
}
wZustand=ABGEHOBEN;
/* Beispiel fuer gewaehlte Nummer vorlesen
i=0;
wZustand=NUMMER_VORLESEN;
*/
break;
case NUMMER_VORLESEN: // Vorlesen der Nummer bis Waehlscheibe oder aufgelegt
if(i >= nummerLaenge || adcAkt < ADC_WAEHLSCHEIBE || adcAkt >= ADC_ABGEHOBEN+50 ){
wZustand=ABGEHOBEN;
break;
}
if(nummer[i]==0){ // Bei Ziffer 0 soll Stueck 10 spielen
spieleMP3(10);
}
else spieleMP3(nummer[i]);
i++;
delay(600); // Warten bis Player in Gang kommt
do{
delay(350); // nicht zu schnell hintereinander isPlaying abfragen
} while(myMP3.isPlaying());
break;
}
}
Testsoftware mit Ausgaben auf dem Seriellen Monitor des Arduino
Testsoftware
/* Telefon-Test V1.0 (cc-by-sa) Oliver Mezger 3.11.2025
* die gewaehlte Nummer wird in char-Array nummer[20], gespeichert.
*/
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
//#define SERIAL_PLOTTER // Signalverlauf auf Serial Plotter ausgeben bei Problemtelefonen
#define P_PIEZO 3 // Piezo-Lautsprecher
#define P_TELEFON A0 // Arduino Pin für Telefon
#define P_BOARD_LED 13 // Board LED zum Testen
#define ADC_ABGEHOBEN 900 // wenn unter diesem Wert, ist Hoerer ABGEHOBEN
#define ADC_WAEHLSCHEIBE 200 // wenn unter diesem Wert, ist Wahlkontakt geschlossen
#define ZIFFER_TIMEOUT 200 // nach dieser Zeit kommt kein weiterer Impuls mehr
#define WAHL_TIMEOUT 2000 // nach dieser Zeit wird keine Ziffer mehr gewaehlt
#define MP3_WAEHLTON 12 // Das MP3 fuer den Waehlton, spielt 0012.mp3
#define ANZ_ZIFFERN 30 // Maximale Anzahl der Ziffern der Nummer
SoftwareSerial mySerial(10, 11); // RX, TX, fuer DF-Player
DFPlayerMini_Fast myMP3;
unsigned char nummerLaenge=0; // Laenge der gewaehlten Nummer
unsigned char nummer[ANZ_ZIFFERN]; // gewaehlte Nummer
typedef enum {RUHE_ENTRY,RUHE,ABGEHOBEN,IMPULS_0,IMPULS_1,NAECHSTE_ZIFFER_ENTRY,NAECHSTE_ZIFFER,NUMMER_AUSWERTEN,EINSTELLUNGEN} wZustandtyp;
wZustandtyp wZustand = RUHE_ENTRY;
void spieleMP3(int n){
myMP3.playFromMP3Folder(n); // spiele MP3
}
void setup() {
Serial.begin(115200); // Ausgabe ueber serieller Monitor
mySerial.begin(9600); // Anbindung des DF-Player
myMP3.begin(mySerial);
#ifndef SERIAL_PLOTTER
Serial.println(F("Setting volume to 20"));
#endif
myMP3.volume(20); // 0..30
delay(20);
pinMode(P_BOARD_LED,OUTPUT);
tone(P_PIEZO,500,200);
}
void loop() {
static unsigned char wImpulse = 0; // Impulszaehler
static unsigned long wMillis; // Zeitmarken fuer TimeOuts
static unsigned int adcAkt; // ADC-Wert aktuell
static unsigned int adcWert=1023; // ADC-Wert an P_TELEFON geglättet
static unsigned int wahl=0; // Nummer als Integer
static unsigned char i=0,k=0; // fuer Zaehlvorgaenge
delay(2); // alle 2 ms ADC-Abtastung
//digitalWrite(P_BOARD_LED,HIGH); // Testen wie lange Wandlung dauert
adcAkt = analogRead(P_TELEFON); // AD-Wert einlesen
adcWert = (adcWert*3+adcAkt)/4; // und glaetten ca. 120µs
#ifdef SERIAL_PLOTTER
static unsigned int plott=0;
static bool messen=false;
if(messen){ // Signalverlauf ausgeben
if(plott++%5==0){ // weniger Messungen übertragen
Serial.print(F("adc:"));
Serial.println(adcAkt);
}
}
#endif
//digitalWrite(P_BOARD_LED,LOW);
switch (wZustand){ // ZustandsAutomat
case RUHE_ENTRY: // wird beim Eintreten des Zustands einmal ausgeführt
myMP3.pause(); // MP3 Player stoppen
#ifndef SERIAL_PLOTTER
Serial.print(F("Aufgelegt, "));
Serial.print(F("AD-Wert: "));
Serial.println(adcAkt);
#endif
wZustand=RUHE;
break;
case RUHE: // Warten auf Hoerer abnehmen
if(adcWert < ADC_ABGEHOBEN){ // Gabelkontakt geschlossen, Sprechstrom
wZustand=ABGEHOBEN;
#ifndef SERIAL_PLOTTER
Serial.print(F("\nAbgehoben-ADCWert: "));
Serial.print(adcAkt);
Serial.print(F(" = "));
Serial.print(adcAkt*5000UL/1024); // umrechnen in mV
Serial.println(F(" mV"));
#endif
spieleMP3(MP3_WAEHLTON); // spiele Waehlton
}
break;
case ABGEHOBEN: // Waehlton spielen und auf Impulse warten
if(adcWert >= ADC_ABGEHOBEN+50){// wieder aufgelegt?
wZustand = RUHE_ENTRY;
}
if(adcWert < ADC_WAEHLSCHEIBE){ // Waehlscheibe betaetigt?
wZustand = IMPULS_0;
wImpulse = 0;
nummerLaenge=0;
myMP3.pause(); // Wählton beenden
}
break;
case IMPULS_0: // Waehlscheibenkontakt geschlossen
if(adcWert > ADC_WAEHLSCHEIBE){ // Impuls Waehlscheibenkontakt geoeffnet?
wMillis=millis(); // Zeitmarke setzen für Ziffer-Timeout
wImpulse++; // Impuls zaehlen
wZustand = IMPULS_1;
#ifdef SERIAL_PLOTTER
messen=true;
#else
Serial.print(F("Impuls1: "));
Serial.println(adcAkt);
#endif
}
break;
case IMPULS_1: // Waehlscheibenkontakt offen
if(adcWert < ADC_WAEHLSCHEIBE){ // Kontakt wieder geschlossen?
wZustand = IMPULS_0;
#ifndef SERIAL_PLOTTER
Serial.print(F("Impuls0: "));
Serial.println(adcAkt);
#endif
}
if(millis()-wMillis > ZIFFER_TIMEOUT){ // kein weiterer Impuls
wZustand = NAECHSTE_ZIFFER_ENTRY;
}
break;
case NAECHSTE_ZIFFER_ENTRY:
wMillis=millis(); // Zeitmarke setzen für Nummer-Timeout
nummer[nummerLaenge++]=wImpulse%10; // 10 Impulse -> Ziffer 0
wZustand = NAECHSTE_ZIFFER;
#ifdef SERIAL_PLOTTER
messen=false;
#else
Serial.print(F("Ziffer: "));
Serial.println(wImpulse);
Serial.print(F("NAECHSTE_ZIFFER, ADC-Wert :"));
Serial.println(adcAkt);
#endif
break;
case NAECHSTE_ZIFFER:
if(millis()-wMillis > WAHL_TIMEOUT){ // Keine weitere Ziffer mehr gewählt
wZustand = NUMMER_AUSWERTEN;
}
if(adcWert < ADC_WAEHLSCHEIBE){ // Waehlscheibe betaetigt?
wZustand = IMPULS_0;
wImpulse = 0;
}
if(adcWert >= ADC_ABGEHOBEN+50){ // wieder aufgelegt?
wZustand = RUHE_ENTRY;
}
break;
case NUMMER_AUSWERTEN: // Aktion mit der gewaehlten Nummer
tone(P_PIEZO,1500,200);
#ifndef SERIAL_PLOTTER
Serial.print(F("\gewählte Nummer: "));
for(i=0;i<nummerLaenge;i++){
Serial.print(nummer[i]);
}
Serial.println();
#endif
wZustand=ABGEHOBEN;
break;
}
}
Die Testsoftware ist auskunftsfreudig und kann dadurch bei Problemen mit dem Telefon helfen. Dazu die Baudrate auf 115200 einstellen.
Nach dem Abheben des Hörers sollte ein Wahlton vernehmbar sein und im Seriellen Monitor wird der gewandelte Spannungswert, hier 766 = 3740 mV angezeigt. Mit einem Spannungsmessgerät könnte man nun zwischen den Telefonleitungen a und b 3.139 V messen.
Setting volume to 20
Aufgelegt, AD-Wert: 1023
Abgehoben-ADCWert: 766 = 3740 mV
Impuls1: 615
Impuls0: 30
Impuls1: 578
Impuls0: 25
Impuls1: 690
Ziffer: 3
NAECHSTE_ZIFFER, ADC-Wert :693
gewählte Nummer: 3
Aufgelegt, AD-Wert: 995
Wenn #define SERIAL_PLOTTER aktiv ist werden beim Wählen einer Ziffer die Spannungswerte für den Seriellen Plotter ausgegeben. Dies lässt Rückschlüsse über den Zustand des Nummernschalters (Verschleißteil) zu.

🚧 Erweitern für Spiele
Mit NeoPixeln (WS2812B) einem Schalter, Piezo-Piper, LDR und Poti können schon einige Spiele mit Wählscheibentelefon realisiert werden.
NeoPixel (WS2812B) braucht pro Pixel bis zu 50mA.




