Bedingungen verknüpfen

Hallo liebe Gemeinde,

ich bin schon wieder Tage mit einem Thema beschäftigt und komme ohne Hilfe nicht weiter.
Könnte mir bitte jemand den entscheidenden Hinweis geben was ich falsch mache :frowning:

Ich habe folgende Funktion geschrieben

void position_0()
{
 unsigned long currentMillis = millis(); 

 if (SPEED1 < 120 || SPEED1 > 150)
 {
  POSITION_0_TIMER_READY = false;
  digitalWrite(POSITION_0_TIMER, LOW);
 }
    if(SPEED1 >= 120 && SPEED1 <= 150)
    {
      POSITION_0_Millis = currentMillis;
      POSITION_0_TIMER_READY = true;
    }
        if(SPEED1 >= 120 && SPEED1 <= 150 && POSITION_0_TIMER_READY)
        {
          if ((unsigned long)(currentMillis - POSITION_0_Millis) >= turnOffDelay) 
          {
            digitalWrite(POSITION_0_TIMER, HIGH);
          }
      }
}

Hier soll bei SPEED1 < 120 oder SPEED1 > 150 eine Variable auf false gesetzt werden.(FUNKTIONIERT)
Bei SPEED1 <=120 und SPEED1 <= 150 selbe Variable auf true gesetzt werden.(FUNKTIONIERT)
Dann wollte ich gerne, wenn SPEED immer noch >=120 und SPEED1 <= 150 ist, dass ein “Timer” mit Millis abläuft und danach etwas anderes (in dem Falle einfach eine LED) geschaltet wird.
So wie ich den Code gepostet habe schaltet er die Variable zwar auf true (habe es im serial monitor mal ausgegeben) auf true, fährt aber mit der Schleife nicht fort.

Wenn ich nun die Bedingungen der ersten SPEED1 Abfrage wie folgt änder, läuft es mit “Timer” durch.

void position_0()
{
 unsigned long currentMillis = millis(); 

 if (SPEED1 < 120 || SPEED1 > 150)
 {
  POSITION_0_TIMER_READY = false;
  digitalWrite(POSITION_0_TIMER, LOW);
 }
    if(SPEED1 >= 120 && SPEED1 <= 122)
    {
      POSITION_0_Millis = currentMillis;
      POSITION_0_TIMER_READY = true;
    }
        if(SPEED1 >= 120 && SPEED1 <= 150 && POSITION_0_TIMER_READY)
        {
          if ((unsigned long)(currentMillis - POSITION_0_Millis) >= turnOffDelay) 
          {
            digitalWrite(POSITION_0_TIMER, HIGH);
          }
      }
}

Hier ist aber die Bedingung nicht wie ich sie haben möchte, da so nach Überschreiten von 150 zwar auf false gesetzt wird, aber zurück eben erst wenn >=120 und <=122 erreicht wurde.

Ich wäre wirklich dankbar für jeden Hinweis!!!

DANKE im Voraus und Gruß

Wie ist SPEED1 definiert? (Typ)

PanTau78:

....

if(SPEED1 >= 120 && SPEED1 <= 150)
    {
      POSITION_0_Millis = currentMillis;
      POSITION_0_TIMER_READY = true;
    }

Durch POSITION_0_Millis = currentMillis; wird ständig die Zeit auf Anfang gesetzt, da das jedes mal, wenn SPEED1 zwischen 120 und 150 ist, die Zeit neu gesetzt wird. Dein Zeitinterval fängt also gar nicht erst an zu laufen. Es musste die Bedingung noch erweitert werden, so dass POSITION_0_Millis = currentMillis; nur einmalig ausgeführt wird, wenn POSITION_0_TIMER_READY vorher false war.
Also

....
    if((SPEED1 >= 120) && (SPEED1 <= 150) && (POSITION_0_TIMER_READY ==false))
    {
      POSITION_0_Millis = currentMillis;
      POSITION_0_TIMER_READY = true;
    }
....

Unabhängig vom schon Gesagten würde ich dazu raten den Code besser zu strukturieren. Das geht mit sauberen Einrückungen los, schon weil das die Lesbarkeit erhöht:

void position_0()
{
	unsigned long currentMillis = millis(); 

	if (SPEED1 < 120 || SPEED1 > 150)
	{
		POSITION_0_TIMER_READY = false;
		digitalWrite(POSITION_0_TIMER, LOW);
	}
    
	if(SPEED1 >= 120 && SPEED1 <= 150)
	{
		POSITION_0_Millis = currentMillis;
		POSITION_0_TIMER_READY = true;
	}

	if(SPEED1 >= 120 && SPEED1 <= 150 && POSITION_0_TIMER_READY)
	{
		if ((unsigned long)(currentMillis - POSITION_0_Millis) >= turnOffDelay) 
		{
			digitalWrite(POSITION_0_TIMER, HIGH);
		}
	}
}

Dann würde ich mal drüber nachdenken was Du da eigentlich für Bedingungsregeln definiert hast und wie die in Verbindung stehen. Warum zwei if-Abfragen mit exakt negierten Bedingungen? Es gibt ja noch einen else-Zweig.

Und warum dann nochmal eine dritte if-Abfrage mit wieder identischen Bedingungen, nur um eine Zusatzbedingung erweitert? Dann lege die doch in den entsprechenden if-Zweig rein und frage nur die Zusatzbedingung ab.

Man versteht dann auch besser was eigentlich passieren soll:

void position_0()
{
	unsigned long currentMillis = millis(); 

	if (SPEED1 < 120 || SPEED1 > 150)
	{
		POSITION_0_TIMER_READY = false;
		digitalWrite(POSITION_0_TIMER, LOW);
	}
	else
	{
		POSITION_0_Millis = currentMillis;
		POSITION_0_TIMER_READY = true;

		if(POSITION_0_TIMER_READY)
		{
			if ((unsigned long)(currentMillis - POSITION_0_Millis) >= turnOffDelay) 
			{
				digitalWrite(POSITION_0_TIMER, HIGH);
			}
		}
	}
}

Der Code dürfte dadurch auch kompakter und schneller werden.

Man sieht dann auch, das POSITION_0_TIMER_READY, falls es nirgendwo anders genutzt wird, eigentlich ganz überflüssig ist bzw. keine Auswirkungen hat, da es nur direkt nach dem Setzen auf true auf Selbiges geprüft wird. Damit könnte das und das Prüfungs-if weg. Da fällt einem dann auch auf, das POSITION_0_Millis immer direkt mit currentMillis() gefüllt wird, bevor die Differenzprüfung ausgeführt wird, die so nie zu einem gültigen Ergebnis führen kann.

hajos118:
Wie ist SPEED1 definiert? (Typ)

int SPEED1 = 0;

@ Theseus, vielen Dank. Das funktioniert einwandfrei!!!

@Vieledinge, auch Dir danke für die Anmerkungen. Ich werde mir das mit dem Einrücken hinter die Ohren schreiben und in Zukunft so umsetzen.

Zum Thema Bedingungen, ich benötige diese leider, da ich noch mehrere Funktionen mit anderen Werten (SPEED1) habe die abgefragt werden. Also quasi andere Geschwindigkeitsbereiche, andere Aktionen.
Ich hoffe ich konnte es verständlich rüberbringen.

Tausend Dank mal wieder, ihr seid wirklich Weltklasse :slight_smile:

Gruß

PanTau78:
...
@Vieledinge, auch Dir danke für die Anmerkungen. Ich werde mir das mit dem Einrücken hinter die Ohren schreiben und in Zukunft so umsetzen.

Das mit der Lesbarkeit ist auch für mich immer wieder ein Problem, weil ich z. B. häufig nicht nachvollziehen kann, wo ein Codeblock anfängt und aufhört.

PanTau78:
... ihr seid wirklich Weltklasse :slight_smile:

Nu übertreib’ nich so :slight_smile:

Gruß

Gregor

Ich werde mir das mit dem Einrücken hinter die Ohren schreiben und in Zukunft so umsetzen.

Dann hätte ich auch noch einen Tipp:

Werten (SPEED1)

Variablen durchzunummerieren ist nicht unbedingt eine gute Idee.
Verwechselungen drohen.
Man erkennt nicht am Namen, was es bedeuten soll.
Hat die andere Variable mit der 1 hinten dran, einen Zusammenhang mit SPEED1?

Meines Erachtens nach ist es besser mit Strukturen/Klassen und Arrays zu arbeiten.
So klebt zusammen, was zusammen gehört.

Styleguide:
Man schreibt Variablen klein.
Konstanten werden groß geschrieben.

combie:
Meines Erachtens nach ist es besser mit Strukturen/Klassen und Arrays zu arbeiten.
So klebt zusammen, was zusammen gehört.

Das ist für viele der einfachen Arduino Aufgaben erst mal der beste Weg. Variablen die zusammengehören in ein struct. Dann ein Array daraus. Und mit einer for-Schleife darüber iterieren.

Leider habe ich festgestellt, dass es dann bei structs erst mal heißt "kenne ich nicht" und/oder "ist zu kompliziert" :confused:

Leider habe ich festgestellt, dass es dann bei structs erst mal heißt "kenne ich nicht" und/oder "ist zu kompliziert" :confused:

Tröste dich, eine Zeit lang dachte ich als Nicht-C-Experte auch, dass Structs und Classes ein Buch mit 7 Siegeln wäre... Viele Jahre der (fast ausschließlich aber gut funktionierenden) prozeduralen Programmierung hatte mich stets davon abgehalten - bis ich mich vor ein paar Wochen mal 3..4 Stunden damit befasst hatte. Einfach so und nur zum Reinschnuppern. Heute ärgere ich mich, dass ich auf den OOP-Zug nicht schon vor Jahren aufgesprungen bin. Viiieeeles hätte ich mir damit extreeem vereinfachen können... :confused: Ich stehe damit zwar erst am Anfang - aber was sich damit abzeichnet ist enorm.

Ein struct hat an sich noch nicht zwangsläufig etwas mit OOP zu tun. Die gab es schon in C und ähnliche Dinge gab es schon Jahrzehnte vorher (seit COBOL), i.d.R. "record" genannt. Das ist nur einen winzigen Schritt von den Basis-Datentypen entfernt und sollte eigentlich zum Grundwissen gehören.

Ein struct braucht in vielen Fällen keinen Konstruktor. Also hat man mit den meisten Feinheiten von Klassen nichts zu tun. Es ist eine Ansammlung von Datentypen. Ja, in C++ können structs auch alles was Klassen können, aber das ist dann ein anderer Anwendungsfall und braucht erst mal nicht zu interessieren.

Außer dadurch dass standardmäßig alle Elemente und Methoden öffentlich sind, unterscheiden sich class und struct nicht.

Im Moment sauge ich in diesem Bereich alles auf wie ein trockener Schwamm und lerne täglich neue Feinheiten dazu. Die Sache mit den Records kenne ich aus Object-Pascal, ebenso Klassen, Methoden, Eigenschaften. Aber es ist ein großer Unterschied, ob man lediglich bestehende Klassen / Records ergänzt wie z.B. unter Delphi - oder ob man solche Dinge von Grund auf neu erschafft, und eventuell sogar in eigene Arduino-Bibliotheken z.B. als Sammlung eigener wiederverwendbarer Funktionen packt. Aber sehr interessant ist es auf alle Fälle.

Whandall:
Außer dadurch dass standardmäßig alle Elemente und Methoden öffentlich sind, unterscheiden sich class und struct nicht.

Natürlich, aber in diesem Zusammenhang reden wir von sowas in der Art:

struct Sensor
{
   int pin;
   float temp;
};

Oder z.B. Pin und Zeiten für das Blinken einer LED. Dafür braucht man sonst nichts über Klassen zu wissen.

Eventuell ein Konstruktor. Aber wenn man Methoden hinzufügt sollte man gleich class nehmen. Auch wenn es vom Programm her sonst keinen Unterschied macht. Und selbst dann hat das noch nicht zwangsläufig was mit den komplexeren Eigenschaften von OOP zu tun die viele Leute abschrecken.

So ein struct würde auch keinen Header haben, sondern einfach in einer .ino oder einer anderen .cpp Datei stehen.

Ist einfach Geschmacksache.

Gefühlsmäßig beschreibt eine struct für mich eher einen Speicherbereich den man irgendwo vorfindet oder erzeugen muss, wo es auf die Positionen und Reihenfolgen der Elemente ankommt. class (auch mit nur öffentlichen Elementen und Methoden) ist (wieder nur für mich) eher 'OOP'.

Ich habe noch nie versucht eine struct Hierachie zu benutzen, aber auf einem Arduino
- ohne Alignment Probleme - ginge wahrscheinlich auch das recht gut.

Schön dass man beim Arduino immer alles einfach testen kann

struct header {
  byte type;
  byte to;
  byte from;
};
struct packet : public header {
  byte payload[32-sizeof(header)];
} Pack;

void setup() {
  Pack.type = '!';
  Pack.to = '*';
  Pack.from = '1';
  Pack.payload[0] = 0x99;
}

void loop() {
}

struct Hierachieen akzeptiert der Compiler also ohne Probleme.

Bei struct denke ich erstmal nicht an Methoden.
Bei class überlege ich, ob man die internen Datenstrukturen tatsächlich public machen sollte.

Dass beides bei c++ eigentlich dasselbe ist, ist eigentlich eher ein Nebeneffekt.

In Datenstrukturen denken ist der erste Schritt in Richtung OOP, da gebe ich RudiDL5 recht ...

RudiDL5:
... eigene Arduino-Bibliotheken z.B. als Sammlung eigener wiederverwendbarer Funktionen packt. Aber sehr interessant ist es auf alle Fälle.

Es lohnt sich unbedingt, sich da tiefer einzuarbeiten. Ich habe mir inzwischen mehr als Bibliothek gestrickt, die ich in fast allen neuen Basteleien benutze (z. B. die neulich erwähnte Piepliothek). Es ist irre, was sich auch mit sehr kleinen und einfachen Dingen erreichen oder erleichtern lässt (und mit sehr geringem Ressourcenverbrauch) .

Gruß

Gregor

Es lohnt sich unbedingt, sich da tiefer einzuarbeiten

Das kann ich nur so unterschreiben... auch wenn es ein paar Stunden Denkarbeit gekostet hatte.

Ich baue mir zur Zeit ebenfalls eine Art "Standard-Bibliothek" mit Objekten auf, die ich immer wieder brauche - wie z.B. Start-/Stopp-Timer, Tastenentprellung, Uhrwerk und all so Sachen. Selbstnatürlich ohne delay(x)... Letzendlich kann ich dadurch die Quelltexte für neue Projekte bzw. "Projektchen" auf ein paar wenige Methodenaufrufe reduzieren, wie ich sie aus Delphi gewöhnt bin und liebgewonnen habe. Und ich habe Platz im Kopf für das "eigentliche" Problem.

Der entscheiden "Tritt" in diese Richtung kam, als ich verstehen wollte, wie LiquidCrystal.h eingentlich funktionierte. Ich habe diese Bibliothek Stück für Stück auseinander genommen und in reine Funktionen umgewandelt. Dadurch (und in Verbindung mit hust Inline-Assembler) konnte ich den Speicherverbrauch für "Hallo Welt" von ursprünglich 2086 Bytes auf mickerige 820 Bytes reduzieren.

Ob es sinnvoll oder flexibel ist sei dahingestellt - mir ging es um den Lerneffekt - und der ist hierbei gekommen und fesselt mich nun für Objekte & Co.

Gruß, Rudi