163 lines
5.2 KiB
C++
163 lines
5.2 KiB
C++
#include <Arduino.h>
|
||
#include <BleKeyboard.h>
|
||
|
||
// --- Pin-Definitionen ---
|
||
#define PIN_HOOK 18 // GRÜN: HIGH = Hörer abgehoben, LOW = aufgelegt
|
||
#define PIN_DIAL 19 // WEISS: HIGH bei Drehung, fallende Flanken = Impulse
|
||
|
||
// --- Hardware & Timing-Konstanten ---
|
||
#define DEBOUNCE_MS 30 // Entprellzeit für Impulse (ms)
|
||
#define DIGIT_TIMEOUT_MS 350 // Pause nach Drehung, bis Ziffer erkannt ist (ms)
|
||
#define COMPLETE_DIAL_TIMEOUT 2500 // Pause nach letzter Ziffer, bis Rufnummer komplett ist (ms)
|
||
|
||
// --- BLE Keyboard Instanz ---
|
||
BleKeyboard bleKeyboard("FeTAp 791 Telefon", "Deutsche Bundespost", 100);
|
||
|
||
// --- Interrupt-Variablen (müssen volatile sein!) ---
|
||
volatile int pulseCount = 0;
|
||
volatile unsigned long lastPulseTime = 0;
|
||
volatile bool isDialing = false;
|
||
|
||
// --- Hauptprogramm-Variablen ---
|
||
bool bleConnectedLastState = false;
|
||
bool hookLastState = false;
|
||
|
||
String dialedNumber = "";
|
||
unsigned long lastDigitCompletedTime = 0;
|
||
bool waitingForCompleteNumber = false;
|
||
|
||
// -------------------------------------------------------------------
|
||
// Interrupt Service Routine (ISR) für die fallende Flanke
|
||
// -------------------------------------------------------------------
|
||
// 'IRAM_ATTR' sorgt dafür, dass dieser Code im schnellen RAM des ESP32 liegt
|
||
void IRAM_ATTR handleDialPulse() {
|
||
unsigned long currentMillis = millis();
|
||
|
||
// Entprellung direkt im Interrupt:
|
||
// Nur zählen, wenn der letzte Impuls länger als DEBOUNCE_MS her ist
|
||
if ((currentMillis - lastPulseTime) > DEBOUNCE_MS) {
|
||
pulseCount++;
|
||
isDialing = true;
|
||
lastPulseTime = currentMillis;
|
||
}
|
||
}
|
||
|
||
void setup() {
|
||
Serial.begin(115200);
|
||
delay(1000);
|
||
|
||
Serial.println("\n=========================================");
|
||
Serial.println("FeTAp 791 - ESP32 mit Hardware-Interrupt");
|
||
Serial.println("=========================================");
|
||
|
||
pinMode(PIN_HOOK, INPUT_PULLDOWN);
|
||
pinMode(PIN_DIAL, INPUT_PULLDOWN);
|
||
|
||
// --- Hardware-Interrupt konfigurieren ---
|
||
// Löst 'handleDialPulse()' EXAKT beim Übergang von HIGH nach LOW aus!
|
||
attachInterrupt(digitalPinToInterrupt(PIN_DIAL), handleDialPulse, FALLING);
|
||
|
||
bleKeyboard.begin();
|
||
Serial.println("[BLE] Warte auf Verbindung zum iPhone...");
|
||
}
|
||
|
||
void triggerIphoneShortcut(String number) {
|
||
if (!bleKeyboard.isConnected()) {
|
||
Serial.println("[BLE] Nicht verbunden – Kurzbefehl kann nicht gesendet werden!");
|
||
return;
|
||
}
|
||
|
||
Serial.printf("[Kurzbefehl] Starte Aktion für Rufnummer: %s\n", number.c_str());
|
||
|
||
// Nummer eintippen und mit 'Enter' bestätigen
|
||
bleKeyboard.print(number);
|
||
delay(100);
|
||
bleKeyboard.write(KEY_RETURN);
|
||
}
|
||
|
||
void processDialedDigit(int pulses) {
|
||
char digitChar;
|
||
if (pulses == 10) {
|
||
digitChar = '0';
|
||
} else if (pulses >= 1 && pulses <= 9) {
|
||
digitChar = '0' + pulses;
|
||
} else {
|
||
Serial.printf("[Wahl] Ungültige Impulsanzahl (%d) ignoriert.\n", pulses);
|
||
return;
|
||
}
|
||
|
||
dialedNumber += digitChar;
|
||
lastDigitCompletedTime = millis();
|
||
waitingForCompleteNumber = true;
|
||
|
||
Serial.printf("[Wahl] Ziffer: %c | Aktuelle Nummer: %s\n", digitChar, dialedNumber.c_str());
|
||
}
|
||
|
||
void loop() {
|
||
unsigned long currentMillis = millis();
|
||
|
||
// 1. Bluetooth-Verbindungsstatus
|
||
bool isConnectedNow = bleKeyboard.isConnected();
|
||
if (isConnectedNow && !bleConnectedLastState) {
|
||
Serial.println("\n[BLE] >>> iPhone erfolgreich VERBUNDEN! <<<");
|
||
bleConnectedLastState = true;
|
||
} else if (!isConnectedNow && bleConnectedLastState) {
|
||
Serial.println("\n[BLE] >>> Verbindung zum iPhone VERLOREN! <<<");
|
||
bleConnectedLastState = false;
|
||
}
|
||
|
||
// 2. Gabelumschalter (Hook)
|
||
bool currentHookState = digitalRead(PIN_HOOK);
|
||
if (currentHookState != hookLastState) {
|
||
delay(20); // Entprellen der Gabel
|
||
currentHookState = digitalRead(PIN_HOOK);
|
||
|
||
if (currentHookState != hookLastState) {
|
||
hookLastState = currentHookState;
|
||
|
||
if (currentHookState == HIGH) {
|
||
Serial.println("\n[Hörer] ABGEHOBEN");
|
||
dialedNumber = "";
|
||
waitingForCompleteNumber = false;
|
||
|
||
// Interrupt aktivieren / Zähler nullen
|
||
pulseCount = 0;
|
||
isDialing = false;
|
||
} else {
|
||
Serial.println("\n[Hörer] AUFGELEGT");
|
||
dialedNumber = "";
|
||
waitingForCompleteNumber = false;
|
||
pulseCount = 0;
|
||
isDialing = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. Auswertung der Ziffern & Rufnummer (nur bei abgehobenem Hörer)
|
||
if (hookLastState == HIGH) {
|
||
|
||
// A) Ist die Ziffern-Drehung beendet? (Timeout seit letztem Interrupt-Impuls)
|
||
if (isDialing && (currentMillis - lastPulseTime > DIGIT_TIMEOUT_MS)) {
|
||
|
||
// Für die sichere Übergabe aus der ISR kurz Interrupts pausieren oder Wert kopieren
|
||
noInterrupts();
|
||
int currentPulses = pulseCount;
|
||
pulseCount = 0;
|
||
isDialing = false;
|
||
interrupts();
|
||
|
||
if (currentPulses > 0) {
|
||
processDialedDigit(currentPulses);
|
||
}
|
||
}
|
||
|
||
// B) Ist die komplette Rufnummer fertig gewählt? (Pause nach letzter Ziffer)
|
||
if (waitingForCompleteNumber && (currentMillis - lastDigitCompletedTime > COMPLETE_DIAL_TIMEOUT)) {
|
||
waitingForCompleteNumber = false;
|
||
Serial.printf("\n[Wahl abgeschlossen] Vollständige Nummer: %s\n", dialedNumber.c_str());
|
||
|
||
// Kurzbefehl am iPhone ausführen
|
||
triggerIphoneShortcut(dialedNumber);
|
||
}
|
||
}
|
||
} |