erstes Projekt -> komisches Verhalten (IR Sender + Empfänger)

Hey Leute,

bin derzeit dabei mein erstes Projekt zu realisieren. Hab dazu auch schon eingies im Forum gelesen und mich im Internet informiert.

meine derzeit verwendeten Bauteile:

  • IR Sender LED - Osram LD274
  • IR Empfänger - TSOP 4836
  • LED
  • einige Widerstände

Vorerst gehts bei dem Projekt nur um eine einfach Lichtschranke, die allerdings relativ wenig Strom brauchen soll. Deshalb wird die Sende LED nur alle paar ms gepulst. (weitere Sparmaßnahmen wie anderer Arduino, Powersafemodes etc. folgt später).

hier der aktuelle Code dazu:

int ledPin = 10;
int sendPin = 11;
int receivePin = 2;
// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(ledPin, OUTPUT); 
  pinMode(sendPin, OUTPUT);
  pinMode(receivePin, INPUT);
  
  // set up Timer 2
  TCCR2A =  _BV(WGM21);  // CTC, toggle OC2A on Compare Match
  TCCR2B = _BV (CS20);   // No prescaler
  OCR2A =  221;          // compare A register value (222 * clock speed)
                         //  = 13.875 nS , so frequency is 1 / (2 * 13.875) = 36036Hz
  
  
  
}

// the loop routine runs over and over again forever:
void loop() {
  noInterrupts();
  TCCR2A |= _BV (COM1A0);
  delayMicroseconds(800);
  if(digitalRead(receivePin) == HIGH)
  {
//    Startup();
    digitalWrite(ledPin, HIGH);
  }
  else
  {
//    delay(3000);
  
  digitalWrite(ledPin, LOW);
  TCCR2A &= ~_BV (COM1A0);
  delay(57);
  
  }
  
}

so jetzt zu meinen Problemen:

  1. Ist die Delayzeit nach dem deaktiveren des Timers (momentan 57ms) kleiner als 57ms funktioniert die Lichtschranke perfekt. wenn ich die delayzeit von (57ms) allerdings auf irgend einen höheren Wert ändere funktioniert die Lichtschranke nicht mehr wenn ich eine Unterbrechung zwischen LED und Receiver mache (es treffen sicher auch keine reflexionen auf den Empfänger!). Zieh ich allerdings den Pin von der Sendeled ab leuchtet die andere LED wie gewollt.
    -> warum ist das so? (Abschirmen und Pin ausstecken sollte doch das selbe sein?)

  2. wird die erste Zeile von der loopschleife gelöscht (noInterrupts():wink: funktioniert das ganze auch nicht mehr, selbst wenn die delayzeit <57ms ist. Zieh ich allerdings wieder den Pin von der Sendeled, geht sofort die andere an.
    -> läuft mein Timer eigentlich weiter wenn ich noInterrupts() mache?!
    -> wozu benötige ich diese Zeile in meinem Programm?

delay komplett raus! Arbeite mit der millis() Methode siehe Playground "Blink without delay". Wenn am Pin kein HIGH Signal anliegt, reagiert dein Programm 3 Sekunden auf keinen Befehl! Auch nicht auf eine Rückmeldung des gegenüber

Das 3 Sekunden Delay ist allerdings schon auskommentiert

Hallo,

der Empfänger ist active low. D.h. wenn er ein Signal empfängt geht er auf LOW und wenn er kein Signal empfängt geht er auf HIGH.

Der Empfänger reagiert nicht darauf, dass die Sende-LED eingeschaltet ist, sondern das die Sende-LED in einer Frequenz von 36 kHz blinkt. Deshalb wird auch der Timer auf 36 kHz eingestellt.

Lt. Datenblatt muss die LED rd. 0,28 ms lang mit einer Frequenz von 36 kHz blinken, damit der Empfänger sie zuverlässig erkennt.

Grob müsste der Ablauf Deines Programmes so sein:

  1. Interrupts freigeben - Sende-LED blinkt
  2. Rd. 1 ms warten
  3. Signal vom Empfänger auswerten
  4. Interrupts sperren - Sende-LED blinkt nicht mehr

Nach diesen vier Schritten solltest Du Pause machen können, solange Du magst.

Wenn noch Fragen bestehen, frag einfach!

Gruß,
Ralf

[Edit:] Hatte mich mit den Einheiten verrechnet, ist korrigiert.

Wobei der Timer in diesem Fall nicht über einen Interrupt läuft. Der läuft einfach ständig und bei einem Compare Match wird ein Pin getoggelt (an dem die LED hängt). Es wird nur umgeschaltet ob das Signal nach außen gelegt wird oder nicht. Intern läuft der Timer weiter.

Man muss auch irgendwann mal mit dem Senden kurz aufhören, da der TSOP eine Pause erwartet und ständige Signale unterdrückt.

Die LED sollte man auch mit einem Transistor ansteuern, da diese LEDs locker mehr als 40mA vertragen. Die LD274 verträgt für die 28µs Pulslänge und 50% duty cycle etwas über 200mA wenn ich das richtig lese (Datenblatt Seite 6, unten).

Serenifly:
Man muss auch irgendwann mal mit dem Senden kurz aufhören, da der TSOP eine Pause erwartet und ständige Signale unterdrückt.

Bist Du Dir da sicher?

100%ig nicht, aber das steht im Datenblatt:

*After each burst which is between 10 cycles and 70 cycles a gap time of at least 14 cycles is necessary.
*For each burst which is longer than 1.8ms a corresponding gap time is necessary at some time in the data stream. This gap time should be at least 4 times longer than the burst.
*Some examples for such disturbance signals which are suppressed by the TSOP48.. are:
**Continuous signal at 38kHz or at any other frequency

Das hab ich auch gelesen, aber auch ich kann es nicht zweifelsfrei für Deine oder nicht Deine Version interpretieren.
Grüße Uwe

Serenifly:
Wobei der Timer in diesem Fall nicht über einen Interrupt läuft.

Hallo,

habe ich ja auch nicht behauptet. Der Timer läuft hier im "Clear Timer on Compare Match"-Modus. Mit interrupts(); bzw. noInterrupts(); sollte man ihn an- oder ausschalten können (bin mir da im Moment gerade nicht 100%ig sicher, müsste ich im Zweifel morgen probieren).

Mein Schritt 1. sollte bedeuten: Funktion interrupts(); aufrufen.
Mein Schritt 4. sollte bedeuten: Funktion noInterrupts(); aufrufen.

Gruß,
Ralf

Hallo,

so, wie angedacht, habe ich das mal ausprobiert.

Also 1. interrupts() und noInterrupts() schaltet die Toggle-Funktion am Ausgabe-Pin nicht an- und aus (wie ich dachte). Anschalten lässt sich die Toggle-Funktion, indem das entsprechende Bit im TCCR2A-Register gesetzt wird

TCCR2A |= _BV (COM1A0);

und wieder ausgeschaltet wird sie durch Rücksetzen des Bits:

TCCR2A &= ~_BV (COM1A0);

Und 2. habe ich mal den Sketch geändert und mit dem Oszilloskop kontrolliert. Es werden Signal-Bursts von 500µs mit einer Frequenz von rd. 36kHz (habe ich auch nur mit dem Oszi gecheckt, nicht mit dem Frequenzzähler, scheint aber zu passen) erzeugt. Nach jedem Burst (die Stelle habe ich im Sourcecode gekennzeichnet) sollte kontrolliert werden, ob ein Signal empfangen wurde. Falls ja -> ok, falls nein -> irgendwas unterbricht den Lichtstrahl. Sollte der Burst für den benutzten Empfänger zu kurz sein, kann die Zeit moderat verlängert werden. Ebenso könnte man mal testen, ob der Empfänger das Signal mit einem kürzeren Burst noch sicher erkennt.

Nach der Signalerkennung kann erstmal eine Pause eingelegt werden, um Batteriekapazität zu sparen. Wie lange diese Pause sein darf, muss sich natürlich danach richten, mit welcher Geschwindigkeit Dinge die Lichtschranke passieren.

Was schon angesprochen wurde: Die LED sollte nicht direkt am Arduino-Pin hängen, sondern an einem Treiber, damit sie etwas mehr Leuchtkraft entwickeln kann. Uwefed und ich hatten ja schon mal eine kleine Diskussion, wie eine solche Schaltung aussehen könnte. Wenn der TO sich für die Schaltungen interessiert, kann er hier noch mal nachlesen: Verständnisfrage IR LED - #8 by system - Deutsch - Arduino Forum

Hätte ich beinahe vergessen XD Hier ist natürlich noch der Sketch für den TO:

int ledPin = 10;
int sendPin = 11;
int receivePin = 2;


void setup() {                
  // Pins auf OUTPUT schalten
  pinMode(ledPin, OUTPUT); 
  pinMode(sendPin, OUTPUT);
  pinMode(receivePin, INPUT);
  
  // Timer 2 einrichten
  TCCR2A =  _BV(WGM21);  // CTC, toggle OC2A on Compare Match
  TCCR2B = _BV (CS20);   // Kein prescaler
  OCR2A =  221;          // compare A register value (222 * clock speed)
                         //  = 13.875 µS , so frequency is 1 / (2 * 13.875) = 36036Hz 
                         // Das hier sind Microsekunden, nicht Nanosekunden
}


void loop() {
  // Sende-LED für 0,5 ms mit 36kHz-Frequenz einschalten
  TCCR2A |= _BV (COM1A0);
  // Diese Zeit evtl. bis 900µs verlängern, falls Signal nicht sicher erkannt wird
  delayMicroseconds(500);
  
  //
  // Hier jetzt nachsehen, ob ein Signal empfangen wird, sollte theoretisch so gehen:
  // if(digitalRead(receivePin == LOW)) {
  //     Signal empfangen
  // }
  
  // SendeLED für die gewünschte Pause ausschalten, hier 0,5s
  TCCR2A &= ~_BV (COM1A0);
  delay(500);  
}

Gruß,
Ralf

Erstmal danke für die ganzen Antworten, hat mir schonmal sehr weitergeholfen.
Allerdings kann ich mir das akutelle verhalten immernoch nicht erklären!

@sschultewolter

die delayzeiten statt millis zu verwenden hat hier schon seinen Grund. er soll in dieser Zeit auch wirklich nichts machen. dadurch benötigt er weniger Strom und die delays werden später durch sleeps ersetzt.
Würde ich millis verwenden würde der Controller einfach weiter laufen und keine wirklich Stromersparnis eintreten.

@Serenifly
Ich habe das genauso vermutet wie du - Der Empfänger benötigt immer eine Pause um ein Signal zu erkennen. Diese Pause sollte allerdings durch mein Programm auch erzeugt werden denke ich.

"*After each burst which is between 10 cycles and 70 cycles a gap time of at least 14 cycles is necessary."

Heisst das der Empfänger geht nach 14 Takten erst wieder auf Low oder warum MINDESTENS 14 zyklen Pause?!

die LED wird später natürlich noch über einen Treiber betrieben. Der Momentane Aufbau ist wirklich nur zu Testzwecken da um und zu sehen was überhaupt möglich ist.

@Schachmann

Danke für deine Hilfe und deine Bemühungen!!

Genau so wie du es in deiner Erklärung hast hab ich mir das auch gedacht! Und genauso wie du hab ich es auch in meiner Version gelöst oder übersehe ich da etwas? Ich kann keine wesentlichen Veränderungen feststellen die das ganze nun zum laufen bringen sollten.

Allerdings benötige ich hier:

// if(digitalRead(receivePin == LOW)) {
// Signal empfangen
// }

meines Erachtens dennoch eine If else Anweisung denke ich. Wenn die Lichtschrank unterbrochen ist geht der Empfänger auf HIGH (Low Activ) und soll dann etwas ausführen.
Wurde die Lichtschranke nicht unterbrochen darf er schlafen (warten).
Das digitalWrite(ledPin, LOW) hab ich nur verwendet um sicher zu gehen das die LED auch wirklich aus ist.

Werde deinen Code heut Abend mal testen und dann berichten ob es funktioniert hat oder nicht!

Chon9:
... meines Erachtens dennoch eine If else Anweisung denke ich. ...
.
.
.
Werde deinen Code heut Abend mal testen und dann berichten ob es funktioniert hat oder nicht!

Nein, wenn ich Deine Absicht richtig verstehe, solltest Du die Pause in jedem Fall einlegen. Also entweder eine Aktion ausführen oder nicht (also das if(...) ) und dann in jedem Fall die Pause von entsprechender Länge (also kein else...).

Aber probier es doch erst mal aus und wenn dann noch Unklarheiten bestehen, können wir weiter schreiben :wink:

Gruß,
Ralf

Dazu komm ich leider erst Abends.

Könntest du mir vielleicht trotzdem kurz erklären worin der genaue Unterschied zu meinem Code ist?

Liegt das nur an dem else Zweig das es nicht funktioniert?!

Chon9:
Könntest du mir vielleicht trotzdem kurz erklären worin der genaue Unterschied zu meinem Code ist?

Ehrlich gesagt, habe ich mir Deinen Code nicht genauer angesehen (zumindest nicht mit dem Oszi) aber mir fällt auf, dass bei Dir das Abschalten der Sende-LED im else-Zweig steht und das gehört da nicht hin. Du machst damit ja die gewünschte Pause vom empfangenen Signal abhängig - aber die Pause soll ja immer eingelegt werden. Außerdem erkenne ich den Sinn von noInterrupts(); nicht. Das hat da meiner Meinung nach auch nichts verloren. Außerdem berücksichtigt Dein Code das Low-Active nicht, sondern scheint von High-Active auszugehen.

Gruß,
Ralf

So habe deinen Code, Schachmann, gerade umgeändert damit auch auch ne Ausgabe sehe und getestet --> Lichtschranke funktioniert nicht!

Hier der Code:

int ledPin = 10;
int sendPin = 11;
int receivePin = 2;

void setup() {
// Pins auf OUTPUT schalten
pinMode(ledPin, OUTPUT);
pinMode(sendPin, OUTPUT);
pinMode(receivePin, INPUT);

// Timer 2 einrichten
TCCR2A = _BV(WGM21); // CTC, toggle OC2A on Compare Match
TCCR2B = _BV (CS20); // Kein prescaler
OCR2A = 221; // compare A register value (222 * clock speed)
// = 13.875 µS , so frequency is 1 / (2 * 13.875) = 36036Hz
// Das hier sind Microsekunden, nicht Nanosekunden
}

void loop() {
// Sende-LED für 0,5 ms mit 36kHz-Frequenz einschalten
TCCR2A |= _BV (COM1A0);
// Diese Zeit evtl. bis 900µs verlängern, falls Signal nicht sicher erkannt wird
delayMicroseconds(500); // auch höherer Wert bringt keine Änderung der Lichtschrankenfunktionalität

//
// Hier jetzt nachsehen, ob ein Signal empfangen wird, sollte theoretisch so gehen: // HIGH wenn Lichtschranke durchbrochen wird
if(digitalRead(receivePin == HIGH)) {
digitalWrite(ledPin, HIGH);
delay(3000);
}

// SendeLED für die gewünschte Pause ausschalten, hier 0,5s
digitalWrite(ledPin, LOW);
TCCR2A &= ~_BV (COM1A0);
delay(1000); // Verringern der Zeit bringt keine Änderung bezüglich der Lichtschranken funktionalität
}

Folgendes passiert mit diesem Code:

Die Led geht für 3 Sekunden an und anschließen für eine Sekunde aus und dann wieder von vorne (ie Zeiten sind ja auch klar, allerdings sollte der TSOP keine unterbrechung, also in der ifabfrage LOW liefern und die LED nicht ansteuern).
--> Hierbei ist es egal ob ich die Lichtschranke unterbreche oder nicht. Es passiert immer das Selbe!

bezüglich dem Low Activ:
Seh ich das dann so falsch, wenn die LED angesteuert wird, geht der PIN auf LOW ---> Lichtschranke nicht unterbrochen
somit liegt HIGH an, wenn die Lichtschranke durchbrochen wird oder`?

bezüglich dem noInterrupts():
da geb ich dir vollkommen recht, macht dort garkeinen Sinn, aber nur so funktionierts einigermaßen!

bezüglich dem else-Zweig:
geb ich dir ebenfalls recht! Sollte allerdings kein Problem für die funktionalität darstellen oder?

Hai,

gute und schlechte Nachricht. Die gute zuerst: Ich habe noch ein paar TSOP1736 hier. Die schlechte: Ich habe heute keine Zeit mehr. Aber ich probier morgen mal aus wo es hängt und melde mich dann nochmal.

Gruß,
Ralf

Was du auch mal probieren kannst ist das hier:

Die Lib ist zwar eigentlich zum Auslesen und Emulieren von Fernbedienungen gedacht, aber das ist auch Code für eine Lichtschranke dabei:

Du musst aber darauf achten, dass du das mit 36kHz initialisierst.

@Serenifly

Danke für die Librarys, werd ich mir aufjedenfall mal anschauen, wobei ich es lieber selbst programmiere!

@Schachmann

Habe einen Fehler in unserem Code entdeckt, der allerdings nicht das Problem löst:

Wir haben "_BV (COM1A0)" statt "_BV (COM2A0)" verwendet

hab ausserdem weitere neue Informationen! scheint wohl am TSOP zu liegen oder am Timersetup.

Hab jetzt mal bisschen mit den Zeiten gespielt und es scheint sehr mit der An/Auszeit der LED zu tun zu haben! Wähle ich ähnliche Werte für An und aus Funtioniert es(ab einem ca 70ms anzeit, kann die auszeit beliebig sein),

Funktioniert:
AN - AUS
10ms - 10ms
1000ms - 1000ms
150ms - 1000ms
10ms - 25ms
20ms - 50ms
25ms - 50ms
70ms - 5000ms

Funktioniert nicht:
10ms - 50ms
10ms - 30ms
20ms - 80ms
15ms - 50ms

dazu der aktuelle Code:

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int ledPin = 10;
int sendPin = 11;
int receivePin = 2;
// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(ledPin, OUTPUT); 
  pinMode(sendPin, OUTPUT);
  pinMode(receivePin, INPUT);
  
  // set up Timer 2
  TCCR2A =  _BV(WGM21);  // CTC, toggle OC2A on Compare Match
  TCCR2B = _BV (CS20);   // No prescaler
  OCR2A =  221;          // compare A register value (210 * clock speed)
                         //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095
}

// the loop routine runs over and over again forever:
void loop() {
//  noInterrupts();
//  TCCR2A |= _BV (COM2A0);
  TCCR2A = 0x42;   //ich beschreibe die Register testweise manuell

  delay(15);
  if(digitalRead(receivePin) == HIGH)
  {
//    Startup();
    digitalWrite(ledPin, HIGH);
    delay(1000);
  }
//  else
//  {
//    delay(3000);
  
  digitalWrite(ledPin, LOW);
//  TCCR2A &= ~_BV (COM2A0);

  TCCR2A = 0x00;   //ich beschreibe die Register testweise manuell
  delay(40);
  
//  }
  
}

kann es sein das die LED/der Arduino zu langsam ist und nicht reagiert, oder Liegt es am TSOP der ein bestimmtes Format wirklich erhalten will und ansonsten nur murks macht?!

Ja, das lässt sich sicherlich auch per Hand machen. :slight_smile:

Beachte auch den einen Kommentar dort:

Here is a fix for the 838 and any others that reject a continuous beam.

void loop()
{
irsend.space(0);
delay(1);
irsend.mark(0);
digitalWrite(PIN_STATUS, !digitalRead(PIN_DETECT));
delay(1);
}

In Abbildung 9 des Datenblatts steht was zum maximalen duty cycle. Der steigt mit der Pulslänge bis zu einem bestimmten Wert und fällt dann wieder ab.

In deinem aktuellen Code scheinst du jetzt 38kHz zu haben. Pass da auf. Die TSOP ICs gibt es für verschiedene Frequenzen. Du hast gesagt, dass du die 36kHz Version hast.

Hallo,

wie es so ist, es lässt einem dann doch keine Ruhe - deswegen habe ich jetzt zu später Stunde nochmal schnell den TSOP1736, die IR-Led (mit Transistoren) und den Arduino zusammengestöpselt und meinen Sketch von oben nochmal real ausprobiert. Wie ich erwartet hatte: Wenn Du nichts an dem Sketch änderst (bzw. kaputt machst ]:smiley: ) dann funktioniert das genau so, wie es soll.

Also, ich habe den Arduino Uno benutzt, die Sende-LED hängt am Pin 11, den Empfänger kannst Du anstöpseln, wo Du magst, musst dann aber den Pin im Sketch eventuell ändern.

Ein Bild vom Oszi habe ich angehängt, da kann man sehen, wie gut das klappt. Getriggert wird auf Kanal 2 (untere Linie), das Oszillogramm beginnt also in dem Moment, in dem die Sende-LED das erste Mal nach der Pause angeht. Kanal 1 (obere Linie) zeigt Dir den Ausgang vom TSOP 1736. Wie man sieht, ist der TSOP am Anfang aus, das Signal ist HIGH (wir hatten ja über active low schon gesprochen).

Die Sende-LED beginnt also mit einer Frequenz von rd. 36kHz zu blinken. Nach etwas mehr als 0,22ms merkt der TSOP, dass die LED blinkt und schaltet entsprechend seinen Ausgang auf LOW, um zu signalisieren, dass er die LED sieht. Nach weiteren knapp 0,3ms hört die Sende-LED auf zu blinken, der Signal-Burst endet. Etwas später merkt der TSOP, dass die LED nicht mehr blinkt und schaltet seinen Ausgang wieder HIGH.

Wenn das bei Dir nicht funktioniert, weiß ich nicht woran es liegt, aber es liegt nicht an dem Sketch den ich Dir gepostet habe. Wie man am Oszillogramm sehen kann, klappt das absolut zuverlässig. BTW: An COM1A0 solltest Du auch nicht schrauben, das ist schon richtig so, wie es ist.

Wenn Du die Kommentarzeichen vor den Zeilen:

  // if(digitalRead(receivePin == LOW)) {
  //     Signal empfangen
  // }

entfernst, kannst Du an die Stelle "// Signal empfangen" die Aktion schreiben, die ausgeführt werden soll, wenn ein Signal empfangen wird (also der Empfänger die Sende-LED sehen kann!). Deswegen steht ja auch im Kommentar "Signal empfangen" - da steht nicht, "Signal nicht empfangen". Für Deinen Fall heißt das, hier kannst Du Deine Anzeige-LED einschalten. Wenn Du auch eine Aktion ausführen möchtest, wenn die Sende-LED an ist und der TSOP sie nicht sehen kann - vulgo jemand unterbricht den Strahl - dann kannst Du einen else-Zweig einfügen. Das müsste dann so aussehen:

  // if(digitalRead(receivePin == LOW)) {
  //     Signal empfangen
  // } else {
  //     Signal nicht empfangen
  // }

Mögliche Fehlerquellen, die Du prüfen solltest: Stimmt die Verkabelung? Hast Du wirklich einen TSOP1736 oder vielleicht einen anderen Typen - den gibt es für verschiedene Frequenzen. Ist Deine Sende-LED wirklich eine IR-LED mit passender Wellenlänge (siehe Datenblätter). Mit einer Digitalkamera kannst Du in der Liveview sehen, ob die LED an ist.

Bedenken musst Du auch noch: Der TSOP1736 ist so konstruiert, dass er möglichst alles was in der passenden Wellenlänge auf seiner Frequenz blinkt sehen soll. Wenn die LED einfach so abstrahlt, dann sieht er sie, auch wenn z.B. ein Monitor zwischen Empfänger und Sender steht, anhand der Reflexionen im Raum. Um halbwegs einen Lichtschranken-Effekt hinzubekommen solltest Du die Sende-LED wenigstens in ein schwarzes Pappröhrchen von sagen wir 6-8cm Länge stecken, damit sie nicht x-beliebig diffus abstrahlen kann. Am besten noch eine Sammellinse auf das Pappröhrchen und auf den TSOP zielen. Und selbst dann musst Du das Röhrchen noch gut abdecken, damit der Empfänger es nicht mehr sehen kann.

Solche Lichtschranken wie Du sie scheinbar im Sinn hast, habe ich mal mit gepulsten Laserpointern als Sender und einfachen Phototransistoren als Empfänger gebastelt, damit hat man dann die Streuwirkung des Lichts recht gut im Griff (so als Denkanstoß).

So, wenn ich mir jetzt kein Karma-Plus verdient habe, weiß ich es auch nicht XD

Gruß,
Ralf