Probleme mit TCRT5000 (IR Lichtschranke)

Hallo, ich bin gerade dabei, einen Auszahlautomat für Plastikchips zu bauen.

Es funktioniert soweit ganz gut, aber halt noch nicht zu 100 %.

Ich verwende einen Encoder, um die gewünschte Anzahl an Chips zu wählen, danach per Klick bestätigen und eine Scheibe dreht sich und schmeißt die Chips in einen Auszahlschacht wo sie dann von einem TCRT5000 gezählt werden.
Das funktioniert so weit auch eigentlich ganz gut, aber ab und an ist es nicht genau die gewünschte Menge. Deswegen wollte ich zum Überprüfen noch einen zweiten TCRT dahinter machen, der wirklich nur die Chips zählt, um das Ergebnis am Ende zu vergleichen und hier beginnt mein Problem.

Meistens stimmt die Anzahl der ausgezahlten Chips mit der Gewünschten überein, aber der Kontrollsensor macht irgendwie, was er will.

Hoffe, die Informationen reichen, dass mir eventuell jemand helfen kann.

Bin auch noch ein Anfänger, also bitte nicht zu grob werden :slight_smile: .

Hier mein Code

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Encoder.h>

//LCD Inizialisieren
LiquidCrystal_I2C lcd(0x27, 16, 2);
unsigned long lastwriteLCD = 0;

//Encoder config
const int CLK = 5;  // Definition der Pins. CLK an D6, DT an D5.
const int DT = 4;
const int SW = 3;              // Der Switch wird mit Pin D2 Verbunden. ACHTUNG : Verwenden Sie einen interrupt-Pin!
long alteAnzahl = -999;        // Definition der "alten" Position (Diese fiktive alte Position wird benötigt, damit die aktuelle Position später im seriellen Monitor nur dann angezeigt wird, wenn wir den Rotary Head bewegen)
unsigned long Click_time = 0;  //Zum Speichern der zeit für den letzten Click
int Click_delay_time = 1000;   //Wie lange soll der Knopf nach dem Clicken aus sein
bool EncoderClickAble = true;  //Kann der click ausgeführt werden



Encoder meinEncoder(DT, CLK);  // An dieser Stelle wird ein neues Encoder Projekt erstellt. Dabei wird die Verbindung über die zuvor definierten Varibalen (DT und CLK) hergestellt.


//Anschlüsse Sensoren
int SensorSignalSchacht = 9;
int SensorSignalKontrolle = 2;

int CoinIst = 0;                 //Int fürs zählen
volatile int CoinKontrolle = 0;  // Int für die überwachung der richtigen Auszahlung

//Anschlüsse H Brücke
int MotorDrehzahl = 6;
int in1 = 8;
int in2 = 7;
int MotorGeschwindigkeit = 255;
int MotorGeschwindigkeitSlow = 100;

int nummer;  // Für die Auszahlung über Serial Monitor
String str;


//Sensoren config
unsigned long Sensor_time = 0;  //Zum Speichern der zeit für den letzten gemessenen Coin
int Sensor_delay_time = 100;    //Wie lange soll der Sensor nach erkennen deaktiviert werden

unsigned long Sensor_time_Kontrolle = 0;  //Zum Speichern der zeit für den letzten gemessenen Coin
int Sensor_delay_time_Kontrolle = 100;    //Wie lange soll der Sensor nach erkennen deaktiviert werden

//Interaktions tracking für timeout
unsigned long Interaktions_time = 0;
bool Interaktions_time_out = true;

// Int für den encoder
int anzahl = 0;

void setup() {

  //Motor
  pinMode(MotorDrehzahl, OUTPUT);  //PWM
  pinMode(in1, OUTPUT);            // +
  pinMode(in2, OUTPUT);            // -

  //Encoder Click
  pinMode(SW, INPUT_PULLUP);

  //initialize lcd screen
  lcd.init();
  // turn on the backlight
  lcd.backlight();

  lcd.clear();
  lcd.print("JGB Coiner");
  lcd.setCursor(0, 1);
  lcd.print("Bereit");


  Serial.begin(9600);

  attachInterrupt(0, KontrollSensor, FALLING);
}

void loop() {
  // put your main code here, to run repeatedly:



  /*if (Serial.available() > 0) {
    // Eingabe lesen:
    nummer = Serial.parseInt();

    // Eingabe ausgeben:
    //Serial.println(nummer);
    str = Serial.readStringUntil('\n');
    CoinsAuszahlen(nummer);
  }*/

  //Serial.println(digitalRead(SensorSignalSchacht));

  Startbild();  //Lcd Standart schrifft

  //Anzahl der Coins die augezahlt werden über Encoder abfragen
  int32_t neueAnzahl = meinEncoder.read();
  bool Encoder_Click = digitalRead(SW);
  if (neueAnzahl <= -3) {
    if (anzahl > 0) {
      anzahl -= 5;
      //Serial.println(anzahl);
      LcdEncoder(anzahl);
    }
    meinEncoder.write(neueAnzahl + 4);
    Interaktion();

  } else if (neueAnzahl >= 3) {
    anzahl += 5;
    //Serial.println(anzahl);
    LcdEncoder(anzahl);
    Interaktion();
    meinEncoder.write(neueAnzahl - 4);
  }

  //Per Encoder Click in die auszahlung
  if (Encoder_Click == LOW && millis() >= Click_time + Click_delay_time && EncoderClickAble == true) {
    Click_time = millis();
    EncoderClickAble = false;
    LcdInAuszahlung(anzahl, 0);
    CoinsAuszahlen(anzahl);
    anzahl = 0;
  }
}


void MotorDrehen(int MotorSpeed) {

  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  analogWrite(MotorDrehzahl, MotorSpeed);
}

void CoinsAuszahlen(int CoinSoll) {
  MotorDrehen(MotorGeschwindigkeit);
  Serial.println("In Coin Auszahl void");
  CoinIst = 0;
  CoinKontrolle = 0;
  Sensor_time = millis();
  Sensor_time_Kontrolle = millis();


  while (CoinIst != CoinSoll) {

    if (millis() >= Sensor_time + 5000) {
      //Serial.println("Coins sind leer");
      //Serial.print("Es sind nur: ");
      //Serial.print(CoinIst);
      //Serial.print(" von: ");
      //Serial.println(CoinSoll);
      //Serial.print("Es fehlen: ");
      //Serial.println(CoinSoll - CoinIst);
      LcdAbbruchAuszahlung(CoinSoll, CoinIst);
      break;
    }

    if (CoinIst == CoinSoll - 1) {
      MotorDrehen(MotorGeschwindigkeitSlow);
    }
    if (digitalRead(SensorSignalSchacht) == LOW && millis() >= Sensor_time + Sensor_delay_time) {
      Sensor_time = millis();
      CoinIst = CoinIst + 1;
      LcdInAuszahlung(CoinSoll, CoinIst);
      Serial.print(CoinIst);
      Serial.print(" ");
      Serial.println(Sensor_time);
      Serial.print(CoinKontrolle);
      Serial.print(" ");
      Serial.print(Sensor_time_Kontrolle);
      Serial.println(" Kontrolle");
    }
  }
  Serial.println("Auszahlung beendet");
  EncoderClickAble = true;
  Interaktion();

  //Motor Stoppen
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  //Auszahlung überprüfen
  Serial.print(CoinIst);
  Serial.println(" = Coin ist");
  Serial.print(CoinKontrolle);
  Serial.println(" = Coin Kontrolle");

  if (CoinSoll == CoinIst && CoinIst == CoinKontrolle)
    Serial.println("Passt");
  else
    Serial.println("Passt nicht " + CoinKontrolle);
}

void LcdInAuszahlung(int s, int i) {
  lcd.clear();
  lcd.print("Auszahlung: ");
  lcd.setCursor(0, 1);
  lcd.print(i);
  lcd.print("/");
  lcd.print(s);
  if (s == i) {
    lcd.clear();
    lcd.print("Fertig");
    lcd.setCursor(0, 1);
    lcd.print(s);
    lcd.print(" Coins");
  }
}
void LcdEncoder(int a) {
  if (millis() - lastwriteLCD > 200) {
    lastwriteLCD = millis();
    lcd.clear();
    lcd.print("Coins: ");
    lcd.print(a);
  }
}
void Startbild() {
  if (millis() >= Interaktions_time + 10000 && Interaktions_time_out == true) {
    lcd.clear();
    lcd.print("JGB Coiner");
    lcd.setCursor(0, 1);
    lcd.print("Bereit");
    Interaktions_time_out = false;
    anzahl = 0;
  }
}
void Interaktion() {
  Interaktions_time = millis();
  Interaktions_time_out = true;
}
void LcdAbbruchAuszahlung(int s, int i) {
  lcd.clear();
  lcd.print("Error: ");
  lcd.print(i);
  lcd.print(" von ");
  lcd.print(s);
  lcd.setCursor(0, 1);
  lcd.print("Es fehlen: ");
  lcd.print(s - i);
}
void KontrollSensor() {
  if (millis() >= Sensor_time_Kontrolle + Sensor_delay_time_Kontrolle) {
    CoinKontrolle = CoinKontrolle + 1;
    Sensor_time_Kontrolle = millis();
  }
}

Der Code ist ja schon gut entwickelt.
Aber deine Beschreibung

ist unpräzise.
Was macht der Kontrollsensor ?

  • zeigt der negative Zählwerte an?
  • zeigt der immer zu niedrige Zählwerte an?
  • zeigt der viel zu große Zählwerte an?
  • zeigt der Kontrollsensor bei jedem Test falsche Werte an?

Du benutzt für den Kontrollsensor einen interrupt
Aber dann prüfst du im Interrupt ob eine bestimmte Zeitspanne vorbei ist.
Warum?
Es wird immer jeweils ein Interrupt ausgelöst wenn am Interruptpin eine fallende Flanke auftritt.
Poste einen Link zu dem Datenblatt von dem Sensor.
Wechselt dieser Sensor pro Chip mehrmals LOW/HIGH/LOW/HIGH?

Ich habe eine State Machine erwartet.

Ich kann nicht verstehen ob es ein Problem der Messung / Programm ist oder der Mechanik / daß mehrere Chips gleichzeitig herauskommen.

Ich würde nicht die Chips zählen, sondern mechanisch garantieren, immer nur einen Chip auf einmal ausgeben. Dann kann man die Anzahl der Chips durch die Drehzahl des Motors bestimmen. Dazu würde ich einen Schrittmotor nehmen.

Grüße Uwe

Wenn du mal ein Foto von der Mechanik postest kann man anfangen sich Gedanken zu machen wie man die Chips vereinzelt und dann einzeln ausgibt.

Wenn ich zum Beispiel 10 auszahle, sind auch wirklich 10 angekommen, aber der Kontrollsensor zählt nur 9.
Dann beim nächsten Mal aber nur 9 oder aber auch Mal, es sind bei 10 gewünschten 9 herausgekommen und der Kontrollsensor sagt 10.

Die Zeit Prüfung habe ich gemacht um den Sensor zu entprellen.
Das mit dem Flanken Wechsel wusste ich nicht.
Ein Datenblatt habe ich leider nicht gefunden aber hier der Link wo ich bestellt haben: KY-033 Linienfolger Modul mit TCRT5000 und Analog-Ausgang


Hier das Bild der Mechanik

Benutzt du den analogen oder den digitalen Ausgang des Sensors?
Welche Farbe hat der Hintergrund wenn gerade kein Coin im Blickfeld des Sensors ist?
Welche Farbe haben die Coins?
Und es gilt immer noch wenn du ein Foto von deiner Mechanik posten würdest könnte man ein viel besseres Verständnis deines Projekte entwickeln

Aha. ist die Scheibe mit den Löchern waagerecht oder senkrecht? Das kann man aus dem Bild nicht erkennen.

Benutze den digitalen Ausgang und die Scheibe ist geneigt so circa 45°

Auf dem Sensormodul ist ein kleines Potentiometer

Hast du damit den Schaltpunkt eingestellt?

Wenn du die Scheibe drehen lässt und sich ein Chip unter dem Sensor durchbewegt
Hast du mal mit einem Testprogramm getestet ob der Sensor mehrfach umschaltet?
Oder schaltet der Sensor immer am gleichen Punkt des "Chip vorbeiwanderns" um und erst wieder zurück wenn der Chip aus dem Blickfeld hinausgewandert ist?

Ja den schaltpunkt habe ich eingestellt, funktioniert auch gut.

Wenn der Chip im Sichtfeld liegen bleibt, zählt der Arduino einfach hoch, deswegen ignoriere ich den Sensor in der If abfragen auch nach einem Chip 100 Millisekunden, damit bin ich auch soweit gut unterwegs, die Zeit von Chip zu Chip beträgt ca. 250 Millisekunden.

Ich habe auch schon etliche Auszahlungen mit 50 Chips durchgeführt, wo es dann tatsächlich auch 50 sind.

Also es funktioniert oft, aber nicht immer und das möchte ich halt beheben.

Wenn ich das richtig verstanden habe dann macht dein Programm bis jetzt folgendes:

Eine If-Bedingung prüft ob Sensor einen Chip detektiert
Dann 100 Millisekunden warten und danach erst wieder neu auf einem Chip prüfen.

Man kann das lösen über sogenannte Flankenerkennung.
Angenommen Chip detektieren heißt Sensorsignal = HIGH
Man macht keine Überprüfung ob der Sensor == HIGH ist
sondern
man prüft ob es einen Wechsel von LOW nach HIGH gibt.
Das nennt man Flankenerkennung.
Bei Wechsel LOW nach HIGH Zähler + 1
dann wird nicht weiter gezählt bis das Gegenteil passiert
HIGH-LOW . Das entspricht dieser Chip ist jetzt durchgewandert
==> dann "scharf" schalten auf den nächsten Flankenwechsel LOW-HIGH

So etwas macht man am besten mit einer Programmiertechnik die man State-Machine nennt.

State-machines oder zu deutsch Zustandsautomaten haben folgende Vorteile:

  • sehr einfach veränderbar / anpassbar
  • minimale Anzahl an If-Bedingungen

Welchen Abstand hat der Sensor zum Chip?
Das Datenblatt https://www.vishay.com/docs/83760/tcrt5000.pdf
sagt am besten 2,5 mm (Zwischensteg Oberrrand bis reflektiven Oberfläche)
grafik

Grüße Uwe

Moin Uwe, Momenten sind es so 3-4 Millimeter.
Das änder ich nachher Mal, danke.


Aha.
Verstehe ich das richtig. Der TCRT5000-Sensor befindet sich unterhalb des Lochs durch den die Chips herunterfallen?

Ja genau so macht der Code es.

Okay dann mach ich mich Mal schlau und passe den Code dementsprechend an.

Sich präzise ausdrücken ist nicht so deine Gewohnheit.
Der Sensor "macht" unterhalb des Fall-Loches montiert sein.

Also das ist eine denkbar ungünstige Art das Vorhandensein eines Chips zu detektieren.
Der Chip fällt durch das Loch und kippt dabei über die Lochkante.

Kannst du mit den eigenen Augen live beobachten ob der Chip immer auf die gleiche Art und Weise herunterfällt ?

Also ob:

  • die Chips immer eine halbe Drehung beim Kippen über die Kante machen?
  • mal flach ohne Drehung herunterfallen / mal mit Drehung herunterfallen?

Wie aufwendig wäre die Konstruktion zu ändern:
Es gibt ein "Guckloch" bei dem der Chip nicht hindurchfallen kann
unter diesem Guckloch sitzt der Sensor hinter dem Guckloch kommt das Fall-Loch.

Ist der Sensor auf dem Foto jetzt der "Hauptsensor" oder der Kontrollsensor?

Meine Antwort bezog sich auf den Code und nicht auf den Sensor, aber ich antworte jetzt ausführlicher, damit der Zusammenhang der Antworten klar ist.

Ich muss den Chip so weit oben erfassen, da sonst der nächste Chip schon vor dem Loch sitzt.

Der Chip fällt immer flach ohne Drehung, kippen kann er nicht.

Die Konstruktion ist sehr einfach zu ändern, nach der Überarbeitung des Codes geht es damit weiter.

Auf dem Bild ist der Hauptsensor zusehen, habe hier nochmal einen Screenshot.

Mach mal ein Demoprogramm das einfach nur folgendes macht
Motor laufen lassen um Chips ins Loch zu fördern

Den Schaltzustand der beiden Sensoren alle 10 Millisekunden auf den seriellen Monitor ausgeben.
Und dann mal die Ausgabe im Forum als Code-Section posten

Habe den Code jetzt geändert und es läuft Hervorragend :slight_smile: .

Habe 10 Tests mit 50 Chips gemacht, 7 davon waren genau 50, 1-mal waren es 51, und die anderen zweimal 49.

Habe diese schleife entfernt:

if (digitalRead(SensorSignalSchacht) == LOW && millis() >= Sensor_time + Sensor_delay_time) {
      Sensor_time = millis();
      CoinIst = CoinIst + 1;
      LcdInAuszahlung(CoinSoll, CoinIst);
      Serial.print(CoinIst);
      Serial.print(" ");
      Serial.println(Sensor_time);
      Serial.print(CoinKontrolle);
      Serial.print(" ");
      Serial.print(Sensor_time_Kontrolle);
      Serial.println(" Kontrolle");
    }

und hiermit ersetzt:

//Sensoren config
unsigned long Sensor_time = 0;  //Zum Speichern der zeit für den letzten gemessenen Coin
int Sensor_delay_time = 5;    //Wie lange soll der Sensor nach erkennen deaktiviert werden
byte Sensor_State = 0;
byte Sensor_Event = 0;

switch (Sensor_State) {
    case 0: 
      if (!digitalRead(SensorSignalKontrolle)) {
        Sensor_State = 1;
        Sensor_time = millis();
        Serial.print("LOW: ");
        Serial.println(Sensor_time);
      }
      break;
    case 1: 
      if (digitalRead(SensorSignalSchacht)) {
        Sensor_State = 0;
        if (millis() - Sensor_time > Sensor_delay_time) {
          Sensor_Event = 1;
          Serial.print("High");
          Serial.println(Sensor_time);
        }
      }
      break;
  }

  //Auswertung Sensor
  if (Sensor_Event == 1) {
    Sensor_Event = 0; //Ereignis zurücksetzten
    CoinIst = CoinIst + 1;
      LcdInAuszahlung(CoinSoll, CoinIst);
      //Serial.print(CoinIst);
      //Serial.print(" ");
      //Serial.println(Sensor_time);
  }

Delay time ist momentan noch auf 5ms, bin mir noch nicht ganz sicher, ob ich das überhaupt brauche.

Werde jetzt nochmal die Einbausituation des Sensors optimieren und dann brauche ich gewiss keine zweite Sicherheit mehr.

Sehr vielen Dank für die schnelle und erfolgreiche Hilfe. :+1:t3:

Die Fehlerrate wäre mir persönlich immer noch viel zu hoch.
So auf die schnelle habe ich auch nicht verstanden was dein switch-case-break jetzt bewirken soll.

Ich würde dem Sensorsignal erst einmal sehr gründlich auf den Zahn fühlen.
Mit besagtem Testprogramm oder noch besser mit einem zweiten Arduino der als Low-Cost-Oszilloskop den Signalpegel anzeigt.
Oder den Signalverlauf mit maximal möglicher Geschwindigkeit auf die serielle Schnittstelle ausgeben und mit einem Serial-Terminalprogramm das die empfangenen Zeichen in eine Datei speichern kann aufzeichnen.

Die Chips sehen aus wie für ein wie auch immer geartetes Glückspiel. In dem Zustand gibt es jetzt noch eine Glück/Pech-Komponente:
Die Anzahl ausgegebener Chips stimmt manchmal nicht. Das hat auch was.