Unverstänlicher Beispielcode führt zu Verständnisproblem. Wer kann helfen?

Hallo,

ich habe in einem Arduinobuch einen Beispielcode für einen Led-Dimmer entdeckt und versuche nun diesen Code zu verstehen. Die Hardwarekonfiguration lautet wie folgt:

2 Taster sind über Digitalpins an den Arduino angeschlossen und werden über die internen Pull up Widerstände auf High gezogen. Bei Tastendruck werde die Taster gegen Ground kurzgeschlossen, so dass der Pin zum Status Low wechselt.

Um die Taster gegeneinander zu verriegeln, wird in einer if else Schleife der Zustand beide Taster gedrückt mittels Boolescher Operatoren ausgeschlossen.

Hier ist der entsprechende Ausschnitt des Codes zur Ansicht:

void loop()
{

 if(!digitalRead(SW1)&&digitalRead(SW2)) 
 {
   if(helligkeit<255)helligkeit++;
   analogWrite(LED,helligkeit);
   delay(10);
 }
 else if(digitalRead(SW1)&&!digitalRead(SW2))
 {
   if(helligkeit!=0)helligkeit--;
   analogWrite(LED,helligkeit);
   delay(10);
 } 
 
}

Den erste IF- Block verstehe ich folgerndermaßen:

Wenn Variable SW 1 nicht wahr und Variable SW 2 wahr, dann führe Anweisungsblock aus.
Anweisungsblock: Solange die Variable "Helligkeit" < 255 erhöhe den Wert der Variablen schrittweise um 1. Warte zwischen den Schritten 10mS.

Trifft die IF-Abfrage nicht zu, dann prüfe 2. IF-Block:

  1. IF-Block: Wenn Variable SW 1 wahr und Variable SW 2 nicht wahr, führe Anweisungsblock aus.
    Anweisungsblock: Wenn Variable Helligkeit nicht gleich 0, verringere den Wert der Variablen schrittweise um 1. Warte zwischen den Schritten 10mS.

Somit wird die Led über SW 1 heller, wenn: Taster 1 gedrückt, Taster 2 nicht gedrückt und die Helligkeit kleiner dem PWM MAximum von 255 ist.
Die Led wird dunkler, wenn Taster 2 gedrückt, Taster 1 nicht gedrückt und die Helligkeit größer 0 ist.

Soweit habe ich den Ablauf ja verstanden, aber muss ich der Prüfschleife nicht auch sagen, auf was die Inhalte der Variablen geprüft werden sollen? Irgendwoher müssen die Booleschen Operatoren doch gesagt bekommen, was sie als wahr oder unwahr annehmen sollen?

Zur Quellenangabe: Der Code stammt aus dem Buch "Arduino - Microcontroller Programmieren mit Arduino/Freeduino" von Uli Sommer aus dem Franzis Verlag.
Sollte dies unter Gesichtspunkten des Copyrights ein Problem darstellen, den Code hier zu diskutieren, so bitte ich einen Moderator den Post zu löschen. Mir geht es keinesfalls darum das Copyright des Autors und Verlages zu verletzen, sondern ich blicke einfach nicht durch und benötige etwas Hilfe.

VG und besten Dank

Confused Electron

:o Das steht so im Buch? Schlechter Stil und das für ein Lehrbuch!

Sinnvoller ist das EVA Prinzip. Ich schreib das jetzt nicht als fertigen Sketch...

void loop() {
// E - Eingabe
byte sw1 = digitalRead(SW1);
byte sw2 = digitalRead(SW2);
static byte val = 0;


// V - Verarbeitung
if(sw1 && !sw2) {
if(val < 255) val++;
} else if(!sw1 && sw) {
if(val > 0) val--;
// ... nur Taster 2 gedrückt
}


// A - Ausgabe
analogWrite(Led, val);
delay(10);
}

Irgendwoher müssen die Booleschen Operatoren doch gesagt bekommen, was sie als wahr oder unwahr annehmen sollen?

Nein. Die Ausdrücke werden automatisch mit true verglichen sofern das vom Datentyp her möglich ist. Generell ist 0 false und alles andere true. Dadurch kann man alles abfragen was sich implizit in einen Integer konvertieren lässt.

Man kann == true und == false (oder in diesem Fall HIGH und LOW) auch explizit hinschreiben. Manchmal macht es das deutlicher. Komplizierte Ausdrücke werden dadurch aber eher noch schlechter lesbar. Zum Teil ist es auch einfach Geschmackssache.

If-Schleife

Ich finde den Code ok.
Könnte von mir sein.

if(!digitalRead(SW1)&&digitalRead(SW2))

if erwartet einen Boolean Wert.
digitalRead(SW1) liefert einen Boolean Wert
!digitalRead(SW1) liefert einen Boolean Wert
&& erwartet 2 Boolean Werte und das Ergebnis ist auch Boolean

Irgendwoher müssen die Booleschen Operatoren doch gesagt bekommen, was sie als wahr oder unwahr annehmen sollen?

Alle Komponenten sind vom Type bool.

Besser geht s doch gar nicht......
Das Ganze ist eine blitzsaubere Nummer.

Danke für die raschen Antworten, ich gehe mal der Reihe nach darauf ein:

sschultewolter:
:o Das steht so im Buch? Schlechter Stil und das für ein Lehrbuch!

Sinnvoller ist das EVA Prinzip. Ich schreib das jetzt nicht als fertigen Sketch...

Natürlich gibts im Buch auch einen Setup-Teil für den Sketch, den habe ich aber nicht reinkopiert um nicht komplett copy&paste mit Teilen des Buchs zu machen.

Dein Code ist zwar nochmal etwas geraffter, aber ich muss sagen, dass mir das als Anfänger zuviel Verkürzung wäre. Um erstmal durchzusteigen ist es in meinen Augen besser, alles ausführlich zu machen.
In den meisten Bücher fände ich es besser, wenn die Grundlagen und Beispiele ausführlich dargestellt wären, und es am Ende noch ein Kapitel "Wie mache ich meinen Sketch kurz, knackig und elegant" oder so gäbe, wo gezeigt wird, wie man Speicherplatz und Programmzeilen sparen kann.

Serenifly:
Nein. Die Ausdrücke werden automatisch mit true verglichen sofern das vom Datentyp her möglich ist. Generell ist 0 false und alles andere true. Dadurch kann man alles abfragen was sich implizit in einen Integer konvertieren lässt.

Man kann == true und == false (oder in diesem Fall HIGH und LOW) auch explizit hinschreiben. Manchmal macht es das deutlicher. Komplizierte Ausdrücke werden dadurch aber eher noch schlechter lesbar. Zum Teil ist es auch einfach Geschmackssache.

Hier wirds für mich deutlich: Mir hat die Grundannahme gefehlt, dass der Taster auf high mit true gleichgesetzt und betrachtet wird. Das dies quasi automatisch passiert, war mir nicht bewusst.

Hier hat das Buch eine Schwäche, dass es diese Annahme nicht explizit erwähnt, sondern nur die Booleschen Operatoren in der Übersicht darstellt, aber sonst etwas ungenau bleibt.

VG
Confused Electron

hi,

damit Du das richtig verstehst: es wird nicht "im hintergrund vom compiler" ein ==true eingefügt, sondern bei einer if-abfrage wird immer das, was in der klammer steht (der komplette ausdruck in der klammer), auf's ergebnis abgefragt.

bei

if (9==5) ist das ergebnis false, also 0

if (sw1==HIGH) ist das ergebnis false oder true, also 0 oder was anderes (das ist in verschiedenen programmiersprachen unterschiedlich, nur die 0 ist meines wissens fix.

if (sw1) ist das ergebnis LOW oder HIGH, also 0 oder 1, ergibt für das if wiederum das richtige ergebnis. oder genau genommen in diesem fall eigentlich das "falsche" ergebnis, weil wenn der schalter gedrückt wird, ein LOW, also eine 0, also ein false geliefert wird. drum muß man das in der programmlogik invertieren, damit eine gedrückte taste richtig interpretiert wird.

gruß stefan

Confusedelectron:
Dein Code ist zwar nochmal etwas geraffter, aber ich muss sagen, dass mir das als Anfänger zuviel Verkürzung wäre. Um erstmal durchzusteigen ist es in meinen Augen besser, alles ausführlich zu machen.

Es geht hier nicht um die Abkürzungen. Das kannst du auch gerne alles ausschreiben. Jedoch finde ich es vom Buch schlecht, wenn man bei den Basics schon von einem gewissen Stil abweicht, an dem man sich, wenn möglich halten soll.
Macht das ganze deutlich übersichtlicher und später einfacher, das ganze zu erweitern.
Im ersten Schritt geht es, die beiden Taster abzufragen. Die Werte speichern wir in einer Variable ab. Hier kann auch der Typ bool/boolean verwendet werden (0 oder 1). Wobei ich meine, dass selbst ein bool Wert den gleicher Speicher auf dem Arduino verbraucht.
Im Abschnitt der Verarbeitung werden diese Werte nur miteinander verglichen. Bzw == true/false haben die anderen das soweit schon korrekt beantwortet.
Erst wenn diese Schritte abgeschlossen sind, soll die Ausgabe als Ergebnis erfolgen. Die Led wird angesteuert.

Hallo,

nochmals vielen Dank für eure Unterstützung. Ich habe mir die Verriegelung der beiden Taster jetzt mal für ein anderes Projekt zu Eigen gemacht, weil ich eigentlich über genau diese Fragestellung auf das Beispiel gestoßen bin.

Allerdings hat sich daraus für mich eine neue Frage ergeben, denn ich würde die Taster natürlich auch gern entprellen.
Dazu kann man eine Abwandlung des "Blink without delay" verwenden, und ich habe dies bei einzelnen Tastern auch so schon gemacht, aber irgenwie stehe ich hier auf dem Schlauch, an welcher stelle ich die Zeilen für das Entprellen einbauen soll, damit es keine Kollision mit der Abfrage der Taster gibt?

Ihr merkt, dass ich in der Sache noch nicht sicher bin, obwohl ihr mir mit euren Erklärungen super geholfen habt, und ich so wieder etwas gelernt habe.

VG
Confused Electron

hi,

wenn's den programmablauf nicht stört, kannst Du das entprellen ruhig auch mit einem kurzen delay erledigen. also nach dem flankenwechsel ein delay(50) rein. wenn das abgelaufen ist, sollte auch das prellen vorbei sein. je nach taster auch kürzer...

gruß stefan

Hi,

ausgerüstet mit euren Beiträgen habe ich mich mal an hingesetzt und versucht die Tasterabfrage mit Verriegelung der Taster gegeneinander mit dem Entprellen unter Verwendung der millis() Funktion auszutüfteln.

Dabei herausgekommen ist dann das hier:

 //Pinvariablen
  const int SW1=3;
  const int SW2=4;
//Variablen
  unsigned long tasterdruck;
  unsigned int Latenz=30;

void setup() {
// Pin-Zuweisung und Pin-Status
  Serial.begin(9600); 
  pinMode (SW1,INPUT);
  digitalWrite (SW1,HIGH);
  pinMode (SW2,INPUT);
  digitalWrite (SW2,HIGH);
}

void loop() {
// Abfrage der Taster
  if(digitalRead(SW1==false)&&digitalRead(SW2==true))
  {
    tasterdruck=millis();
  }
  if(millis()- tasterdruck >= Latenz && digitalRead(SW1==false)&&digitalRead(SW2==true))
  {
    Serial.println("Taster 1 wurde gedrückt.");  
  }
  if(digitalRead(SW1==true)&&digitalRead(SW2==false))
  {
    tasterdruck=millis();
  }
  if(millis()- tasterdruck >= Latenz && digitalRead(SW1==true)&&digitalRead(SW2==false))
  {
    Serial.println("Taster 2 wurde gedrückt.");  
  }
}

Heute Abend habe ich leider nicht mehr die Gelegenheit den Sketch auszuprobieren, aber morgen nach der Arbeit stöpsel ich den Aufbau auf dem Breadboard mal zusammen und gucke was daraus wird.

Sollte jemand jetzt schon Kinken sehen, dann bitte darauf hinweisen.

btW: Wenn ich für etwas eine Library einbinde, wie kriege ich denn den genauen (kompletten) Befehlssatz der Library raus? Irgendwie sind viele Libraries recht dürftig kommentiert.

VG
Confused Electron

btW: Wenn ich für etwas eine Library einbinde, wie kriege ich denn den genauen (kompletten) Befehlssatz der Library raus? Irgendwie sind viele Libraries recht dürftig kommentiert.

Rein schauen!
Sezieren ...

  if(digitalRead(SW1==false)&&digitalRead(SW2==true))

Da stimmt was mit den Klammern nicht.

Du liest so nur Pin 0 ein, denn der Test (SW1==false) ist selbst false, weil SW1 nicht 0 ist.
digitalRead(false) ist das gleiche wie digitalRead(0).

Funktionen ( wie z.B. digitalRead ) werden so aufgerufen:

   digitalRead(SW1)

dies kann dann auf HIGH oder LOW ( oder meinetwegen auch auf true / false ) abgefragt werden.

if ( digitalRead(SW1) == false )

Mehrere solcher Abfragen können mit && verknüpft werden, wenn du das wirklich willst.

if ( (digitalRead(SW1) == false) && (digitalRead(SW2) == true) )

Hier sind mehr Klammern als unbedingt notwendig drin, nur zum leichteren ( :wink: ) Verständnis.

Vorsicht beim Sezieren. Was man als Befehlssatz (API) geboten bekommt ist der offzielle Teil. Und nur dieser wird gewährleistet. Sollte irgendwann mal Update kommen und man zieht sich dieses Update zur Lib, muss eine "geheime" Funktion, die man sich durchs Sezieren ausgetüftelt hat nicht mehr vorhanden sein.

Lediglich die offiziellen APIs sollten auch nach dem Update unverändert funktionieren. Das ist zwar kein Grundgesetz, soll heisen, es gibt auch Autoren die das einfach ändern, die Doku anpassen und es allenfalls als Randnotiz irgendwo erwähnen. Aber eigentlich schlechter Stil. APIs sollten immer bleiben und sich allenfalls um neue Funktionen erweitern, nicht alte Funktionen verändern.

Daher sollte man sowas erst machen, wenn man richtig tief in der Materie steckt und mehr als nur Ahnung davon hat.

In den Header schauen reicht oft. Da sind alle Prototypen drin. Anhand der Methoden-Namen, Rückgabewerte und Parameter sieht man dann schon oft wie es funktioniert

In der .cpp Datei muss man nicht unbedingt die Implementierung analysieren. Aber es gibt Libraries die da Kommentare enthalten wie die Methoden zu verwenden sind. Ist aber etwas seltener.

Hallo,

also irgendwie funktioniert die Sache nicht. Ich habe den Sketch nochmal überarbeitet und dann ausprobiert

 //Pinvariablen
  const int SW1=3;
  const int SW2=4;
//Variablen
  //unsigned long tasterdruck;
  //unsigned int Latenz=30;

void setup() {
// Pin-Zuweisung und Pin-Status
  Serial.begin(9600); 
  pinMode (SW1,INPUT);
  digitalWrite (SW1,HIGH);
  pinMode (SW2,INPUT);
  digitalWrite (SW2,HIGH);
}

void loop() {
// Abfrage der Taster
  if ( (digitalRead(SW1) == false) && (digitalRead(SW2) == true) )
  {
    Serial.println("Taster 1 wurde gedrueckt.");  
  }
  if ( (digitalRead(SW1) == true) && (digitalRead(SW2) == false) )
  {
    Serial.println("Taster 2 wurde gedrueckt.");  
  }
}

Der Arduino sendet aber die ganze Zeit nur das Serial.println("Taster 2 wurde gedrueckt."); über den seriellen Monitor, als ob er die if Abfragen überhaupt nicht machen würde.

Hardwareseitig sieht es folgendermaßen aus:

Die Taster sind an GND angeschlossen und führen zum ihrem jeweilieg Pin.

VG
Confused Electron

Der Arduino sendet aber die ganze Zeit nur das Serial.println("Taster 2 wurde gedrueckt."); über den seriellen Monitor, als ob er die if Abfragen überhaupt nicht machen würde.

Kann ich erstmal in deinem Sketch nicht sehen, und vermute daher einen simplen Verdrahtungsfehler...

Nur am Rande: "Neuerdings" gibt es   [color=green] pinMode(pin, INPUT_PULLUP); [/color] als Ersatz für

[color=maroon]pinMode(pin, INPUT);
digitalWrite(pin, HIGH);[/color]

Hallo Michael,

die Verwendung von INPUT_PULLUP hats gebracht, nun läuft der Sketch. Auch das Entprellen über millis() ließ sich erfolgreich einbinden.
Danke für den Tipp.

Im nächsten Schritt werde ich mal versuchen ein Bluetoothmodul mit SoftSerial anstelle der USB-Verbindung zu verwende.

Ich werde berichte, wie es läuft. Danke nochmals.

VG
Confused Electron