I have ghost external interrupt on a rainmeter connected on an esp32. The esp32 send data to domoticz via wifi with http/json API.
The external interrupt FALLING fire 3 times/hours up to 1 time every 8 hours.
I thought the problem was long wire (15 meters) but I removed everything from the ESP12 and the problem persist with ESP32 only with nothing connect to (INPUT_PULLUP on the interrupt pin).
This can be usefull to understand my programs:
- The rainmeter is not easy to de-bounce because the reed switch is only closed from 119ms to 141 ms. My strategy was to ignore any FALLING 500 ms after the first FALLING detected.
- Interrupt are used because http connection can last more than 150ms
- ESP32 is a 2 core micro-controller with multitask capabilities. Conflict are possible when you try to read/write the same variable in interrupt/main process. portENTER_CRITICAL_ISR(&mux); are used to avoid this.
- I think volatile is not the right strategy for ESP32 (maybe I am wrong)
- The program autoreconnect on wifi disconnect
- SendDzAlarm log reconnect and http error (once reliable communication is back).
More détails on the hardware on the domoticz forum:
https://www.domoticz.com/forum/viewtopic.php?p=316387
The rainmeter is a reed switch connect to the interrupt pin and ground.
First, the simplified ESP32 program with all the wifi/http removed: there is no ghost interrupt on that one:
#define interruptPin 25 //IO25/D2
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; //pour eviter l'accès simultané aux variables
bool rainCountInitFlag = false;
double rainCount = 0.0;
long rainCountNew = 0; //ISR - tipping bucket number
long rainCountOld = 0;
double rainRate = 0.0; //ISR
uint64_t tRainCount = 0; //ISR
uint64_t tRainCountO = 0; //ISR
uint64_t tTimer = 0;
void IRAM_ATTR reedSwitch() { //interrupt
portENTER_CRITICAL_ISR(&mux);
tRainCount = esp_timer_get_time();
if (tRainCount - tRainCountO > 500000) { //0.5s - RISING bounce will not be seen
rainCountNew++;
if (tRainCountO > 1) {
rainRate = 3.6e9 * 0.2 / double(tRainCount - tRainCountO);
}
tRainCountO = tRainCount;
}
portEXIT_CRITICAL_ISR(&mux);
}
void SendDz() {
digitalWrite(LED_BUILTIN, HIGH);
portENTER_CRITICAL_ISR(&mux);
long rainRate100 = rainRate * 100.0;
double rainCountCopy = rainCount;
portEXIT_CRITICAL_ISR(&mux);
Serial.println((String)((float)esp_timer_get_time() / 1.0e6) + "s Rain: " + (String)rainCountCopy + " Rain rate: " + (String)rainRate100);
digitalWrite(LED_BUILTIN, LOW);
}
void setup() {
setCpuFrequencyMhz(80);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
digitalWrite(LED_BUILTIN, HIGH);
Serial.begin(115200);
Serial.println("\nConnecting");
attachInterrupt(digitalPinToInterrupt(interruptPin), reedSwitch, FALLING);
}
void loop() {
uint64_t t = esp_timer_get_time();
portENTER_CRITICAL_ISR(&mux);
long drain = rainCountNew - rainCountOld;
rainCount = (float)rainCountNew * 0.2;
portEXIT_CRITICAL_ISR(&mux);
if (drain > 0) { //interrupt with raincount occurs
Serial.print("Count:");
SendDz();
tTimer = t;
portENTER_CRITICAL_ISR(&mux);
long rainCountOCopy = rainCountOld;
rainCountOld = rainCountNew;
portEXIT_CRITICAL_ISR(&mux);
}
if (t - tTimer > 300e6) { //update tous les 300s=5min
//recalcul rainrate
portENTER_CRITICAL_ISR(&mux);
if (rainCountNew > 1) {
rainRate = 3.6e9 * 0.2 / double(t - tRainCountO);
if (rainRate < 0.2) rainRate = 0.0;
}
portEXIT_CRITICAL_ISR(&mux);
Serial.print("Timer:");
SendDz();
tTimer = t;
}
}
With the full program and wifi/http, I have ghost interrupt:
#include <ArduinoJson.h>
#include <WiFi.h>
#include <HTTPClient.h>
#define interruptPin 25 //IO25/D2
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
// WiFi settings
#define ssid "ssidToChange"
#define password "passToChange"
HTTPClient http;
String url1 = "http://88.89.90.91:8080/json.htm?type=command¶m=getdevices&rid=100";
String url2 = "http://88.89.90.91:8080/json.htm?type=command¶m=udevice&idx=100&nvalue=0&svalue=";
int httpCode = -1;
int httpCodeO = -1;
bool rainCountInitFlag = true;
double rainCount = 0.0; //ISR
long rainCountNew = 0; //ISR - tipping bucket number
long rainCountOld = 0; //ISR
double rainRate = 0.0;
uint64_t tRainCount = 0; //ISR
uint64_t tRainCountO = 0; //ISR
uint64_t tTimer = 0;
void IRAM_ATTR reedSwitch() { //interrupt
portENTER_CRITICAL_ISR(&mux);
tRainCount = esp_timer_get_time();
if (tRainCount - tRainCountO > 500000) { //0.5s - RISING bounce will not be seen
rainCountNew++;
if (tRainCountO > 1) {
rainRate = 3.6e9 * 0.2 / double(tRainCount - tRainCountO);
}
tRainCountO = tRainCount;
}
portEXIT_CRITICAL_ISR(&mux);
}
void ConnectedToAP_Handler(WiFiEvent_t wifi_event, WiFiEventInfo_t wifi_info) {
Serial.println("Connected To The WiFi Network");
}
void GotIP_Handler(WiFiEvent_t wifi_event, WiFiEventInfo_t wifi_info) {
Serial.print("Local ESP32 IP: ");
Serial.println(WiFi.localIP());
SendDzAlarm(3, "ESP32+Reconnection");
if (rainCountInitFlag) {
GetRaincount();
if (!rainCountInitFlag) {
SendDzAlarm(1, "Rain+Count+OK");
}
}
}
void WiFi_Disconnected_Handler(WiFiEvent_t wifi_event, WiFiEventInfo_t wifi_info) {
Serial.println("Disconnected From WiFi Network");
// Attempt Re-Connection
WiFi.begin(ssid, password);
}
void GetRaincount() {
Serial.println("getraincount");
HTTPClient http;
http.begin(url1); //Specify the URL
httpCode = http.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
String payload = http.getString();
Serial.println("GetRaincount http code: " + (String)httpCode);
//Serial.println(payload);
//deserialize json
JsonDocument doc;
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, payload);
// Test if parsing succeeds.
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
} else {
String data = doc["result"][0]["Data"];
Serial.println("JSON data: " + data);
int index = data.indexOf(";");
if (index == -1) {
Serial.println("pas de ; dans la chaine");
} else {
String rainCountS = data.substring(index + 1);
portENTER_CRITICAL_ISR(&mux);
rainCountNew = 5.0 * rainCountS.toFloat() + 0.05; //0.05 to avoid 1->0.99->4.95->4
portEXIT_CRITICAL_ISR(&mux);
Serial.println("rainCount: " + (String)rainCountS.toFloat());
rainCountInitFlag = false;
digitalWrite(LED_BUILTIN, LOW);
}
}
} else {
Serial.println("Error on HTTP request");
}
http.end(); //Free the resources
}
void SendDz() { //send rainCount to DZ
if (rainCountInitFlag) { //send only if get rainCount in DZ
Serial.println("Erreur: rainCount non récupérer sur DZ");
} else {
digitalWrite(LED_BUILTIN, HIGH);
portENTER_CRITICAL_ISR(&mux);
long rainRate100 = rainRate * 100.0;
double rainCountCopy = rainCount;
portEXIT_CRITICAL_ISR(&mux);
Serial.print((String)((float)esp_timer_get_time() / 1.0e6) + "s Rain: " + (String)rainCountCopy + " Rain rate: " + (String)rainRate100);
HTTPClient http;
//Serial.println(url2 +(String)rainRate100+";"+ (String)rainCountCopy);
http.begin(url2 + (String)rainRate100 + ";" + (String)rainCountCopy);
httpCode = http.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
//String payload = http.getString();
Serial.println(" SendDZ http code: " + (String)httpCode);
//Serial.println(payload);
digitalWrite(LED_BUILTIN, LOW);
} else {
Serial.println(" Error on HTTP request");
}
http.end(); //Free the resources
}
}
void SendDzAlarm(byte level, String text) { //send rainCount to DZ
digitalWrite(LED_BUILTIN, HIGH);
HTTPClient http;
float tConnect = (float)esp_timer_get_time() / 1e6;
http.begin("http://88.89.90.91:8080/json.htm?type=command¶m=udevice&idx=101&nvalue=" + (String)level + "&svalue=" + text + "+a+" + (String)tConnect + "+s");
httpCode = http.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
//String payload = http.getString();
//Serial.println("SendDZAlarm http code: " + (String)httpCode);
//Serial.println(payload);
digitalWrite(LED_BUILTIN, LOW);
} else {
Serial.println(" Error on HTTP request");
}
http.end(); //Free the resources
}
void setup() {
setCpuFrequencyMhz(80);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
digitalWrite(LED_BUILTIN, HIGH);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.onEvent(ConnectedToAP_Handler, SYSTEM_EVENT_STA_CONNECTED); //ARDUINO_EVENT_WIFI_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED
WiFi.onEvent(GotIP_Handler, SYSTEM_EVENT_STA_GOT_IP); // ARDUINO_EVENT_WIFI_STA_GOT_IP SYSTEM_EVENT_STA_GOT_IP
WiFi.onEvent(WiFi_Disconnected_Handler, SYSTEM_EVENT_STA_DISCONNECTED); // ARDUINO_EVENT_WIFI_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED
WiFi.begin(ssid, password);
Serial.println("\nConnecting");
attachInterrupt(digitalPinToInterrupt(interruptPin), reedSwitch, FALLING);
}
void loop() {
uint64_t t = esp_timer_get_time();
portENTER_CRITICAL_ISR(&mux); //a mettre si risque ecriture simultannee
long drain = rainCountNew - rainCountOld;
rainCount = (float)rainCountNew * 0.2;
portEXIT_CRITICAL_ISR(&mux);
if (drain > 0) { //interrupt with raincount occurs
Serial.print("Count:");
SendDz();
tTimer = t;
if (httpCode != httpCodeO) {
SendDzAlarm(2, "RetouR+d+un+hhtp+Code+" + (String)httpCodeO + "+vers+" + (String)httpCode);
httpCodeO = httpCode;
}
//to be removed
portENTER_CRITICAL_ISR(&mux);
long rainCountOCopy = rainCountOld;
portEXIT_CRITICAL_ISR(&mux);
if (rainCountOld == 0)
SendDzAlarm(0, "Rafaichissement+au+demarrage");
else
SendDzAlarm(1, "Basculement+d+auget+detecte");
//to be removed end
portENTER_CRITICAL_ISR(&mux);
rainCountOld = rainCountNew;
portEXIT_CRITICAL_ISR(&mux);
}
if (t - tTimer > 300e6) { //update tous les 300s=5min
if (rainCountInitFlag) {
GetRaincount();
if (!rainCountInitFlag) {
SendDzAlarm(2, "Rain+Count+non+initialise+au+demmarrage");
}
}
//recalcul rainrate
portENTER_CRITICAL_ISR(&mux);
if (rainCountNew > 1) {
//rainrate calculation only if 2 tipping bucket after reset
rainRate = 3.6e9 * 0.2 / double(t - tRainCountO);
if (rainRate < 0.2) rainRate = 0.0;
}
portEXIT_CRITICAL_ISR(&mux);
Serial.print("Timer:");
SendDz();
tTimer = t;
if (httpCode != httpCodeO) {
SendDzAlarm(2, "Retour+d+un+hhTp+Code+" + (String)httpCodeO + "+vers+" + (String)httpCode);
httpCodeO = httpCode;
}
}
}
Sorry for that long code but I can't reproduce the error with a shorter code!