Arduino Forum

International => Deutsch => Topic started by: someuser on Mar 06, 2018, 07:03 pm

Title: Code funktioniert nur manchmal
Post by: someuser on Mar 06, 2018, 07:03 pm
hi,


Ich habe meinen code mal versucht wie vorgeschlagen zu ändern.

zwei Beispiele unten,
das erste funktioniert reibungslos, das zweite geht ein paar mal hintereinander, ein paar mal nicht,1
dann geht´s wieder  :smiley-confuse:  :smiley-confuse:

im seriellen Monitor kann ich die Eingabe der Zahlen verfolgen, und auch das
Ausgang a oder b geschaltet wird. das gehrt mal 3 mal fünfmal hintereinander, dann
sehe ich nur noch den code, aber es springt nicht weiter. nach ein paar weiteren versuchen
geht es wieder 1-2 mal nach einem hardwarereset auch meistens.

Habe das Gefühl das Programm verhaspelt sich beim codereset.


Kurze Zwischenfrage zu:
static byte counter = 0;
Warum muss es ein static byte sein, kein int, bzw warum  funktioniert int nicht?

und return false ist doch das gleiche wie return 0?


hier die codes:

Code: [Select]

#include <Keypad.h>

#define LED 12

char masterCode[] = "123456";
char limitedCode[]= "112233";

const byte ROWS = 4; // vier Reihen
const byte COLS = 4; // vier Spalten
// Define the Keymap
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 2, 3, 4, 5 };
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 6, 7, 8, 9 };


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


boolean validCode()
{
 static byte counter = 0;
 static char code[sizeof(masterCode)];
 char key = keypad.getKey();
 if (!key) return false; // keine Taste gedrückt, kann nicht gültig sein
 if (counter==0) memset(code,0,sizeof(code)); // clear code string
Serial.println(key); // comment out if not needed for debugging
 switch (key)
 {
   case '*': counter=0; // start new code
             break;
   case '#': if (strcmp(code,masterCode)==0) // maybe correct code?
             {
               counter=0;
               return true; // correct masterCode was entered
             }
             if (true==true) // hier die limitierende Bedingung einsetzen
             {
               if (strcmp(code,limitedCode)==0) // mayby the limitedCode?
               {
                 counter=0;
                 return true; // yes, the limitedCode is correct
               }
             }
             break;
   default : code[counter]=key; // just add the keystroke to the code
             if (counter<sizeof(code)-1) counter++;
 
 }
 return false; // es wurde kein gültiger Code eingegeben 
}


void setup(){
 Serial.begin(9600);
 pinMode(LED,OUTPUT);
 digitalWrite(LED,HIGH);
}
 
void loop(){
 if (validCode()) 
 {
   Serial.println("VALID CODE");
 
   
     digitalWrite(LED,LOW);
     delay(2500);
     digitalWrite(LED,HIGH);
     
   
 }
}


Code: [Select]

#include <Keypad.h>

#define LED1 12
#define LED2 11

char masterCode[] = "123456";
char limitedCode[] = "112233";

const byte ROWS = 4; // vier Reihen
const byte COLS = 4; // vier Spalten
// Define the Keymap
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 2, 3, 4, 5 };
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 6, 7, 8, 9 };


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


int validCode()
{
static byte counter = 0;
 static char code[sizeof(masterCode)];
 char key = keypad.getKey();
 if (!key) return false; // keine Taste gedrückt, kann nicht gültig sein
 if (counter==0) memset(code,0,sizeof(code)); // clear code string
Serial.println(code); // comment out if not needed for debugging
   Serial.println(key); // comment out if not needed for debugging
 switch (key)
 {
   case '*': counter=0; // start new code
             break;
   case '#': if (strcmp(code,masterCode)==0) // maybe correct code?
      {
        counter=0;
        return 1; // correct masterCode was entered
      }
        if (true==true) // hier die limitierende Bedingung einsetzen
      {
        if (strcmp(code,limitedCode)==0) // mayby the limitedCode?
        {
          counter=0;
          return 2; // yes, the limitedCode is correct
        }
      }
      break;
    default : code[counter]=key; // just add the keystroke to the code
      if (counter<sizeof(code)-1) counter++;
 }
  if (counter == 0) memset(code,0, sizeof(code)); // clear code string
 return false; // es wurde kein gültiger Code eingegeben
}


void setup(){
 Serial.begin(9600);
 pinMode(LED1,OUTPUT);
  digitalWrite(LED1, LOW);
  pinMode(LED2, OUTPUT);
 digitalWrite(LED2,HIGH);
 
}

void loop(){

 if (validCode()==1)
{


        Serial.println("aVALID CODE");

        digitalWrite(LED1, HIGH);
        delay(1500);
        digitalWrite(LED1, LOW);

}

  if (validCode()==2)

{
       Serial.println("bVALID CODE");
 
        digitalWrite(LED2, LOW);
        delay(1500);
        digitalWrite(LED2, HIGH);

 }       
}
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 06, 2018, 07:26 pm
Hallo,

du liest und vergleichst einzelne Ziffern und nicht die Zahlenfolge.
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 06, 2018, 08:15 pm
verstehe ich nicht ganz.
an welcher stelle?

ich sehe im monitor ja eigentlich den ganzen code und er wird
auch nur akzeptiert wenn er vollständig ist.
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 06, 2018, 08:59 pm
validCode() solltest du in loop() nur einmal aufrufen. Speichere den Rückgabewert in einer Variablen. Dann kannst du diese mehrmals vergleichen

Dann hast du für den Rückgabewert den Datentyp boolean, aber Werte von 0-2. Das geht nicht. boolean kann nur 0 oder 1 sein. Verwende Byte als Rückgabewert wenn du mehr als true/false brauchst!
Und um den Werten sprechende und aussagekräftige Namen zu geben gibt es enums.


Quote
Kurze Zwischenfrage zu:
static byte counter = 0;
Warum muss es ein static byte sein, kein int, bzw warum  funktioniert int nicht?
Lokale statische Variablen behalten ihren Inhalt nach dem Verlassen der Funktion


Hier sollte beides in die if-Abfrage
Code: [Select]

    default : code[counter]=key; // just add the keystroke to the code
      if (counter<sizeof(code)-1) counter++;

Sonst kannst du die letzte Ziffer immer wieder überschreiben
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 06, 2018, 09:35 pm
Quote
validCode() solltest du in loop() nur einmal aufrufen. Speichere den Rückgabewert in einer Variablen. Dann kannst du diese mehrmals vergleichen
hab irgendwo gelesen das das zwar nicht schön ist aber geht.
glaube aber inzwischen auch, das so wie ich das aufrufe keine gute idee ist.

Quote
Dann hast du für den Rückgabewert den Datentyp boolean, aber Werte von 0-2.
hab in dere 2. version eigentlich auf int gestellt. warum byte statt int? wegen weniger speicher?

Quote
Hier sollte beides in die if-Abfrage
Code: [Select]

    default : code[counter]=key; // just add the keystroke to the code
      if (counter<sizeof(code)-1) counter++;

Sonst kannst du die letzte Ziffer immer wieder überschreiben
das versteh ich nicht ganz.so:?
Code: [Select]

default :
if(counter<sizeof(code)-1)
{
code[counter]=key; // just add the keystroke to the code
counter++
};


wo genau ist der unterschied?
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 06, 2018, 09:40 pm
hab in dere 2. version eigentlich auf int gestellt. warum byte statt int? wegen weniger speicher?
Int geht auch. Aber du hast hier keine negativen Zahlen.

Int nimmt man bei sowas um mit 0-255 eine Anzahl oder einen Wert zurückzugeben und -1 für "nichts" oder "ungültig". Vergleiche z.B. Serial.read()

Quote
das versteh ich nicht ganz.so:?
Gehe es im Kopf mal durch, dann sollte das doch sofort auffallen. Bei deiner Version wird eine Ziffer am Ende des Array eingetragen, auch wenn es voll ist. Du verhinderst zwar dass man darüber hinaus schreibt, aber die letzte Stelle wird immer überschrieben wenn man weiter drückt. Das verursacht keinen Fehler, aber es ist einfach nicht intuitiv.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 06, 2018, 10:16 pm
Hallo,

sorry das ich den Code nicht richtig gelesen habe, dass das in switch-default abläuft habe ich übersehen.
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 06, 2018, 11:50 pm
Quote
Bei deiner Version wird eine Ziffer am Ende des Array eingetragen
eigentlich hab ich den hier irgendwo geklaut ::)


Quote
dass das in switch-default abläuft habe ich übersehen
das hat ne ziemliche weile gedauert bis ich das raus hatte...


werds morgen nochmal testen
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 07, 2018, 03:38 pm
Hallo,

die letzte Stelle immer überschreiben würde ich sogar hier für nicht falsch halten. Damit bekommt eine fremde Person nicht die Anzahl der Ziffern mit die überhaupt abgefragt werden. Falls jemand blind probiert.

Das case-switch würde ich jedoch zum Zustandsautomaten umbauen mit richtigen Namen, wie WARTEN, READ, START, ENDE oder sonstwas was leserliches und die Start/Ende Abfrage # & # mit in key beim einlesen abfragen. Je nachdem würde hier dann der Zustand geändert. Stichwort enum.
Der Code springt also nicht wie mit if ins case sondern er verweilt im letzten case Zustand.
default würde ich "frei" lassen zum Fehler abfangen. Wenn man das fürs einlesen verwendet hat man keine Möglichkeit mehr Fehler abzufangen. Jedensfalls nicht so einfach. Für jeden benötigten Zustand sollte es ein case geben. Wenn dann etwas schief geht, was nie geplant war kann das mit default abgefangen werden.

Das wären so meine Gedanken.
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 07, 2018, 11:44 pm
habs jetzt soweit am laufen, tnx.

hab noch ne frage zu einer erweiterung des codes.
mache da morgen am besten nen neuen thread zu.
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 08, 2018, 07:10 pm
tja, zu früh gefreut ::)


im Prinzip funktioniert es.
Es soll nach erstmaligem akzeptieren des codes, für weitere 10 sekunden
möglich sein mit einer beliebigen taste den Ausgang nochmal zu schalten.

Das geht soweit auch, wenn man eine taste innerhalb der 10 sek drückt wird geschaltet
und nach Ablauf der Zeit geht es nicht mehr.
drückt man allerdings nicht während dieser Zeit, kann man beliebig später durch einen
beliebigen Tastendruck den Ausgang schalten.
Die Schleife wird dann scheinbar nicht verlassen.

jemand eine idee?

Code: [Select]

#include <Keypad.h>

#define Tor1 11
#define Tor2 12

char masterCode[] = "123456";
char limitedCode[] = "112233";

const byte ROWS = 4; // vier Reihen
const byte COLS = 4; // vier Spalten
// Define the Keymap
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 2, 3, 4, 5 };
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 6, 7, 8, 9 };


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


int validCode()
{
  static byte counter = 0;
  static char code[sizeof(masterCode)];
  char key = keypad.getKey();
  if (!key) return false; // keine Taste gedrückt, kann nicht gültig sein
  if (counter == 0) memset(code, 0, sizeof(code)); // clear code string
  Serial.println(code); // comment out if not needed for debugging
  Serial.println(key); // comment out if not needed for debugging
  switch (key)
  {
    case '*': counter = 0; // start new code
      break;
    case '#': if (strcmp(code, masterCode) == 0) // maybe correct code?
      {
        counter = 0;
        return 1; // correct masterCode was entered
      }
      if (true == true) // hier die limitierende Bedingung einsetzen
      {
        if (strcmp(code, limitedCode) == 0) // mayby the limitedCode?
        {
          counter = 0;
          return 2; // yes, the limitedCode is correct
        }
      }
      break;
    default : code[counter] = key; // just add the keystroke to the code
      if (counter < sizeof(code) - 1) counter++;
  }
  if (counter == 0) memset(code, 0, sizeof(code)); // clear code string
  return false; // es wurde kein gültiger Code eingegeben
}


void setup() {
  Serial.begin(9600);
  pinMode(Tor1, OUTPUT);
  digitalWrite(Tor1, HIGH);
  pinMode(Tor2, OUTPUT);
  digitalWrite(Tor2, HIGH);

}


void loop() {

  byte schluessel = validCode();
  unsigned long startzeit = millis();

  switch (schluessel) {
    case 1:
      Serial.println("Master Code");
      Serial.println("aVALID CODE");

      digitalWrite(Tor1, LOW);
      delay(800);
      digitalWrite(Tor1, HIGH);


      while (millis() - startzeit <= 10000)
      {
label2:
        char taste = keypad.getKey();

        if (!taste) goto label2; // keine Taste gedrückt, kann nicht gültig sein
        Serial.println("Fehler Anfang while");
        if (taste == 'A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9')
          Serial.println(taste);


        digitalWrite(Tor1, LOW);
        delay(800);
        digitalWrite(Tor1, HIGH);


      };

      // mach was bei Master Code
      break;

     
    case 2:
      Serial.println("Limited Code");
      Serial.println("bVALID CODE");

      digitalWrite(Tor2, LOW);
      delay(800);
      digitalWrite(Tor2, HIGH);

      while (millis() - startzeit <= 10000)
      {
label3:
        char taste = keypad.getKey();

        if (!taste) goto label3; // keine Taste gedrückt, kann nicht gültig sein
        Serial.println("Fall 2");
        if (taste)
          Serial.println(taste);

        digitalWrite(Tor2, LOW);
        delay(800);
        digitalWrite(Tor2, HIGH);

      };
      // mach was bei Limited Code
      break;
  }
}

Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 08, 2018, 07:26 pm
Goto hat bis auf sehr wenige, sehr spezielle Ausnahmen in einem Programm nichts verloren

Mache wie oben gesagt einen Zustandsautomaten daraus. Dann hast du diese Probleme nicht. Jeder Fall/Case ist dann der aktuelle Zustand. Aber statt in einem Zustand irgendwie durch Schleifen zu bleiben läuft loop() einfach immer durch


Das macht nicht was du denkst:
Code: [Select]

if (taste == 'A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9')

Naiv musst du jedes Zeichen einzeln vergleichen

Schau dir die ASCII Tabelle an. Und verinnerliche dir dass ASCII Zeichen auch nur Integer sind. Dann sollte dir klar werden dass man die auch mit <= und >= auf Bereiche abfragen kann
Title: Re: Code funktioniert nur manchmal
Post by: michael_x on Mar 08, 2018, 08:08 pm
Quote
Goto hat bis auf sehr wenige, sehr spezielle Ausnahmen in einem Programm nichts verloren
... und wenn du schon mit millis() arbeitest (sehr schön), solltest du dir auch while - Schleifen abgewöhnen, damit es einfacher wird.

Kannst ja mal zum Spass eine LED diese 8 Sekunden lang leuchten lassen. Das wäre dann der Zustand, in dem die Zusatz-Taste gültig wäre.
Title: Re: Code funktioniert nur manchmal
Post by: postmaster-ino on Mar 08, 2018, 08:23 pm
Hi

Wieder was gelernt - goto gibt's hier auch noch :o

MfG
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 08, 2018, 08:36 pm
Quote
Goto hat bis auf sehr wenige, sehr spezielle Ausnahmen in einem Programm nichts verloren
:D  dachte mir das das kommt.

im original Programm wurde die if bedingung durch return 0 beendet, das hat mich aber unerwarteter weise nicht  an den anfang von while geworfen, sondern wohl an den anfang der ganzen funktion. wusste nicht wie ich das ohne große änderungen hinbekomme und auf den ersten blick scheint es zu funktionieren.

Quote
Das macht nicht was du denkst:
Code: [Select]

if (taste == 'A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9')
Hab im inet grade nur das gefunden:
Quote
this is c++, the conditions will all be checked until the first one fails the condition, and then it will fall out.
heisst das sobald eine taste "nicht true" ist beendet die if funktion? geht aber irgendwie...

im zweiten case habe ich die ganzen tastenabfragen weggelassen, war mir aber nicht sicher ob man
sich damit nicht irgendwelche undefinierten zustände einfängt. wäre diese Methode besser?


Quote
Kannst ja mal zum Spass eine LED diese 8 Sekunden lang leuchten lassen. Das wäre dann der Zustand, in dem die Zusatz-Taste gültig wäre.
wenn die zeit in while unter 10 sek. ist, spingt die funktion dann nach:
Quote
char taste = keypad.getKey();

        if (!taste) goto label3; // keine Taste gedrückt, kann nicht gültig sein
        Serial.println("Fall 2");
        if (taste)
und bleibt da hängen?

würde do while() gehen?

Quote
... und wenn du schon mit millis() arbeitest (sehr schön), solltest du dir auch while - Schleifen abgewöhnen, damit es einfacher wird.
generell keine while schleifen? warum das?
apropos millis(): laufe ich in Probleme nach 50 tagen oder gehts einfach von vorne los?
ist unsigned long lang genug?
Title: Re: Code funktioniert nur manchmal
Post by: Tommy56 on Mar 08, 2018, 08:42 pm
generell keine while schleifen? warum das?
apropos millis(): laufe ich in Probleme nach 50 tagen oder gehts einfach von vorne los?
ist unsigned long lang genug?
Du hast doch schon eine immer wiederkehrende Schleife: loop

Zum millis-Überlauf wurden hier im Forum schon mehrere Diskussionen bis zum endgültigen Beweis geführt. Nutze einfach die Suchfunktion.

Gruß Tommy
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 08, 2018, 08:47 pm
im original Programm wurde die if bedingung durch return 0 beendet, das hat mich aber unerwarteter weise nicht  an den anfang von while geworfen, sondern wohl an den anfang der ganzen funktion. wusste nicht wie ich das ohne große änderungen hinbekomme und auf den ersten blick scheint es zu funktionieren.
Du willst einen Zustandsautomaten

Quote
this is c++, the conditions will all be checked until the first one fails the condition, and then it will fall out.
Das ist du falsch verstanden. 'A' || 'B' ist true

Quote
würde do while() gehen?
Denke anders! Komme davon ab irgendwo stehen bleiben zu wollen. Deine Schleife ist loop(). Dann machst du pro Durchlauf nur das was zu tun ist. Oder nichts.
Title: Re: Code funktioniert nur manchmal
Post by: postmaster-ino on Mar 08, 2018, 08:55 pm
Hi

Mit millis() prüfst Du, ob genug Zeit vergangen ist, um den nächsten Schritt zu machen.
Natürlich kannst Du weiterhin while-Schleifen benutzen, nur nicht für's Warten!
Wenn Du millis wie folgt benutzt:
millis()-letzte_Startzeit>=Wartezeit
Passiert auch in 47,batsch Tagen Nichts Außergewöhnliches!
Die Werte sollten alle unsigned (vorzeichenlos) long (32bit) sein, damit der Compiler nicht meckert.
Wenn Dir eine Wartezeit von 255ms reicht, könnte Wartezeit (bzw. klein geschrieben, da man Variablennamen klein beginnt, KONSTANTEN komplett groß und noch ein/zwei Besonderheiten, auf Die man sich Mal geeinigt hat, damit man den Code besser lesen kann) auch den Typ byte bekommen (ist 'von Haus aus' vorzeichenlos) ist 8bit breit und kann Werte von 0...255 annehmen.
Hierbei könnte aber der Compiler meckern, da die Differenz zweier 32-bit-Zahlen mit einer 8-bit-Zahl verglichen werden soll.

Mit Deinem 'A' || 'B' bekommst Du IMMER true, da beide Werte != Null sind und true oder true ist true.

'A' müsste 65 sein (@ ist 64 ;) ), Du kannst also 'A'-'E' mit 65...69 gleich setzen.
Bei den Zahlen, '0' ist 0x30 (hexadezimal, die letzte Ziffer entspricht der Wertigkeit unseres Zahlensystem, nebenbei) oder 48 in Dezimal.
'0'...'9' wäre dann 48...57.

Oder Du benutzt switch case:
Code: [Select]

switch (variable){
  case 'A':
  case 'B':
  ...
  case '7':
    //deinen Code, Der ausgeführt werden soll, wenn die Taste A, B, ..., oder 7 war.
}

WICHTIG hierbei: nach dem Einsprung (dem passendem case) werden ALLE folgenden Befehle abgearbeitet, bis break; gefunden wird, oder die switch-Abfrage zu ende ist.
Wenn also bei 'A' was Anderes als bei 'B' gemacht werden soll, muß am Ende vom 'A'-Bereich ein break; stehen.

MfG
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 08, 2018, 09:05 pm
Am kürzesten geht es so:
Code: [Select]

if ((taste >= 'A' && taste <= 'D') || taste == '*' || taste == '#' || (taste >= '0' && taste <= '9') )


Oder so:
Code: [Select]

switch (taste)
{
   case 'A' ... 'D':
   case '0' ... '9':
   case '*':
   case '#':
}



Aber wichtiger ist erst mal den grundlegenden Ablauf des Programms zu ändern hin zu einem Zustandsautomaten. Komplett anders muss es nicht sein. Ein switch/case ist ja da. Das muss nur richtig verwendet werden. Also erst mal auf den aktuellen Zustand abfragen. Und dann - wenn nötig - auf einen Tastendruck.
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 08, 2018, 09:11 pm
puh das muss ich mir morgen mal alles live anschauen.

würde denn:

if (taste == ('A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'))
machen was ich denke?


und was macht
Quote
if (taste)
?
auf den ersten blick funktioniert es so wie ich denke und schaltet im richtigen case weiter soblad irgendeine taste gedrückt wird.
Title: Re: Code funktioniert nur manchmal
Post by: michael_x on Mar 08, 2018, 09:20 pm
Quote
würde
if (taste == ('A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'))
machen was ich denke?
Natürlich nicht. Wenn ich das richtig sehe, meinst du
if (taste != 0) // wenn irgendeine Taste gedrückt wurde
Title: Re: Code funktioniert nur manchmal
Post by: Tommy56 on Mar 08, 2018, 09:22 pm
if (taste == ('A' || 'B' || 'C' || 'D' || '*' || '#' || '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'))
Das widerspricht den Bedingungsregeln.
Andere Tasten hast Du doch überhaupt nicht. Damit ist diese Abfrage sinnlos.
Da sollte if (taste) ausreichen, weil das Keypad 0 (==false) liefert, wenn keine Taste gedrückt wurde.

Gruß Tommy
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 08, 2018, 09:24 pm
Quote
if (taste)
Macht genau was da steht. 0 ist false und alles ungleich 0 ist true. Das geht in dem Fall weil dir egal ist welche Taste gedrückt würde.

Aber bei sowas mit negativen Zahlen aufpassen, welche auch true sind. Es gibt auch mal Libraries die für "nichts" -1 zurück geben. Bei KeyPad ist es aber 0.

Das ist aber wie gesagt nicht dein Hauptproblem :)
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 08, 2018, 10:12 pm
Quote
Natürlich nicht. Wenn ich das richtig sehe, meinst du
if (taste != 0) // wenn irgendeine Taste gedrückt wurde
ich wollte wenn irgendeine taste gedrückt ist, völlig egal welche, das die bedingung erfüllt ist.

ich will vergleichen steht in der variablen "taste" entweder A oder B oder C usw.

Quote
Es gibt auch mal Libraries die für "nichts" -1 zurück geben.
wegen solcher eventualitäten mit denen ich mich nicht auskenne wollte ich nicht nur
eben genau die Werte nehmen die auch physikalisch exisiteren.
Quote
Das ist aber wie gesagt nicht dein Hauptproblem :)
mein hauptproblem ist eigentlich alles - da fang ich lieber mal ganz unten an zu verstehn^^

Title: Re: Code funktioniert nur manchmal
Post by: Tommy56 on Mar 09, 2018, 10:12 am
Gib Dir doch einfach mal im Loop auf den seriellen Monitor aus, was in taste steht. Dann siehst Du doch ganz einfach, was Deine Lib zurück gibt, wenn keine Taste gedrückt wurde.

Gruß Tommy
Title: Re: Code funktioniert nur manchmal
Post by: someuser on Mar 09, 2018, 06:22 pm
Quote
Gib Dir doch einfach mal im Loop auf den seriellen Monitor aus, was in taste steht. Dann siehst Du doch ganz einfach, was Deine Lib zurück gibt, wenn keine Taste gedrückt wurde.
hab ich hier und da schon mal gemacht, funzt ganz gut.
mir ging es eher darum zustände zu vermeiden die ich nicht absehen kann.


im moment funktioniert das Programm - hab einfach den goto sprung eine zeile höher vor die if bedingung gemacht - eigentlich zu einfach. mal sehn wies läuft.

Title: Re: Code funktioniert nur manchmal
Post by: Tommy56 on Mar 09, 2018, 06:41 pm
Gewöhne Dir Goto gleich wieder ab. Es gibt << 1% an Problemlösungen, in denen ein Goto sinnvoll sein könnte. In allen anderen Anwendung ist es einfach Murks.

Gruß Tommy
Title: Re: Code funktioniert nur manchmal
Post by: noiasca on Mar 09, 2018, 08:08 pm
OT: Tommy:
Real Programmers aren't afraid to use GOTO's. (http://web.mit.edu/humor/Computers/real.programmers)

bestimmt bekannt, aber das muss jetzt einfach sein ;-)

Title: Re: Code funktioniert nur manchmal
Post by: Tommy56 on Mar 09, 2018, 08:14 pm
OT: Tommy:
Real Programmers aren't afraid to use GOTO's. (http://web.mit.edu/humor/Computers/real.programmers)

bestimmt bekannt, aber das muss jetzt einfach sein ;-)
Kenne ich ;)
Aber der Weg vom Neuling zu "Real Programmers" ist sehr weit.

Gruß Tommy
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 09, 2018, 08:36 pm
Ich mag die extreme Ablehnung von Gotos auch nicht.
Denn bei den wenigen Anwendungen, wo es lohnend ist, macht es keinen Sinn, sich den Blick von Dogmen versperren zu lassen.

Gerade endliche Automaten, wie Parser usw. sind potentiell gute Kandidaten für eine Goto Anwendung.


:o  :o  :o  :o  :o


Aber wie immer:
> Wer einmal kapiert hat, wie ein Hammer funktioniert,
> für den sieht jedes Problem, wie ein Nagel aus.
Und das wollen wir doch nicht!
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 09, 2018, 08:58 pm
Ich habe ja gesagt dass ein paar spezielle Anwendung gibt wo es seine Berechtigung hat. Aber hier wird es klar als unnötige Krücke verwendet.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 09, 2018, 09:58 pm
Ja,,,,,
Hier wird das Goto, etwas suboptimal, als Schleifenersatz, genutzt.

Wobei:
Das ist eigentlich genau die Stelle, ziemlich auf den Punkt getroffen, wo man Nebenläufigkeit brauchen können würde. Zumindest würde ich so denken.
Und damit ist (computed) Goto dann wieder voll im Rennen.

Viele Möglichkeiten hat man an der Stelle nicht, wenn man einen Automaten bauen will/muss.

1. irgendwas mit Enum Switch usw.
2. Functionpointer
3. Ein geschachteltes if else Gewirre
4. oder eben Goto


Man nimmt eben das, was schöner und praktischer ist.
!Ein Hoch auf Übersichtlichkeit und Wartungsfreundlichkeit!
Es ist auch eine Geschmacksfrage.
Title: Re: Code funktioniert nur manchmal
Post by: michael_x on Mar 09, 2018, 10:06 pm
Quote
Aber wie immer:
> Wer einmal kapiert hat, wie ein Hammer funktioniert,
> für den sieht jedes Problem, wie ein Nagel aus.
Mal ganz ehrlich, ist hier nicht jedes Problem ideal für einen endlichen Automaten (Nachtwächter) ?

Eine Variante des goto ist übrigens das return; mitten im Code, mit dem man einfach an den Anfang von loop zurückspringt. Das wird eher toleriert und nur von echten Puristen wie ein goto ausgebellt.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 09, 2018, 10:35 pm
Quote
Mal ganz ehrlich, ist hier nicht jedes Problem ideal für einen endlichen Automaten (Nachtwächter) ?
Meiner Ansicht nach, sind die meisten, wenn nicht sogar alle, funktionierenden Arduino Programme endliche Automaten.
Und dabei ist es völlig egal, ob der Ersteller das wollte, oder nicht.

Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 09, 2018, 11:57 pm
Hallo,

>95% sind Zustandsautomaten würde ich behaupten.

Nur goto halte ich hier für völlig fehl am Platz. Schon weil sich die Lesbarkeit in Größenordnungen verschlechtert. Ein sauberes switch-case mit klaren enum Namen und gut ist. Dann ist der Code auch Jahre später einfach wartbar.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 10, 2018, 12:35 am
Quote
Schon weil sich die Lesbarkeit in Größenordnungen verschlechtert.
Goto verschlechtert die Lesbarkeit?
Wie macht es das?


Das sehe ich nicht so.


Mit jedem Sprachmittel kann man Mist bauen, ohne Zweifel!
Aber das ist doch dann nicht die Schuld des Sprachmittels...

Und wenn ich mal so ehrlich sein darf:
Hier im Forum habe ich schon viel mehr hässliche Switch/Case Konstrukte gesehen, als Goto Einsätze.

Ganz nebenbei:
Bei größeren Konstrukten bietet das Goto gar einen Vorteil in Sachen Codegröße und Performance.
Bei kleinen Konstrukten wird  Switch/Case weitestgehend optimiert. Aber ab einer bestimmten Größe ist Schluss damit.

Das soll alles keine Werbung für den sinnfreien Goto Einsatz sein!
Sondern nur mein Beitrag gegen Dogmatismus.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 10, 2018, 01:06 am
Hallo,

wenn man switch case richtig macht, indem sich die enum Zustandsvariable nur innerhhalb switch-case verändert, dann ist das in Sachen Übersichtlichkeit nicht zu überbieten. Man kann ordentlich lesbare Zustandsnamen verwenden und ordentliche Funktionsnamen die dann aufgerufen werden. Das hilft die Funktionsweise wesentlich schneller zu überblicken.

Mit goto muss man erst das Label im gesamten Code suchen um zu wissen wo es weitergeht und was dort weiter geht.
Ich hätte gern ein konkretes Bsp. gewusst wo goto sinnvoll ist. Ich kann es mir derzeit nicht vorstellen.

Performance, da lese ich leider gegenteiliges, Abschnitt unten.
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/008_c_kontrollstrukturen_012.htm (http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/008_c_kontrollstrukturen_012.htm)
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 10, 2018, 01:19 am
Ich hätte gern ein konkretes Bsp. gewusst wo goto sinnvoll ist. Ich kann es mir derzeit nicht vorstellen.
Fehlerbehandlung und Resourcenfreigabe am Ende einer Funktion:
https://blog.regehr.org/archives/894 (https://blog.regehr.org/archives/894)

Wird z.B. im Linux Kernel sehr oft verwendet

Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 10, 2018, 05:52 am
Performance, da lese ich leider gegenteiliges, Abschnitt unten.
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/008_c_kontrollstrukturen_012.htm (http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/008_c_kontrollstrukturen_012.htm)
Es tut mir leid, das sagen zu müssen:

Erstens:
Das ist ein C Buch, keine C++
OK, das hat wenig Auswirkungen an der Stelle... aber computed Goto kann altes C nicht.

Zweitens:
Das mit der Performance ist in dem Buch gelogen. Oder, zumindest nicht belegt. Du darfst diese Unwahrheit glauben. Aber weitererzählen, tue es bitte nicht. Denn sonst bezichtige ich dich der bewussten Lüge. Des bewussten weitererzählens dieser Unwahrheit .

Ok ok, vielleicht ist es ja keine bewusste Lüge von dem Autor, sondern nur ein Irrtum. Aber eigentlich gehören auch Irrtümer aus Dogmatismus auf den Haufen der Geschichte. Irrtümer und Lügen unterscheiden sich nur durch die dahinter stehende Absicht. Die Folgen sind nahezu identisch.

Drittens:
Das sich der Autor vom Dogmatismus gefangen fühlt hört man schon an dem Satz:
> Ich habe mir lange überlegt, ob ich Goto überhaupt in diesem Buch erwähnen soll, ....

Ein Fachbuchautor, welcher wegen solcher Gedanken mit sich hadert, ist an der falschen Stelle unterwegs. Unterdrückung und Zensur von Sprachmitteln in Sprachfachbüchern, ist so ziemlich das schlimmste, was man lernenden antun kann. Meiner Meinung nach.


---

Mache dir doch bitte bewusst, dass dieser anti Goto Dogmatismus aus einer Zeit stammt, als Subroutinen und Funktionen noch ein Fremdwort waren. Oder gerade eingeführt wurden. Der  anti Goto Dogmatismus stammt nicht aus C, oder gar aus C++, sondern ist ein Basic stämmiger  anti Goto Dogmatismus.
Goto war eine unabwendbare, absolute Notwendigkeit, alter Basic Dialekte.
Das hat mit uns hier doch gar nichts zu tun

Vergleichbares Argument:
Schokolade ist für Menschen giftig, weil ein Hund an einer Tafel Zartbitter stirbt, sterben kann.
Das mit dem Hund ist richtig. Aber die Übertragung auf den Menschen ist falsch. Denn, anderer Stoffwechsel.
Ein Informaltionsübertragungsfehler im Kopf.


Liebster Doc_Arduino, wärest du nicht selber diesem Dogma unreflektiert erlegen, dann würdest du das Performance Argument selber prüfen. Dass du nicht mal auf die Idee kommst, belegt deine Gläubigkeit.

---

Weiterhin:
Ja, mit Goto kann man Mist bauen.
Aber ein verantwortungsvoller Umgang damit ist möglich.
Das gilt für jedes Sprachmittel.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 10, 2018, 11:29 am
Hallo,

das gezeigte Bsp. von Serenifly macht Sinn, sehe ich ein, in dem Fall kürzerer und besser lesbarer Code mit goto.

Ich verweigere mich nicht grundsätzlich Neuen, ich habe nur immer erstmal eine klare Meinung zu einem Thema. Kennt ihr ja.  :)   Wenn dann irgend jemand um die Ecke kommt und was anderes behauptet und ich verstehe es nicht oder kann es erstmal nicht nachvollziehen, dann frage ich nach und bohre nach. Genau das kann manchmal unbequem sein. Manchmal filtert das Unwissende von Wissenden heraus.  :)  Auch ich bin in der Lage meine Meinung zu ändern, man glaubt es kaum, nur ändere ich diese nicht auf Grund von dahin gewurfenen weiteren Behauptungen. Ansonsten fördert es oder sollte es das tiefere Verständnis fördern um das um was es geht. Gleichzeitig werden die Erklärungen immer besser und verständlicher.

ruhiges WE euch allen ...


Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 10, 2018, 03:34 pm
Hier ist ein guter Artikel zur Performance von computed goto und wieso switch langsamer ist:
https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables (https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables)

Das ist aber auch ein spezieller Fall. computed goto ist nicht das gleiche wie ein normales goto. Hier braucht man die extra Geschwindigkeit nicht (außerdem gibt eine keine branch prediction die etwas beschleunigen könnte). Und man sollte verstehen was man macht und wieso.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 10, 2018, 07:18 pm
Quote
Und man sollte verstehen was man macht und wieso.
Eigentlich soll Computed Goto Distanzen berechnen können.
Damit relative Gotos ermöglichen.
Das gilt für unseren GCC, noch als broken.(soweit mir bekannt)

Also eine Warnung:
Computed Gotos verwenden, ok.
Aber bitte keine Rechenoperationen auf den Lable Adressen ausführen und dann anspringen.
Das kann ins Auge gehen.

//----

Übrigens, eine Suche über meinen SketchOrdner, inklusive Libraries und Hardwaredefinitionen, führte über 1300 Gotos ans Licht.
Auch, oder eher gerade, in namhaften Libraries.

//----

Du möchtest ja Beispiele sehen......
Hier ein Doppelblinker Beispiel, aus meiner Wühlkiste, für einen kleinen endlichen Automaten mit Goto:
Code: [Select]

const byte taster     = 2;

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(const bool blinkAnforderung)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
      if(!blinkAnforderung) return;
      digitalWrite(ledPin,HIGH);
      sprungZiel = &&HellPhase;
      zeitMerker = millis();
   
    HellPhase:
      if(millis()-zeitMerker<hellPhase) return;
      digitalWrite(ledPin,LOW);
      sprungZiel = &&DunkelPhase;
      zeitMerker = millis();
 
    DunkelPhase:
      if(millis()-zeitMerker<dunkelPhase) return;
      sprungZiel = 0;
  }
 
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};


Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13,100, 500},
                          {12,500,1000},
                        };




void setup()
{
  pinMode(taster,INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
}

void loop()
{
  bool blinkAnforderung = !digitalRead(taster);
  for(Blinker &blinker:blinkGruppe) blinker.run(blinkAnforderung);
}


Ob das jetzt ein gutes Beispiel ist, kann ich nicht sagen. Aber dennoch ist es mit Goto und dabei recht übersichtlich.

Bei der Verwendung von enum Switch/case würde der Compiler versuchen eine Sprungtabelle anzulegen.
Dann würden sich erstmal keine Performance Vorteile, für Goto, ergeben.

Aber:
Wenn die case nicht fortlaufend definiert sind, oder die Anzahl ein gewisses Maß überschreitet, dann generiert der Compiler ein Konstrukt, welches einer langen if Kette entspricht. Dann dauert es eben diese Anzahl Vergleiche, bis der betreffende Case erkannt und ausgeführt wird. Natürlich, je weiter ein Fall hinten steht, desto länger dauert es dann bis zur Ausführung.




Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 10, 2018, 07:53 pm
Fehlerbehandlung und Resourcenfreigabe am Ende einer Funktion:
https://blog.regehr.org/archives/894 (https://blog.regehr.org/archives/894)

Wird z.B. im Linux Kernel sehr oft verwendet
Weil der Linux Kernel in C geschrieben ist.

C++ Programmierer benutzen zumindestens für die Resourcen bevorzugt Konstruktoren/Destruktoren.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 10, 2018, 08:25 pm
Quote
C++ Programmierer
Richtige C++ Programmierer würden wohl Exceptions nutzen.

Aber das dürfen wir AVRler leider nicht.


Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 10, 2018, 08:41 pm
Das würde die Fehlerbehandlung abdecken, was zumindestens auf den kleinen Arduinos nicht geht,
weil die einfach zu wenig Speicher haben.

Auf einem ESP32 dürften Exceptions durchaus benutzbar sein.

Auf Microcontrollern ist OOP auf alle Fälle nützlich und sinnvoll einsetzbar.

Nur goto halte ich hier für völlig fehl am Platz. Schon weil sich die Lesbarkeit in Größenordnungen verschlechtert.
Ein sauberes switch-case mit klaren enum Namen und gut ist. Dann ist der Code auch Jahre später einfach wartbar.
Dem kann ich nur zustimmen.  :D

Bei geeigneten (zusammenhängenden) Auswahlwerten erzeugt der Kompiler ein computed goto.
Das ist bei enums überwiegend der Fall.
Weiterhin kann der Kopiler feststellen, wenn man einen der Fälle vergessen hat.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 12:40 am
Hallo,

combie, meinste nicht auch das dein goto mit einem switch case nicht deutlich klarer lesbar wäre? Bist du nicht der der lesbaren Code vorzieht ...  :)   duck und weg   :)   Ist nur Spass, ist dein Code.

Computed goto kenne ich nicht und befinde mich damit außerhalb meines Wissensstandes. Interessant zu wissen.

if hatte ich schon einmal gegen switch case antreten lassen. Wenn ich mich recht erinnere wurde if mit jedem Vergleich langsamer und switch case war immer konstant gleich schnell/langsam, nur abhängig von der Summe der case Vergleiche. if kann man dafür wiederum von Hand optimieren in dem man die Reihenfolge der Vergleiche vertauscht. Welcher Vergleich wahrscheinlich öfterer zuschlägt wie andere.

Meine Bedenken sind das die Gefahr besteht, wenn das hier Neulinge lesen, die gewöhnen sich goto an weil es bequem ist und dann endet das in Unübersichtlichkeit. Selbst mit einer Batchdatei (Windows) erlebt. Zwangsweise goto zur Ablaufsteuerung verwendet, bin bald blöde gewurden im wilden Gehüpfe.

Ich sage mal so. Ich weiß jetzt das es hier und da sinnvoll sein kann. Nur selbst habe ich es noch nie benötigt und ich denke ich werde es auch nicht benötigen.  :)   Stand heute.  Ich befasse mich dann doch lieber immer weiter mit leserlicheren Code zu schreiben für mich und für andere. Auch meine switch case kann ich hier und da noch aufräumen. Es hat eben jeder seine Herangehensweise. Ich nehme gern neue Dinge zur Kenntnis, schau mir das an und entscheide dann für mich alleine ob ich das anwende/verwende oder auch nicht. In dem Fall hier wie man sieht nicht.

Zitat aus "Shooter".   "langsam ist präzise und präzise ist schnell"    :)

In diesem Sinne allen einen ruhigen Sonntag.
Title: Re: Code funktioniert nur manchmal
Post by: Serenifly on Mar 11, 2018, 12:47 am
if hatte ich schon einmal gegen switch case antreten lassen. Wenn ich mich recht erinnere wurde if mit jedem Vergleich langsamer und switch case war immer konstant gleich schnell/langsam,
Selbstverständlich. Die Zeit ist konstant weil switch/case i.d.R. als Sprungtabelle umgesetzt wird. Allerdings gehen die schnellen RJMPs (relative jump) "nur" +/- 2kB.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 12:55 am
Hallo,

noch einer wach.  Mit 2kB meinst du den Speicherbereich in dem gesprungen wird oder darf die angelegte Sprungtabelle nicht 2kB belegten Speicher überschreiten?
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 12:04 pm
Quote
combie, meinste nicht auch das dein goto mit einem switch case nicht deutlich klarer lesbar wäre? Bist du nicht der der lesbaren Code vorzieht ...  :)   duck und weg   :)   Ist nur Spass, ist dein Code.
Dann zeige doch mal wie du einen solchen Doppelblinker bauen würdest...
Dann können wir das nebeneinander halten und an der Sache die "Schönheit", "Eleganz", "Wartungsfreundlichkeit" und "Übersichtlichkeit" bewerten.

Vielleicht kann ich ja von dir noch was lernen!
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 12:46 pm
Ich finde eine Version mit switch und enum ist besser zu lesen, aber schau selbst
Code: [Select]
void run(const bool blinkAnforderung) {
  switch (aktuell) {
    case Start:
      if (!blinkAnforderung)
        return;
      digitalWrite(ledPin, HIGH);
      aktuell = Hell;
      zeitMerker = millis();
    case Hell:
      if (millis() - zeitMerker < hellPhase)
        return;
      digitalWrite(ledPin, LOW);
      aktuell = Dunkel;
      zeitMerker = millis();
    case Dunkel:
      if (millis() - zeitMerker >= dunkelPhase)
        aktuell = Start;
  }
}


Hier noch mal alles zusammen
Code: [Select]
enum Zustand {
  Start,
  Hell,
  Dunkel,
};

const byte taster = 2;

class Blinker
{
  protected:
    const byte ledPin;
    const unsigned long hellPhase;
    const unsigned long dunkelPhase;
    unsigned long zeitMerker = 0;
    Zustand aktuell = Start;

  public:
    Blinker(const byte ledPin, const unsigned long hellPhase, const unsigned long dunkelPhase)
      : ledPin(ledPin), hellPhase(hellPhase), dunkelPhase(dunkelPhase) {}

    void run(const bool blinkAnforderung) {
      switch (aktuell) {
        case Start:
          if (!blinkAnforderung)
            return;
          digitalWrite(ledPin, HIGH);
          aktuell = Hell;
          zeitMerker = millis();
        case Hell:
          if (millis() - zeitMerker < hellPhase)
            return;
          digitalWrite(ledPin, LOW);
          aktuell = Dunkel;
          zeitMerker = millis();
        case Dunkel:
          if (millis() - zeitMerker >= dunkelPhase)
            aktuell = Start;
      }
    }

    void init() {
      pinMode(ledPin, OUTPUT);
    }
};

Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
  {13, 100, 500},
  {12, 500, 1000},
};

void setup() {
  pinMode(taster, INPUT_PULLUP);
  for (Blinker &blinker : blinkGruppe)
    blinker.init();
}

void loop() {
  bool blinkAnforderung = !digitalRead(taster);
  for (Blinker &blinker : blinkGruppe)
    blinker.run(blinkAnforderung);
}

Ich habe so wenig wie möglich geändert.
Was nicht heissen soll, dass ich "einen solchen Doppelblinker" so schreiben würde...
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 01:03 pm
Quote
Was nicht heissen soll, dass ich "einen solchen Doppelblinker" so schreiben würde...
Wie würdest du ihn schreiben?

Und ja, so wie deins sehen eigentlich alle meine hier je geposteten Blink Klassen aus.

Mit einer Ausnahme:
Den enum würde ich nicht global machen, weil er nicht global gebraucht wird.

Und vermutlich würde ich meine "neue" CombieLib für IO und Timer einsetzen.
Aber das Argument gilt noch nicht, weil die Lib noch nicht fertig ist.

Also die von mir "verbesserte" Version:
Code: [Select]


const byte taster = 2;

class Blinker
{
  protected:
    enum Zustand {  Start,  Hell,  Dunkel,};
    const byte ledPin;
    const unsigned long hellPhase;
    const unsigned long dunkelPhase;
    unsigned long zeitMerker = 0;
    Zustand aktuell = Start;

  public:
    Blinker(const byte ledPin, const unsigned long hellPhase, const unsigned long dunkelPhase)
      : ledPin(ledPin), hellPhase(hellPhase), dunkelPhase(dunkelPhase) {}

    void run(const bool blinkAnforderung) {
      switch (aktuell) {
        case Start:
          if (!blinkAnforderung)
            return;
          digitalWrite(ledPin, HIGH);
          aktuell = Hell;
          zeitMerker = millis();
        case Hell:
          if (millis() - zeitMerker < hellPhase)
            return;
          digitalWrite(ledPin, LOW);
          aktuell = Dunkel;
          zeitMerker = millis();
        case Dunkel:
          if (millis() - zeitMerker >= dunkelPhase)
            aktuell = Start;
      }
    }

    void init() {
      pinMode(ledPin, OUTPUT);
    }
};

Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
  {13, 100, 500},
  {12, 500, 1000},
};

void setup() {
  pinMode(taster, INPUT_PULLUP);
  for (Blinker &blinker : blinkGruppe)
    blinker.init();
}

void loop() {
  bool blinkAnforderung = !digitalRead(taster);
  for (Blinker &blinker : blinkGruppe)
    blinker.run(blinkAnforderung);
}


Die Schachtelungstiefe ist allerdings um eins höher, als bei der Goto Version.
Auch werden zwei Sprachkonstrukte genutzt, statt nur eins.
Aber beides ist kein KO Kriterium, für diese Variante.

Ansonsten, wie schon gesagt:
Quote
Wie würdest du ihn schreiben?
Jetzt frei, ohne dich an meine Vorlage zu halten.
Nur, identische Funktion, also gleiche Aufgabe.

Denn:
Quote
Vielleicht kann ich ja von dir noch was lernen!
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 01:19 pm
Deine run Funktion benutzt mehr unsaubere und/oder exotische Sprachelemente als eine Version mit enum und switch.

Code: [Select]
    void *sprungZiel = 0; // zustandsmerker
      if (!sprungZiel) sprungZiel = && Start;
      goto *sprungZiel;
      sprungZiel = 0;

Wenn das deinem Kodestil entspricht, kannst du noch viel lernen, egal von wem.  ;)
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 01:24 pm
Quote
du noch viel lernen, egal von wem.
Genau!

Damit ich dich richtig verstehe...
Kritik ja.
Zeigen nein.
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 01:29 pm
Damit ich dich richtig verstehe...
Kritik ja.
Zeigen nein.
Ich habe dir gerade gezeigt wie man es einfacher verständlich und weniger exotisch formulieren kann.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 01:32 pm
Du sprachest:
Quote
Was nicht heissen soll, dass ich "einen solchen Doppelblinker" so schreiben würde...
Und ich fragte darauf hin:
Quote
Wie würdest du ihn schreiben?
Aber ist ok, wenn du nicht magst, dann lass es...


Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 01:38 pm
Ich habe schon verstanden, dass du vom Thema ablenken willst.

Es geht hier um goto, &&, void* versus struct und enum.

Da ist - meiner Ansicht nach - enum und switch zu bevorzugen,
was ich mit meinem Beispiel versucht habe deutlich zu machen.

Wenn du anderer Ansicht bist, von mir aus.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 02:24 pm
Hallo,

goto umgewandelt in switch case, ich finde das ist besser lesbar, jetzt könnte man noch die Logikwechsel entwirren, mal sehen ob ich das auch noch hinbekomme ...

Vergleichstest ergeben beide Sketche verhalten sich gleich. Zufällig, war keine Absicht, ist der switch case Sketch um 14 Byte kleiner. Ehrlich gesagt dachte ich der wird etwas größer. Egal.

Code: [Select]

const byte taster = 2;

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  typedef enum {START, HELLPHASE, DUNKELPHASE} state;    // Steuerzustände
  state sprungZiel = START;

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(const bool blinkAnforderung)
  {
    switch (sprungZiel) {
      case START:
                  if(!blinkAnforderung) break;
                  digitalWrite(ledPin,HIGH);
                  zeitMerker = millis();
                  sprungZiel = HELLPHASE;
                  break;
  
      case HELLPHASE:
                  if(millis()-zeitMerker<hellPhase) break;
                  digitalWrite(ledPin,LOW);
                  zeitMerker = millis();
                  sprungZiel = DUNKELPHASE;
                  break;
 
      case DUNKELPHASE:
                  if(millis()-zeitMerker<dunkelPhase) break;
                  sprungZiel = START;
                  break;
                  
      default:    digitalWrite(ledPin,LOW);         // sollte nie eintreten
                  break;          
    }            
  }
 
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};


Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {30,100, 500},
                          {31,500,1000},
                        };




void setup()
{
  pinMode(taster,INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
}

void loop()
{
  bool blinkAnforderung = !digitalRead(taster);
  for(Blinker &blinker:blinkGruppe) blinker.run(blinkAnforderung);
}


Edit:
ihr habt ja schon ohne mich weitergemacht, habt ihr die breaks in den cases absichtlich weggelassen? Der Code rammelt doch ohne break durch? Gleich einmal testen ...
Edit 2:
okay, wegen der jeweiligen if Abfrage wird das durchrammeln verhindert  ;)
Gefällt mir auch noch nicht so richtig, der Status darf erst bei Bedarf weitergeschalten werden und sollte dann auch ausgeführt werden ohne zusätzliche Hinderung, mal sehen ob mir da nochwas einfällt ...
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 02:38 pm
ihr habt ja schon ohne mich weitergemacht, habt ihr die breaks in den cases absichtlich weggelassen? Der Code rammelt doch ohne break durch? Gleich einmal testen ...
Ich habe versucht möglichst wenig zu verändern und der originale Kode fiel durch, also habe ich das so gelassen.
Wenn man breaks benutzt wiirde es wahrscheinlich einen Hauch schneller, aber auch grösser werden.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 03:16 pm
Hallo,

so wenig wie möglich ändern war auch meine erste Herangehensweise. Man muss sich erstmal reindenken.
Zufrieden war ich mit dem switch case noch nicht.  ;)
Meine neue Version ist nun auch 6 Byte größer. Egal. Ich finde es noch besser lesbar. Man sieht was wann passiert.
Da man naturgemäß mit positiver Logik besser klar kommt, habe ich die positive Logik, die combie in loop mit der Tasterabfrage schon macht in switch case weitergeführt. Die Zustände namentlich machen jetzt auch das was sie aussagen. Das war mein Ziel.
Bsp. der Zustand AUS bleibt aus, solange wie nicht erneut geblinkt werden soll, erst dann ändert sich der Zustand.
EINSCHALTEN schaltet wirklich nur ein und übergibt in den nächsten Zustand.
Danach bleibt der Zustand HELLPHASE solange erhalten wie er gültig ist, macht demnach auch hier genau das was der Name sagt usw.

Alle wieder versöhnt?

switch default könnte man hier weglassen, da alle Zustände behandelt werden, das prüft ja der Compiler.

Code: [Select]
const byte taster = 2;

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  typedef enum {AUS, EINSCHALTEN, HELLPHASE, DUNKELPHASE} state;    // Steuerzustände
  state sprungZiel = AUS;

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(const bool blinkAnforderung)
  {
   
    switch (sprungZiel) {
      case AUS:   
                  if(blinkAnforderung == true) {
                    sprungZiel = EINSCHALTEN;
                  }
                  break;
                 
      case EINSCHALTEN:
                  digitalWrite(ledPin,HIGH);
                  zeitMerker = millis();
                  sprungZiel = HELLPHASE;
                  break;
                 
      case HELLPHASE:
                  if(millis()-zeitMerker > hellPhase) {
                    digitalWrite(ledPin,LOW);
                    zeitMerker = millis();
                    sprungZiel = DUNKELPHASE;
                  }
                  break;
   
      case DUNKELPHASE:
                  if(millis()-zeitMerker > dunkelPhase) {
                    sprungZiel = AUS;
                  } 
                  break;
                 
      default:    digitalWrite(ledPin,LOW);         // sollte nie eintreten
                  break;         
    }           
  }
 
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};


Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {30,100, 500},
                          {31,500,1000},
                        };




void setup()
{
  pinMode(taster,INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
}

void loop()
{
  bool blinkAnforderung = !digitalRead(taster);
  for(Blinker &blinker:blinkGruppe) blinker.run(blinkAnforderung);
}



Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 03:17 pm
Quote
habt ihr die breaks in den cases absichtlich weggelassen?
Ich, nicht absichtlich.
Eher Faulheit.

Ein break bzw. return ist bei dem Ablauf, an der Stelle, nicht nötig.
(Edit: wie du ja gerade selber bemerkt hast)

Und was nicht nötig ist, lasse ich gerne weg....
Mein Fokus liegt da eher auf sprechende Bezeichner, möglichst flache Struktur, einfache Bedingungen.
u.s.w.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 03:32 pm
Hallo,


Quote
sprechende Bezeichner ...
genau das macht laut meiner Meinung die neue Version. Die alte Version war schon immer einen Zustand weiter der dann wiederum künstlich aufgehalten wurde. Das musste ich entknoten.  ;)

wünsche allen noch einen ruhigen sonnigen Sonntag.


PS: wenn jemand noch die Frage zu #47 (46) beantworten kann, bin ich mit allen glücklich und zufrieden
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 03:44 pm
Quote
habe ich die positive Logik,... in switch case weitergeführt.
Und dir damit eine weitere Einrückungsebene eingehandelt.

Ich will damit nicht sagen, dass Blockklammer Schachtelungstiefen per se böse sind. Das sind sie nicht.
Aber der Mensch kann nicht viel auf einen Blick erfassen.
Bis zu einer Tiefe von 3 mag einfach so gehen, und da bist du jetzt angekommen. Mit der Klassenklammer auch schon bei 4.
Ab 7 wirds kritisch, würde ich mal so sagen...

Meiner Erfahrung nach:
Je flacher ein Code ist, desto flüssiger ist er von oben herab zu lesen.

Je tiefer verschachtelt, desto schwieriger gestaltet sich die Fehlersuche.
Um so schlimmer, wenn noch komplizierte Bedingungen dazu kommen.


>  if(blinkAnforderung == true)
Dafür fehlt mir allerdings etwas das Verständnis.
Klar ist das richtig so....
Aber if(blinkAnforderung) würde meinem Hang zur Faulheit eher entsprechen.


Das ist hier alles noch lange nicht im schlimmen Bereich.
(soweit ich das beurteilen mag)

Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 03:47 pm
Selbstverständlich. Die Zeit ist konstant weil switch/case i.d.R. als Sprungtabelle umgesetzt wird. Allerdings gehen die schnellen RJMPs (relative jump) "nur" +/- 2kB.
Hallo,

noch einer wach.  Mit 2kB meinst du den Speicherbereich in dem gesprungen wird oder darf die angelegte Sprungtabelle nicht 2kB belegten Speicher überschreiten?
siehe: RJMP (https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_RJMP.html)
Die Sprungtabelle kannst du so groß machen, wie du willst.
Aber der Kompiler tut das nicht.
Ab wenige Dutzend case im Switch stellt er auf eine if Artige Entscheidungsliste um. Mit den bekannten Laufzeit Nachteilen.

RJMP und Sprungtabelle haben übrigens nicht unbedingt was miteinander zu tun.

Bei Sprungtabellen liegen oftmals nur die Zieladressen im Speicher
Bei RJMP ist die Zieladresse fest im Kommando einkompiliert.

Legst du allerdings RJMP Kommandos in die Tabelle, dann darf die Tabelle wirklich nicht größer sein, denn man möchte ja meist ein Ziel außerhalb der Tabelle anspringen.
Title: Re: Code funktioniert nur manchmal
Post by: postmaster-ino on Mar 11, 2018, 03:57 pm
Hi
PS: wenn jemand noch die Frage zu #47 (46) beantworten kann, bin ich mit allen glücklich und zufrieden
Relative Sprünge haben nur einige Bits Platz in dem Befehl, deshalb gehen relative Sprünge auch nur x Adressen hoch oder runter.
Wenn der Abstand zu groß wird, setzt der Compiler einen negierten relativen Sprung über einen absoluten Sprung.
Statt einem brcc (branch relativ if carry clear) wird ein brcs +2 (branch relativ if carry set) über den jmp-Befehl gesetzt, jmp Ziel-Adresse Absolut.
Wenn rjmp nicht mehr ausreicht, springt man mit negierter Logik über einen JMP, also absoluten Jump.

Habe gerade nur das Datenblatt des ATtiny45 offen, Da sind es 7 Bit, womit Er -64 ... +63 Worte springen kann.
Dem nackten Chip ist Es egal, wie groß eine Tabelle ist, für Ihn sind's eh nur irgend welche Bytes, Die das Programm sinnvoll zu nutzen weiß - je nach Progger :o (der Compiler wird aber wissen, was Er Da verzapft)


MfG
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 04:31 pm
Hallo,

RJMP ... okay, habe wohl übersehen. Danke.

Zum ganzen Rest muss ich nun leider sagen, du suchst irgendwie nach Ausflüchte.
Anders gesagt verteidigst du deine Meinung genauso hartnäckig wie ich meine verteidige. 

4 Zustände haben nichts mit Schachtlungstiefe zu tun. Das sind schlicht 4 Vergleiche auf gleicher Ebene.
Es ersetzt nur deinen ersten if Vergleich vorm Label Start. Es macht nichts anderes nur das es nun ein case ist.  :o
Darf es demzufolge Zustandsautomaten mit mehr als 3 Vergleich nicht geben ... :o
Es gibt nichts einfacheres wie 100 namentlich benannte (oder Ganzzahlen) case Vergleiche im Code zu lesen.
Die if Zeitvergleiche sind hier wie da vorhanden nur jetzt in der Logik umgedreht. Keine Änderung der Tiefe.
Und ob man nun  == true)  schreibt oder nicht, hat mit all dem nichts zu tun.
Wie gesagt, die Zustände machen jetzt genau das was der Name sagt. Ist weniger verwirrend.

Kurzum, du wolltest sehen, ich hab gezeigt. Dabei belasse ich es auch. Sonst diskutieren wir noch über Syntaxe. Das mach keinen Sinn.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 04:44 pm
Quote
Zum ganzen Rest muss ich nun leider sagen, du suchst irgendwie nach Ausflüchte.
Schwachsinn!

Auch vorher schon im Thread habe ich gesagt, dass du tiefer schachteln wirst.
Denn ich kann in die Zukunft schauen.
Ich 2 tiefer gesagt, und du 2 tiefer gemacht.


Quote
4 Zustände haben nichts mit Schachtlungstiefe zu tun. Das sind schlicht 4 Vergleiche auf gleicher Ebene.
Du hast mich nicht verstanden.

Bei dir:
Tiefe 1 Klassenblock
Tiefe 2 Methodenblock
Tiefe 3 Switch Block
Tiefe 4 If Block

Bei mir:
Tiefe 1 Klassenblock
Tiefe 2 Methodenblock

Dein Code hat eine Schachtelungstiefe von 4, meiner von 2


Quote
Kurzum, du wolltest sehen, ich hab gezeigt
Meinen Dank dafür.


Quote
Anders gesagt verteidigst du deine Meinung genauso hartnäckig wie ich meine verteidige. 
Ich stelle den "Goto ist immer böse" Dogmatismus an den Pranger.
Mehr nicht.
Und wenn du dabei bleiben willst, ist das deine Sache.
Aber so wie mir scheint, hast selbst du mittlerweile geschnallt, dass diese dogmatische Sicht ein Irrweg ist.
Nur ein nachplappern irriger Ansichten.
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 05:40 pm
Es geht nicht um "goto ist immer böse". (Strohmann-Argument nennt man das.) (https://de.wikipedia.org/wiki/Strohmann-Argument)

Es gibt - unbestritten - sinnvolle Anwendungen für goto.
Dennoch sollte man es nur einsetzen, wenn es nötig ist, d.h.
wenn eine Implementation mit anderen Konstrukten verwirrend oder super schwierig ist.

Auch Amputationen sind nicht immer böse, aber man sollte sie nur durchführen, wenn sie nötig sind.
Title: Re: Code funktioniert nur manchmal
Post by: combie on Mar 11, 2018, 06:12 pm
Quote
Es gibt - unbestritten - sinnvolle Anwendungen für goto.
Und ebenso unbestritten ist es möglich damit Mist zu bauen!
Der Mist hat sogar einen Namen: Spaghetticode (https://de.wikipedia.org/wiki/Spaghetticode)

Und das ist eben die Argumentation die Doc_Arduino ins Feld geführt hat.
Alle seine Argumente sind gültig gegen Spaghetticode.
Aber nicht prinzipiell auf Goto zutreffend.

Offenbar hat er Spaghetticode und Goto unreflektiert in einen Topf geworfen.

Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 06:15 pm
Und ebenso unbestritten ist es möglich damit Mist zu bauen!
Der Mist hat sogar einen Namen: Spaghetticode (https://de.wikipedia.org/wiki/Spaghetticode)
So hart hätte ich deine Implementation oben jetzt nicht bezeichnet, aber im Grunde hast du Recht.
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 06:28 pm
Hallo,

muss doch nochmal antworten, auch wenn mir das langsam zu blöd wird. Wenn du jetzt noch mit dem Begriff "Spaghetticode" nach mir wirfst, dann wird es nur noch unsachlich. Deine Erklärungen sind nicht stimmig. Ist mir an den Haaren herbeigezogen. Du hast demnach keine if Blöcke?  Ich sehe jede Menge davon. Auch wenn die geschweiften Klammern fehlen sind es welche. Der switch-case Code ist nicht komplizierter. Im Gegenteil aufgeräumter, einfacher nachvollziehbar. Die Jongliererei mit dem Zeiger ist auch weg. Genau darum ging es mir. Nicht mehr und nicht weniger. Man muss ja die gesamte run Funktion betrachten und nicht nur den switch Teil. Alles andere macht keinen Sinn. Wenn man hier noch default weg lässt, dann ist er auch nur unwesentlich länger. Aber gut, wir haben darüber gesprochen, es ist dein Code, wenn dir goto lieber ist, dann ist das eben dein Programmierstil. Jeder hat seinen eigenen.

(https://forum.arduino.cc/index.php?action=dlattach;topic=533466.0;attach=248962)
Title: Re: Code funktioniert nur manchmal
Post by: Whandall on Mar 11, 2018, 06:31 pm
Wenn du jetzt noch mit dem Begriff "Spaghetticode" nach mir wirfst,
Mit dem Begriff hat er nach seinem Kode geworfen, nicht nach deinem (oder hast du goto's benutzt?):
Title: Re: Code funktioniert nur manchmal
Post by: Doc_Arduino on Mar 11, 2018, 08:21 pm
Hallo,

hmm, habe das in der kurzen Aufregung falsch verstanden.