lichtschrankenauswertung, zeitweise sperrung Eingang

OP #5927325
Lesenswert?

Hallo,
es geht um eine vermeintlich simple Angelegenheit,
jedoch klappt es bei mir nach vielen Versuchen immer noch nicht.

Aufgabe:
Vier Lichtschranken (eine pro Bahn), detektieren fallende Teile als 
positive Flanke und geben bei jeder Auslösung ein digitales Signal aus.

Durch die Geometrie der Teile ist eine doppelt Auslösung der 
Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s 
deaktiviert sein.

--> Alles über eine SoftwareAuswertung, aber meine Versuche mit millis() 
scheitern bisher

Klingt einfach, ich bekomm es aber leider nicht gebacken.
Ich freue mich über jede Hilfe!


Vielen Dank.

Gruß
Gast #5927516
Lesenswert?

T1waz T. schrieb:
> Durch die Geometrie der Teile ist eine doppelt Auslösung der
> Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s
> deaktiviert sein.

Der Lichtschranke wirst du nur schwer verbieten können, ein zweites Mal 
auszulösen, sofern du nicht nach der ersten Auslösung den Sender 
abschaltest.

Am einfachsten wird es sein, wenn du das zweite Signal in deiner 
Software ignorierst, z.B. indem du einen neuen Unterbrechungspuls erst 
nach einer gewissen Mindestzeit akzeptierst.
OP #5927659
Lesenswert?

Wolfgang schrieb:
> T1waz T. schrieb:
>> Durch die Geometrie der Teile ist eine doppelt Auslösung der
>> Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s
>> deaktiviert sein.
>
> Der Lichtschranke wirst du nur schwer verbieten können, ein zweites Mal
> auszulösen, sofern du nicht nach der ersten Auslösung den Sender
> abschaltest.
>
> Am einfachsten wird es sein, wenn du das zweite Signal in deiner
> Software ignorierst, z.B. indem du einen neuen Unterbrechungspuls erst
> nach einer gewissen Mindestzeit akzeptierst.

Das war wohl schlecht ausgedrückt, ich will nicht die Lichtschranke 
pausieren,
sondern wie du sagst, erst nach einer Mindest Zeit das nächste Signal 
akzeptieren.

Genau daran scheitere ich.

Ich poste später mal den Code
OP #5929314
Lesenswert?

Hier ist mein Code.
1
const int counterPin = D2;
2
const int sensorPin[4] = {D3,D4,D5,D6};
3

4
const long interval = 2000;
5
unsigned long previousMillis[4] = {0,0,0,0};
6
unsigned long currentMillis[4];
7
int sensorPinBlocked[4] = {0,0,0,0};
8
int buttonState[4];
9
int lastButtonState[4];
10
unsigned long myCounter = 0;
11
unsigned long myCounterOld = 0;
12

13
void setup() {
14
  // put your setup code here, to run once:
15
  
16
  Serial.println("Hau rein....");
17
  pinMode(counterPin, OUTPUT);
18

19
  for (int i = 0; i < 4; i++) {
20
     pinMode(sensorPin[i], INPUT);
21
  }
22
   Serial.begin(9600);
23
}
24

25
void loop() {
26
    for (int i = 0; i < 4; i++) {
27
      currentMillis[i] = millis() + interval;
28
      buttonState[i] = digitalRead(sensorPin[i]);
29
      
30
      if (myCounter == HIGH) {
31
        if (currentMillis[i] - previousMillis[i] >= interval){
32
           previousMillis[i] = currentMillis[i];
33
           sensorPinBlocked[i] = HIGH;   
34
        } else {
35
           sensorPinBlocked[i] = LOW;
36
        }
37
      }
38
      
39
      if (sensorPinBlocked[i] == LOW) {
40
        if (buttonState[i] != lastButtonState[i]) {
41
          myCounter = HIGH;
42
        }else {
43
          myCounter = LOW;
44
        }
45
      }
46

47
      delay(299);
48
      lastButtonState[i] = buttonState[i];
49
    }
50
}

soweit meine Idee.
Wo die Flankenauswertung zurückgesetzt wird, ist mir nicht ganz klar.

Zusätzliche Frage: Gibt es eine gute Möglihckeit für Debuggen oder 
Simulieren, um das Programm zeilenweise mit Werten zu sehen?
#5929324
Lesenswert?

T1waz T. schrieb:
> soweit meine Idee.
> Wo die Flankenauswertung zurückgesetzt wird, ist mir nicht ganz klar.

Wenn das Deine Idee ist, solltest Du eigentlich wissen, wo Du was zurück 
setzt.
Ich finde es recht kompliziert und schwer zu verstehen.
Schreib das Programm einfach erstmal nur für eine Lichtschranke. Und 
erst, wenn es funktioniert, erweitere es.
#5929362
Lesenswert?

Die Logik ist verkehrt,
sensorPinBlocked ist nur für eine Schleifeniteration HIGH, sonst immer 
LOW.
Daher wird wohl dann immer myCounter=HIGH.
Wobei ich den Code für etwas unübersichtlich halte.
1
      currentMillis[i] = millis() + interval;
2
      buttonState[i] = digitalRead(sensorPin[i]);
3
      
4
      if (myCounter == HIGH) {
5
        if (currentMillis[i] - previousMillis[i] >= interval){
6
           previousMillis[i] = currentMillis[i];
7
           sensorPinBlocked[i] = LOW;   
8
        } else {
9
           sensorPinBlocked[i] = HIGH;
10
        }
11
      }
12
      
13
      if (sensorPinBlocked[i] == LOW) {
14
        if (buttonState[i] != lastButtonState[i]) {
15
          myCounter = HIGH;
16
          lastButtonState[i] = buttonState[i];
17
        }else {
18
          myCounter = LOW;
19
        }
20
      }
21

22
      delay(299);
23
      //lastButtonState[i] = buttonState[i];
Gast #5929380
Lesenswert?

T1waz T. schrieb:
> Genau daran scheitere ich.
>
> Ich poste später mal den Code

Nee, laß das mal mit dem Posten von nicht funktionierendem Code.

Aber warum scheiterst du an so etwas einfachem? Ich komme mir schon vor 
wie eine tibetanische Gebetsmühle, aber anstatt mal zuzuhören und zu 
lernen, halten sich die Leute ihre Augen fest zu.

Also lerne aus der Lernbetty oder irgend einem anderen Zeugs, was ich 
hier mal gepostet hatte und arbeite mit Events. Das löst dein Problem im 
Handumdrehen.

Wie geht's?
Anstatt auf irgendwas zu warten, wird das Eintreffen irgendeines 
Ereignisses in eine Event-Warteschlange geschrieben und dann im 
Grundprogramm ausgewertet. Und mit einer Systemuhr (so alle 1..10 ms 
oder so) kannst du dann auch verzögerte Events haben. Das sind welche, 
die erst nach einer von dir wählbaren Zeit von der Systemuhr in die 
Event-Warteschlange geschrieben werden.

mit sowas kannst du "Entprell"-Totzeiten machen, ohne deinen µC 
irgendwie mit Warten auf Entprellung zu blockieren.

W.S.
OP #5929412
Lesenswert?

W.S. schrieb:
> Aber warum scheiterst du an so etwas einfachem?

Gute Frage, darum habe ich hier gepostet.

Was Event Warteschlangen sind, bzw. wie ich sowas implementiere in meine 
Anforderung, weiß ich leider nicht.

Könnte daran liegen, dass ich mich noch nicht ewig damit beschäftige


Zu meiner Frage mit Simulation /Debugging, gibts da irgendwas?
#
Gruß
#5929432
Lesenswert?

W.S. schrieb:
> arbeite mit Events

seid wann hat Arduino events?

> mit sowas kannst du "Entprell"-Totzeiten machen, ohne deinen µC
> irgendwie mit Warten auf Entprellung zu blockieren.
das ist doch garnicht das Problem hier.

Thomas W. schrieb:
> sensorPinBlocked ist nur für eine Schleifeniteration HIGH, sonst immer
> LOW.
stimmt nicht ganz.
nach einer Flanke am Pin außerhalb der "Totzeit" wird myCounter high, 
dadurch wird sensorPinBLocked high bis Ende der "Totzeit" und myCounter 
bleibt gezwungenermaßen high. Am Ende der "Totzeit" wird 
sensorPinBlocked low, erst dann kann myCounter low werden, aber dann ist 
sensorPinBlocked auf low festgesetzt.



Probleme bei deinem Code:

Wenn der Pin sich innerhalb der Blockierzeit ändert, wird die Erkennung 
bis zum Ende der Blockierzeit aufgeschoben.

myCounter ist sowohl nach einer steigenden als auch einer fallenden 
Flanke high, und zwar für die "Totzeit" *(stimmt so nicht ganz, siehe 
nächster Absatz). beide Flanken sind gleichwertig, wenn der Pin high 
wird wird genau gleich behandelt udn gibt intern das gleiche Signal als 
ob der Pin low wird.

das Echte Problem ist, dass PreviousMillis nur am Ende der letzten 
Totzeit gesetzt wird. das heißt deine "Totzeit" beginnt nicht bei einer 
Flanke zu zählen, sondern bei Ende der letzten "Totzeit". daher ist die 
Dauer der echten Totzeit auch nicht 2000ms sondern (2000ms - Zeit seit 
Ende der letzten "Totzeit"). Der beginn der Totzeit muss beim Ändern des 
Pins gesetzt werden.

Beispiel
- wenn vor 10s die letzte Flanke kam, ist previousmillis die Zeit von 
vor 8s
- bei der nächsten Flanke wird myCounter high
- im nächsten durchlauf ist die Totzeit bereits >8s, daher bleibt 
sensorpinblocked low
- nun wird sofort myCounter wieder low, da die "Totzeit" schon lange 
abgelaufen ist.
-> das funktioniert bei steigender und fallender Flanke gleich

das hier dürfte besser funktionieren:
1
 if (currentMillis[i] - previousMillis[i] >= interval){
2
           sensorPinBlocked[i] = LOW;   
3
        } else {
4
...
5

6
 if (buttonState[i] != lastButtonState[i]) {
7
          previousMillis[i] = currentMillis[i];
8
          myCounter = HIGH;
9
          lastButtonState[i] = buttonState[i];
10
        }else {
#5929489
Lesenswert?

Ich hab noch einen Vorschlag (weil mir grad langweilig war):
1
    unsigned long lastEventTime[4] = {0,0,0,0};
2
    bool lastInput[4] = {digitalRead(sensorPin[0],
3
                         digitalRead(sensorPin[1],
4
                         digitalRead(sensorPin[2],
5
                         digitalRead(sensorPin[3]};
6

7
    for (int i = 0; i < 4; i++) {
8
      unsigned long now = millis();
9
      bool input = digitalRead(sensorPin[i]);
10

11
      if (input != lastInput[i]) {
12
        lastInput[i] = input;
13
        if (now < lastEventTime[i] + interval) {
14
          // ignore
15
        }
16
        else {
17
          lastEventTime[i] = now;
18
          // do something here
19
        }
20
      }
21
      delay(299);
22
    }
Gast #5929569
Lesenswert?

Thomas W. schrieb:
1
>         if (now < lastEventTime[i] + interval) {
2
>           // ignore
3
>         }
4
>         else {
5
>           lastEventTime[i] = now;
6
>           // do something here
7
>         }
8
>       }
9
>       delay(299);
10
>     }

Dein Vorschlag hat aber ein inherentes Problem, das nach etwa 49 Tagen 
ununterbrochenen Betriebs zum Vorschein kommt. Spielt hier vielleicht 
keine Rolle, sollte aber von vornherein vermieden werden.

Wenn "interval = 2000" ist, und wenn "lastEventTime[i] = now" bei einem 
millis() Stand von z.B. (2^32 - 1000) gesetzt wird, dann wird beim 
nächsten Schleifendurchlauf in der Abfrage "if (now < lastEventTime + 
interval)" die Summe von "lastEventTime[i] + interval" =1000 ergeben. Da 
aber millis() noch keinen Überlauf hatte, wird die Abfrage gleich wieder 
nach "do something here" reinlaufen, obwohl die Zeit noch nicht 
abgelaufen ist.

Also Zeitspannen immer über eine Differenz abprüfen:

if (now - lastEventTime[i] >= interval)
#5929577
Lesenswert?

T1waz T. schrieb:
> W.S. schrieb:
>> Aber warum scheiterst du an so etwas einfachem?
>
> Gute Frage, darum habe ich hier gepostet.

Also keine gute Frage von W.S.

T1waz T. schrieb:
> Was Event Warteschlangen sind, bzw. wie ich sowas implementiere in meine
> Anforderung, weiß ich leider nicht.

Brauchst Du auch nicht zu wissen. Man muß nicht alles unnötig 
verkomplizieren.
Warteschlangen braucht man erst, wenn temporär mehr Ereignisse 
eintreffen, als gerade behandelt werden können. Z.B. ne GUI ist gerade 
damit ausgelastet, nervtötende Animationen auf das GLCD zu malen.
OP #5930732
Lesenswert?

Hab jetzt eine Kombi aus dem von MinMax und Thomas W.,
vielen Dank, scheint genau zu machen, was ich will.
1
if (now - lastEventTime[i] <= interval)
hier nur in kleiner Zeichen umgedreht.

1
void loop() {
2
  for (int i=0; i<4; i++){
3
    currentMillis = millis() + interval;
4
    input =  digitalRead(sensorPin[i]);
5
   
6
    if (input != lastInput[i]) {
7
        lastInput[i] = input;
8
        
9
        if (currentMillis - previousMillis[i] <= interval){
10
         Serial.println(" blocked:" );
11
         Serial.println(input);
12
         digitalWrite(counterPin, LOW);
13
         delay(1);
14
        }
15
        else {
16
          previousMillis[i] = currentMillis;
17
          // do something here
18
          Serial.println("counting:" );
19
          Serial.println(input);
20
          digitalWrite(counterPin, HIGH);
21
        }
22
      }
23

24
  }   
25

26
}

das
1
currentMillis = millis() + interval
habe ich gesetzt, dass nach dem einschalten sofort einmal gezählt werden 
kann,somit ist das quasi als Startwert definiert.

Bei irgendwelchen Anmerkungen oder Kritik gerne nochmals posten.

Ansonsten bedanke ich mich für die fast durchgehende sinnvolle Kritik.

Gruß
Roman
OP #5930987
Lesenswert?

war leider etwas vorschnell, da mein externer Zähler das nicht packt.
Möglicherweise das Signal zu kurz oder die Zeit dazwischen zu klein.

daher folgende Lösung
1
void loop() {
2
  for (int i = 0; i < 4; i++) {
3
    currentMillis[i] = millis() + interval;
4
    input[i] =  digitalRead(sensorPin[i]);
5
    pulseDuration = millis();
6

7
    //Zeit zwischen zwei Signalen, bevor Pin auf High gesetzt werden kann
8
    currentMillisCounter = millis();
9
    if (counter != counterOld) {
10
      if (currentMillisCounter - previousMillisCounter <= intervalCounter + pulseInterval) {
11
        //do nothing
12
      } else {
13
        previousMillisCounter = currentMillisCounter;
14
        counterOld = counterOld + 1;
15
        digitalWrite(counterPin, HIGH);
16
        pulseDurationOld = pulseDuration;
17
      }
18
    }
19
    
20
   //Länge des Signals, danach der Pin auf Low gesetzt
21
  if (pulseDuration - pulseDurationOld <= pulseInterval) {
22
    //do Nothing
23
  }else{
24
    digitalWrite(counterPin, LOW);
25
  }
26

27
  //Flankenauswertung der Lichtschranken, erhöht einen internen Zähler 
28
    if (input[i] != lastInput[i]) {
29
      lastInput[i] = input[i];
30

31
      if ((currentMillis[i] - previousMillis[i] <= interval)) {
32
        //do nonthing
33
      }
34
      else {
35
        if (input[i] == HIGH){
36
          previousMillis[i] = currentMillis[i];
37
          // do something here
38
          counter = counter + 1;
39
        }
40
      }
41
    }
42

43
  }
44

45
}
Gast #5934497
Lesenswert?

K. S. schrieb:
> seid wann hat Arduino events?

"seit".

Und:
Wenn man es will, hat es jeder µC.

Das Prinzip ist ausgesprochen einfach: Es gibt ne Event-Warteschlange 
(ein simper FIFO bzw. Ringpuffer) und einige Funktionen zum Füllen, 
Leeren, Abfragen und für Sonstiges. Gefüllt wird diese Warteschlange von 
diversen Quellen: Interrupts und sonstigen Routinen. Geleert wird sie in 
der Grundschleife, die ja zumeist in main() angesiedelt ist. Und für 
verzögerte Events (eben das, was hier gebraucht wird) braucht man noch 
irgend ein Uhrprogramm im µC, also nen Interrupt, der so alle 1..10 ms 
eine ISR aktiviert.

Siehe angehängtes Zeug. Ich hatte das wohl schon mal gepostet und 
entsinne mich, daß sich da Leute drüber aufgeregt hatten, daß ich beim 
SysTick auch eine simple Wartefunktion vorgehalten habe. Sowas ist 
nützlich für den System-Anlauf, wenn wesentliche Teile der Firmware noch 
nicht laufen, weil sie ja erstmal aufgesetzt werden müssen. Für später 
baucht man sowas dann nichr mehr.

So, mal ein Beispiel:
1
#define Totzeit  1000  // Millisekunden
2

3
#define Schranke_1  1  
4
#define Tot_1       101
5

6
// und für weitere Schranken:
7
#define Schranke_2  2
8
#define Tot_2       102
9

10
#define Schranke_3  3
11
#define Tot_3       103
12
// usw.
13

14
// Interrupt-Handler für Lichtschranke 1:
15
 if (!Exists_Delayed_Event(Tot_1))
16
 { Add_Event(Schranke_1);
17
   Add_Delayed_Event(Totzeit,Tot_1);
18
 }

So, das reicht. Ist doch ein recht kurzes Stück Quelle.
Jedesmal, wenn die Lichtschranke anspricht, wird getestet, ob die 
Totzeit schon vorüber ist. Wenn ja, dann wird ein Event "Schranke_1" in 
die Queue geworfen. Der wird dann in main() gelesen und eine 
entsprechende Aktion ausgeführt (was auch immer). Zusätzlich wird auch 
ein Event "Tot_1" mit einer Verzögerung von 1000 ms erzeugt. Solange der 
noch in der zeitabhängigen Warteschlange ist, wird die schranke 
ignoriert. Auch diesen Event kriegt main() dann ab, wenn seine Zeit um 
ist und kann dann auch darauf reagieren.

Ach ja, das angehängte Zeugs ist für 32 Bit µC's, weswegen die Events 
als 32 Bit Worte definiert sind. Für kleiner µC's kann das problemlos 
zusammengekürzt werden wenn man nur wenige Event-Typen braucht.

Ebenso kann man das Zeitraster von Millisekunden und Zeiten in long auf 
ein gröberes Zeitraster von vielleicht 10 oder 20 ms und Zeiten in int 
zusammenkürzen, was auf 8 Bittern dann weniger Ressourcen braucht.

W.S.
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren