Only one serial command per run works

I have written a code to run on my Arduino Leonardo, that is supposed to count the signals from a gas meter (mechanically, it’s basically a switch, that closes and opens once per signal). So far, everything works just fine.

My problem is, that I also have three commands to send via serial communication from my PC.

The first one is supposed to run once when started and give the possibility to set a starting time:

Serial.println("Startzeit aendern? [Ja/Nein]");
  while (!Serial.available());
  while (Serial.available()) {
    character = Serial.read();
    content.concat(character);
  }
  if (content == "Ja") {
    Serial.println("Versuchsdauer in Stunden eingeben");
    while (!Serial.available());
    while (Serial.available()) {
      intervall = Serial.parseInt();
    }
    content = "default";
    Serial.println("Zeit angepasst");
    Serial.println("");

The next can be called at any time to clear the stored variables and end the program:

while (Serial.available()) {  // überprüft, ob etwas an das Board gesendet wird
      character = Serial.read();  // character ist jeweils ein Buchstabe
      content.concat(character);  // character wird an content angehängt
    }

    if (content == "clear") {    // Vorgehen, falls "clear" gesendet wurde
      Serial.println("...");
      for (int i = 0; i <= 3; i++) {    //
        EEPROM.write(i, 0);             // signalCounter wird wieder auf 0 gesetzt
        Serial.print("Position ");
        Serial.println(i);
        delay(250);
      }
      Serial.println("Variablen zurueckgesetzt, Programm gestoppt");
      while (1) {}
    }

And finally, the third one can also be called when required and lets you also change the time in the serial output:

if (content == "Zeit") {    // Vorgehen, falls "Zeit" gesendet wurde
      Serial.println("Versuchsdauer in Stunden eingeben");
      while (!Serial.available());
      while (Serial.available()) {
        intervall = Serial.parseInt();
      }
      content = "default";
      Serial.flush();
      Serial.println("Zeit angepasst");
    }

Now the strange thing is, that every one of these works just fine, but only once per run. After one of them is used, the Arduino seems to not accept serial input anymore. Then I have to pull out the USB-cable and put it back in to use another command.

Does anyone know, what the problem might be?

Thank you in advance!

PS: As German is my mother language, all the comments are in German… If necessary, I can translate them for you.

PPS: Here is the whole code, if anyone needs more information:

#include <EEPROM.h>

void EEPROMWritelong(int address, long value)    // die Funktion EEPROMWritelong() wird etabliert
{ //
  byte four = (value & 0xFF);                    //dabei wird eine 4 Byte große long-Variable
  byte three = ((value >> 8) & 0xFF);            //in ihre einzelnen Bytes zerlegt (one ... four),
  byte two = ((value >> 16) & 0xFF);             //die dann auf separate Speicherplätze gelegt
  byte one = ((value >> 24) & 0xFF);             //werden.
  //
  EEPROM.write(address, four);                   //
  EEPROM.write(address + 1, three);              //
  EEPROM.write(address + 2, two);                //
  EEPROM.write(address + 3, one);                //
}

long EEPROMReadlong(long address)                // Funktion EEPROMReadlong wird etabliert
{ //
  long four = EEPROM.read(address);              // zunächst werden die vier Bytes ausgelesen
  long three = EEPROM.read(address + 1);         //
  long two = EEPROM.read(address + 2);           //
  long one = EEPROM.read(address + 3);           //

  // mithilfe der return-Funktion wird die ursprüngliche long-Variable wieder zusammengesetzt:
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

const int gaszaehlerIn = 2;  // definiert den Anschluss des Gaszählers
const float grundVolumen = 3.57;  // Volumen pro Ausschlag; empirisch ermittelt

int currentState = 0;  // aktueller Status (offen / geschlossen)
int lastState = 0;  // vorheriger Status

long signalCounter = 0;  // Anzahl an Signalen vom Gaszähler
long lastSignalCounter = 0;  // Ausschläge bis zur jeweils aktuellen Messung

long starttime = 0;
long endtime = 0;

long intervall = 0;  // Zeitangabe (1h - Intervalle)

String content = "";
char character;

void setup() {
  pinMode(gaszaehlerIn, INPUT_PULLUP);  // eingehende Signale werden registriert

  Serial.begin(9600);  // Baud für Verbindung wird festgelegt
  while (!Serial) {  // Board wartet auf eine serielle Verbindung
    ;
  }
  /*
  Serial.println("Startzeit aendern? [Ja/Nein]");
  while (!Serial.available());
  while (Serial.available()) {
    character = Serial.read();
    content.concat(character);
  }
  if (content == "Ja") {
    Serial.println("Versuchsdauer in Stunden eingeben");
    while (!Serial.available());
    while (Serial.available()) {
      intervall = Serial.parseInt();
    }
    content = "default";
    Serial.println("Zeit angepasst");
    Serial.println("");
  }
  
  if (content == "Nein") {
    ;
  }
  */  
  Serial.print("Zeit [h]");                         //
  Serial.print("  ");                                //
  Serial.print("Volumen gesamt [ml]");              //
  Serial.print("  ");                                //
  Serial.print("aktueller Volumenstrom [ml/min]");  //
  Serial.print("  ");                                //
  Serial.println("Ausschlaege");                    // Benennung der Tabellenspalten

  signalCounter = EEPROMReadlong(0);  // signalCounter vom letzten Mal wird geladen

  lastSignalCounter = signalCounter;

  Serial.print(intervall);  // Zeit der Messung wird eingetragen
  Serial.print("  ");
  Serial.print(signalCounter * grundVolumen);  // Berechnung des Gesamtvolumens
  Serial.print("  ");
  Serial.print((signalCounter - lastSignalCounter) * grundVolumen / 60);  // Berechnung des aktuellen Volumenstroms
  Serial.print("  ");
  Serial.println(signalCounter);  // Menge der Ausschläge wird eingetragen
  intervall = intervall + 4;  // die Zeitangabe wird aktualisiert
}

void loop() {
  do {
    starttime = millis();

    currentState = digitalRead(gaszaehlerIn);
    if (currentState != lastState) {  // eine Änderung des Signals vom Gaszähler initiiert den nächsten Schritt
      if (currentState == LOW) {    // ist das Signal LOW? (=liegt eine Spannung an?)
        signalCounter++;   // ist dem so, wird der Counter um 1 erhöht
        delay (250);   // 250ms Wartezeit, damit ein Signal nicht als mehrere gezählt wird
      }
      else {   // für den Fall, dass das Signal LOW ist:
        ;
      }
    }
    lastState = currentState;  // lastState und currentState werden wieder gleich gesetzt

    while (Serial.available()) {  // überprüft, ob etwas an das Board gesendet wird
      character = Serial.read();  // character ist jeweils ein Buchstabe
      content.concat(character);  // character wird an content angehängt
    }

    if (content == "clear") {    // Vorgehen, falls "clear" gesendet wurde
      Serial.println("...");
      for (int i = 0; i <= 3; i++) {    //
        EEPROM.write(i, 0);             // signalCounter wird wieder auf 0 gesetzt
        Serial.print("Position ");
        Serial.println(i);
        delay(250);
      }
      Serial.println("Variablen zurueckgesetzt, Programm gestoppt");
      while (1) {}
    }

    if (content == "Zeit") {    // Vorgehen, falls "Zeit" gesendet wurde
      Serial.println("Versuchsdauer in Stunden eingeben");
      while (!Serial.available());
      while (Serial.available()) {
        intervall = Serial.parseInt();
      }
      content = "default";
      Serial.flush();
      Serial.println("Zeit angepasst");
    }

  } while ((starttime - endtime) < 10000);  // für 4h wird der Loop wiederholt
  endtime = starttime;  // End- und Startzeit werden wieder gleichgesetzt

  Serial.print(intervall);  // Zeit der Messung wird eingetragen
  Serial.print("  ");
  Serial.print(signalCounter * grundVolumen);  // Berechnung des Gesamtvolumens
  Serial.print("  ");
  Serial.print((signalCounter - lastSignalCounter) * grundVolumen / 240);  // Berechnung des aktuellen Volumenstroms
  Serial.print("  ");
  Serial.println(signalCounter);  // Menge der Ausschläge wird eingetragen

  intervall = intervall + 4;  // die Zeitangabe wird aktualisiert

  long address = 0;
  EEPROMWritelong(address, signalCounter);  // falls das Arduino zwischenzeitlich vom PC getrennt wird, wird der signalCounter gespeichert
  address += 4;

  lastSignalCounter = signalCounter;
}

I wonder if the problem is the way you have the serial reads at specific locations in your code. Those locations may not coincide with the activity on the PC. I would re-design the program so loop() looks like this

void loop() {
   currentMillis = millis();
   readSerialData();
   readTheMeter();
   showResults();
}

That way anything that comes from the PC is picked up asynchronously compared to the other activities. See planning and implementing a program for a full example.

Also, have a look at the examples in serial input basics for simple ways to receive data without blocking. Substitute one of the examples for readSerialData() in the above code.

...R

Thank you Robin! Restructuring the code actually worked.

If anybody is interested, here’s the complete and working version of it:

#include <EEPROM.h>

const int gaszaehlerIn = 2;    // digitalPin des Gaszählers
const float volumen = 3.57;    // Volumen pro Ausschlag; empirisch ermittelt

int currentState = 0;    // aktueller Status des Reed-Schalters (offen / geschlossen)
int lastState = 0;    // vorheriger Status

long signalCounter = 0;    // Anzahl an Ausschlägen
long lastSignalCounter = 0;    // Ausschläge bis zur jeweils letzten seriellen Ausgabe der Daten

unsigned long startTime = 0;    // Laufzeit in ms
unsigned long endTime = 0;    // markiert das Ende des Messungs-Loops

long intervall = 0;    // Zeitangabe

String answer = "";
String command = "";

void setup() {
  pinMode(gaszaehlerIn, INPUT_PULLUP);  // eingehende Signale werden registriert

  Serial.begin(9600);  // Baud für Verbindung wird festgelegt
  while (!Serial) {  // Board wartet auf eine serielle Verbindung
    ;
  }
  
  Serial.println("Startzeit aendern? [Ja/Nein]");
  while (!Serial.available());  
  answer = Serial.readString();
  if (answer == "Ja") {
    Serial.println("Versuchsdauer in Stunden eingeben");
    while (!Serial.available());
    while (Serial.available()) {
      intervall = Serial.parseInt();
    }
    Serial.println("Zeit angepasst");
    Serial.println("");
  }
  else {
    ;
  }
  
  printHeader();
  
  signalCounter = EEPROMReadlong(0);    // signalCounter vom letzten Mal wird geladen
  
  lastSignalCounter = signalCounter;
  
  printResults();
  intervall = intervall + 4;    // Zeitangabe wird aktualisiert
}

void loop() {
  do {
    startTime = millis();

    currentState = digitalRead(gaszaehlerIn);
    if (currentState != lastState) {  // eine Änderung des Signals vom Gaszähler initiiert den nächsten Schritt
      if (currentState == LOW) {    // ist das Signal LOW? (=liegt eine Spannung an?)
        signalCounter++;   // ist dem so, wird der Counter um 1 erhöht
        delay (250);   // 250ms Wartezeit, damit ein Signal nicht als mehrere gezählt wird
      }
      else {   // für den Fall, dass das Signal LOW ist:
        ;
      }
    }
    lastState = currentState;  // lastState und currentState werden wieder gleich gesetzt
    
    command = Serial.readString();
    if (command == "clear") {    // Vorgehen, falls "clear" gesendet wurde
      Serial.println("...");
      for (int i = 0; i <= 3; i++) {    //
        EEPROM.write(i, 0);             // signalCounter wird wieder auf 0 gesetzt
        Serial.print("Position ");
        Serial.println(i);
        delay(250);
      }
      Serial.println("Variablen zurueckgesetzt, Programm gestoppt");
      while (1) {}
    }

    if (command == "Zeit") {    // Vorgehen, falls "Zeit" gesendet wurde
      Serial.println("Versuchsdauer in Stunden eingeben");
      while (!Serial.available());
      while (Serial.available()) {
        intervall = Serial.parseInt();
      }
      Serial.println("Zeit angepasst");
    }
  } while ((startTime - endTime) < 10000);    // Dauer des Loops
  endTime = startTime;
  
  printResults();
  intervall = intervall + 4;
  
  long address = 0;
  EEPROMWritelong(address, signalCounter);
  address += 4;
  
  lastSignalCounter = signalCounter;
}

void EEPROMWritelong(int address, long value)    // die Funktion EEPROMWritelong() wird etabliert
{ //
  byte four = (value & 0xFF);                    //dabei wird eine 4 Byte große long-Variable
  byte three = ((value >> 8) & 0xFF);            //in ihre einzelnen Bytes zerlegt (one ... four),
  byte two = ((value >> 16) & 0xFF);             //die dann auf separate Speicherplätze gelegt
  byte one = ((value >> 24) & 0xFF);             //werden.
  //
  EEPROM.write(address, four);                   //
  EEPROM.write(address + 1, three);              //
  EEPROM.write(address + 2, two);                //
  EEPROM.write(address + 3, one);                //
}

long EEPROMReadlong(long address)                // Funktion EEPROMReadlong wird etabliert
{ //
  long four = EEPROM.read(address);              // zunächst werden die vier Bytes ausgelesen
  long three = EEPROM.read(address + 1);         //
  long two = EEPROM.read(address + 2);           //
  long one = EEPROM.read(address + 3);           //

  // mithilfe der return-Funktion wird die ursprüngliche long-Variable wieder zusammengesetzt:
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

void printHeader() {
  Serial.print("Zeit [h]");                          //
  Serial.print("  ");                                //
  Serial.print("Volumen gesamt [ml]");               //
  Serial.print("  ");                                //
  Serial.print("aktueller Volumenstrom [ml/min]");   //
  Serial.print("  ");                                //
  Serial.println("Ausschlaege");                     // Benennung der Tabellenspalten
}

void printResults() {
  Serial.print(intervall);  // Zeit der Messung wird eingetragen
  Serial.print("  ");
  Serial.print(signalCounter * volumen);  // Berechnung des Gesamtvolumens
  Serial.print("  ");
  Serial.print((signalCounter - lastSignalCounter) * volumen / 60);  // Berechnung des aktuellen Volumenstroms
  Serial.print("  ");
  Serial.println(signalCounter);  // Menge der Ausschläge wird eingetragen
}