Die Make (Ausgabe 2/2026) enthielt einen Vorschlag für die Nutzung eines alten Wählscheibentelefons mit einem Raspberry Pi und einem LLM inklusive Sprachausgabe. Insgesamt ein witziges Projekt, aber irgendwie war es mir ein bisschen zu viel „KI“.
Ich hatte jedoch noch so ein altes Schätzchen: Einen Fernsprechtischapparat FeTAp 611 in Grün. Es ist das Telefon meiner Schwiegereltern (und ja, die hatten damals tatsächlich eine dreistellige Telefonnummer!). Ich selbst habe es während meines Studiums in der Studentenbude genutzt. Da ich mich nur schwer von solchen Dingen trennen kann, fristete es seitdem sein Dasein irgendwo im Keller.

Der Make-Artikel inspirierte mich schließlich dazu, dem alten Teil wieder Leben einzuhauchen. Fast alle Geräte in unserem Haus werden mittlerweile über Home Assistant gesteuert – meist vollautomatisch über Sensoren oder Zeitpläne. Gelegentlich nutze ich aber auch das Tablet im Flur, auf dem noch eine alte FTUI von FHEM läuft (weil ich das Dashboard mit HA einfach nicht so schick hinbekomme), oder eben die Home Assistant App auf dem Handy.
Die Umsetzung
Mittels eines ESP32 WROOM wurde die notwendige Technik eingepflanzt. Das Schöne dabei: Das Innenleben blieb weitestgehend original, ich habe nichts ausgebaut.
- Hörergabel: Den Schalter habe ich direkt auf der Rückseite der Platine angelötet.
- Wählscheibe: Die Impulse werden einfach am ursprünglichen Stecker abgegriffen (Pin 1 & 2).
- Audio: Für die Tonausgabe sorgt ein MAX98357. Lediglich für den Anschluss des alten Lautsprechers habe ich zwei Kabel durchgeschnitten und per Lüsterklemme verbunden (ja, es musste am Ende schnell gehen).





Das Telefon kann nun
- Sich ins WLAN einbinden
- Verbindung zu meinem MQTT-Server aufbauen
- WLAN und/oder MQTT-Status per LED anzeigen
- Den klassischen 425-Hz-Wählton ausgeben, sobald der Hörer abgehoben wird.
- Die gewählte Nummer an den MQTT-Server übermitteln
Anschlussbelegung
| Bauteil | Pin |
| LED | 26 |
| Wählscheibe | 14 |
| Hörer | 27 |
| I2S_BCLK (MAX98357) | 18 |
| I2S_LRC (MAX98357) | 19 |
| I2S_DOUT (MAX98357) | 23 |
Der Code
Was bisher implementiert ist: Der ESP32 übernimmt das Management der WLAN-Verbindung, die MQTT-Kommunikation und die Dekodierung der Wählscheiben-Impulse. Über den MAX98357 wird der klassische Wählton via I2S direkt im Code generiert und mit einem leichten Rauschen unterlegt, um den alten analogen Charakter des Hörers beizubehalten.
#include <WiFi.h>
#include <PubSubClient.h>
#include <driver/i2s.h>
// ================== WLAN ==================
const char* ssid = "hier_die_ssid";
const char* password = "hier_das_wlan_passwort";
// ================== MQTT ==================
const char* mqtt_server = "192.168.xxx.xxx";
int mqtt_fail_count = 0;
const int mqtt_max_retries = 5;
bool mqtt_blocked = false;
// ================== LED ===================
const int ledPin = 26;
enum LedState {
LED_WIFI_CONNECTING,
LED_WIFI_CONNECTED,
LED_MQTT_CONNECTING,
LED_MQTT_CONNECTED
};
LedState currentState = LED_WIFI_CONNECTING;
// ================== Wifi ==================
WiFiClient espClient;
PubSubClient client(espClient);
// ================== Fetap ==================
const int dialPin = 14;
const int hookPin = 27;
// ================== I2S AUDIO ==================
#define I2S_BCLK 18
#define I2S_LRC 19
#define I2S_DOUT 23
bool toneActive = false;
uint32_t toneIndex = 0;
// ================== Waehlen ==================
volatile int pulses = 0;
volatile unsigned long lastPulseMicros = 0;
unsigned long lastActivity = 0;
bool lastHookState = false;
// ================== Interrupt ==================
void IRAM_ATTR onPulse() {
unsigned long now = micros();
if (now - lastPulseMicros < 80000) return;
pulses++;
lastPulseMicros = now;
lastActivity = millis();
}
// ================== SETUP ==================
void setup() {
Serial.begin(115200);
pinMode(dialPin, INPUT_PULLUP);
attachInterrupt(dialPin, onPulse, FALLING);
pinMode(hookPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// ================== I2S INIT ==================
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = 8000,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCLK,
.ws_io_num = I2S_LRC,
.data_out_num = I2S_DOUT,
.data_in_num = I2S_PIN_NO_CHANGE
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
// ================== WLAN ==================
currentState = LED_WIFI_CONNECTING;
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
updateLED();
delay(300);
}
currentState = LED_WIFI_CONNECTED;
client.setServer(mqtt_server, 1883);
Serial.println("Telefon bereit");
}
// ================== LOOP ==================
void loop() {
if (!client.connected()) reconnect();
client.loop();
updateLED();
bool hookReading = (digitalRead(hookPin) == LOW);
// ================== HOOK CHANGE ==================
if (hookReading != lastHookState) {
delay(40);
if (hookReading == (digitalRead(hookPin) == LOW)) {
lastHookState = hookReading;
if (hookReading) {
Serial.println("Hörer ABGENOMMEN");
pulses = 0;
sendStatus(true);
toneActive = true;
toneIndex = 0;
} else {
Serial.println("Hörer AUFGELEGT");
sendStatus(false);
toneActive = false;
i2s_zero_dma_buffer(I2S_NUM_0);
i2s_stop(I2S_NUM_0);
i2s_start(I2S_NUM_0);
}
}
}
// ================== Wählscheibe ==================
unsigned long now = millis();
if (lastHookState) {
if (pulses > 0 && (now - lastActivity) > 900) {
int number = pulses;
if (number == 10) number = 0;
Serial.print("Gewählt: ");
Serial.println(number);
sendMQTT(number);
pulses = 0;
}
}
// ================== 425 Hz Ton ==================
if (toneActive) {
size_t written;
float freq = 425.0;
float t = (float)toneIndex / 8000.0;
int16_t sample =
(int16_t)(2000 * sin(2 * PI * freq * t));
sample += random(-300, 300);
i2s_write(I2S_NUM_0, &sample, sizeof(sample), &written, portMAX_DELAY);
toneIndex++;
if (toneIndex > 8000) toneIndex = 0;
}
}
// ================== MQTT ==================
void sendMQTT(int number) {
if (!client.connected()) return;
client.publish("fetap_611/nummer", String(number).c_str());
}
// ================== STATUS ==================
void sendStatus(bool onHook) {
if (!client.connected()) return;
client.publish("fetap_611/hoerer", onHook ? "ON" : "OFF");
client.publish("fetap_611/nummer", "-1");
}
// ================== MQTT reconnect ==================
void reconnect() {
if (mqtt_blocked) return;
while (!client.connected()) {
currentState = LED_MQTT_CONNECTING;
if (client.connect("fetap_phone")) {
mqtt_fail_count = 0;
currentState = LED_MQTT_CONNECTED;
} else {
mqtt_fail_count++;
if (mqtt_fail_count >= mqtt_max_retries) {
mqtt_blocked = true;
return;
}
delay(1500);
}
}
}
// ================== LED ==================
void updateLED() {
static unsigned long lastBlink = 0;
static bool state = false;
unsigned long now = millis();
switch (currentState) {
case LED_WIFI_CONNECTING:
if (now - lastBlink > 200) {
state = !state;
digitalWrite(ledPin, state);
lastBlink = now;
}
break;
case LED_WIFI_CONNECTED:
if (now - lastBlink > 800) {
state = !state;
digitalWrite(ledPin, state);
lastBlink = now;
}
break;
case LED_MQTT_CONNECTING:
if (now - lastBlink > 150) {
state = !state;
digitalWrite(ledPin, state);
lastBlink = now;
}
break;
case LED_MQTT_CONNECTED:
digitalWrite(ledPin, HIGH);
break;
}
}
Die Herausforderung: Timing und Entprellung
Die Abfrage eines so alten mechanischen Geräts bringt ein paar Besonderheiten mit sich, die man im Code berücksichtigen muss. Hier sind zwei Punkte, bei denen man eventuell individuell nachjustieren muss:
- Entprellen der Wählscheibe: Im Interrupt nutze ich eine Sperrzeit von
80ms. Das ist notwendig, da die mechanischen Kontakte beim Öffnen und Schließen kurz „prellen“. Wenn eure Wählscheibe falsche Ziffern erkennt, ist dieser Wert der erste Hebel, an dem ihr ansetzen solltet. - Der Hörerschalter (Gabel): Auch dieser Schalter ist mechanisch und neigt zum Prellen. Ich habe hier ein
delay(40)eingebaut, um sicherzugehen, dass der Zustand stabil ist, bevor die MQTT-Meldung „Hörer abgenommen“ rausgeht.
Warum das Ganze?
Im Gegensatz zu moderner Digitaltechnik gibt die Wählscheibe einfach nur mechanische Impulse (Unterbrechungen) aus, etwa 10 Impulse pro Sekunde (10Hz). Eine gewählte „3“ sind drei kurze Unterbrechungen der Leitung. Da der ESP32 im Vergleich zur Wählscheibe sehr schnell ist, würde er ohne diese künstlichen „Gedenksekunden“ (Delays und Sperrzeiten) jedes mechanische Zittern als extra Impuls interpretieren.
Die aktuell im Code gewählten Zeiten und Mechanismen funktionieren bei meinem Telefon sehr zuverlässig.
Automatisierung in HA
Auf die Änderungen des entsprechenden MQTT-Topics reagiere ich mit einer Automatisierung im Home Assistant. Im folgenden Beispiel werden zwei Lampen bei Wahl der 1 oder der 2 getoggelt
alias: FeTap Steuerung
description: Schaltet Aktoren basierend auf Sensorwert 1-9
triggers:
- entity_id: sensor.fetap_nummer
trigger: state
conditions:
- condition: template
value_template: "{{ trigger.to_state.state in ['1','2','3','4','5','6','7','8','9'] }}"
actions:
- choose:
- conditions:
- condition: state
entity_id: sensor.fetap_nummer
state: "1"
sequence:
- action: light.toggle
target:
entity_id:
- light.lampe_fensterbank
- conditions:
- condition: state
entity_id: sensor.fetap_nummer
state: "2"
sequence:
- action: light.toggle
target:
entity_id:
- light.lampe_couch
default: []
mode: restart
Ausblick
Das war ein erster PoC was man mit dem alten Telefon alles machen kann. Der Quellcode ist nicht wirklich aufgeräumt und die WLAN Credentials noch direkt im Code enthalten. Ich glaube die alten Telefone haben auch mit dem 425Hz-Ton aufgehört sobald man gewählt hat.
Ideen für die nächsten Schritte:
- WLAN-Credentials komfortabel über eine Webseite z.B. im SPIFFS/LittleFS konfigurieren.
- Wählton deaktivieren, sobald die erste Ziffer gewählt wurde (originalgetreuer).
- Ein kurzes akustisches Signal ausgeben, wenn das MQTT-Telegramm erfolgreich gesendet wurde.
- Mittels Step-Up-Wandler die alte mechanische Klingel des Telefons für Benachrichtigungen integrieren.
- Das Mikrofon in Betrieb nehmen (eventuell durch eine moderne Elektret-Kapsel ersetzen).
- Ziffernfolgen implementieren (aktuell funktioniert das nur mit einzelnen Ziffern)
Viel Spaß beim Bauen und erweitern!
Gruß
Chris