Misteriöses Problem mit Eingabe über die Serielle Konsole und serialEvent

Kommentiere ich in nachfolgenden Sketch die debug Ausgaben in Zeile 34 oder 36 via Serial.print aus, bleibt der inputString leer und mein Ergebnis converted ist leer, ein auskommentieren der Zeile 35 stört nicht. Verstehe die Welt nicht mehr. Arbeite mir Arduino Nano. Bin auch kein blutiger Anfänger aber auch nicht der Top Profi, programmiere aber seit knapp 50 Jahren, meisten mehr als Hobby.

// For serial Input
char inputString[10];
bool stringComplete = false;
unsigned int converted;

void setup() {
Serial.begin(115200);
delay(200);
Serial.println("Begin");
}

void loop() {
  {
    if (stringComplete) 
      {
        Serial.print("inputString: ");
        Serial.println(inputString);
        converted = strtol(inputString,0,16);
        Serial.print("converted: ");
        Serial.print(converted);
        Serial.print(" ");
        Serial.println(converted,HEX);
        stringComplete = false;
      }
  }

}

// For searial input
void serialEvent() {
  int i = 0;
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    Serial.print(i);
    Serial.print(" ");
    Serial.println(inChar);
    // add it to the inputString:
    inputString[i] = inChar;
    i++;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      inputString[i] = 0x00;
      stringComplete = true;
      Serial.print("INPUT:");
      Serial.print(inputString);
    }
  }
}

Danke für jegliche Hilfe

Rainer

P.S. mit den Debug Ausgaben ist alles OK, d.h. meine Eingabe wird sauber in ein unsigned int konvertiert.

Oh sorry I am in the english part, so again the problem, if I con line 34 or 36 in the above code (debug outputs) the input string will be empty the converted int will be zero. otherwise with the debug code conversion works as it should. Im no novice, I am programing as hobby since 50 years.

need help, hint
thanks rainer

setze mal die Geschwindigkeit etwas runter
und überprüfe bitte die >=7V am Nano Vin

Wo rufst Du serialEvent überhaupt auf?

das serialEvent wird automatisch in loop aufgerufen, muss nicht extra sein, den Tip hab ich von https://www.arduino.cc/en/Tutorial/SerialEvent

Habe jetzt aug 9600 gesetzt selbes Problem.

Danke für die schnellen Antworten

Das Problem ist hier:

int i = 0;

Der Grund:
SerialEvent wird häufiger aufgerufen als du denkst.
Jedesmal setzt du den Zeiger/Index auf Null und verlierst/überschreibst dann alle vorherigen Eingaben.

Tipp:
Ein Parser ist ein endlicher Automat.
Dein i ist Teil des Status, dieses Automaten.

Genau, dein Programm geht davon aus, dass die ganze Zeile innerhalb eines Aufrufes von serialEvent empfangen wird. Das ist aber normalerweise nicht der Fall. Nur durch deine Print-Ausgaben in der while-Schleife wird das ganz so langsam, dass es funktioniert ( ein delay(1) an der Stelle hätte denselben Effekt - ist aber natürlich keine vernünftige Lösung). Mit 9600 Baud wird es übrigens noch schlechter, da die Daten dann noch langsamer ankommen.
Du musst die Funktion so aufbauen, dass sie die Daten auch über mehrere Aufrufe von serialEvent hinweg aufsammelt.

P.S. der Vorteil des 'serialEvent()' gegenüber dem eigenen Aufruf einer entsprechenden Funktion im loop() erschließt sich mir nicht. Zumal das auch nicht bei allen Arduinos funktioniert. Ich hatte es zuerst auf einem Micro versucht - da geht es z.B. schon nicht.

der Vorteil des 'serialEvent()' gegenüber dem eigenen Aufruf einer entsprechenden Funktion im loop() erschließt sich mir nicht.

Das eine ist funktional das gleiche wie das andere.
Bis auf die Abhängigkeit irgend was in loop() zu notieren.
Dass es nicht mit allen Arduinos funktioniert steht in der Doku.

Also, mir gefällt serialEvent fast sehr gut!
Bin ein bekennender Fan der Ereignisorientierten Programmierung.
Leider wird das Konzept/Pattern "Inversion of Control" im Arduino Umfeld arg vernachlässigt (auch von mir).

So wie ich das verstehe ist serialEvent() ein Interrupt Handler, der für jedes ankommende Zeichen aufgerufen wird. Damit kann eine Eingabe gleich in einen eigenen Puffer umgeleitet werden, so geht kein Zeichen verloren egal was sonst in loop() abläuft. Das "doesn't work" dürfte IMO "not implemented" bedeuten, also reine Faulheit und nicht Unmöglichkeit.

So wie ich das verstehe ist serialEvent() ein Interrupt Handler, der für jedes ankommende Zeichen aufgerufen wird.

Es ist kein Interrupthandler.

Sie wird vor/nach loop() aufgerufen, sobald irgendwas im Buffer der Serielen ist.
Die loop Laufzeiten verzögern also die serialEvent Aufrufe.

ja genau das int i, wars Lösung (nicht sehr elegant. statt i incount und die global definiert und mit 0 initialisiert. Dann im seriellEvent nicht am Anfang initialisiert, sondern am Ende zurückgesetzt.

Das funktioniert soweit sauber.

Danke für die Hilfe
Allen ein Gutes Neues Jahr

muekno:
jicht sehr elegant. statt i incount und die global definiert und mit 0 initialisiert.

Du musst es ja nicht global definieren. 'static' reicht.

// For serial Input
const int bufferSize    = 10;
bool stringComplete     = false;
unsigned int converted  = 0;
int bufferIndex         = 0;
char inputString[bufferSize];

void setup() 
{
  Serial.begin(9600);
  Serial.println("Begin");
}

void loop() 
{
  if (stringComplete)
  {
    Serial.print("inputString: ");
    Serial.println(inputString);
    converted = strtol(inputString,nullptr,16);
    Serial.print("converted: ");
    Serial.print(converted);
    Serial.print(" ");
    Serial.println(converted,HEX);
    stringComplete = false;
  }
}

// For searial input
void serialEvent() 
{
  if(stringComplete) return; 
  if(bufferIndex > bufferSize-3) return; // bufferuerberlauf muss noch korrekt abgehandelt werden (das lasse ich für dich ueber)
  
  int inChar = Serial.read();
  switch(inChar)
  {
    case 'a' ... 'f' : /* fall through*/
    case 'A' ... 'F' : /* fall through*/         
    case '0' ... '9' : inputString[bufferIndex] = inChar; 
                       bufferIndex++;
                       inputString[bufferIndex] = 0;
                       break;

    case '\n'        : stringComplete = true;
                       bufferIndex = 0;
                       break;  
                       
    case '\r'        : break;  // ignorieren

    default          : bufferIndex = 0;  // Schrottzeichen im Strom
                       break;
  }
}

Wie groß wird i denn?
Der Buffer ist 10 groß. Kann i größer als 9 werden?
Grüße Uwe

10 ist schon zuviel, ich will nur 2 stellige Hex Zahlen in int wandeln. Es geht darum Knoten in einen CAN Bus System bestimmte Daten mitzuteilen die dann auf dem Knoten im EEPROM abgelegt werden. D.. ich mache auf einem speziellen Konfigurationsknoten nach Tastendruck eine erste Abfrage nach dem Ziel, z.B. 0xA7 und eine zweite Abfrage nach dem zu senden Wert, z.B. 0x37. Da die Eingabe von der Konvertierung int = strtol(buf, 0, 16); sowohl mit führendem 0x als auch ohne akzeptiert wird reichen 5 notfalls sogar 3 Byte für den String. Aber etwas Reserver für eventuelle Anpassungen ist ja nicht schlecht.

Gruß

Rainer