Zähler mit zwei Lichtschranken

Hallo zusammen, ich versuche gerade mithilfe zweier IR-Sensoren einen Counter zu programmieren. Der Counterstand soll auf einer 7 Segment Anzeige angezeigt werden. Wenn Sensor 1 vor sensor 2 ausgelöst wird soll er vorwärts zählen und umgekehrt rückwärts. Das Vorwärtszählen funktioniert auch, Verwende dieses Symbol um Code zu postennur das Rückwärts nicht. Der Counter ist in ein ganzes Projekt verbaut ich werde den gesamten Code posten.




#include <TM1637Display.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <RTClib.h>

// RTC Modul initialisieren
RTC_DS3231 rtc;

// Servo initialisieren
Servo servoMotor;

// LCD Display (16x2) initialisieren, Adresse 0x27 für 16x2 I2C LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// 4-Digit 7-Segment Anzeige initialisieren (TM1637) an Pin 3 und 4
#define CLK 4
#define DIO 3
TM1637Display display(CLK, DIO);

// Definierte Pins und Variablen
const int irSensor1Pin = 2;  // Digitaler Pin für den ersten IR Zähler (Vorwärtszähler)
const int irSensor2Pin = 7;  // Digitaler Pin für den zweiten IR Zähler (Rückwärtszähler)
const int servoPin = 9;      // Pin für den Servo Motor
const int buttonPin = 8;     // Pin für den Taster
const int redPin = 10;       // PIN für die rote LED (RGB)
const int greenPin = 11;     // Pin für die grüne LED (RGB)

int counter = 0;             // Zähler für IR-Erfassung
int maxCounter = 12;         // Grenzwert für das Schließen der Klappe
bool klappeOffen = false;    // Zustand der Klappe
bool buttonState = LOW;      // Aktueller Zustand des Tasters
bool lastButtonState = LOW;  // Letzter Zustand des Tasters (zur Entprellung)

bool sensor1Triggered = false;  // Überprüft, ob Sensor 1 ausgelöst wurde
bool sensor2Triggered = false;  // Überprüft, ob Sensor 2 ausgelöst wurde

// Variablen für den Wechsel der LCD Anzeige
unsigned long previousMillis = 0;
const long displayInterval = 5000;      // Intervall für den Wechsel (5 Sekunden)
bool zeigeZustand = false;              // Anzeigestatus (false = Uhrzeit/Datum, true = Klappenstatus)
unsigned long zustandChangeMillis = 0;  // Zeitstempel des letzten Zustandswechsels

void setup() {
  Serial.begin(9600);  // Initialisiert die serielle Kommunikation für Debugging

  // Initialisiert das RTC Modul
  if (!rtc.begin()) {
    // Ausgabe einer Fehlermeldung auf dem LCD
    lcd.begin(16, 2);
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("RTC nicht");
    lcd.setCursor(0, 1);
    lcd.print("gefunden");
    while (1)
      ;  // Endloser Loop, wenn RTC nicht gefunden wird
  }

  // Initialisiert den Servo
  servoMotor.attach(servoPin);
  servoMotor.write(0);  // Klappe ist geschlossen

  // Setzt die IR-Sensor-Pins als Eingang
  pinMode(irSensor1Pin, INPUT);
  pinMode(irSensor2Pin, INPUT);

  // Pin-Modi für LEDs und Taster
  pinMode(buttonPin, INPUT_PULLUP);  // Button-Pin als Eingang definieren
  pinMode(redPin, OUTPUT);           // RedPin als Ausgang definieren
  pinMode(greenPin, OUTPUT);         // GreenPin als Ausgang definieren

  // LCD Display starten
  lcd.begin(16, 2);
  lcd.backlight();

  // 7-Segment Display initialisieren
  display.setBrightness(0x0f);  // Max Helligkeit

  // Wert des Zählers anzeigen
  display.showNumberDec(counter);

  // Klappe auf geschlossen setzen
  klappeSchliessen();
}

void loop() {
  unsigned long currentMillis = millis();

  // Uhrzeit und Datum vom RTC auslesen
  DateTime jetzt = rtc.now();

  // Wenn der Zustand gewechselt wurde, wird der Zeitstempel gesetzt
  if (currentMillis - zustandChangeMillis >= displayInterval) {
    zeigeZustand = !zeigeZustand;         // Wechsel zwischen Uhrzeit/Datum und Klappenzustand
    zustandChangeMillis = currentMillis;  // Setzt den Zeitstempel des letzten Zustandwechsels
  }

  // Anzeige auf dem LCD je nach Zustand
  lcd.clear();
  if (zeigeZustand) {  // Klappenstatus anzeigen
    lcd.setCursor(0, 0);
    lcd.print("Klappe: ");
    if (klappeOffen) {
      lcd.print("Offen");
    } else {
      lcd.print("Geschlossen");
    }
  } else {  // Zeige Uhrzeit und Datum
    lcd.setCursor(0, 0);
    lcd.print(jetzt.day(), DEC);
    lcd.print("/");
    lcd.print(jetzt.month(), DEC);
    lcd.print("/");
    lcd.print(jetzt.year(), DEC);

    lcd.setCursor(0, 1);
    lcd.print(jetzt.hour(), DEC);
    lcd.print(":");
    if (jetzt.minute() < 10) lcd.print("0");
    lcd.print(jetzt.minute(), DEC);
    lcd.print(":");
    if (jetzt.second() < 10) lcd.print("0");
    lcd.print(jetzt.second(), DEC);
  }

  // Steuerlogik für die Klappe

  // Überprüft, ob es 7 Uhr morgens ist und die Klappe geschlossen ist
  if (jetzt.hour() == 7 && jetzt.minute() == 0 && jetzt.second() == 0 && !klappeOffen) {
    klappeOeffnen();
  }

  // Überprüft, ob der Zähler >= maxCounter ist und es 20 Uhr oder später ist
  if (counter >= maxCounter && jetzt.hour() >= 20 && klappeOffen) {
    klappeSchliessen();
  }

  // IR-Sensor-Logik

  // Zustand der IR-Sensoren abfragen
  int sensor1State = digitalRead(irSensor1Pin);
  int sensor2State = digitalRead(irSensor2Pin);

  // Debug-Ausgaben
  Serial.print("Sensor1: ");
  Serial.print(sensor1State);
  Serial.print(" Sensor2: ");
  Serial.println(sensor2State);

  // Logik zum Vorwärtszählen (Erkennung der Reihenfolge: Sensor1 -> Sensor2)
  if (sensor1State == LOW && !sensor1Triggered) {  // Sensor 1 wird ausgelöst
    sensor1Triggered = true;
    delay(50);  // Entprellzeit
    Serial.println("Sensor 1 ausgelöst");
  }

  if (sensor2State == LOW && sensor1Triggered && !sensor2Triggered) {  // Sensor 2 nach Sensor 1
    sensor2Triggered = true;
    counter++;
    display.showNumberDec(counter);  // Zeigt den Zählerstand auf der 7-Segment-Anzeige
    sensor1Triggered = false;  // Nach erfolgreicher Zählung zurücksetzen
    sensor2Triggered = false;
    delay(500);  // Kurze Wartezeit, um Mehrfachzählungen zu verhindern
    Serial.println("Zähler vorwärts");
  }

  // Logik zum Rückwärtszählen (Erkennung der Reihenfolge: Sensor2 -> Sensor1)
  if (sensor2State == LOW && !sensor2Triggered) {  // Sensor 2 wird ausgelöst
    sensor2Triggered = true;
    delay(50);  // Entprellzeit
    Serial.println("Sensor 2 ausgelöst");
  }

  if (sensor1State == LOW && sensor2Triggered && !sensor1Triggered) {  // Sensor 1 nach Sensor 2
    sensor1Triggered = true;
    counter--;
    display.showNumberDec(counter);  // Zeigt den Zählerstand auf der 7-Segment-Anzeige
    sensor1Triggered = false;  // Nach erfolgreicher Zählung zurücksetzen
    sensor2Triggered = false;
    delay(500);  // Kurze Wartezeit, um Mehrfachzählungen zu verhindern
    Serial.println("Zähler rückwärts");
  }

  // Taster zur manuellen Steuerung der Klappe
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH && lastButtonState == LOW) {
    delay(50);  // Entprellzeit
    if (klappeOffen) {
      klappeSchliessen();
    } else {
      klappeOeffnen();
    }
    zustandChangeMillis = millis();
  }
  lastButtonState = buttonState;
  delay(1000);
}

// Funktion zum Steuern des Servo-Motors und der RGB-LED
void klappeOeffnen() {
  servoMotor.write(180);  // Klappe auf Position offen bewegen
  klappeOffen = true;
  rgbLedGruen();  // Grüne LED leuchtet
  lcd.setCursor(0, 0);
  lcd.print("Klappe geoeffnet");
}

void klappeSchliessen() {
  servoMotor.write(0);  // Klappe auf Position geschlossen bewegen
  klappeOffen = false;
  rgbLedRot();  // Rote LED leuchtet
  lcd.setCursor(0, 0);
  lcd.print("Klappe geschlossen");
}

// Funktionen zur RGB-LED Steuerung
void rgbLedRot() {
  digitalWrite(redPin, HIGH);
  digitalWrite(greenPin, LOW);
}

void rgbLedGruen() {
  digitalWrite(redPin, LOW);
  digitalWrite(greenPin, HIGH);
}

````Verwende dieses Symbol um Code zu posten`

Die Ir-Sensoren und die Siebensegment anzeige sind korrekt angeschlossen und funktionieren mit Probeprogrammen auch.

Vielen Dank schonmal im Vorraus

Viele Grüße FranziVerwende dieses Symbol um Code zu posten

Weisst du, warum ich solche Abfragen hasse: Weil sie unnötig kompliziert sein.

Und dazu noch

und das Chaos ist perfekt.

Deine Anwendung schreit geradezu nach einer Statemaschiene. In den States hast du dann nur noch so einfache Abfragen wie

und fast alle Hilfsvariabeln wie

werden auch überflüssig.

--> Schau dir mal an, wie eine Statemaschiene mit Switch case und millis() programmiert wird. Und bitte nicht so entprellen.

Wenn du lernen willst, schau dir das mal an: Arduino: Programmiertechniken/Statemaschine – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbücher

Oder entprelle mit einer Lib (z.B. Onebutton)

Eine Frage
Sind die beiden sensoren so nahe beieinander montiert, daß das zu dedektierende Objekt immer beide abdunkelt oder dunkelt das Objekt zuerst den einen ab, dann wieder nicht und dann erst den Zweiten?

Grüße Uwe

Ich habe beides probiert es sollte jedoch eigentlich so sein das sie nacheinander ausgelöst werden nicht gleichzeitig

Erstmal danke für deine Rückmeldung. Ich habe noch nicht so viel Erfahrung im Programmieren. Ich habe mir deinen Link angesehen Weiß jetzt aber nicht genau wie ich das in meinem Programm ändern soll ohne das dann garnichts mehr funktioniert.
Könntest du mir weiter helfen

Dauert noch ein wenig. Ich bereite gerade Zaehler - Wokwi ESP32, STM32, Arduino Simulator vor. Ich habe erst mal die delay() ausgebaut. Dadurch reagiert der Taster auch sofort. Um es einfach zu halten habe ich mal die OneButton Lib benutzt.

Kannst du mal zeigen, wie die Lichtschranken angeordnet sind?
Was macht der Counter eigentlich?

Also die Lichtschranken sind so angeordnet das erst die eine Schranke aktiviert wird dann nicht mehr aktiviert ist und dann die zweite aktiviert wird. Also einfach hintereinander mit ein paar cm Abstand.
Bei diesem Projekt handelt es sich eigentlich um eine Hühnerklappe, der Counter soll überprüfen das alle Hühner durch die Klappe gegangen sind. Deshalb sollte der Counter eben auch rückwärts zählen können falls eine Huhn rein und wieder raus geht.

Kann es vorkommen dass ein Huhn umdreht und nur eine Lichtschranke aktiviert ist ohne dass die andere dazu kommt?

Ich habe in der Simulation Zaehler - Wokwi ESP32, STM32, Arduino Simulator den Zähler als Statemaschiene eingebaut. Das Programm beginnt mit alle Hühner (5) sind drin und die Klappe ist zu.
Grüner Button: Klappe auf / Zu
Gelber und blauer Button: Lichtschranken.
Wenn ein Huhn raus will: zuerst gelber Button (= innere Lichtschranke) dann blauer Button.
Counter wird erniedrigt. Geht nur, wenn die Klappe auf ist.

Es fehlt:

  • Hühner wollen rein
  • Klappe automatisch schließen wenn alle Hühner drin sind.
  • Timeouts in der Statemaschine

Meine ich denke der Durchgang ist zu schmal das sie umdrehen könnten.

Okay vielen Dank, die Funktion für das Hochzählen programmiere ich dann genauso wie für das Runterzählen nur Umgekehrt? Und das Die Klappe sich schließt wenn alle Hühner drin sind kann ich dann in eine If schleife Packen ?
Die Statemaschiene habe ich noch nicht ganz verstanden

Es gibt keine if Schleife (da wird nichts wiederholt), nur if Abfragen

Gruß Tommy

Ich habe den Anfang der 2. Schrittkette mal eingefügt:

  //Hühnerzähler
  switch (HuhnState){
    case staKlappeIstZu:
      //nix machen
      break;
    case staKlappeIstOffen:      
      if (!sensorInnenTriggered) {
        lastAction = currentMillis;
        HuhnState = staHuhnWillRaus1;
        Serial.print("A ");
      } 
      if (!sensorAussenTriggered) {
        lastAction = currentMillis;
        HuhnState = staHuhnWillRein1;
        Serial.print("I ");
      }
      break;

Das musst du (wie in deinem alten Code) hier einbauen:

    } else if (jetzt.hour() >= 20 && jetzt.minute() == 0 && jetzt.second() == 0 && klappeOffen) {
      klappeSchliessen();
    }

Gehe sie mal gedanklich Schritt für Schritt durch. Beachte: Die Switchanweisung wird jede Sekunde hunderte mal durchlaufen.

Warum das ?
Das erhöht die Chance, dass die Klappe lange auf bleibt, erheblich, weil es nur eine Sekunde lang (jeweils um 20:00:00, 21:00:00, 22:00:00, 23:00:00 ) prüft ob was zu tun ist.

@franzi_33 : Wenn du die state maschine in @wwerner s Simulator nicht verstehst, bist du nicht allein :slight_smile: ( sobald die im Zustand I landet, hängt sie )

Das habe ich geschrieben, das es noch nicht fertig ist.

Auch das habe ich als Baustelle gekennzeichnet.

Ich will dem TO ja auch noch etwas Arbeit lassen.

Ich habe die Simulation noch mal angepasst. Jetzt sollte alles gehen.

Zugegeben, der Code ist länger. Aber es gibt keine verschachtelten if Abfragen mehr.

Satt

nur noch so einfache wie

if (!sensorInnenTriggered) {

Beachte auch, dass ich den Variablen sprechende Namen gegeben habe. Also statt sensor1Triggered sensorInnenTriggered. Macht das lesen viel leichter.

Okay dann vielen Dank ich werde mal ausprobieren ob ich so weiter kommen.

Ich habe das ganze jetzt mehrfach ausprobier und die sieben-sgement-anzeige zeigt bei mir immernoch nichts an... reagiert nicht auf die Sensoren? Fehlt vielleicht nich etwas im Code ?

Und noch eine Frage könnte mir jemand erklären wie diese Statemaschiene funktioniert weil ich versthe nicht was das alles beduetet und finde deshalb den fehler nicht.
Das ganze soll ein Schulprojekt werden weshalb es wirklich wichtig ist das ich den gesamnten Code verstehe...
Vielen Danke schonmal

In der Wokwi Simulation geht es ja. Prüfe noch mal die Verdrahtung

Bei mir funktioniert die Simulation auch nicht