Very odd behavior of Serial stream

static volatile bool flag = 0;

void setup() {
  pinMode(2, INPUT_PULLUP);
  Serial.begin(57600); //This pipes to the serial monitor
  while (!Serial);
  Serial1.begin(57600); //This is the UART, pipes to sensors attached to board
  while (!Serial1);
  Serial1.setTimeout(10000);
  attachInterrupt(1, fall, FALLING );
}

void loop() {
  while ( Serial.available() ) {
    char ch = Serial.read();
    if (ch == 'S' && 'M' == Serial.peek()) {
      char tmp[10] = {0};
      tmp[0] = ch;
      Serial.readBytes(tmp + 1, 2);
      if (!strcmp(tmp, "SMS")) {
        while (Serial.available()) Serial.read();
        send_sms();
      }
      else Serial1.write(tmp);
    }
    else Serial1.write(ch);
  }

  while ( Serial1.available() ) {
    Serial.write( Serial1.read() );
  }


  if (flag) {
    flag = 0;
    Serial1.println("ATH");
    delay(1000);
    //send_sms();
    attachInterrupt(1, fall, FALLING );
  }
}

void config () {
  Serial1.println("AT");
  while (!Serial1.find("OK"));
  Serial.println("OK");
  Serial1.println("AT+CMGF=1");
  while (!Serial1.find("OK"));
  Serial.println("CMGF=1");
  Serial1.println("AT+CSCS=\"GSM\"");
  while (!Serial1.find("OK"));
  Serial.println("GSM");
}

void send_sms() {
  config();
  char message[40] = {0}; uint8_t i = 0; char c;
  Serial1.println("AT+cusd=0,\"*444#\"");
  Serial.println("send");
  delay(2000);
  while (!Serial1.find("+CUSD:"));
  while (Serial1.available()) Serial.write(Serial1.read());
  Serial.println("end");
}

void fall() {
  detachInterrupt(1);
  flag = 1;
  Serial.println("lowlevel");
}

board is micro/leonardo (2 serial)
so when I send from PC to modem SMS command send_sms() is calling and
despite the fact that I put these two lines
delay(2000);
while (!Serial1.find("+CUSD:"));
special for print "end" after all serial stream (when stream is terminated/no incoming bytes)
but as you can see on the output

CMGF=1
GSM
send
end
Abonement: 1155 min v seti, 75 min vne seti i 1001.92MB deistvitelinye do kontsa meseatsa

  1. Info
  2. Loialinosti
  3. Moi nomer
    ",15

OK

"end" is printing before USSD command was executed
How to print "end" after all stream ?

aren't these lines comparing the last char read to the next char that will be read, even if the next char is not available?

This does not read input 'until there is no more'. It only reads input until the buffer is empty. If there is any gap in the input the buffer may empty before more data arrives.

You could use a timeout:

  unsigned long startTimer = millis();
  while (millis() - startTimer <= 5000) // Allow 5 seconds for input
  {
    if (Serial1.available()) 
    {
      startTimer = millis();  // re-start the timeout
      Serial.write(Serial1.read());
    }
  }

That will accept input until it has been 5 seconds since the last character received.

you are right. was written quickly just to verify serial stream. but. at this moment issue not in this line. commands from PS(Serial) were sending successfully.

will try this tomorrow ... just want to add small notes , line

while (!Serial1.find("+CUSD:"));

is working as expected. find is waiting 10 sec (Serial1.setTimeout(10000); ) for "+CUSD:". and after ~2..4 sec this target is coming ... and +CUSD text is excluding from Output. but! somehow , line
Serial.println("end"); which is after find, is executing before find. How ?!

The Arduino is faster than you imagine.

while (!Serial1.find("+CUSD:"));
At this point the ':' of "+CUSD:" has just been taken out of the buffer. At 57600 baud, it will be about 174 microseconds (2777 instruction cycles) until the next character arrives.

The buffer is empty so this loop does nothing:

  while (Serial1.available()) 
    Serial.write(Serial1.read());

This line puts "end\n" in the Serial output buffer.
Serial.println("end");

send_sms() now returns.

Now we are back in loop() and start repeating:

  while ( Serial1.available() ) {
    Serial.write( Serial1.read() );
  }

That will (eventually) show everything that arrived after "end\n" was put in the output buffer.

after some tests I figured out that "delay" in the input string can be in any place... so might be exist only one solution to resolve this issue.. using timeout as you suggested or write my own timeRead function like in the Stream class

you might consider readBytesUntil() if the string you're looking for ends with a known char (e.g. ":")

for readBytesUntil you should know exact lenght..
no I mean to call instead serial.read

    uint8_t timedRead() {
      uint32_t prev_millis = millis();
      do {
        if (peek()) return read();
      } while (millis() - prev_millis < time_out);
      return 0;               // indicates timeout
    }

you need to specify the size of the buffer, but you can certainly read something into that buff that is shorter and it will time out eventually


char s [80];

void
loop ()
{
    if (Serial.available ())  {
        memset (s, 0, sizeof(s));
        Serial.readBytesUntil (':', s, sizeof(s));

        Serial.println (s);
    }
}

void
setup ()
{
    Serial.begin (9600);
}

Did you mean Serial1.peek()? I don't think that ever returns 0. If the buffer is empty I would expect it to return -1 (like Seria1.read() would).

I think you meant:

    uint8_t timedRead() {
      uint32_t prev_millis = millis();
      do {
        if (Serial1.available() != 0) return read();
      } while (millis() - prev_millis < time_out);
      return 0;               // indicates timeout
    }

That does basically what I wrote except the caller has to check after every call to timedRead() to see if there was a timeout (returns 0) or not.