Reedschalter entprellen/ Status speichern / Bei fallender Flanke reagieren

Hallo,

ich habe ein Problem mit meinem Code, wahrscheinlich ein Denkproblem.

Ich will meinen Eingangspin der über einen Reed Schalter (an meinem Gaszähler hängt) an 5 bzw 3,3V liegt nutzen um den Gaszählerstand zu ermitteln bzw zu errechnen.

Fallende Flanke, da der Umlaufmagnet durchaus mal auf 0 stehen bleibt und rein zeitliches Delay nicht funktioniert.

Ich habe für diese Detektion eine Funktion geschrieben die mit 2 Countern/Zählern arbeitet.

Ich habe auch bereits mit der Interrup Funktion getestet, die aber auf meinem WEMOS D1 Mini auch mit Entprellen nicht funktioniert und bei Einrichtung von FALLING auch bei RISING reagiert, daher die "aufwendige" Variante

void schalter_geschlossen()
{
  // Wenn auf LOW Startzeit für HIGH Counter speichern
  if (digitalRead(eingangspin) == HIGH)  // Reed Kontakt geschlossen = Spannung liegt am Eingangspin an
  {
    startzeit = millis(); // Startzeit speichern // wird ständig aktualisiert wenn geschlossen
  }


  // Wartezeit
  if (millis() - startzeit > wartezeit_status && digitalRead(eingangspin) == HIGH)
  {
    //Nach Wartezeit
    ausgeloest = true;
    Serial.println("HIGH nach Wartezeit erkannt");
  }  //Ende Zeitschleife


  else  // LOW
  {
    //Wenn Reed offen
    startzeit = millis(); // Startzeit speichern
  }
  // Wartezeit
  if (millis() - startzeit > wartezeit_status && digitalRead(eingangspin) == LOW)
  {
    //Nach Wartezeit
    ausgeloest = true;
    Serial.println("LOW nach Wartezeit erkannt");
  }  //Ende Zeitschleife
}// Ende Else


// Prüfen auf Flankenwechsel von HIGH auf LOW / ausgelöst true auf false
if (ausgeloest_alt == true && ausgeloest == false)
{
  Serial.println("Flankenwechsel von HIGH auf LOW\t");
  zaehler_addieren();
  display_anzeigen();
}
// Letzen Zustand merken
ausgeloest_alt = ausgeloest;


}// schalter

Hallo .....

Du kannst dir gerne mal meine Lösung anschauen.

Gaszähler auslesen

Gruß Fips

@phiwa
Wenn du schon Code postest, dann bitte komplett, damit wir auch die Loop, deine Definitionen und das Setup sehen können.

Hallo,

kein Problem:

// Get ESP8266 going with Arduino IDE
// - https://github.com/esp8266/Arduino#installing-with-boards-manager
// Required libraries (sketch -> include library -> manage libraries)
// - PubSubClient by Nick ‘O Leary
// - DHT sensor library by Adafruit

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <SFE_MicroOLED.h>  // Include the SFE_MicroOLED library

#define wifi_ssid "XXXX"
#define wifi_password "XXXX"

#define mqtt_server "XXXX"
#define mqtt_user "XXX"
#define mqtt_password "XXXX"
#define mqtt_client "ESP8266Client_Gas"

#define inTopic1 "MQTT_Gas/gaszahlerstand_korrektur"
#define inTopic2 "MQTT_Gas/impuls_korrektur"

#define gaszaehler_topic "sensor/gaszaehler"
#define gas_kwh_topic "sensor/gas_kwh"

//OLED
#define PIN_RESET 255  //
#define DC_JUMPER 0  // I2C Addres: 0 - 0x3C, 1 - 0x3D

int eingangspin = D8; // Wenn der Pin auf HIGH geht Reed geschlossen
bool ausgeloest, ausgeloest_alt
  = false;

float zaehlerstand = 22421.800; //24.06.18
float impuls = 0.01; //statt 0.01
float heizwert = 11.452;
float kwH  = 0.0;
uint16_t wartezeit_status = 1000 * 2; // 2 Sekunden

// Timer
unsigned long startzeit, previousMillis = 0;

uint32_t interval_daten_senden = 60 * 1000; // 1 Min

WiFiClient espClient;
PubSubClient client(espClient);
//OLED
MicroOLED oled(PIN_RESET, DC_JUMPER); // Example I2C declaration

void setup() {
  Serial.begin(115200);
  //setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  // PINS---------------------------------------------
  pinMode(eingangspin, INPUT);  // switchPin is an input
  pinMode (D4, OUTPUT);         // ledPin is an output
  //OLED----------------------------------------------
  oled.begin();
  oled.clear(ALL);// Clear the display's memory (gets rid of artifacts)
  oled.clear(PAGE);
  oled.setFontType(0); // set font type 0, please see declaration in SFE_MicroOLED.cpp
  //Titelzeile  1, 3
  oled.setCursor(1, 3);
  oled.print("Gaszaehler");
  oled.setCursor(1, 12);
  oled.print("Stand");
  oled.setCursor(1, 21);
  oled.print(zaehlerstand);
  oled.setCursor(1, 30);
  oled.print("impuls");
  oled.setCursor(1, 39);
  oled.print(impuls);
  oled.display();
  // ENDE OLED--------------------------------------

  // Grundeinstellungen anzeigen--------------------
  Serial.println("Gundeinstellungen: ");
  Serial.print("Zählerstand: ");
  Serial.println(zaehlerstand, 3);
  Serial.print("Interval m3: ");
  Serial.println(impuls, 3);
  // -----------------------------------------------

}// Setup Ende

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(wifi_ssid);

  WiFi.begin(wifi_ssid, wifi_password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    // If you do not want to use a username and password, change next line to
    // if (client.connect("ESP8266Client")) {
    if (client.connect(mqtt_client, mqtt_user, mqtt_password))
    {
      Serial.println("connected");
      // reagieren auf dieses Topic:
      client.subscribe(inTopic1);
      client.subscribe(inTopic2);
    }
    else
    {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


bool checkBound(float newValue, float prevValue, float maxDiff) {
  return !isnan(newValue) &&
         (newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  // Ausgabe der Payload
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  // Ende der Payload
  payload[length] = '\0';

  if (strcmp(topic, inTopic1) == 0)
  {
    Serial.println("");
    Serial.println("gaszahlerstand_korrektur");
    Serial.print("Alt: ");
    Serial.println(zaehlerstand, 3);
    // Zahl konvertieren
    String s = String((char*)payload);
    zaehlerstand = s.toFloat();
    Serial.print("Neu: ");
    Serial.println(zaehlerstand, 3);
  }

  if (strcmp(topic, inTopic2) == 0)
  {
    Serial.println("");
    Serial.println("impuls_korrektur");
    Serial.print("Alt: ");
    Serial.println(impuls, 3);
    // Zahl konvertieren
    String s = String((char*)payload);
    impuls = s.toFloat();
    Serial.print("Neu: ");
    Serial.println(impuls, 3);
  }

}//callback

void led_ansteuern()
{
  if (digitalRead(eingangspin) == HIGH)  // Reed Kontakt geschlossen = Spannung liegt am Eingangspin an
  {
    // LED an
    digitalWrite(D4, LOW); // LOW ist an
  }
  else
  {
    digitalWrite(D4, HIGH);
  }
}//LED ansteuern

void display_anzeigen()
{
  //OLED
  oled.clear(PAGE);
  delay(100);
  oled.display();

  oled.setFontType(0); // set font type 0, please see declaration in SFE_MicroOLED.cpp
  //Titelzeile  1, 3
  oled.setCursor(1, 3);
  oled.print("Gaszaehler");
  oled.setCursor(1, 12);
  oled.print("Stand");
  oled.setCursor(1, 21);
  oled.print(zaehlerstand);
  oled.print("m3");
  //oled.setCursor(1, 30);
  //oled.print("kWh");
  //oled.setCursor(1, 39);
  //oled.print(kwH);
  oled.display();
  // ENDE OLED



}//display

void schalter_geschlossen()
{
  // Wenn auf LOW Startzeit für HIGH Counter speichern
  if (digitalRead(eingangspin) == HIGH)  // Reed Kontakt geschlossen = Spannung liegt am Eingangspin an
  {
    startzeit = millis(); // Startzeit speichern // wird ständig aktualisiert wenn geschlossen
  }

  // Wartezeit
  if (millis() - startzeit > wartezeit_status && digitalRead(eingangspin) == HIGH)
  {
    //Nach Wartezeit
    ausgeloest = true;
    Serial.println("HIGH nach Wartezeit erkannt");
  }  //Ende Zeitschleife

  else  // LOW
  {
    //Wenn Reed offen
    startzeit = millis(); // Startzeit speichern
  }
  // Wartezeit
  if (millis() - startzeit > wartezeit_status && digitalRead(eingangspin) == LOW)
  {
    //Nach Wartezeit
    ausgeloest = true;
    Serial.println("LOW nach Wartezeit erkannt");
  }  //Ende Zeitschleife
}// Ende Else

// Prüfen auf Flankenwechsel von HIGH auf LOW / ausgelöst true auf false
if (ausgeloest_alt == true && ausgeloest == false)
{
  Serial.println("Flankenwechsel von HIGH auf LOW\t");
  zaehler_addieren();
  display_anzeigen();
}
// Letzen Zustand merken
ausgeloest_alt = ausgeloest;

}// schalter
void zaehler_addieren()
{
  // Zählerstand addieren
  zaehlerstand = zaehlerstand + impuls;
  //Serial.print("Impuls detektiert ");
  Serial.print("Zählerstand: ");
  Serial.print(String(zaehlerstand).c_str());

  // kwh
  kwH = zaehlerstand * heizwert;
  Serial.print("\tkwh: ");
  Serial.println(String(kwH).c_str());
  //Warten
  //delay(wartezeit_zaehlen);
}//Zähler addieren


void daten_senden()
{
  // Daten senden alle X Minuten
  if (millis() - previousMillis > interval_daten_senden)
  {
    previousMillis = millis() ;
    Serial.println("Sende Daten");
    client.publish(gaszaehler_topic, String(zaehlerstand).c_str(), true);
    client.publish(gas_kwh_topic, String(kwH).c_str(), true);
    //------ Ende Ausführung in Zeitschleife
  }//Zeitschleife
}//daten senden


void loop()
{
  /*  if (!client.connected())
    {
      reconnect();
    }
    client.loop();
  */
  // Hier gehts los-----------------------------------
  led_ansteuern();
  // Wenn Kontakt geschlossen ------------------------
  schalter_geschlossen();
  // OLED --------------------------------------------
  //display_anzeigen(); // Nur wenn wirklich geschlatet wurde in Funktion schalter_geschlossen();
  // Daten Senden
  daten_senden();
}// Loop

ich hab gerade kleine Anpassungen gemacht, weil ich nicht im Heimnetzwerk bin, daher teils Auskommentierungen, der Funktionsumfang kann auch noch etwas schmaler werden, weil ich erst noch was anderes mit implementieren wollte (DHT Sensor)

Ich habe mal einen Reedkontakt an einem Regenmesser entprellt. Das dürfte auch für Dich funktionieren:

const byte regenPin = 2;
const uint16_t PRELL_ZEIT = 5; // in ms
volatile uint16_t regenCounter = 0;
volatile uint32_t lastRegenMillis = 0;

void ICACHE_RAM_ATTR regenISR() {  // ICACHE_RAM_ATTR für ESP8266
  if (millis() - lastRegenMillis >= PRELL_ZEIT) {
    regenCounter++;
    lastRegenMillis = millis();
  }  
}

void setup() {
  Serial.begin(115200);
  Serial.println("Start");
  pinMode(regenPin,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(regenPin),regenISR,FALLING);

}

void getRegenCount() {
static uint16_t regenCounterOld = 0;
uint16_t regenCounterLocal;
uint32_t lastRegenMillisLocal;

  cli();
  regenCounterLocal = regenCounter;
  lastRegenMillisLocal = lastRegenMillis;
  sei();
  if (regenCounterLocal != regenCounterOld) {
    regenCounterOld = regenCounterLocal;
    Serial.print("Counter: ");Serial.print(regenCounterLocal);
    Serial.print(" Millis: ");Serial.println(lastRegenMillisLocal);
  }
}

void loop() {
  getRegenCount();

}

Gruß Tommy