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 Auswerteschaltung versorgt das Telefon über einem 330Ω Vorwiderstand mit Strom und misst mit dem AD-Wandler die Spannung am Telefon.
Ein 🔗DFPlayer Mini ermöglicht eine Audioausgabe im Telefonhörer.

🚨 Tastentelefone funktionieren mit dieser Schaltung nicht! Auch nicht wenn sie auf Impulswahl umgestellt wurden. Sie benötigen eine höhere Spannung als 5V.
Wählscheibentelefone
Auswerteschaltung für Wählscheiben-Telefone mit Impulswahl
Das Prinzip der alten Technik wird hier kurz angerissen:
- Mit zwei Leitungen (La/Lb) wird das Telefon angeschlossen. Bei Kabel ohne Stecker findet man oft die Leitungsfarben Weiß La, Braun Lb, gelb E und grün bzw. blau. für W. Nimm die weiße und braune Leitung als La und Lb.
- 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 eigentlich egal ob La mit lila bzw. schwarz und Lb mit schwarz bzw. lila verbunden wird. Falls jedoch ein Erd-Taster abgefragt werden soll sollte La (weiß) mit GND (schwarz) verbunden werden. 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 schaltet auf La | gelb |
Funktioniert das Telefon überhaupt noch für den Zweck?

Mit einem Oszilloskop mit 5V und 330Ω Vorwiderstand aufgezeichnet. So sieht das Signal bei der Wahl der Ziffer 3 beim alten Amtsstubentelefon aus 📖 Kabeltelefone. Drei Spannungsbereiche sind zu sehen:
- Spannung ist nahe 0, die Wählscheibe ist aufgezogen, der Wählkontakt nsi angeschlossen, der Telefon-Widerstand sollte jetzt gering (<15Ω) sein.
- Beim Wählen wird der nsi-Kontakt geöffnet die Spannung am Telefon ist hoch.
- Bei abgenommen Hörer und am Ende des Wahlvorgangs ist der Telefonwiderstand mittelgroß.
- Das „Rauschen“ bei den höheren Spannungen ist der Wählton des MP3-Players.
Alte Telefone haben teilweise schlechte, korrodierte Kontakte, der Widerstand ist zu groß und sie prellen. Um die Software auch auf „schwierige“ Kandidaten zu optimieren ersteigerte ich extra einige richtig alte Telefone.
Ich habe darauf hin die Auswertung neu gedacht und verbessert. Bei starkem Verschleiß kann die Software keine Wunder vollbringen, da hilft nur eine Instandsetzung.
🍀 Telefon Testsoftware V3.1 (neu neu)
Wird ein Wählton gewünscht und soll die gewählte Nummern angesagt werden,
ist die MP3 Lib und eine bespielte SD-Karte notwendig. Im Quellcode die // entfernen://#define MP3_PLAYER // Ist ein DF-PLAYER angeschlossen
Die Software misst mit dem Analog-Digital-Wandler alle 2ms an A0 die Spannung. Der gemessene AD-Wert wird in drei Bereiche klassifiziert:
Achtung: Wenn die Lautstärkeeinstellung des MP3-Players im setup() >20 ist kann dies zu Fehlfunktionen im Zustand ABGEHOBEN kommen, die gemessene Spannung kann die TZ_HIGH Schaltschwelle übersteigen und es wird ständig zwischen RUHE und ABGEHOBEN gewechselt.
- TZ_HIGH: Die Spannung ist > 4,4V, der Telefonhörer ist entweder aufgelegt oder ein HIGH-Impuls beim Wählen
- TZ_ABG: Die Spannung ist zwischen 1V und 4,4V, der Telefonhörer ist abgehoben.
- TZ_LOW: die Spannung ist <1V, der Nummernschalter ist geschlossen, bzw. gibt LOW-Impuls aus
Hier ein Oszillogramm eines problematischen Nummernschalters:

Der erste Impuls ist länger, ist kein Problem, könnte aber Rückschlüsse auf das Telefonmodell ermöglichen. Der zweite Impuls mit 61ms hat eine normale Länge. Danach wäre die Wahl der Ziffer 2 beendet und die Spannung sollte im TZ_ABG (Hörer ist abgehoben) Bereich liegen. Hier gibt es zwei Auffälligkeiten:
- Ein 18ms LOW-Impuls am Ende des Wahlvorgangs. Wird von der Software bis zu einer Länge von 26ms beim Zählen ignoriert. Jedoch ab einer Länge von ENTPRELLZEIT*2ms kann ein Fehler ausgegeben werden. ToDo: Lernen ob dieser Impuls durch Justage am Nummernschalter vermeidbar ist sonst Fehlermeldung entfernen.
- Zwei weitere Nachlaufimpulse weil einer der Nummernschalterkontakte nicht richtig schließt. Es wird eine Fehlermeldung „Nummerschalterkontakt“ ausgegeben, Problem lässt sich durch Kontaktpflege lösen.
TelefonTestsoftware aufklappen, click mich!
// Telefon-Test V3.1 (cc-by-sa) Oliver Mezger 2.01.2026
#define MP3_PLAYER // Ist ein DF-PLAYER angeschlossen
//#define IMPULSE_ANZEIGEN // Gibt die Impulslängen aus
#ifdef MP3_PLAYER
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
SoftwareSerial mySerial(10, 11); // RX, TX, fuer DF-Player
DFPlayerMini_Fast myMP3;
#endif
#define P_PIEZO 3 // Piezo-Lautsprecher
#define P_TELEFON A0 // Arduino Pin für Telefon
#define TEL_VORWIDERSTAND 330 // 330 Ohm Vorwiderstand zwischen 5V und Telefon
#define P_BOARD_LED 13 // Board LED zum Testen
#define T_AD_HIGH 900 // wenn über diesem Wert, ist Aufgelegt oder IMPULS1
#define T_AD_LOW 200 // wenn unter diesem Wert, ist Wahlkontakt geschlossen 200
#define ABTAST_PERIODE 2 // Zeitdauer zwischen AD-Wandlung in ms
#define ENTPRELLZEIT 6 // Abtastperioden bis Signal sicher
#define T_ZUSTAND_TIMEOUT 40 // Max Signallänge überschritten * ABTAST_PERIODE
#define WAHL_TIMEOUT 2000 // nach dieser Zeit (ms) 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
#define T_LOW tZustand == TZ_LOW
#define T_ABG tZustand == TZ_ABG
#define T_HIGH tZustand == TZ_HIGH
unsigned char nummerLaenge = 0; // Laenge der gewaehlten Nummer
unsigned char nummer[ANZ_ZIFFERN]; // gewaehlte Nummer
// Wahlzustände beim Wählen
typedef enum { RUHE_ENTRY,
RUHE,
ABGEHOBEN_ENTRY,
ABGEHOBEN,
AUFGEZOGEN,
IMPULS_0,
IMPULS_1_ENTRY,
IMPULS_1,
WAHLENDE,
NAECHSTE_ZIFFER_ENTRY,
NAECHSTE_ZIFFER } wZustandtyp;
wZustandtyp wZustand = RUHE_ENTRY;
// Telefonzustände klassifizieren die AD-Werte der Messung
typedef enum { TZ_LOW,
TZ_ABG,
TZ_HIGH } tZustandTyp; // Wählkontakt geschlossen, Abgenommen, Offen
tZustandTyp tZustand = TZ_HIGH;
unsigned char tZustDauer = 0; // Wie viele Mess-Perioden das Signal den Zustand hat
int telLOW = -1, telABG = -1, telHIGH = -1; // AD-Werte merken für Telefonbeurteilung -1 = undefiniert
unsigned char ersterImpuls = 0, tel0zeit = 0, tel1zeit = 0; // Impulslängen
bool messen = false;
void abfragenTelefon() { // frägt AD-Wander ab, klassifiziert Wert, entprellt
unsigned int adcTelefon; // ADC-Wert aktuell
adcTelefon = analogRead(P_TELEFON); // AD-Wert einlesen
tZustandTyp tzust = TZ_ABG; // Telefon abgehoben
if (adcTelefon < T_AD_LOW) tzust = TZ_LOW;
else if (adcTelefon > T_AD_HIGH) tzust = TZ_HIGH;
tZustDauer++;
if (tZustDauer == ENTPRELLZEIT) { // Abspeichern der Spannungen
tZustand = tzust;
switch (tZustand) { // Spannungen merken
case TZ_LOW: telLOW = adcTelefon; break;
case TZ_ABG: telABG = adcTelefon; break;
case TZ_HIGH: telHIGH = adcTelefon; break;
}
} else if (tZustDauer > ENTPRELLZEIT && (tZustand != tzust || tZustDauer > T_ZUSTAND_TIMEOUT)) { // Zeitabschnitt vorbei
dispatch();
#ifdef IMPULSE_ANZEIGEN
if (messen) {
switch (tZustand) {
case TZ_LOW:
Serial.print(F(" LOW"));
break;
case TZ_ABG:
Serial.print(F(" ABG"));
break;
case TZ_HIGH:
Serial.print(F(" HIGH"));
break;
}
Serial.print(F(": "));
Serial.print(tZustDauer * ABTAST_PERIODE);
}
#endif
tZustDauer = 0;
}
}
void spieleMP3(int n) {
#ifdef MP3_PLAYER
myMP3.playFromMP3Folder(n); // spiele MP3
#endif
}
void pauseMP3() {
#ifdef MP3_PLAYER
myMP3.pause();
#endif
}
void auswertenNummer() { // hier wird die gewählte Nummer ausgewertet
tone(P_PIEZO, 1500, 200);
Serial.print(F(" gewählte Nummer: "));
for (int i = 0; i < nummerLaenge; i++) {
Serial.print(nummer[i]);
}
Serial.println();
}
void setup() {
Serial.begin(115200); // Ausgabe ueber serieller Monitor
#ifdef MP3_PLAYER
mySerial.begin(9600); // Anbindung des DF-Player
myMP3.begin(mySerial);
myMP3.volume(20); // 0..30
delay(200);
#endif
pinMode(P_BOARD_LED, OUTPUT);
tone(P_PIEZO, 500, 200);
}
void fehler(const __FlashStringHelper* f) {
tone(P_PIEZO, 2000, 200);
Serial.print(F("\nFehler: "));
Serial.print(f);
Serial.print(F(" Dauer "));
Serial.println(tZustDauer * ABTAST_PERIODE);
}
void dispatch() { // Auswerten des Impulses
static unsigned char wImpulse = 0; // Impulszaehler
static unsigned long wahlZeit = 0; // Zeitmarke
static float f; // Berechnungen
bool durchgang;
//wahlZeit += ABTAST_PERIODE; // zählt ms hoch
do { // weiterer Durchgang bei Entrys
durchgang = false;
switch (wZustand) { // ZustandsAutomat
case RUHE_ENTRY: // wird beim Eintreten des Zustands einmal ausgeführt
messen = false;
pauseMP3(); // MP3 Player stoppen
delay(300);
Serial.print(F("Aufgelegt: LOW "));
Serial.print(telLOW);
f = TEL_VORWIDERSTAND / (1024.0 / telLOW - 1); // Widerstand berechnen
Serial.print(F(" ("));
Serial.print(f);
Serial.print(F(" Ohm) ABG "));
Serial.print(telABG);
f = TEL_VORWIDERSTAND / (1024.0 / telABG - 1); // Widerstand berechnen
Serial.print(F(" ("));
Serial.print(f);
Serial.print(F(" Ohm) HIGH "));
Serial.print(telHIGH);
Serial.print(F(" ersterImpuls: "));
Serial.print(ersterImpuls * ABTAST_PERIODE);
Serial.print(F("ms tel0: "));
Serial.print(tel0zeit * ABTAST_PERIODE);
Serial.print(F("ms tel1: "));
Serial.print(tel1zeit * ABTAST_PERIODE);
Serial.print(F("ms Impulsverhaeltnis: "));
f = 1.0 * tel1zeit / tel0zeit; // Impulsverhältnis berechen, soll 1.6 sein
Serial.println(f);
if(telLOW>55){ // Widerstand zu groß
tone(P_PIEZO, 1800, 200);
Serial.println(F("Kontakte pflegen!"));
delay(500);
tone(P_PIEZO, 2200, 200);
}
wZustand = RUHE;
break;
case RUHE: // Warten auf Hoerer abnehmen
if (T_ABG) { // Gabelkontakt geschlossen, Sprechstrom
Serial.print(F("\nAbgehoben-ADCWert: "));
Serial.print(telABG);
Serial.print(F(" = "));
Serial.print(telABG * 5000UL / 1024); // umrechnen in mV
Serial.print(F(" mV Widerstand: "));
f = TEL_VORWIDERSTAND / (1024.0 / telABG - 1); // Widerstand berechnen
Serial.print(f);
Serial.println(F(" Ohm"));
spieleMP3(MP3_WAEHLTON); // spiele Waehlton
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
}
if (T_LOW){
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
fehler(F("RUHE: T_LOW Gabelkontakt"));
}
break;
case ABGEHOBEN_ENTRY:
nummerLaenge = 0;
wZustand = ABGEHOBEN;
break;
case ABGEHOBEN: // Waehlton spielen und auf Impulse warten
if (T_HIGH) { // wieder aufgelegt?
wZustand = RUHE_ENTRY;
durchgang = true;
}
if (T_LOW) { // Waehlscheibe betaetigt?
wZustand = AUFGEZOGEN;
pauseMP3(); // Wählton beenden
Serial.print(F("Waehlscheibe Aufgezogen: "));
Serial.print(telLOW);
f = TEL_VORWIDERSTAND / (1024.0 / telLOW - 1); // Widerstand berechnen
Serial.print(F(" = "));
Serial.print(f);
Serial.println(F(" Ohm Kontakt-Widerstand"));
}
break;
case AUFGEZOGEN: // Wählscheibe am Fingeranschlag, warten auf Loslassen
if (T_HIGH) { // Impuls Waehlscheibenkontakt geoeffnet?
ersterImpuls = tZustDauer;
wImpulse = 0;
wZustand = IMPULS_1_ENTRY;
durchgang = true;
messen = true;
}
if (T_ABG) {
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
fehler(F("AUFGEZOGEN: T_ABG"));
}
break;
case IMPULS_0: // Waehlscheibenkontakt geschlossen
if (T_HIGH) {
wZustand = IMPULS_1_ENTRY;
durchgang = true;
}
if (T_ABG) {
wZustand = NAECHSTE_ZIFFER_ENTRY;
durchgang = true;
fehler(F("IMPULS_0: T_ABG"));
}
break;
case IMPULS_1_ENTRY:
tel1zeit = tZustDauer;
wImpulse++; // Impuls zaehlen
wZustand = IMPULS_1;
break;
case IMPULS_1: // Waehlscheibenkontakt offen
if (T_LOW) { // Kontakt wieder geschlossen?
if (tZustDauer > 13) {
tel0zeit = tZustDauer;
wZustand = IMPULS_0;
} else {
fehler(F("IMPULS_1: Kurzer Impuls"));
wZustand = WAHLENDE;
}
}
if (T_ABG) { // kein weiterer Impuls
messen = false;
wZustand = WAHLENDE;
}
if (T_HIGH) {
fehler(F("IMPULS_1->Aufgelegt"));
wZustand = RUHE_ENTRY;
durchgang = true;
}
break;
case WAHLENDE:
if (T_HIGH) {
if (tZustDauer > T_ZUSTAND_TIMEOUT) {
wZustand = RUHE;
} else {
fehler(F("Nummerschalterkontakt"));
}
}
if (T_ABG) {
if (tZustDauer > T_ZUSTAND_TIMEOUT) {
wZustand = NAECHSTE_ZIFFER_ENTRY;
durchgang = true;
}
}
break;
case NAECHSTE_ZIFFER_ENTRY:
spieleMP3(wImpulse); // Ziffer vorlesen
nummer[nummerLaenge] = wImpulse % 10; // 10 Impulse -> Ziffer 0
messen = false;
Serial.print(F(" nummer["));
Serial.print(nummerLaenge);
Serial.print(F("]: "));
Serial.println(wImpulse);
nummerLaenge++;
wahlZeit = millis() + WAHL_TIMEOUT; // Timeout für nächste Ziffer wählen
wZustand = NAECHSTE_ZIFFER;
break;
case NAECHSTE_ZIFFER:
if (millis() >= wahlZeit) { // Keine weitere Ziffer mehr gewählt
auswertenNummer();
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
}
if (T_LOW) { // Waehlscheibe betaetigt?
wZustand = AUFGEZOGEN;
}
if (T_HIGH) { // wieder aufgelegt?
wZustand = RUHE_ENTRY;
durchgang = true;
}
break;
}
} while (durchgang);
}
void loop() {
static unsigned long messperiode = micros() + ABTAST_PERIODE * 1000; // Zeitmarken fuer TimeOuts
while (micros() < messperiode)
; // aufhalten bis zeit um
messperiode = micros() + ABTAST_PERIODE * 1000; // ms einstellen
digitalWrite(P_BOARD_LED, HIGH); // Testen wie lange Wandlung dauert
abfragenTelefon();
digitalWrite(P_BOARD_LED, LOW);
}
Telefon anschließen, Seriellen Monitor mit 115200 Baud einstellen, Hörer abnehmen, die 3 wählen und wieder auflegen, Beispielausgaben:
FeTAp 11.90
Abgehoben-ADCWert: 811 = 3959 mV Widerstand: 1256.48 Ohm
Waehlscheibe Aufgezogen: 22 = 7.25 Ohm Kontakt-Widerstand
HIGH: 68 LOW: 32 HIGH: 66 LOW: 34 HIGH: 68 nummer[0]: 3
gewählte Nummer: 3
Aufgelegt: LOW 13 (4.24 Ohm) ABG 773 (1016.29 Ohm) HIGH 1023 ersterImpuls: 68ms tel0: 34ms tel1: 68ms Impulsverhaeltnis: 2.00
Siemens Fg 7.54
Abgehoben-ADCWert: 461 = 2250 mV Widerstand: 270.21 Ohm
Waehlscheibe Aufgezogen: 82 = 28.73 Ohm Kontakt-Widerstand
HIGH: 70 LOW: 38 HIGH: 58 LOW: 36 HIGH: 58 nummer[0]: 3
gewählte Nummer: 3
Aufgelegt: LOW 61 (20.90 Ohm) ABG 455 (263.88 Ohm) HIGH 1023 ersterImpuls: 70ms tel0: 36ms tel1: 58ms Impulsverhaeltnis: 1.61
Kontakte pflegen!
Erklärung der Softwareausgaben
- Abgehoben-ADCWert: Gibt den AD-Wert und die Spannung am Telefon und den abgeschätzten Widerstand bei abgehobenen Hörer an.
- Wählscheibe Aufgezogen: Bei aufgezogener Wählscheibe sollte der Widerstand gering <15Ω sein, sonst deutet es auf korrodierte Kontakte hin.
- Bei einem optisch sehr schönen „überprüften“ W48 gab es keinen Kontakt mehr, musste erst mit Kontaktspray und Tuch die Funktion wieder herstellen.
- HIGH..: Gibt die Impulsfolge mit der jeweiligen Länge des Impulses in ms aus. Am Ende wird die erkannte Ziffer angezeigt. 📖 Impulsverhältnis
- gewählte Nummer: Zeigt die gewählte Nummer an, kann auch aus mehreren Ziffern bestehen, wenn nach einer Wahl nicht länger als 2 sec gewartet wird.
- Aufgelegt: Nach dem Auflegen werden die gesammelten Messwerte des letzten Wahlimpulses ausgegeben:
- LOW: Geschlossener nsi-Kontakt, AD-Wert und errechneter Widerstand des Kontakts
- ABG: Abgehobener Hörer, AD-Wert und errechneter Widerstand des Sprechkreises
- HIGH: nsi-Kontakt offen AD-Wert sollte nahe 1023 sein, sonst Verdacht auf Kriechstrom
- ersterImpuls: Impulslänge des ersten Impulses bei offenem nsi-Kontakt.
- tel0: Impulslänge geschlossener nsi-Kontakt. Ideal 38 ms
- tel1: Impulslänge offener nsi-Kontakt. Ideal 62 ms
- Impulsverhaeltnis: Sollte ideal zwischen 1,3 bis 1,9 liegen
- Fehlermeldungen, sind manchmal auch nur Hinweise auf Fehlbedienung:
- RUHE: T_LOW Gabelkontakt: Wählscheibe ist aufgezogen und dann wird der Hörer abgenommen
- AUFGEZOGEN: T_ABG: Wählscheibe ist aufgezogen und dann wird aufgelegt
- IMPULS_0: T_ABG: Theoretisch möglicher Übergang, dürfte nie vorkommen
- IMPULS_1: Kurzer Impuls: LOW-Impuls am Ende des Wahlvorgangs, ist >12ms und <= 26ms ToDo: Erfahrungen sammeln!
- IMPULS_1->Aufgelegt: HIGH-Zustand ist zu lang, es wurde aufgelegt
- Nummerschalterkontakt: Nach Wahlende kommen noch weiter Impulse-> Kontakte pflegen
- Kontakte pflegen! Der Widerstand des Nummernschalters ist zu groß
ToDo: Anleitung zum Kontakte pflegen und Nummernschalter einstellen…
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.
Nach dem Abheben des Hörers ertönt ein Wählton (MP3_WAEHLTON).
Ein Array nummer[ANZ_ZIFFERN] speichert die gewählten Ziffern.
Wird 2s (WAHL_TIMEOUT) keine weitere Ziffer mehr gewählt, wird die Nummer ausgewertet.
Vor der Auswertung kann die Nummer vorgelesen werden, bei NAECHSTE_ZIFFER als Folgezustand VORLESEN oder VORLESEN2 auswählen.
/* ExitRoomTelefon V3.1 (cc-by-sa) Oliver Mezger 2.01.2026
* die gewaehlte Nummer wird in char-Array nummer[20] gespeichert.
*/
//#define NUMMER_VORLESEN // Nummer vor Auswertung nochmal vorlesen
#include <SoftwareSerial.h>
#include <DFPlayerMini_Fast.h>
SoftwareSerial mySerial(10, 11); // RX, TX, fuer DF-Player
DFPlayerMini_Fast myMP3;
#define P_PIEZO 3 // Piezo-Lautsprecher
#define P_MP3_BUSY 4 // Busy-Pin des MP3 Players
#define P_TELEFON A0 // Arduino Pin für Telefon
#define TEL_VORWIDERSTAND 330 // 330 Ohm Vorwiderstand zwischen 5V und Telefon
#define P_BOARD_LED 13 // Board LED zum Testen
#define T_AD_HIGH 900 // wenn über diesem Wert, ist Aufgelegt oder IMPULS1
#define T_AD_LOW 200 // wenn unter diesem Wert, ist Wahlkontakt geschlossen 200
#define ABTAST_PERIODE 2 // Zeitdauer zwischen AD-Wandlung in ms
#define ENTPRELLZEIT 6 // Abtastperioden bis Signal sicher
#define T_ZUSTAND_TIMEOUT 40 // Max Signallänge überschritten * ABTAST_PERIODE
#define WAHL_TIMEOUT 2000 // nach dieser Zeit (ms) 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
#define ANZ_ZIFFERN 30 // Maximale Anzahl der Ziffern der Nummer
#define T_LOW tZustand == TZ_LOW
#define T_ABG tZustand == TZ_ABG
#define T_HIGH tZustand == TZ_HIGH
#define MP3_IDLE digitalRead(P_MP3_BUSY) // Spielt gerade nicht
unsigned char nummerLaenge = 0; // Laenge der gewaehlten Nummer
unsigned char nummer[ANZ_ZIFFERN]; // gewaehlte Nummer
unsigned char loesung[] = { 4, 7, 1, 1 }; // gesuchte Lösung
unsigned char loesungLaenge = sizeof(loesung) / sizeof(loesung[0]);
// Wahlzustände beim Wählen
typedef enum { RUHE_ENTRY,
RUHE,
ABGEHOBEN_ENTRY,
ABGEHOBEN,
AUFGEZOGEN,
IMPULS_0,
IMPULS_1_ENTRY,
IMPULS_1,
WAHLENDE,
NAECHSTE_ZIFFER_ENTRY,
NAECHSTE_ZIFFER,
VORLESEN,
VORLESEN2 } wZustandtyp;
wZustandtyp wZustand = RUHE_ENTRY;
// Telefonzustände klassifizieren die AD-Werte der Messung
typedef enum { TZ_LOW,
TZ_ABG,
TZ_HIGH } tZustandTyp; // Wählkontakt geschlossen, Abgenommen, Offen
tZustandTyp tZustand = TZ_HIGH;
unsigned char tZustDauer = 0; // Wie viele Mess-Perioden das Signal den Zustand hat
int telLOW = -1, telABG = -1, telHIGH = -1; // AD-Werte merken für Telefonbeurteilung -1 = undefiniert
unsigned char ersterImpuls = 0, tel0zeit = 0, tel1zeit = 0; // Impulslängen
void abfragenTelefon() { // frägt AD-Wander ab, klassifiziert Wert, entprellt
unsigned int adcTelefon; // ADC-Wert aktuell
adcTelefon = analogRead(P_TELEFON); // AD-Wert einlesen
tZustandTyp tzust = TZ_ABG; // Telefon abgehoben
if (adcTelefon < T_AD_LOW) tzust = TZ_LOW;
else if (adcTelefon > T_AD_HIGH) tzust = TZ_HIGH;
tZustDauer++;
if (tZustDauer == ENTPRELLZEIT) { // Abspeichern der Spannungen
tZustand = tzust;
switch (tZustand) { // Spannungen merken
case TZ_LOW: telLOW = adcTelefon; break;
case TZ_ABG: telABG = adcTelefon; break;
case TZ_HIGH: telHIGH = adcTelefon; break;
}
} else if (tZustDauer > ENTPRELLZEIT && (tZustand != tzust || tZustDauer > T_ZUSTAND_TIMEOUT)) { // Zeitabschnitt vorbei
dispatch();
tZustDauer = 0;
}
}
void spieleMP3(int n) {
myMP3.playFromMP3Folder(n); // spiele MP3
}
void auswertenNummer() { // hier wird die gewählte Nummer ausgewertet
tone(P_PIEZO, 1500, 200);
Serial.print(F("gewählte Nummer: "));
for (int i = 0; i < nummerLaenge; i++) {
Serial.print(nummer[i]);
}
bool richtig = nummerLaenge == loesungLaenge; // stimmt die Länge?
if (richtig) {
for (unsigned char i = 0; i < nummerLaenge; i++) { // stimmen die Ziffern
if (loesung[i] != nummer[i]) {
richtig = false; // Ziffer stimmt nicht
break;
}
}
}
if (richtig) {
Serial.println(F(" Richtig"));
spieleMP3(MP3_RICHTIG); // Richtig
} else {
Serial.println(F(" Falsch"));
spieleMP3(MP3_FALSCH); // Falsch
}
}
void setup() {
Serial.begin(115200); // Ausgabe ueber serieller Monitor
mySerial.begin(9600); // Anbindung des DF-Player
myMP3.begin(mySerial);
myMP3.volume(20); // 0..30
delay(200);
pinMode(P_BOARD_LED, OUTPUT);
tone(P_PIEZO, 500, 200);
}
void fehler(const __FlashStringHelper* f) {
tone(P_PIEZO, 2000, 200);
Serial.print(F("\nFehler: "));
Serial.print(f);
Serial.print(F(" Dauer "));
Serial.println(tZustDauer * ABTAST_PERIODE);
}
void dispatch() { // Auswerten des Impulses
static unsigned char wImpulse = 0; // Impulszaehler
static unsigned long wahlZeit = 0; // Zeitmarke
static float f; // Berechnungen
bool durchgang;
static unsigned char vZiffer = 0; // VorleseZiffer
do { // weiterer Durchgang bei Entrys
durchgang = false;
switch (wZustand) { // ZustandsAutomat
case RUHE_ENTRY: // wird beim Eintreten des Zustands einmal ausgeführt
myMP3.pause();
delay(300);
Serial.print(F("Aufgelegt: LOW "));
Serial.print(telLOW);
f = TEL_VORWIDERSTAND / (1024.0 / telLOW - 1); // Widerstand berechnen
Serial.print(F(" ("));
Serial.print(f);
Serial.print(F(" Ohm) ABG "));
Serial.print(telABG);
f = TEL_VORWIDERSTAND / (1024.0 / telABG - 1); // Widerstand berechnen
Serial.print(F(" ("));
Serial.print(f);
Serial.print(F(" Ohm) HIGH "));
Serial.print(telHIGH);
Serial.print(F(" ersterImpuls: "));
Serial.print(ersterImpuls * ABTAST_PERIODE);
Serial.print(F("ms tel0: "));
Serial.print(tel0zeit * ABTAST_PERIODE);
Serial.print(F("ms tel1: "));
Serial.print(tel1zeit * ABTAST_PERIODE);
Serial.print(F("ms Impulsverhaeltnis: "));
f = 1.0 * tel1zeit / tel0zeit; // Impulsverhältnis berechen, soll 1.6 sein
Serial.println(f);
if (telLOW > 55) { // Widerstand zu groß
tone(P_PIEZO, 1800, 200);
Serial.println(F("Kontakte pflegen!"));
delay(500);
tone(P_PIEZO, 2200, 200);
}
wZustand = RUHE;
break;
case RUHE: // Warten auf Hoerer abnehmen
if (T_ABG) { // Gabelkontakt geschlossen, Sprechstrom
Serial.print(F("\nAbgehoben-ADCWert: "));
Serial.print(telABG);
Serial.print(F(" = "));
Serial.print(telABG * 5000UL / 1024); // umrechnen in mV
Serial.print(F(" mV Widerstand: "));
f = TEL_VORWIDERSTAND / (1024.0 / telABG - 1); // Widerstand berechnen
Serial.print(f);
Serial.println(F(" Ohm"));
spieleMP3(MP3_WAEHLTON); // spiele Waehlton
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
}
if (T_LOW) {
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
fehler(F("RUHE: T_LOW Gabelkontakt")); // Wählscheibe ist aufgezogen und dann wird der Hörer abgenommen
}
break;
case ABGEHOBEN_ENTRY:
nummerLaenge = 0;
wZustand = ABGEHOBEN;
break;
case ABGEHOBEN: // Waehlton spielen und auf Impulse warten
if (T_HIGH) { // wieder aufgelegt?
wZustand = RUHE_ENTRY;
durchgang = true;
}
if (T_LOW) { // Waehlscheibe betaetigt?
wZustand = AUFGEZOGEN;
myMP3.pause(); // Wählton beenden
Serial.print(F("Waehlscheibe Aufgezogen: "));
Serial.print(telLOW);
f = TEL_VORWIDERSTAND / (1024.0 / telLOW - 1); // Widerstand berechnen
Serial.print(F(" = "));
Serial.print(f);
Serial.println(F(" Ohm Kontakt-Widerstand"));
}
break;
case AUFGEZOGEN: // Wählscheibe am Fingeranschlag, warten auf Loslassen
if (T_HIGH) { // Impuls Waehlscheibenkontakt geoeffnet?
ersterImpuls = tZustDauer;
wImpulse = 0;
wZustand = IMPULS_1_ENTRY;
durchgang = true;
}
if (T_ABG) {
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
fehler(F("AUFGEZOGEN: T_ABG")); // Wählscheibe ist aufgezogen und dann wird aufgelegt
}
break;
case IMPULS_0: // Waehlscheibenkontakt geschlossen
if (T_HIGH) {
wZustand = IMPULS_1_ENTRY;
durchgang = true;
}
if (T_ABG) {
wZustand = NAECHSTE_ZIFFER_ENTRY;
durchgang = true;
fehler(F("IMPULS_0: T_ABG")); // Theoretisch möglicher Übergang, dürfte nie vorkommen
}
break;
case IMPULS_1_ENTRY:
tel1zeit = tZustDauer;
wImpulse++; // Impuls zaehlen
wZustand = IMPULS_1;
break;
case IMPULS_1: // Waehlscheibenkontakt offen
if (T_LOW) { // Kontakt wieder geschlossen?
if (tZustDauer > 13) {
tel0zeit = tZustDauer;
wZustand = IMPULS_0;
} else {
fehler(F("IMPULS_1: Kurzer Impuls")); // LOW-Impuls am Ende des Wahlvorgangs, ist >12ms und <= 26ms
wZustand = WAHLENDE;
}
}
if (T_ABG) { // kein weiterer Impuls
wZustand = WAHLENDE;
}
if (T_HIGH) {
fehler(F("IMPULS_1->Aufgelegt")); // HIGH-Zustand ist zu lang, es wurde aufgelegt
wZustand = RUHE_ENTRY;
durchgang = true;
}
break;
case WAHLENDE:
if (T_HIGH) {
if (tZustDauer > T_ZUSTAND_TIMEOUT) {
wZustand = RUHE;
} else {
fehler(F("Nummerschalterkontakt")); // Nach Wahlende kommen noch weiter Impulse-> Kontakt pflegen
}
}
if (T_ABG) {
if (tZustDauer > T_ZUSTAND_TIMEOUT) {
wZustand = NAECHSTE_ZIFFER_ENTRY;
durchgang = true;
}
}
break;
case NAECHSTE_ZIFFER_ENTRY:
spieleMP3(wImpulse); // Ziffer vorlesen
nummer[nummerLaenge] = wImpulse % 10; // 10 Impulse -> Ziffer 0
Serial.print(F(" nummer["));
Serial.print(nummerLaenge);
Serial.print(F("]: "));
Serial.println(wImpulse);
nummerLaenge++;
wahlZeit = millis() + WAHL_TIMEOUT; // Timeout für nächste Ziffer wählen
wZustand = NAECHSTE_ZIFFER;
break;
case NAECHSTE_ZIFFER:
if (millis() >= wahlZeit) { // Keine weitere Ziffer mehr gewählt
#ifdef NUMMER_VORLESEN
vZiffer = 0;
wZustand = VORLESEN2; // Vorlesen mit MP3-Busy-Pin
#else
auswertenNummer();
wZustand = ABGEHOBEN_ENTRY;
#endif
durchgang = true;
}
if (T_LOW) { // Waehlscheibe betaetigt?
wZustand = AUFGEZOGEN;
}
if (T_HIGH) { // wieder aufgelegt?
wZustand = RUHE_ENTRY;
durchgang = true;
}
break;
case VORLESEN: // Nummer soll vorgelesen werden, ohne Busy-Pin
if (vZiffer >= nummerLaenge || T_LOW || T_HIGH) {
auswertenNummer();
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
break;
}
if (nummer[vZiffer] == 0) { // Bei Ziffer 0 soll Stueck 10 spielen
spieleMP3(10);
} else spieleMP3(nummer[vZiffer]);
vZiffer++;
delay(600); // Warten bis Player in Gang kommt
do {
delay(350); // nicht zu schnell hintereinander isPlaying abfragen
} while (myMP3.isPlaying());
break;
case VORLESEN2: // Nummer soll vorgelesen werden, mit Busy-Pin
if (MP3_IDLE) { // wenn MP3 nicht mehr spielt
if (vZiffer >= nummerLaenge || T_LOW || T_HIGH) {
auswertenNummer();
wZustand = ABGEHOBEN_ENTRY;
durchgang = true;
break;
}
if (nummer[vZiffer] == 0) { // Bei Ziffer 0 soll Stueck 10 spielen
spieleMP3(10);
} else spieleMP3(nummer[vZiffer]);
vZiffer++;
delay(600); // Warten bis Player in Gang kommt
}
break;
}
} while (durchgang);
}
void loop() {
static unsigned long messperiode = micros() + ABTAST_PERIODE * 1000; // Zeitmarken fuer TimeOuts
while (micros() < messperiode)
; // aufhalten bis zeit um
messperiode = micros() + ABTAST_PERIODE * 1000; // ms einstellen
digitalWrite(P_BOARD_LED, HIGH); // Testen wie lange Wandlung dauert
abfragenTelefon();
digitalWrite(P_BOARD_LED, LOW);
}
🚧 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.




Telefonumbau für Spielzwecke 🚧
Synopsis: 🔗https://prilchen.de/belegungsplan-esp32-lolin32-lite/ 🔗https://github.com/olikraus/u8g2/wiki/setup_tutorial 🔗https://mischianti.org/esp32-wemos-lolin32-lite-high-resolution-pinout-and-specs/ 🔗https://docs.espressif.com/projects/arduino-esp32/en/latest/api/adc.html 🔗https://github.com/adafruit/Adafruit_NeoPixel 🔗https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/timer.html
🔗https://wolles-elektronikkiste.de/async-webserver-mit-dem-esp32

Aus zwei defekten alten W48 Telefonen ein 📖SuperHirn Telefon basteln.
Mit LOLIN32 Lite, DFPlayer, OLED-Display und Neopixel, Akku betrieben!
Ein Webserver ist auch dabei. Viele Probleme, steiniger Weg..
ESP32, OLED, WLAN-Einbindung, Webserver, REST-API, Websocket, JSON..
Immer wieder langes Einlesen in verschiedene Quellen und Designentscheidungen…


LOLIN32 Lite ist für meinen ursprünglichen Einsatzzweck SolarPowered wohl ungeeignet
Gekauft bei [AZ-Delivery ESP32 Lolin LOLIN32🔗]. Wollte damit meine Gartensolarlampen usw. Sachen betreiben.
Nun stellt sich raus, dass der Ruhestrom 🔗gigantisch und der AD-Wandler 🔗ziemlicher Schrott ist.
In SuperHirn-Telefonen könnten sie ihren Gnadenhof finden.
Weitere Teilprobleme
- Nummernschalter so umbauen, dass die aktuelle Softwarebasis verwendet werden kann, letzte Denkfehler erkennen und beseitigen. ToDo Bild und Schaltplan.
- Spannungen und Ströme messen, Akku-Ladestand beurteilen.
- Problem: Bescheidener AD-Wandler des ESP!
- 🔗ESP32 ADC – Read Analog Values with Arduino IDE
- ToDo: Mathematik drauf werfen.
- OLED-Anzeige über I2C anschließen und Library auswählen. I2C Anschlüsse sind zum Glück frei wählbar.
- DFPlayer Mini über UART anschließen. UART Anschlüsse der Hardware-UARTs sind zum Glück frei wählbar. Es gibt deshalb keine Software Serial.
- Den Wecker mit 3,8V klingeln lassen!
- Mein Telefon hat zwei Weckerspulen und ein Test ergab, dass die 3,8V des Akkus reichen, um es mit zwei BS170 Transistoren klingeln zu lassen.
- Telefonnummernschalter alle 2ms abfragen mit Timer-ISR
- Problem mit Timing bekommen, Interprozesskommunikation 📖Semaphor?
- WLAN verwenden, Varianten
- Als Access-Point, Smartphone bucht sich ein
- Normal ins WLAN einbuchen und Multidings..
- In Firmen-, Schulnetz einbuchen
- Webserver (synchron, asynchron), welchen verwenden?
- Webseiten, Bilder, CSS, JavaScript-Dateien speichern: Dateisystem SPIFFS vs. LittleFS
- Interaktion REST-API vs. Websocket
- JSON vs. simple Texte
- Over the Air Updates



















Ein Kommentar
Die Kommentare sind geschlossen.