Probleme mit Lichtschranke (Laser+Fotozelle) und zwei Drehgebern

Hallo liebe Gemeinde,

ich habe folgendes Problem :

Ich möchte einen Kamera-Auslöser mit einer Lichtschranke erstellen.
Beim durchbrechen der Lichtschranke soll eine Kamera ausgelöst werden (zu Testzwecken im Moment eine Leuchtdiode). Es sollen zwei Drehgeber verwendet werden (in diesem Fall KY-040). Mit dem ersten Drehgeber soll die Auslöseverzögerung einstellbar sein. Mit dem zweiten Drehgeber soll der Schwellenwert (im Bezug auf die Lichtverhältnisse) für die Auslösung einstellbar sein. Erstelle ich den Sketch nur mit Drehgeber für die Verzögerung, funktioniert alles einwandfrei. Doch wenn der zweite Drehgeber hinzu kommt, lässt sich dieser nicht zuverlässig, bzw. mit extremer Verzögerung einstellen. Ein weiteres Problem ist (allerdings ein sekundäres), das ich die Werte nur einstellen kann, wenn der Laser auf die Fotozelle gerichtet ist. Die Werte werden auf einem zwei-zeiligen Display ausgegeben.

PS : Ich bin mir der Gefahr eines Laser´s (in diesem Fall der KY-008) bewusst. Wenn die ganze Elektronik in einem Gehäuse verbaut ist, bekommt der Laser einen Schalter als Unterbrecher.

Mein Ziel, die High-Speed Fotografie (fallende Gegenstände ins Wasser z.B.).
Die Werte sollen einstellbar sein,unabhängig vom der Aktivität des Laser´s.. Beide Drehgeber sollen vernünftig einstellbar sein (der für die Verzögerung ist es ja, allerdings nur wenn er auf die Fotozelle gerichtet ist). Soweit funktioniert alles, aber eben nicht reibungslos. Kann mir da jemand helfen ?

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

// Pins für den Drehgeber
const int laserPin = 9; // Pin für die Laserdiode
const int ledPin = 10;   // Pin für die Leuchtdiode
const int ldrPin = A0; // Pin für die Fotozelle
const int pin_vz_A = 2; // DT - Drehgeber für Verzögerung PIN A
const int pin_vz_B = 3; // CLK - Drehgeber für Verzögerung PIN B
const int pin_sw_A = 4; // DT - Drehgeber für Schwellenwert PIN A
const int pin_sw_B = 5; // CLK - Drehgeber für Schwellenwert PIN B

// Erstelle einen Encoder-Objekt
Encoder verzoegerung(pin_vz_A, pin_vz_B); // Drehgeber Verzögerung
Encoder schwelle(pin_sw_A, pin_sw_B); // Drehgeber Schwellenwert

// Erstelle ein LCD-Objekt
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD-Adresse (muss ggf. angepasst werden)

int verz = 300; // Anfangswert für Verzögerung
int sw = 200; // Anfangswert für Schwellenwert

void setup() {
  pinMode(laserPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(laserPin, HIGH); // Laserdiode einschalten
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("VZ-Wert:");
  lcd.setCursor(0, 1);
  lcd.print("SW-Wert:");
  updateDisplay();
}

void loop() {

  long pos1 = verzoegerung.read(); // Lese die Encoder-Position
  // Berechne den neuen Wert in 5er-Schritten
  if (pos1 % 5 == 0) {
     verz = constrain(pos1 + 300, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
     updateDisplay();
  }

  long pos2 = schwelle.read(); // Lese die Encoder-Position 
  // Berechne den neuen Wert in 5er-Schritten
  if (pos2 % 5 == 0) {
     sw = constrain(pos2 + 150, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
     updateDisplay();
  }  

 int sensorValue = analogRead(ldrPin);
  if (analogRead(ldrPin) > sw) { // Bestätige die Unterbrechung
    delay(verz); // Warte die eingestellte Verzögerung ab
    digitalWrite(ledPin, HIGH); // LED einschalten
    delay(1000); // Warte die eingestellte Verzögerung ab
    digitalWrite(ledPin, LOW); // LED ausschalten
  }

}

// Funktion zum Aktualisieren des Displays
void updateDisplay() {
  // Erste Zeile für Verzögerung
  lcd.setCursor(9, 0); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 0); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(verz); // Zeige den aktuellen Wert an
  
  // Zweite Zeile für Schwellenwert
  lcd.setCursor(9, 1); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 1); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(sw); // Zeige den aktuellen Wert an
}

ja und warum blockierst du deinen Sketch hier mit delays?

mach das so wie gezeigt in "Blink Without delay" und mach das nebenläufig.

Zu dem schau dir die Lib an, habe mit der gute Erfahrungen.

Hallo. Danke für die Info. Das habe ich mir fast gedacht. Bei den Millis bin ich schon gestrandet.... das war´s. Ich weiß noch nicht wirklich wie ich diese in diesem Sketch einsetzen soll (bin Anfänger).

Frage 1: wenn der LDR nicht hell ist hat, siehst du dann ein flackern am LCD?

mir ist der Ablauf noch unklar:
Frage 2: nach einer Verzögerung von verz wird die LED für genau eine Sekunde eingeschaltet? Das ist alles?

Mein erster Vorschlag wäre die Encoder so zu erkabeln, dass zumindest der erste Parameter 2 bzw 3 ist. Also der zweite bekommt auch einen Interrupt ...

Es flackert nichts..... habe jetzt mal das ganze auf Millis geändert, manchmal geht´s, sehr oft aber nicht. Der mal reagiert der zweite Drehgeber, mal nicht, mal setzt er den Wert nach oben, obwohl er runtergehen soll...... Der Drehgeber für die Verzögerung funktioniert einwandfrei. Ja ich könnte den Wert für die Schwelle im Bezug auf einen Laser festsetzen (liegt bei der seriellen Monitorausgabe bei ca. 55), so das bei einem Wert unter 100 ausgelöst wird. Ich möchte mir aber mit der Einstellung für eine andere Lichtquelle diesen Wert offen lassen.
Zu Frage 2 : Ja es soll (zur Zeit nur die Leuchtdiode) leuchten. Später soll dort ein Relais dran, wobei die Auslösedauer (ich schätze mal so auf 200ms) reduziert wird. Mit diesem Relais wird dann eine Kamera ausgelöst. Zur Zeit sieht der Code so aus :

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

// Pins für den Drehgeber
const int laserPin = 9; // Pin für die Laserdiode
const int ledPin = 10;   // Pin für die Leuchtdiode
const int ldrPin = A0;   // Pin für die Fotozelle
const int pin_vz_A = 2;  // DT - Drehgeber für Verzögerung PIN A
const int pin_vz_B = 3;  // CLK - Drehgeber für Verzögerung PIN B
const int pin_sw_A = 4;  // DT - Drehgeber für Schwellenwert PIN A
const int pin_sw_B = 5;  // CLK - Drehgeber für Schwellenwert PIN B

// Erstelle einen Encoder-Objekt
Encoder verzoegerung(pin_vz_A, pin_vz_B); // Drehgeber Verzögerung
Encoder schwelle(pin_sw_A, pin_sw_B);     // Drehgeber Schwellenwert

// Erstelle ein LCD-Objekt
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD-Adresse (muss ggf. angepasst werden)

int verz = 300; // Anfangswert für Verzögerung
int sw = 200;   // Anfangswert für Schwellenwert

unsigned long previousMillis = 0; // speichert den letzten Zeitpunkt
unsigned long ledMillis = 0;       // speichert den Zeitpunkt für die LED
bool ledState = false;             // Status der LED
bool ledTriggered = false;         // Signal, dass die LED aktiviert werden soll

void setup() {
  pinMode(laserPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(laserPin, HIGH); // Laserdiode einschalten
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("VZ-Wert:");
  lcd.setCursor(0, 1);
  lcd.print("SW-Wert:");
  updateDisplay();
}

void loop() {
  long pos1 = verzoegerung.read(); // Lese die Encoder-Position
  // Berechne den neuen Wert in 5er-Schritten
  if (pos1 % 5 == 0) {
    verz = constrain(pos1 + 300, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
    updateDisplay();
  }

  long pos2 = schwelle.read(); // Lese die Encoder-Position 
  // Berechne den neuen Wert in 5er-Schritten
  if (pos2 % 5 == 0) {
    sw = constrain(pos2 + 150, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
    updateDisplay();
  }  

  int sensorValue = analogRead(ldrPin);
  if (sensorValue > sw) { // Bestätige die Unterbrechung
    if (!ledTriggered) { // Wenn die LED nicht bereits aktiviert wurde
      ledTriggered = true;
      previousMillis = millis(); // Setze den Zeitpunkt für die Verzögerung
    }
  }

  // LED-Logik
  if (ledTriggered) {
    if (millis() - previousMillis >= verz) { // Prüfe, ob die Verzögerung abgelaufen ist
      ledState = true; // LED einschalten
      digitalWrite(ledPin, HIGH);
      ledMillis = millis(); // Zeitpunkt speichern für das Ausschalten der LED
      ledTriggered = false; // Reset Trigger
    }
  }

  // LED ausschalten nach 1 Sekunde
  if (ledState) {
    if (millis() - ledMillis >= 1000) { // Nach 1 Sekunde
      digitalWrite(ledPin, LOW); // LED ausschalten
      ledState = false; // Reset LED Status
    }
  }
}

// Funktion zum Aktualisieren des Displays
void updateDisplay() {
  // Erste Zeile für Verzögerung
  lcd.setCursor(9, 0); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 0); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(verz); // Zeige den aktuellen Wert an
  
  // Zweite Zeile für Schwellenwert
  lcd.setCursor(9, 1); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 1); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(sw); // Zeige den aktuellen Wert an
}

wie gesagt, ändere mal die pins

z.B.

zu mehr komm ich heute nicht mehr in dieser Sache.

Es wird jedenfalls bei jedem loop Durchlauf das Display 2 mal aktualisiert.

Ich habe es mal wie folgt angepasst, ist aber ohne Garantie weil ohne Test:

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

class Timer {
  public:
    void start() {
      timeStamp = millis();
    }
    bool operator()(const uint32_t duration) {
      return (millis() - timeStamp >= duration) ? true : false;
    }

  private:
    uint32_t timeStamp {0};
};

enum class Status : byte {swIdle, swActive, swReadyWait};

// Pins für den Drehgeber
const int laserPin = 9; // Pin für die Laserdiode
const int ledPin = 10;   // Pin für die Leuchtdiode
const int ldrPin = A0; // Pin für die Fotozelle
const int pin_vz_A = 2; // DT - Drehgeber für Verzögerung PIN A
const int pin_vz_B = 3; // CLK - Drehgeber für Verzögerung PIN B
const int pin_sw_A = 4; // DT - Drehgeber für Schwellenwert PIN A
const int pin_sw_B = 5; // CLK - Drehgeber für Schwellenwert PIN B

// Erstelle einen Encoder-Objekt
Encoder verzoegerung(pin_vz_A, pin_vz_B); // Drehgeber Verzögerung
Encoder schwelle(pin_sw_A, pin_sw_B); // Drehgeber Schwellenwert
Timer wait;

// Erstelle ein LCD-Objekt
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD-Adresse (muss ggf. angepasst werden)

int verz = 300; // Anfangswert für Verzögerung
int sw = 200; // Anfangswert für Schwellenwert

void setup() {
  pinMode(laserPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(laserPin, HIGH); // Laserdiode einschalten
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("VZ-Wert:");
  lcd.setCursor(0, 1);
  lcd.print("SW-Wert:");
  updateDisplay();
}

void loop() {
  static long prevPos1 {0};
  static long prevPos2 {0};
  static Status swStatus{Status::swIdle};

  long pos1 = verzoegerung.read(); // Lese die Encoder-Position
  if (pos1 != prevPos1) {
    prevPos1 = pos1;
    // Berechne den neuen Wert in 5er-Schritten
    if (pos1 % 5 == 0) {
      verz = constrain(pos1 + 300, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
      updateDisplay();
    }
  }

  long pos2 = schwelle.read(); // Lese die Encoder-Position
  if (pos2 != prevPos2) {
    prevPos2 = pos2;
    // Berechne den neuen Wert in 5er-Schritten
    if (pos2 % 5 == 0) {
      sw = constrain(pos2 + 150, 0, 1000); // Begrenze den Wert auf 0 - 1 Sekunden
      updateDisplay();
    }
  }

  if (analogRead(ldrPin) > sw) { // Bestätige die Unterbrechung
    wait.start();
    swStatus = Status::swActive;
  }
  switch (swStatus) {
    case Status::swActive:
      if (wait(verz)) {
        wait.start();
        digitalWrite(ledPin, HIGH); // LED einschalten
        swStatus = Status::swReadyWait;
      }
      break;
    case Status::swReadyWait:
      if (wait(1000)) {
        digitalWrite(ledPin, LOW); // LED ausschalten
        swStatus = Status::swIdle;
      }
  }
}

// Funktion zum Aktualisieren des Displays
void updateDisplay() {
  // Erste Zeile für Verzögerung
  lcd.setCursor(9, 0); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 0); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(verz); // Zeige den aktuellen Wert an

  // Zweite Zeile für Schwellenwert
  lcd.setCursor(9, 1); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 1); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(sw); // Zeige den aktuellen Wert an
}

Also ich weiß nicht wie Ihr in "Sekunden-Schnelle" einen Code ändern könnt..... Aber es scheint zu funktionieren :smiley: Auch wenn der zweite Drehgeber (habe schon mehrere versucht) mal "hängt" und keine Wert-Änderung gibt.

Das mit dem Drehgeber scheint noch ein gesondertes Problem zu sein ... aber die Delays sind zumindest schon mal raus. Kannst du näher beschreiben, welche Encoder Bibliothek du verwendest?

Das ist die ganz normale encoder.h die in der Arduino-Bibliothek vorhanden ist (von Paul Stoffregen, Vers. 1.4.4)

Probiere mal folgenden Code:

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

class Timer {
  public:
    void start() {
      timeStamp = millis();
    }
    bool operator()(const uint32_t duration) {
      return (millis() - timeStamp >= duration) ? true : false;
    }

  private:
    uint32_t timeStamp {0};
};

enum class Status : byte {swIdle, swActive, swReadyWait};

// Pins für den Drehgeber
const int laserPin = 9; // Pin für die Laserdiode
const int ledPin = 10;   // Pin für die Leuchtdiode
const int ldrPin = A0; // Pin für die Fotozelle
const int pin_vz_A = 2; // DT - Drehgeber für Verzögerung PIN A
const int pin_vz_B = 3; // CLK - Drehgeber für Verzögerung PIN B
const int pin_sw_A = 4; // DT - Drehgeber für Schwellenwert PIN A
const int pin_sw_B = 5; // CLK - Drehgeber für Schwellenwert PIN B

// Erstelle einen Encoder-Objekt
RotaryEncoder encVz {pin_vz_A, pin_vz_B, RotaryEncoder::LatchMode::FOUR3};
RotaryEncoder encSw {pin_sw_A, pin_sw_B, RotaryEncoder::LatchMode::FOUR3};
Timer wait;

// Erstelle ein LCD-Objekt
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD-Adresse (muss ggf. angepasst werden)

int verz = 300; // Anfangswert für Verzögerung
int sw = 200; // Anfangswert für Schwellenwert

bool queryEncoder(RotaryEncoder &enc, int &value, int step, int min, int max) {
  byte flag {true};
  enc.tick();
  switch (enc.getDirection()) {
    case RotaryEncoder::Direction::NOROTATION: flag = false; break;
    case RotaryEncoder::Direction::CLOCKWISE: value = value < max ? value + step : min; break;
    case RotaryEncoder::Direction::COUNTERCLOCKWISE: value = value > min ? value - step : max; break;
  }
  return flag;
}

// Funktion zum Aktualisieren des Displays
void updateDisplay() {
  // Erste Zeile für Verzögerung
  lcd.setCursor(9, 0); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 0); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(verz); // Zeige den aktuellen Wert an

  // Zweite Zeile für Schwellenwert
  lcd.setCursor(9, 1); // Setze den Cursor auf die zweite Zeile
  lcd.print("        "); // Leere Zeile
  lcd.setCursor(9, 1); // Setze den Cursor wieder auf die zweite Zeile
  lcd.print(sw); // Zeige den aktuellen Wert an
}

void setup() {
  Serial.begin(115200);
  pinMode(laserPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(laserPin, HIGH); // Laserdiode einschalten
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("VZ-Wert:");
  lcd.setCursor(0, 1);
  lcd.print("SW-Wert:");
  updateDisplay();
}

void loop() {
  static Status swStatus{Status::swIdle};
  
  if (queryEncoder(encVz, verz, 5, 0, 1000)) { updateDisplay(); }
  if (queryEncoder(encSw, sw, 5, 0 , 1000)) { updateDisplay(); }

  if (analogRead(ldrPin) > sw) { // Bestätige die Unterbrechung
    wait.start();
    swStatus = Status::swActive;
    if (digitalRead(ledPin) == HIGH) {digitalWrite(ledPin,LOW);}
  }
  switch (swStatus) {
    case Status::swActive:
      if (wait(verz)) {
        wait.start();
        digitalWrite(ledPin, HIGH); // LED einschalten
        swStatus = Status::swReadyWait;
      }
      break;
    case Status::swReadyWait:
      if (wait(1000)) {
        digitalWrite(ledPin, LOW); // LED ausschalten
        swStatus = Status::swIdle;
      }
  }
}

Allerdings wird hier die RotaryEncoder Bibliothek von Matthias Hertel verwendet. Die gibt es auch über die Bibliotheksverwaltung.

Ich habe einige Einwände.

  1. Fotowiderstände sind langsam. Außerdem ist Dein Aufbau ziemlich Umgebungslichtempfindlich.
    Ich würde als Empfänger einen IR-Empfänger wie sie für Infrarotfernsteuerungen verwendet werden benutzen. Da gibt es Versionen extra für Lichtschranken. Ich weiß nicht ob diese auch mit einem roten Laser funktionieren oder ob Du eine IR LED brauchst. Diese einfach mit der Frequenz des Empfänger (meist 38kHz) ansteuern. So wird die Lichtschranke Umgebungslichtunabhängig.
  2. Der Auslöser einer Kamera ist langsam. Meist werden solche Aufnahmen im abgedunkelten Raum, mit offenen Verschluß und Blitz gemacht. Der Blitz wird mit dem Ereignis getriggert. Das hat den Vorteil daß man ein statisches eingefrohrenes Bild erhält.

Grüße Uwe

Hallo Uwe. Danke für die Info´s. Zu 1 : Mit IR-Dioden hab ich es versucht, war nix halbes und nix ganzes. Der Abstand soll ca. 50cm-75cm betragen, da hat das unterbrechen selten was ausgelöst (allerdings waren es die Standardteile aus einem Set). Das die LDR Umgebungslichtabhängig ist weiß ich, da habe ich mir schon ein kleines Gehäuse gedruckt, wo die Zelle einen Rücksprung zur Öffnunng hat. Der Umgebungslichtwert den die Zelle laut Serialmonitor erhält liegt in einem normalen Raum bei ca. 300-400. Trifft der Lase auf die Zelle liegt dieser Wert bei ca.75. Sollte von der Theorie her reichen. Zu 2 : Der Raum könnte abgedumkelt werden, kein Thema. Die Blitze werden von der Kamera gesteuert. Ob es hinterher tatsächlich reicht, weiß ich natürlich nicht. Es gibt Leute (laut FB), denen reicht das und es funktioniert. Ich dachte mir es einfach mal zu versuchen, da ich eh alle Teile hier habe und dabei noch lernen kann. Die speziellen Laserauslöser für sowas kosten richtig viel (Ok, vielleicht haben diese Dinger Ihre Dasein´s-Berechtigung, ist mir aber zu teuer). Ich hab´s tatsächlich mal komplett manuell versucht, ein Bonbon in eine Wassergefüllte Schale fallen zu lassen und den richtigen Moment zu erwischen. Naja..... von 25 Versuchen war ein halbwegs vernünftiger Treffe bei :slight_smile:

Damit funktioniert es :slight_smile: Dankeschön. Sind aber einige neue Funktionen drin, mit denen ich mich mal auseinandersetzen muss :cold_sweat:

Dieser Lasersensor hier ist im Prinzip eine Reflektor Lichtschranke

Achtung dieser Lasersensor ist Laserklasse 2 = 5 mW optische Leistung.
Das verursacht Augenschäden wenn man direkt in den Strahl blickt.

In Laserpointern sind maximal 0,5 mW erlaubt.

Auf hellem Reflektionshintergrund (z.B. weißer Karton) funktioniert es bis 1,5m Abstand.

Mit einem Katzenauge als Reflektor funktioniert es auch auf eine Entfernung von 10 m.
Das Laser-Licht wird mit 180 kHz gepulst. Das heißt der Sensor ist relativ Umgebungslicht-unabhängig. Bei direktem Sonnenlicht hatte ich aber doch Probleme.

Daher die Empfehlung das Blickfeld des Empfängers das ist das Ding mit der Linse zusätzlich mit einem innen mattschwarzen Rohr (im einfachsten Fall schwarze Bastelpappe) einzuengen.

Wie bei Lasern so überlich sendet der einen ganz scharf gebündelten Strahl. Das Objekt muss also sehr präzise durch den Strahl fallen damit der Laserstrahl unterbrochen wird.

Freut mich wenn es funktioniert. Wenn etwas noch unklar ist, dann frag ruhig.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.