Übungsprojekt mit viel geblinke und sensoren usw. funktioniert nicht so ganz

Haloo mal wieder, ich hab mal einfach irgendwelche leds und anzeigen und senoren und potis auf ein Steckboard getan, und ein Progamm geschrieben (eine 7-Segment Anzeige, vier Leds, ein Taster, zwei mal Poti, zweimal billig-LDR). Am Anfang vom loop soll entschieden werden, ob der Taster gedrückt ist oder nicht. Wenn ja, soll auf grund von den potis in einem Abstand von logischerweise 1- 1023 ms die vierte led (pin 13) 10 sekunden lang an und aus gehen. wenn er nicht gedrückt ist, soll die Anzeige von drei bis 1 runterzählen, dann nacheinander die drei anderen leds (rod, orange, grün) aufblinken. Und am Schluss von der loop Schleife soll (also unabhängig vom Taster) Noch Lichtgsteuert was aufblinken. Funktioniert auch alles so weit (was ein Wunder!) Bis auf das mit dem Taster am Anfang. Ich habe den Schaltkreis und das Programm doppelt und dreifach nachgecheckt und sogar zusätzlich noch mit den Beispielen auf der Lerning-Abteilung dieser Seite verglichen, aber irgendwie will mir nicht in den Kopf gehen, was daran nicht stimmt. bitttttte Hilfe, hier der Code.

int ledred = 9;
int ledorange = 10;
int ledgreen = 11;
int balk1 = 2;
int balk2 = 3;
int balk3 = 4;
int balk4 = 5;
int balk5 = 6;
int balk6 = 7;
int balk7 = 8;
int button = 12;
int blinkled = 13;
int trimmer1 = A0;
int trimmer2 = A1;
int lightsensor1 = A2;
int lightsensor2 = A3;

int zeit;
int warten1;
int warten2;

void setup() {
  pinMode(ledred, OUTPUT);
  pinMode(ledorange, OUTPUT);
  pinMode(ledgreen, OUTPUT);
  pinMode(balk1, OUTPUT);
  pinMode(balk2, OUTPUT);
  pinMode(balk3, OUTPUT);
  pinMode(balk4, OUTPUT);
  pinMode(balk5, OUTPUT);
  pinMode(balk6, OUTPUT);
  pinMode(balk7, OUTPUT);
  pinMode(button, INPUT);
  Serial.begin(300);
}

void loop() {
  if (digitalRead(button) == HIGH) {
    Serial.println("~~~~~~~~~~RESET~~~~~~~~~~");
    zeit = millis();
    while(millis() <= zeit + 10000) {
      if (blinkled == LOW) {
        digitalWrite(blinkled, HIGH);
        warten1 = analogRead(trimmer1);
        delay(warten1);
      } else {
        digitalWrite(blinkled, LOW);
        warten2 = analogRead(trimmer2);
        delay(warten2);
      }
    }
  } else {
    digitalWrite(ledgreen, LOW);
    digitalWrite(balk1, HIGH);
    digitalWrite(balk2, HIGH);
    digitalWrite(balk3, HIGH);
    digitalWrite(balk6, HIGH);
    digitalWrite(balk7, HIGH);
    digitalWrite(balk4, LOW);
    digitalWrite(balk5, LOW);
    delay(1000);
    digitalWrite(balk1, HIGH);
    digitalWrite(balk2, HIGH);
    digitalWrite(balk3, HIGH);
    digitalWrite(balk5, HIGH);
    digitalWrite(balk6, HIGH);
    digitalWrite(balk4, LOW);
    digitalWrite(balk7, LOW);
    delay(1000);
    digitalWrite(balk1, HIGH);
    digitalWrite(balk7, HIGH);
    digitalWrite(balk2, LOW);
    digitalWrite(balk3, LOW);
    digitalWrite(balk4, LOW);
    digitalWrite(balk5, LOW);
    digitalWrite(balk6, LOW);
    delay(1000);
    digitalWrite(ledred, HIGH);
    digitalWrite(balk1, LOW);
    digitalWrite(balk7, LOW);
    digitalWrite(balk2, LOW);
    digitalWrite(balk3, LOW);
    digitalWrite(balk4, LOW);
    digitalWrite(balk5, LOW);
    digitalWrite(balk6, LOW);
    delay(1000);
    digitalWrite(ledorange, HIGH);
    digitalWrite(ledred, LOW);
    delay(1000);
    digitalWrite(ledgreen, HIGH);
    digitalWrite(ledorange, LOW);
    delay(1500);
  }
  
  if (analogRead(lightsensor1) + 300 < 342) {
    digitalWrite(ledred, HIGH);
    digitalWrite(ledorange, LOW);
    digitalWrite(ledgreen, LOW);
  } else if (analogRead(lightsensor1) + 300 >341 && analogRead(lightsensor1) + 300 < 683) {
    digitalWrite(ledred, HIGH);
    digitalWrite(ledorange, HIGH);
    digitalWrite(ledgreen, LOW);
  } else if (analogRead(lightsensor1) + 300 > 682) {
    digitalWrite(ledred, HIGH);
    digitalWrite(ledorange, HIGH);
    digitalWrite(ledgreen, HIGH);
  } else {
    Serial.println("ERROR: LIGHTSENSOR-INPUT IS NOT WORKING!");
  }
  
  if (analogRead(lightsensor2) + 300 < 342) {
    digitalWrite(balk1, HIGH);
    digitalWrite(balk2, HIGH);
    digitalWrite(balk3, HIGH);
    digitalWrite(balk6, HIGH);
    digitalWrite(balk7, HIGH);
    digitalWrite(balk4, LOW);
    digitalWrite(balk5, LOW);
  } else if (analogRead(lightsensor2) + 300 >341 && analogRead(lightsensor2) < 683) {
    digitalWrite(balk1, HIGH);
    digitalWrite(balk2, HIGH);
    digitalWrite(balk3, HIGH);
    digitalWrite(balk5, HIGH);
    digitalWrite(balk6, HIGH);
    digitalWrite(balk4, LOW);
    digitalWrite(balk7, LOW);
  } else if (analogRead(lightsensor2) + 300 > 682) {
    digitalWrite(balk1, HIGH);
    digitalWrite(balk7, HIGH);
    digitalWrite(balk2, LOW);
    digitalWrite(balk3, LOW);
    digitalWrite(balk4, LOW);
    digitalWrite(balk5, LOW);
    digitalWrite(balk6, LOW);
  }
  
  delay(1000);
  digitalWrite(balk1, LOW);
  digitalWrite(balk2, LOW);
  digitalWrite(balk3, LOW);
  digitalWrite(balk4, LOW);
  digitalWrite(balk5, LOW);
  digitalWrite(balk6, LOW);
  digitalWrite(balk7, LOW);
  digitalWrite(ledred, LOW);
  digitalWrite(ledorange, LOW);
  digitalWrite(ledgreen, LOW);
  
  delay(10);
  Serial.println("_FINISH_");
  Serial.println("_START PROGRAMM_");
  delay(10);
  
}

übrigens: Fehlermeldungen: 0 .

Dasichbinich: Ich habe den Schaltkreis und das Programm doppelt und dreifach nachgecheckt und sogar zusätzlich noch mit den Beispielen auf der Lerning-Abteilung dieser Seite verglichen, aber irgendwie will mir nicht in den Kopf gehen, was daran nicht stimmt. bitttttte Hilfe, hier der Code.

Hat der Button einen PullDown-Widerstand in der Schaltung? Wenn ja, OK. Wenn nein, nimm als pinMode INPUTT_PULLUP statt INPUT und teste auf LOW statt HIGH beim Betätigen.

Im übrigen funktioniert Deine "int zeit;" nur ca. 32 Sekunden lang wie gewünscht und läuft danach über.

Versuchst mal mit: unsigned long zeit;

Im übrigen funktioniert Deine “int zeit;” nur ca. 32 Sekunden lang wie gewünscht und läuft danach über.

Wieso? Ich daklariere sie doch jedes mal in void loop neu !?

Hat der Button einen PullDown-Widerstand in der Schaltung?

Ja. Funktioniert trotzdem nicht! =(
Ich habe es exakt so gebaut wie auf http://arduino.cc/en/Tutorial/DigitalReadSerial

Ich daklariere sie doch jedes mal in void loop neu !?

Nein, aber selbst wenn du sie deklarieren würdest, brächte dir das nichts:

void loop() {
  int zeit;  // Das wäre eine Deklaration
  zeit = millis(); 
  while(millis() <= zeit + 10000) {  // das ist ein Vergleich von unsigned long, 32 sec nach reset wird das nie mehr true
  }
}

Damit es auch 49 Tage nach reset immer funktioniert, wäre das hier optimal:

unsigned long zeit = millis();
while (millis() - zeit <= 10000) {
// selbst bei Überlauf eines unsigned long ist die Differenz richtig !
}

… wenn man mal davon absieht, dass es selten optimal ist, wenn loop() 10 sec hängt…

Dasichbinich:

Im übrigen funktioniert Deine "int zeit;" nur ca. 32 Sekunden lang wie gewünscht und läuft danach über.

Wieso? Ich daklariere sie doch jedes mal in void loop neu !?

Du deklarierst "zeit" nicht jedes mal in der loop neu, sondern Du weist in der loop bei jedem Durchlauf einen neuen Wert zu.

Vielleicht fängst Du erstmal ganz klein mit einer Sache an, bevor Du ganz viel Geblinke mit vielen Sensoren und LEDs machen möchtest.

Ein "int zeit" ist eine 16-Bit Variable, und millis() ist eine Funktion mit einem 32-Bit Rückgabewert ("long"). Wenn Du einen 32-Bit Wert an einen 16-Bit Variable zuweist, dann bekommt diese immer nur die "unteren 16 Bit" der 32-Bit Variablen zugewiesen.

Und ein 16-Bit "int" läuft nun mal prinzipbedingt beim maximalen Wert von 32.767 über. Erhöhst Du diesen Wert um 1, ist der nächste Wert negativ, und zwar ?32.768.

Das bedeutet, dass Deine "zeit" immer von 0 hochzählt bis 32767, dann auf einen maximalen negativen Wert von -32768 springt, dann wieder auf Null steigt, und dann wieder von vorne. Das alles "circa" einmal pro Minute.

Kleines Testprogramm, das wegen des delay(1000) eigentlich immer um ungefähr 1000 Millisekunden hochzählen sollte:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

int zeit;

void loop() {
  // put your main code here, to run repeatedly: 
  zeit=millis();  
  Serial.println(zeit);
  delay(1000);
}

Beobachte mal die Ausgabe auf dem seriellen Monitor nach Sekunde 32!

Dasichbinich: Funktioniert trotzdem nicht! =(

Wenn Du die "zeit" Variable ausreichend groß deklarierst (Vorschlag "unsigned long" statt "int"), ein PullDown Widerstand vorhanden ist, und Dein Sketch trotzdem nicht funktioniert, werden wohl noch mehr Fehler drin sein.

Um (weitgehend) fehlerfreie Programme zu schreiben, gibt es zwei Methoden: 1. Du schreibst erstmal einen ganzen Roman aus völlig irrsinnigem Code zusammen, und wenn der nicht funktioniert, beseitigst Du den ersten Fehler, den zweiten Fehler, den dritten Fehler, den vierten Fehler und so weiter, bis der Code ausreichend fehlerfrei ist. 2. Oder Du schreibst zunächst mal ein Grundgerüst mit einer Programmstruktur, unterteilst eine große Aufgabe in viele Teilaufgaben mit einzelnen Funktionen, und füllst die Funktionen dann nach und nach mit einem Teil des Codes, den Du einzeln testest. Sobald eine Funktion fehlerfrei funktioniert und getestet ist, nimmst Du Dir die nächste vor, bis das Programm nach schrittweiser Verfeinerung fertig ist und im Zusammenspiel mit allen Teilfunktionen funktioniert.

Also mehr so wie man einen Aufsatz schreibt, wo man sich zuerst eine "Gliedrung" überlegt. Bei einem einfachen Aufsatz hast Du einfach nur: - Einleitung - Hauptteil - Schluss entsprechend beim Arduino-Programm als Gliederung - Variablendeklaration - setup() - loop()

Und wenn das Programm nicht ganz trivial ist, gliederst Du eben das Programm in mehr Unterfunktionen, die jede für sich eine Teilaufgabe erledigt.

Für ein interaktives Programm, das auf Knopfdruck reagieren und irgendwas machen soll, ist es auch ungünstig, die Programmausführung immer wieder für längere Zeit anzuhalten, wie Du es mit den ganzen delays machst. Wenn Dein Button nicht gedrückt ist, läuft folgendes ab:

else {
  ...
    delay(1000);
  ...
    delay(1000);
  ...
    delay(1000);
  ...
    delay(1000);
  ...
    delay(1000);
  ...
    delay(1500);
  }

Falls also die Taste nicht gedrückt ist, macht das Programm im anderen Zweig für "nicht gedrückt" erstmal lockere 6500 ms = 6,5 Sekunden Pause und rödelt da rum. Am Ende der loop kommen dann noch drei Delays dazu.

Das bedeutet: Damit Dein Programm auf "Taste gedrückt" reagieren kann, muß die Taste tatsächlich bis zu fast 10 Sekunden am Stück gedrückt bleiben, sonst erfolgt keine Reaktion.

Lange Delays sind für interaktive Programme ein absolutes No-Go, das ist vom User-Interface niemandem zumutbar, dass er Knöpfe drückt, und nichts passiert, wenn die Knöpfe nicht lange genug gedrückt werden. Am besten gewöhnst Du Dir an, ohne die delay()-Funktion zu programmieren. Delays sind böse, und zwar für interaktive Programme genauso wie für zeitkritische Programme. Also mit Ausnahme einiger weniger Spezialfälle fast immer.

int blinkled = 13; Hier wird blinkled als Integer definiert und mit dem Wert 13 belegt, entspricht dem Pin der LED die Du willst ... pinMode(balk6, OUTPUT); pinMode(balk7, OUTPUT); pinMode(button, INPUT); Und da isser schon nicht mehr dabei, Pin 13 wird nicht als Ausgang definiert! ABER es geht noch weiter, unten ... if (blinkled == LOW) {... und jetzt fragst Du, ob blinkled, oben belegt mit dem Wert 13, dem Wert LOW entspricht?! Macht blinkled nicht, es ist immer noch 13 in blinkled gespeichert. Sicher wolltest Du den aktuellen Zustand des Ausgangs abfragen und ändern. So, weiter kommst Du sicher selbst, sieht ja gar nicht sooo schlecht aus bisher.

Ihr habt oben gesagt:

Ein "int zeit" ist eine 16-Bit Variable, und millis() ist eine Funktion mit einem 32-Bit Rückgabewert ("long"). Wenn Du einen 32-Bit Wert an einen 16-Bit Variable zuweist, dann bekommt diese immer nur die "unteren 16 Bit" der 32-Bit Variablen zugewiesen.

Und ein 16-Bit "int" läuft nun mal prinzipbedingt beim maximalen Wert von 32.767 über. Erhöhst Du diesen Wert um 1, ist der nächste Wert negativ, und zwar ?32.768.

Das bedeutet, dass Deine "zeit" immer von 0 hochzählt bis 32767, dann auf einen maximalen negativen Wert von -32768 springt, dann wieder auf Null steigt, und dann wieder von vorne. Das alles "circa" einmal pro Minute.

und:

Wenn Du die "zeit" Variable ausreichend groß deklarierst (Vorschlag "unsigned long" statt "int")

Ausreichend groß. Aber egal wie groß man sie deklariert, sie läuft doch so oder so über ?! :( Gibt es denn nicht einen Weg, sie sozusagen komplett zu löschen bevor man sie benutzt, damit sie nicht direkt nach dem 32 mal überläuft???

millis() liefert immer die verstrichene Zeit seit dem Start des Arduino in Millisekunden, da gibt es nichts zu löschen. Wenn Du aber die Variable, in der Du den millis() Wert speicherst, als unsigned long deklarierst, kommt der Überlauf erst nach knapp 50 Tagen. Ich schätze mal, dass Deine Übungsprojekte nicht so lange am Stück laufen. Und wenn man ein paar Kleinigkeiten bei der Programmierung beachtet, dann macht sogar der Überlauf keine Probleme.

Einfach als ungeschriebenes Gesetz betrachten: Rückgabewerte von millis() IMMER in unsigned long speichern

Dasichbinich: Aber egal wie groß man sie deklariert, sie läuft doch so oder so über ?!

Ja. Der millis-Timer läuft nach ca. 50 Tagen über und fängt danach wieder bei 0 an zu zählen.

Dasichbinich: Gibt es denn nicht einen Weg, sie sozusagen komplett zu löschen bevor man sie benutzt, damit sie nicht direkt nach dem 32 mal überläuft???

Du kannst Dir natürlich mit Funktionen Deine eigenen "rücksetzbaren Timer" programmieren, die dann auf 0 gehen, wenn Du es so programmierst.

Du kannst Dir auch Timer programmieren, die langsamer als im Millisekundentakt zählen. Mal angenommen, Du programmierst einen Timer, der pro Sekunde um 1 hochzählt, dann läuft ein 32-Bit Timer nicht alle 50 Tage über, sondern alle ca. 50000 Tage, was dann erst in ca. 136 Jahren der Fall sein würde.

Du kannst auch mit den überlaufenden Timern arbeiten, solange Du im wesentlichen Zeitdifferenzen zwischen zwei Ereignissen ermitteln möchtest. Bei entsprechender Programmierung funktioniert das völlig einwandfrei, solange die Ereignisse häufiger auftreten als die Timer-Überläufe, also bei millis()-Timer häufiger als ca. alle 50 Tage.

Im Endeffekt kommt es in der Digitaltechnik nur drauf an, was Du machen möchtest: Wenn Du es sauber und logisch korrekt formulieren kannst, kannst Du es auch sauber und logisch korrekt programmieren.

jurs, Danke, das wollte ich wissen. Aber ich kann nicht so ganz verstehen, warum eine Variable nicht einfach volkommen auf 0000000000000000 gesetzt werden kann, sondern immer der neue Wert "dazugepackt" wird. Außerdem, so würde man doch gar nicht so viele bits brauchen ?!!

sie läuft doch so oder so über ?! Gibt es denn nicht einen Weg, sie sozusagen komplett zu löschen bevor man sie benutzt, damit sie nicht direkt nach dem 32 mal überläuft???

Der “Trick” ist, dass ein Überlauf überhaupt nicht stört, wenn du mit unsigned arbeitest und zuerst eine Differenz bildest.
Zum einfacheren Verständnis, mit byte ( 0 … 255 )

byte x; // zählt hoch
byte start;  // merkt den Start des Intervalls
const byte interval = 100;
bool running; // true bis 100 nach Start
 
if (trigger) { start=x; running=true; }
if ( x - start >= interval ) { running = false; }

Wenn x beim Start z.B. 150 oder kleiner ist, ist alles klar: bei x=250 gilt 250-150 >= 100 : Ende

Wenn x beim Start z.B. 200 ist, passiert dies:
x start
200 - 200 = 0
255 - 200 = 55 ( < 100 ) ok
000 - 200 = 56 ( -200 == +56 bei vorzeichenlosen Bytes !!! )
001 - 200 = 57

044 - 200 = 100 (-156 == +100 bei vorzeichenlosen Bytes !!!) : Ende

Da die Differenz von zwei unsigned auch unsigned ist, geht das sogar für größere intervalle ( bis 255 im Fall von byte ).


Mit unsigned long und einem Vergleich mit millis() kannst du – wie oben im byte-Beispiel – einen großen Zeitraum
(max. 0xffffffff = 4294967295 ms = 1193 Stunden = 49 Tage)
noch nach beliebig langer Programmlaufzeit abmessen.

Das Ergebnis von millis() brauchst du nicht zurückzusetzen.

Dasichbinich: jurs, Danke, das wollte ich wissen. Aber ich kann nicht so ganz verstehen, warum eine Variable nicht einfach volkommen auf 0000000000000000 gesetzt werden kann, sondern immer der neue Wert "dazugepackt" wird. Außerdem, so würde man doch gar nicht so viele bits brauchen ?!!

Welche Variable willst Du denn auf 0 setzen? Wie ich ober schon geschrieben hatte: millis() ist eine Funktion welche die verstrichene Zeit seit dem Start des Arduino in Millisekunden als unsigned long zurückliefert.