Millis() Counter und 7-Segment Anzeige

Hallo zusammen!

Ich habe für eine Ausstellung ein interaktives Exponat gebaut was mit einem Arduino Uno gesteuert wird.
Ein Teil des Exponats ist eine 7-Segment Anzeige, welche sekundenweise von 1 bis 9 hochzählt, so lange eine Lichtschranke unterbrochen wird (licht3==LOW).
Das funktioniert auch eine Weile.
Irgendwann aber zeigt die Anzeige nur noch 0 und der Counter zählt nicht mehr. Die Lichtschranke funktioniert, der Rest des Programms auch, nur der Counter nicht.

Komischerweise hilft Neustarten des Arduino nicht um den Fehler zu beheben, nur wenn ich das Programm neu aufspiele funktioniert es wieder (eine Weile, wie lange genau konnte ich noch nicht reproduzieren).

Der dazugehörige Programmteil ist die letzte große If-Abfrage "if (countdown){...}" ganz am Ende:

#include "SevSeg.h"         //7-Segment-Anzeig Bib
#include <Servo.h>          //Servo Bib
Servo ESC;                  // Servo Objekt zur ESC-Steuerung initialisieren
SevSeg sevseg;              //Ein sieben Segment Objekt initialisieren

int potipin= A1;
int potiwert = 0;
int zahl = 10;
int gruen=11;
int gelb=12;
int rot=13;
int licht1;
int licht2;
int licht3;
int drehzahl;
int ESCwert;
bool countdown=false;
bool gewonnen=false;
int pos;
unsigned long previousMillis = 0; // speichert den Zeitpunkt an dem zuletzt geschalten wurde
const long interval = 1000;       // Länge der Pause in ms
int potimin=260;                  // Poitwert in Ruheposition (auslesen per skript)
int potimax=612;                  // Poitwert in Maximalposition (auslesen per skript)

void setup() {
   ESC.attach(0,1000,2000);    //Motorsteuerung (pin, min pulse width, max pulse width in microseconds)
   pinMode(1, OUTPUT);         //Segment b
   pinMode(2, OUTPUT);         //Segment a
   pinMode(3, OUTPUT);         //Segment f
   pinMode(4, OUTPUT);         //Segment g
   pinMode(5, OUTPUT);         //Segment e
   pinMode(6, OUTPUT);         //Segment d
   pinMode(7, OUTPUT);         //Segment c
   pinMode(11, OUTPUT);         //LED grün
   pinMode(12, OUTPUT);         //LED orange
   pinMode(13, OUTPUT);         //LED rot
   pinMode(8, INPUT_PULLUP);    //Lichtschranke 3
   pinMode(9, INPUT_PULLUP);    //Lichtschranke 2
   pinMode(10, INPUT_PULLUP);   //Lichtschranke 1
   pinMode(A0, OUTPUT);         //Drehzahlmesser
  

  //Einstellungen 7-Segment-Anzeige:
  byte numDigits = 1; //Hier wird die Anzahl der Ziffern angegeben
  byte digitPins[] = {}; //Die Pins zu den Ziffern werden festgelegt
  byte segmentPins[] = {2, 1, 7, 6, 5, 3, 4}; //Die Pins zu den Segmenten a-f werden festgelegt
  bool resistorsOnSegments = true;
  byte hardwareConfig = N_TRANSISTORS; 
  bool updateWithDelays = false; // Default 'false' is Recommended
  bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros
  bool disableDecPoint = true; // Use 'true' if your decimal point doesn't exist or isn't connected. Then, you only need to specify 7 segmentPins[]
  sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
  updateWithDelays, leadingZeros, disableDecPoint); 

   digitalWrite(gruen, HIGH);              //Grüne LED ein
sevseg.setBrightness(1); 

}

void loop() {

licht1 = digitalRead(10);                     //Lichtschranken auslesen
licht2 = digitalRead(9);
licht3 = digitalRead(8);

potiwert =analogRead(potipin);                // liest den Poti aus

drehzahl = map(potiwert,potimin+40,potimax,31,4300);      // Wert skalieren: 31...4300Hz für Drehzahlmesser
if(potiwert<potimin+10){                           // Nullstellung des Drehzahlmessers bei kleinem Potiwert erzwingen
noTone(A0);                                        // Frequenzgenerator für Drehzahlmesser aus
digitalWrite(A0, LOW);                             // Ausgang für Drehzahlmesser LOW 
}
else{
tone(A0,drehzahl);                            // Rechtecksignal an Drehzahlmesser ausgeben
}

ESCwert = map(potiwert,potimin,potimax, 0, 180);     // Wert skalieren (value between 0 and 180)
ESC.write(ESCwert);                           // Signal an ESC senden


  if(licht1==LOW){                           //Startposition erkennen
    pos=1;
    digitalWrite(rot, LOW);
    digitalWrite(gruen, HIGH);
    sevseg.blank();
    sevseg.refreshDisplay(); 
    zahl = 10;
  }                 

  if(licht2==LOW && pos==1)                 // von unten in Lichtschranke 2 fahren
  {
   digitalWrite(gruen, LOW);
   digitalWrite(gelb, HIGH);
   countdown=true;
   pos=2;
  }


if (countdown){

  if (millis() - previousMillis >= interval) {  // Falls mehr als 1000 ms vergangen sind
     previousMillis = millis();                 // Zeitpunkt der letzten Schaltung wird festgehalten 
    
     zahl--;
     sevseg.setNumber(10-zahl);                       // Zahl für Segmentanzeige setzten: hochzählen: (10-zahl) , runterzählen: (zahl)
     sevseg.refreshDisplay();                      // Anzeige aktualisieren
      

     if(zahl==0){                               //Countdown abgelaufen
        digitalWrite(gruen, HIGH);
        digitalWrite(gelb, LOW);
        //sevseg.blank();                       // Segmentanzeige zurücksetzen
        sevseg.setNumber(9-zahl);               //Letzte Zahl anzeigen
        sevseg.refreshDisplay();
        countdown=false;
        gewonnen=true;
        zahl=10;
        
     } 
     
  }

  if (licht3==LOW){                       //Linie wird überfahren
    countdown=false;
    //sevseg.blank();                       // Zahl für Segmentanzeige zurücksetzen
    //sevseg.refreshDisplay();
    zahl=10;
    gewonnen=false;
    pos=3;
    digitalWrite(gelb, LOW);
    digitalWrite(rot, HIGH);
    ESC.write(40);                        //Motor langsamer werden lassen
    delay(500);
    ESC.write(0);                         // Motor aus
    noTone(A0);                           // Rechecksignal aus
    digitalWrite(A0, LOW);                // Ausgang auf LOW
    delay(2000);
    while(potiwert>potimin+10){           // Warten bis hebel in Nullstellung zurückgebracht wurde
      potiwert =analogRead(potipin);      // Poti wieder aktiveren
      delay(100);
      }

  }
  if (licht2==HIGH){                      //Countdown bereich wird nach unten verlassen
    countdown=false;
    sevseg.blank();                       // Zahl für Segmentanzeige setzten
    sevseg.refreshDisplay();
    zahl=10;
    gewonnen=false;
    pos=1;
    digitalWrite(gelb, LOW);
    digitalWrite(gruen, HIGH);
  }

  }

    



}


hast du Schaltschema oder Bilder?

Alle diese Variablen sind local und verfallen am ende von setup()

Hier übergibst du die variablen.

Nach der Übergabe verfallen die Variablen und deine Siebensegment arbeitet mit illegalen Daten.
Das fliegt dir um die Ohren, sobald der Speicherbereich von anderen Funktionen genutzt wird.

Edit:
Scheint doch ok zu sein.
Siehe Post #9

Danke für den Hinweis. Wie kann ich das beheben?

      if (zahl == 0) {                           //Countdown abgelaufen
        digitalWrite(gruen, HIGH);
        digitalWrite(gelb, LOW);
        //sevseg.blank();                       // Segmentanzeige zurücksetzen
        sevseg.setNumber(9 - zahl);

wenn "zahl" ist 0 dann zeigen 9 - 0, darf man einfach 9 schreiben?

btw
зображення

so steht auch im Lib Beispiel

ok...
Gerade nachgesehen: https://github.com/DeanIsMe/SevSeg/blob/master/SevSeg.cpp#L169
Die Arrays werden wirklich kopiert!
Ich muss meine Aussage korrigieren. Das ist wohl nicht der Fehler.
(auch wenn es ein recht unkonventionelles Verfahren ist ....)

es ist ein Problem mit dem Ablauf des Programs.
Man erkennt eine Aktion mit Lichtschranken und damit es nicht zweimal aufgenommen ist, setzt man "pos" höher ein, als entsprechende Reaktion, aber nur an einer Stelle wird es uberhaupt geprüft, hier:

if (licht2 == LOW && pos == 1)

Ich würde die Pins 0 und 1 frei lassen (für mögliche upload) und stattdessen zB die Pin A2 und A3 (pin D16 und D17).

Was macht der Transistor an pin A0?

Grüße Uwe

Der Transistor an pin A0 steuert einen analoges Drehzahlmessinstrument an. Hier wird ein Rechteck (EDIT: kein Sinus) mit variabler Frequenz über die Funktion tone() ausgegeben welcher die Zeigerposition steuert.

Tone macht keinen Sinus sondern ein Rechtecksignal mit definierter Frequenz.

if (licht2 == LOW && pos == 1)

Die Variable pos (für Position) dient dazu, um zu erkennen aus welcher Richtung in die Lichtschranke 2 eingefahren wird.

Zum Verständnis:

Es ist ein Wagen auf einer Schiene der auf seinem Weg 3 Lichtschranken durchfährt. Beim Durchfahren der Lichtschranke 2 soll der Counter laufen, so lange die Lichtschranke unterbrochen ist.

Wie beschrieben, das ganze funktioniert auch ohne Probleme für eine ganze Weile, bis der Counter nicht mehr zählt. Die restlichen Teile des Programms (Lichtschranken auslesen, LEDs schalten,...) funktionieren auch dann weiterhin, nur der Counter zeigt "0".

Also entwerder bei millis() oder sevseg stimmt irgendwas nicht.

Ich hatte die Vermutung dass da irgend eine Variabe überläuft o.ä.

Stimmt! Rechteck, und kein Sinus.

nein, definitiv nicht, weil:

Mit dem Risiko, dass ich wieder falsch liege.....

Mir lässt die Initialisierung in setup() keine ruhe....
Und siehe da, einen Fehler gefunden.

Das führt hier dazu, dass über das Ende des leeren Arrays hinweg gelesen wird!

Denn in der Lib findet sich:

  for (uint8_t digitNum = 0 ; digitNum < numDigits ; digitNum++) {
    digitPins[digitNum] = digitPinsIn[digitNum];
  }

  // Set the pins as outputs, and turn them off
  for (uint8_t digit = 0 ; digit < numDigits ; digit++) {
    pinMode(digitPins[digit], OUTPUT);
    digitalWrite(digitPins[digit], digitOffVal);
  }

Da wird also irgendein Pin auf Output gesetzt. Und keiner weiß welcher.

Es scheint also wirklich in der sevseg zu liegen.

Ich habe ja nur eine Ziffer und nutze den Digit Pin nicht, sondern die gemeinsame Anode (danke für den Hinweis @kolaha) der Anzeige liegt auf +12V.
Ich könnte natürlich einen unbelegten Analogpin nehmen, z.B. A2 um das Array zu füttern. Spricht was dagegen? An der Außenbeschaltung werde ich möglichst nichts mehr ändern müssen.

Von Software Seite aus nicht.

Nööö.. es ist deine Initialiaiserung.
In dem Punkt ist die Lib unschuldig.

Obwohl man das hätte besser/stabiler/sicherer gestalten können.
Hier ist es die alte dumme C Notwendigkeit beides, einen Zeiger auf das Array und die Anzahl Elemente, übergeben zu wollen. Das fordert Fehler quasi heraus.
In C++ geht das auch anders.

Somit:
Eine alte Idee, hat dir ein Fettnäpfchen aufgestellt und du bist prompt da rein getappt.

Ob woanders noch irgendwelche Böcke versteckt sind, kann ich dir nicht sagen.
Also: Keine Gewähr für gar nix.

Danke, ich werdes probieren.
Leider ist die Ausstellung einige Autostunden entfernt und ich will nicht 2mal hinfahren müssen. Ich bau die Schaltung jetzt nochmal nach und versuche den Fehler zu reproduzieren und dann zu beheben.

Alternativ zu sevseg bliebe noch eine eigene kleine Funktion für die Ausgabe der Zahl auf der Segment-Anzeige die mit switch case die Segmente ansteuert. Ist ja bei einer Ziffer überschaubar.

Z.B. sowas: 7-Segment Display Interfacing with Arduino UNO | Arduino

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