Forum: Haus & Smart Home Probleme mit Interrupt (Türklingel)


von Silva (Gast)


Lesenswert?

Guten Morgen,

ich habe vor einiger Zeit eine Türklingelüberwachung aufgebaut. Diese
basiert auf einem Wemos D1 Mini (ESP8266). Ich verwende vier Dioden und 
einen Lastwiderstand um die Wechselspannung (12V) gleichzurichten, 
danach geht das auf einen Optokoppler (PC817), der widerrum an dem Wemos 
D1 Mini hängt.
Beim drücken des Klingelknopfes erfolgt eine Benachrichtigung per MQTT.

Ich habe aktuell keine Bilder vom Aufbau, da ich momentan nicht zuhause 
bin.

Nun ist es jedoch so, dass wenn ich den Klingelknopf drücke in ca. 1/3 
der Fällen keine Benachrichtigung erfolgt, anscheinend wird auch der 
Interrupt nicht ausgelöst. Der ESP ist während der Zeit jedoch per 
Netzwerk erreichbar, es werden auch keine MQTT Pakete verschickt (habe 
ich mitgesnifft).

Der Code sieht in etwa wie folgt aus (nur die relevanten Teile):
1
/* Doorbell state */
2
volatile byte doorbell_state = HIGH;
3
4
/* Timer */
5
Timer t;
6
7
/* Setup functipn */
8
void setup() {
9
  /* Startup delay */
10
  delay(100);
11
12
  /* Set CPU frequency */
13
  system_update_cpu_freq(CPU_FREQ);
14
15
  /* Deactive LED_BUILTIN */
16
  pinMode(LED_BUILTIN, OUTPUT);  
17
  LED_OFF();
18
19
  /* Initialize serial */
20
  initSerial();
21
22
  /* Connect to WIFI */
23
  initWifi();
24
  if(!connectWifi()) {
25
    /* Show WIFI status */
26
    wifiStatus();
27
    
28
    _print(F("Failed."));
29
    ESP.restart();
30
    delay(10);
31
    while(1);
32
  }
33
34
  /* Display WiFi status */
35
  wifiStatus();
36
37
  /* Initialize mqtt */
38
  mqtt.begin(MQTT_RECEIVER, client);
39
40
  /* Initialize OTA */
41
  #ifdef USE_OTA
42
  initOTA();
43
  #endif
44
45
  /* Initialize sensors */
46
  /* Pull-up doorbell PIN to prevent floating input */
47
  pinMode(DOORBELL_PIN, INPUT_PULLUP);
48
49
  /* Attach interrupt handler to doorbell PIN */
50
  _debug("Attaching interrupt");
51
  attachInterrupt(digitalPinToInterrupt(DOORBELL_PIN), ISR_doorbell, FALLING);
52
53
  /* Setup timers */
54
  t.every(60*1000, sendMQTTHello);
55
}
56
57
/* Loop */
58
void loop() {
59
  /* Handle OTA */
60
  #ifdef USE_OTA
61
  handleOTA();
62
  #endif
63
64
  /* Check doorbell state */
65
  if(doorbell_state == LOW) {
66
    doorbell_state = HIGH;
67
  
68
    /* Send data */
69
    _debug("PIN changed to low");
70
71
    /* Send doorbell state */
72
    sendDoorbellState(true);
73
74
    /* Delay some time before reattaching interrupt */
75
    delay(500);
76
77
    /* Re-attach interrupt handler to doorbell PIN */
78
    attachInterrupt(digitalPinToInterrupt(DOORBELL_PIN), ISR_doorbell, FALLING);
79
  }
80
81
  /* Update timer */
82
  t.update();
83
}
84
85
/* Doorbell ISR */
86
void ISR_doorbell() {
87
  /* Disable interrupt */
88
  detachInterrupt(digitalPinToInterrupt(DOORBELL_PIN));
89
    
90
  /* Set state to low */
91
  doorbell_state = LOW;
92
}

Hat jemand eine Idee wo ich ansetzen könnte?

Vielen Dank!

von Silva (Gast)


Lesenswert?

push Jemand eine Idee? :)

von Johannes S. (Gast)


Lesenswert?

wofür ist das detachInterrupt und re-Attach in der loop? Normalerweise 
reicht es einmal den Int scharf zu schalten.

von Silva (Gast)


Lesenswert?

Ich habe das so als "einfachen" debounce verwendet, könnte das ein 
Problem darstellen?

von Johannes S. (Gast)


Lesenswert?

bin kein Arduino/ESP Experte, aber im detach passiert schon einiges.
Ein einfaches debounce wäre in der loop erst nach dem MQTT senden und 
warten den 'doorbell_state = HIGH;' auszuführen.

von Silva (Gast)


Lesenswert?

Danke, das werde ich heute abend mal testen!

von Silva (Gast)


Lesenswert?

Hallo zusammen,

so ich habe den Code jetzt gestern mal wie folgt verändert, das Problem 
scheint etwas besser geworden zu sein, jedoch besteht es immer noch (ca. 
jedes 6 Klingeln führt nicht zu einer Benachrichtigung):
1
/* WiFi client */
2
WiFiClient client;
3
4
/* MQTT */
5
MQTTClient mqtt(2000);
6
7
/* Doorbell state */
8
volatile byte doorbell_state = HIGH;
9
10
/* Enable ADC_VCC mode */
11
ADC_MODE(ADC_VCC);
12
13
/* Timer */
14
Timer t;
15
16
/* Setup functipn */
17
void setup() {
18
    /* Startup delay */
19
  delay(100);
20
21
  /* Set CPU frequency */
22
  system_update_cpu_freq(CPU_FREQ);
23
24
  /* Deactive LED_BUILTIN */
25
  pinMode(LED_BUILTIN, OUTPUT);  
26
  LED_OFF();
27
28
  /* Initialize serial */
29
  initSerial();
30
31
  /* Connect to WIFI */
32
  initWifi();
33
  if(!connectWifi()) {
34
    /* Show WIFI status */
35
    wifiStatus();
36
    
37
    _print(F("Failed."));
38
    ESP.restart();
39
    delay(10);
40
    while(1);
41
  }
42
43
  /* Display WiFi status */
44
  wifiStatus();
45
46
  /* Initialize mqtt */
47
  mqtt.begin(MQTT_RECEIVER, client);
48
49
  /* Initialize OTA */
50
  #ifdef USE_OTA
51
  initOTA();
52
  #endif
53
54
  /* Initialize sensors */
55
  /* Pull-up doorbell PIN to prevent floating input */
56
  pinMode(DOORBELL_PIN, INPUT_PULLUP);
57
58
  /* Attach interrupt handler to doorbell PIN */
59
  _debug("Attaching interrupt");
60
  attachInterrupt(digitalPinToInterrupt(DOORBELL_PIN), ISR_doorbell, FALLING);
61
62
  /* Setup timers */
63
  t.every(60*1000, sendMQTTHello);
64
}
65
66
/* Loop */
67
void loop() {
68
  /* Handle OTA */
69
  #ifdef USE_OTA
70
  handleOTA();
71
  #endif
72
73
  /* Check doorbell state */
74
  if(doorbell_state == LOW) {
75
    /* Debug information */
76
    _debug("PIN changed to low");
77
78
    /* Send doorbell state */
79
    sendDoorbellState(true);
80
81
    /* Reset doorbell state */
82
    doorbell_state = HIGH;
83
  }
84
85
  /* Update timer */
86
  t.update();
87
}
88
89
/* Doorbell ISR */
90
void ICACHE_RAM_ATTR ISR_doorbell() {
91
  static unsigned long last_interrupt_time = 0;
92
  unsigned long interrupt_time = millis();
93
94
  /* Debounce handling */
95
  if(interrupt_time - last_interrupt_time > DEBOUNCE_TIME) {
96
    /* Set state to low */
97
    doorbell_state = LOW;
98
  }
99
100
  last_interrupt_time = interrupt_time;
101
}


Hat jemand noch eine Idee? Ich glaube langsam fast nicht mehr, dass es 
am
Code liegt...


Danke für alle Antworten!

von Johannes S. (Gast)


Lesenswert?

testest du immer mit der Klingelschaltung oder einem einfachem Taster 
gegen GND um das Senden auszulösen?
Kommt das Hello immer durch? Beim ESP gibt es ja das Standardproblem das 
der eine gute Versorgung braucht und möglichst noch einen 
Pufferkondensator über das Board.

von Silva (Gast)


Lesenswert?

Hi!

Also ich teste immer direkt mit der Klingel, der ESP ist inzwischen fest 
eingebaut. Die Hellos kommen immer durch, ja. Ich habe den Traffic 
komplett mitgesnifft (tcpdump). Es scheint einfach so, dass der 
Interrupt manchmal nicht ausgelöst wird und daher auch keine Daten 
gesendet werden.

Der Aufbau ist übrigens auf einer Platine, alles ist fest verlötet. Ich 
habe diese nochmals geprüft, Kontakte sind soweit ich das gesehen habe, 
alle in Ordnung, habe auch alles - soweit sinnvoll - durchgemessen.

Gibt es evtl. konzeptionell noch etwas, was ich falsch machen könnte?

Der Aufbau ist wie gesagt eigentlich sehr einfach, vier Dioden als 
Brückengleichrichter, ein Lastwiderstand, danach auf den Optokoppler 
(PC817), der geht dann direkt an den ESP. Der ESP hängt an einem 
separaten, stabilisierten  5V Netzteil (kann max 15W liefern).

Viele Dank!

von Johannes S. (Gast)


Lesenswert?

Und der PC817 hat einen passenden Vorwiderstand? Ein kleiner Elko wäre 
auch sinnvoll, der GL liefert ja eine pulsierende Gleichspannung und der 
Int wird mit 100 Hz getriggert.

von Silva (Gast)


Lesenswert?

Hallo,

ja einen passenden Vorwiderstand hat er. Elko ist aktuell keiner 
verbaut,
dieser dann direkt nach dem Brückengleichrichter und vor dem 
Optokoppler?
Von der Größe reichen vermutlich ein paar uF oder?


Danke für deine Hilfe!

von Theor (Gast)


Lesenswert?

Silva schrieb:
> [...] Es scheint einfach so, dass der
> Interrupt manchmal nicht ausgelöst wird und daher auch keine Daten
> gesendet werden.
>
> [...]

Es scheint mir (sic!) sinnvoller die Schaltung aus dem Gleichrichter, 
Widerstand und Optokoppler zunächst einzeln zu testen. Falls Du ein 
Oszilloskop hast, würde ich, mir das Signal nach dem Optokoppler 
anschauen.
So erkennst Du auch, ob und was für ein Prellen evtl. vorkommt und ob 
Deine  Entprellung durch einfaches Warten, tatsächlich hinreicht. (Im 
allgemeinen ist das eher nicht der Fall).

Sinnvoll scheint mir das deswegen, weil Du eine vergleichsweise komplexe 
Kette von Funktionen (Hardware und Software) im Moment von einem Ende 
(Klingeltaster) zu anderen Ende (Netzwerk-Paket) testest und darin, eben 
weil soviel in dieser Kette steckt, auch viel schief gehen kann.
Du erwähnst auch immer wieder, dass die Netzwerkfunktionen wohl gegeben 
sind, was mir den Eindruck vermittelt, dass Du trotz Deiner Worte 
darüber einen Zweifel nicht ganz ausschliessen kannst. Könnte ich auch 
nicht.
In solchen Fällen geht "man" so vor, dass man zunächst die 
Einzelfunktionen testet, bzw. die Kette schrittweise verlängert bzw. 
vervollständigt.
Dann, im nächsten Schritt, nur eine LED aufleuchten lassen, wenn die 
Betätigung erkannt wurde.

von Silva (Gast)


Lesenswert?

Hi Theor,

danke für deine Antwort. Ja das wird vermutlich das sinnvollste sein. 
Ich hatte das natürlich vor dem Einbau auch getestet, aber wohl nicht 
ausführlich genug. Ich werde mal versuchen ein Oszi auszuleihen und mir 
das Signal anzusehen, das kann aber ein paar Tage dauern.

Ich hatte hauptsächlich auch geschrieben, damit jemand mal den Code 
+berfliegt, manchmal ist es ja so, dass man einen Fehler selbst einfach 
nicht  sieht und jemand anderem fällt das dann sofort auf.

Vielen Dank!

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

Silva schrieb:
> Danke für deine Hilfe!

keine Ursache, ich bastel auch an Funkkomponenten fürs Haus und die Idee 
ist gut. Ich benutze nur RFM Module und wollte versuchen den Klingelpuls 
als Stromversorgung zu nutzen ohne zusätzliches Netzteil.

Vor dem Einbau teste ich sowas auch erst länger, wie auch von Theor 
vorgeschlagen.

Bei dem Analogkram nutze ich auch gerne LTSpice, da kann man das schnell 
mal virtuell zusammenlöten. In diesem Fall hängt es natürlich vom 
Optokoppler und dem R ab, aber 10-22 µ / 50 V sollten reichen.

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

ohne Elko ist klar das 100 Ints/s ankommen, da ist das Tasterprellen 
nicht das Problem. Und ob der ESP solche Interruptraten mag weiss ich 
nicht, im Hintergrund will der ja sein WLAN bedienen.

von Anal Phabet (Gast)


Lesenswert?

Johannes S. schrieb:
> SimuGLohneElko.png

Gemnial diese LTSpice Plots bei denen die Knoten einfach
durchnumeriert sind. Da weiss man gleich wo man suchen muss.

von Silva (Gast)


Lesenswert?

Danke für die ganzen Tipps, ich werde mir das heute abend nochmal
 genauer ansehen und berichte dann!

von Johannes S. (Gast)


Lesenswert?

Anal Phabet schrieb:
> Gemnial diese LTSpice Plots bei denen die Knoten einfach
> durchnumeriert sind. Da weiss man gleich wo man suchen muss.

Sorry, war jetzt nur quick + dirty zusammengeklickt, natürlich kann man 
den Netzen Namen geben und auch Labels setzen. Hier ist so einfach das 
man hoffentlich sieht was gemeint ist, die Ausgangsspannung und im 
anderen Beispiel ist noch der Strom durch den 1k R.

von Michael U. (amiga)


Lesenswert?

Hallo,

ist das eine Mischung für die ArduinoIDE?
Dein Handling verstehe nur teilweise. initWifi() vernisse ich, welchen 
MQTT-Client Du benutzt weiß ich im Moment auch nicht.
Ich habe da bei mir mal ziemlich lange einen ähnlichen Effekt gesucht, 
WiFi war connected und MQTT nicht. Da ich generell bei MQTT lastWill 
benutze, fiel es mir nur daran auf, daß mir der Broker die 
Offline-Message schickte obwohö FEHM bei Ping-keepalive alles ok 
meldete.

Allerdings müßte ich dazu Dein Konzept verstehen. Was macht der ESP 
sonst noch alles außer Klingel abfragen?

Gruß aus Berlin
Michael

: Bearbeitet durch User
von Silva (Gast)


Lesenswert?

Hallo Michael,

ich habe nicht den kompletten Code gepastet, sondern nur die 
(hoffentlich) relevanten Stellen, insbesondere das Interrupt Handling.

Um die Fragen zu beantworten:

- Der ESP triggert eine MQTT Meldung, wenn jemand die Türklingel 
betätigt

- Der ESP schickt regelmäßig eine MQTT Meldung (Hello), diese wird alle 
60
Sekunden versendet und kommt auch immer an

Das initWifi() / connectWifi() sieht so aus:
1
/* Initialize WIFI */
2
void ICACHE_FLASH_ATTR initWifi() {
3
  /* Set station mode */
4
  WiFi.mode(WIFI_STA);
5
  WiFi.disconnect();
6
7
  #ifdef USE_STATIC
8
  IPAddress ip;
9
  IPAddress gateway;
10
  IPAddress subnet;
11
  IPAddress dns;
12
13
  ip.fromString(_WIFI_IP);
14
  gateway.fromString(_WIFI_GATEWAY);
15
  subnet.fromString(_WIFI_SUBNET);
16
  dns.fromString(_WIFI_DNS);
17
 
18
  WiFi.config(ip, gateway, subnet, dns);
19
  WiFi.hostname(_WIFI_HOSTNAME);
20
  wifi_station_set_hostname(_WIFI_HOSTNAME);
21
  #endif
22
}
23
24
/* Connect to WIFI */
25
bool ICACHE_FLASH_ATTR connectWifi() {
26
  WiFi.begin(_WIFI_SSID, _WIFI_PASS);
27
  
28
  uint8_t i = 0;
29
  uint8_t status = WiFi.status();
30
  while(status != WL_CONNECTED && i++ < MAX_WIFI_ATTEMPTS-1) {
31
    _print_args("-> Error: WIFI status: %d (%s)\n", status, mapWifiStatus(status).c_str());
32
    
33
    status = WiFi.status();
34
    delay(1000);
35
  }
36
37
  return i != MAX_WIFI_ATTEMPTS;
38
}

Die sendDoorBellState Funktion erstellt nur einen JSON buffer und ruft 
dann sendMQTT(JsonObject &json) auf, diese Funktion ändert den Buffer 
nochmals (fügt einige Daten wie DEVICE_NAME, VERSION, Free Heap Space 
hinzu).
Dannn wird geprüft, ob bereits eine WLAN und MQTT Verbindung besteht 
(falls nicht, werden diese erneut aufgebaut) und dann wird das MQTT 
publish durchgeführt.

Als MQTT Lib verwende ich https://travis-ci.org/256dpi/arduino-mqtt

Tatsächlich hatte ich auch erst in Verdacht, dass die MQTT Pakete nicht 
ankommen, davor hatte ich die ArduinoMQTT Lib verwendet (und dort btw 
auch die MQTT Packet Size entsprechend erhöht).

Danke!

von Rainer W. (rainer_w)


Lesenswert?

Silva:

>Ich verwende vier Dioden und
>einen Lastwiderstand um die Wechselspannung (12V) gleichzurichten,
>danach geht das auf einen Optokoppler (PC817), der widerrum an dem Wemos
>D1 Mini hängt.

Irgendwo könnte man noch eine Anzeige LED machen die bei Tasterdruck 
leuchtet. Das ist Überschaubarer.

>Tatsächlich hatte ich auch erst in Verdacht, dass die MQTT Pakete nicht
>ankommen

Diesen Verdacht könnte man öfter haben. Eine Haustürklingel kann auch 
mal lange nicht benutzt werden.
Deshalb würde ich regelmäßig Pakete senden und zum Beispiel zählen (ggf. 
mit Zeit vergleichen) Hier dann eventuell auch eine LED.

: Bearbeitet durch User
von Tierfreund (Gast)


Lesenswert?

Rainer W. schrieb:
> Eine Haustürklingel kann auch
> mal lange nicht benutzt werden.
> Deshalb würde ich regelmäßig Pakete senden...

Man könnte aber auch die Stereoanlage volle Pulle sporadisch von der 
Steuerung einschalten lassen, etwa des Nachts zwischen 1 und 5 Uhr. Dann 
kommt der Nachbar und drückt den Klingelknopf mit Sicherheit und auch 
fest genug.

von Rainer W. (rainer_w)


Lesenswert?

Tierfreund:
>Rainer W. schrieb:
> Eine Haustürklingel kann auch
> mal lange nicht benutzt werden.
> Deshalb würde ich regelmäßig Pakete senden...

>Man könnte aber auch die Stereoanlage volle Pulle sporadisch von der
>Steuerung einschalten lassen, etwa des Nachts zwischen 1 und 5 Uhr. Dann
>kommt der Nachbar und drückt den Klingelknopf mit Sicherheit und auch
>fest genug.

Übertagungs oder Verbindungsprobleme können immer mal auftreten.
Besser wenn man sich darauf einstellen kann.

Ich schreibe von einer Bereitschaftsanzeige mit Verbindungsprüfung. Ohne 
Geräusche. Wie oft am Tag (oder alle wie viel Tage) das sein soll weiß 
ich nicht.

>Nachts zwischen 1 und 5 Uhr

Je nach dem was einfacher ist.

von Rainer W. (rainer_w)


Lesenswert?

Vor meinem Beitrag hatte ich übersehen das sie Verbindungsprüfung
 bereits größten teils existiert.

>/* Setup timers */
>t.every(60*1000, sendMQTTHello);
.....
>- Der ESP schickt regelmäßig eine MQTT Meldung (Hello), diese wird alle
>60
>Sekunden versendet und kommt auch immer an


Das hatte ich überlesen. sorry.

Klingeltrafo Gleichrichter sind  schon da.
Erweitert man diese Platine um einen Eingang und  einen Ausgang 
+Transistor+Relais
hat man seine Schaltung komplett in der Meldekette der 
Verbindungsprüfung. Und es ergeben sich Vorteile wie die 
Nachrüstmöglichkeit einer Anwesenheitsanzeige.

Den zweiten Interrupt Eingang kann man dann zusätzlich über einen 
normalen Eingang  abfragen (wenn es Interrupt sein soll).

von Johannes S. (Gast)


Lesenswert?

eine andere Frage ist ob das Klingeln überhaupt per Interrupt gemeldet 
werden muss, Polling mit zB. 20 ms sollte auch locker reichen 
(Kondensator hinter dem GL vorausgesetzt).
Oder den WakeUp Pin nutzen und den ESP ständig schlafen lassen, dann 
läuft der auch mit Batterien lange.

von Michael U. (amiga)


Lesenswert?

Hallo,

mir ist der Aufwand z.B. beim WiFi-Connect etwas unklar.
Die Frage, warum Interrupt würde ich auch stellen. Der ESP läuft ja 
ohnehin durch, da kann man auch den Pin einfach im Loop abfragen.
Ob man eine feste IP nutzen will oder DHCP weiß man bei solche einem 
Projekt ja auch und macht das Setup eben entsprechend.

Ich nutze bei Sachen, die im Deepsleep mit Akku laufen den PubSubClient, 
ansonsten AsyncMQTT. Allerdings schicke ich per MQTT auch keine Romane. 
;)

Zurück zum Grund meiner Frage. Wenn WiFi einen reconnect macht, das 
macht der ESP ohnehin alleine wenn man es ihm nicht verbietet, meldet 
WiFi.status(); WL_CONNECTED wenn die Verbindung steht.
Bei DHCP kann es aber passieren, daß er zu diesem Zeitpunkt noch keine 
gültige IP hat. Wenn jetzt sofort ein MQTT connect folgt läuft der gegen 
die Wand...

Ich schicke beim WLAN und MQTT connect als erstes MODULNAME/Status 
online und MODULNAME/RSSI Rssi-Wert. Außerdem wird generell lastWill 
gesetzt auf MODULNAME/Status offline, keepAlive vom MQTT ist meist auf 5 
Minuten gesetzt.

Für die Bearbeitung der Topics ist ohnehin bei mir FHEM zuständig, da 
kann der lastWill notfalls einen Hinweis auf den Ausfall anzeigen. 
Außerdem läuft für alle ESP in FHEM ein ping-Timer, der alle Minute die 
Clients anpingt und bei Bedarf Alarm schlägt, wenn keine Antwort kommt.

Gruß aus Berlin
Michael

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.