Multiple While loops within loop()

Hi, I want the sketch to wait for serial input using: while (Serial.available() != 0); Next I want to read the input with: while (Serial.available() > 0) {...} The result is that ony one character is read. The rest is read on the next loop. The following code shows what happens:

(the output follows the code)

void loop() { while (Serial.available() != 0); // Wait for serial unput int Idx = 0; // Init buffer char Buf[80]; // while (Serial.available() > 0) // Get chars in buffer { Buf[Idx++] = Serial.read(); // Get next char Buf[Idx] = 0; // terminate string } Serial.println(Buf);

}

Input:

123456789

Output: 1

23456789

while (Serial.available() != 0); --> while (Serial.available() == 0);

OK, You are right... it's a typo... In the original sketch I used: "while (!Serial.available());" The output changes also when I add somme debugging lines... Strange but I cannot find any logic Or do I something wrong?

I like to think of my Serial statements as sentences. Consider you sent 12345 but, depending on where the Arduino is you get 345, which is not what you sent, so you’d want to reject 345. If you sent <12345>, you have the beginning and end of a sentence. Now if you get 345> you have a trigger to reject the sentence. So you do not start to save any data till a < is received and you data stream is not complete till you get a >. Upon receipt of a > you can print. If, in the serial buffer their is a <12345><45678>, such a scheme allows you to separate your sentences.

With the issue you reported, One time through you’d receive the <1, which is not a complete sentence. The < is used to enable your serial storage variable to begin saving incoming data. On the next loop you get 2345>. The > is used to kick you out of the serial loop for further processing.

As an example:

void fReceiveSerial_LIDAR( void * parameters  )
{
  bool BeginSentence = false;
  sSerial.reserve ( StringBufferSize300 );
  char OneChar;
  for ( ;; )
  {
    EventBits_t xbit = xEventGroupWaitBits (eg, evtReceiveSerial_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( LIDARSerial.available() >= 1 )
    {
      while ( LIDARSerial.available() )
      {
        OneChar = LIDARSerial.read();
        if ( BeginSentence )
        {
          if ( OneChar == '>')
          {
            if ( xSemaphoreTake( sema_ParseLIDAR_ReceivedSerial, xTicksToWait0 ) == pdTRUE ) // wait on semaphore
            {
              xQueueOverwrite( xQ_LIDAR_Display_INFO, ( void * ) &sSerial );
              xEventGroupSetBits( eg, evtParseLIDAR_ReceivedSerial );
              //
            }
            BeginSentence = false;
            break;
          }
          sSerial.concat ( OneChar );
        }
        else
        {
          if ( OneChar == '<' )
          {
            sSerial = ""; // clear string buffer
            BeginSentence = true; // found begining of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
  }
  vTaskDelete( NULL );
} //void fParseSerial( void * parameters  )

Consider changing the while to an if and use loop() to do what its name suggests

Have a look at Serial input basics - updated for some ideas on reading serial input

@OP

1. The Serial Reception Process should follow the following chain (Fig-1). Code it carefully; compile and upload. From the Serial Monitor, send the code/message <12345> (or any other code/message), you will be able to show exactly 12345 back on the Serial Monitor. Note: < is the Beginning Marker of message (12345); > is the Ending Marker of message (12345).
flow8z.png
Figure-1: Flow Chart for receiving characters from Serial Monitor

2. Coding for the Flow Chart of Fig-1 (Tested using UNO)

char dataBuffer[50] = "";
int bufferIndex = 0;
bool flag1 = false;
byte x;

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

void loop()
{
  if (Serial.available() > 0) //L1:
  {
    if (flag1 != true)    //L2:
    {
      x = Serial.read();  //L3:
      if (x == '<') //Beginning Marker
      {
        flag1 = true;          //L4:
      }
    }
    else     //L5:
    {
      x = Serial.read();
      if ( x != '>')     //L6: Ending Marker
      {
        dataBuffer[bufferIndex] = x;  //L7:
        bufferIndex++;
      }
      else
      {
        Serial.println(dataBuffer);  //L8:
        bufferIndex = 0;
        flag1 = false;
      }
    }
  }
}

flow8z.png

Update: Some more debugging showed that the problem is in the Serial.availble() function. In setup() as in loop() it retuns always 1 (or 0), regardless of the number of characters entered. So, as a workaround I retrieved the 1st character separately and then checked if still more character are available to continue the while loop. This worked... intill I removed the debugging (Serial.print) statements. So it broke again! The loop is terminated after the 1st character...

I don't understand the lgic behind this... :-(

// ========= Test on Serial.available ======================

include "Arduino.h"

//int Idx = 0; // Init buffer //char Buf[80]; //

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

Serial.println("------------------------"); int chars = Serial.available(); if ( chars == 0 ); Serial.println("Waiting for serial input (in setup)"); while (Serial.available() == 0); // Wait for serial unput Serial.print ("Serial.available = "); Serial.println(Serial.available());

Serial.println("Now reading serial input");

int Idx = 0; char Buf[80] = {""}; while (Serial.available()) // Get chars in buffer { if (!Idx) { Buf[Idx++] = Serial.read(); // Get 1st char Buf[Idx] = 0; // terminate string Serial.print ("1st char = "); Serial.println(Buf); } Serial.print ("Still available = "); Serial.println(Serial.available()); if (Serial.available()) { Buf[Idx++] = Serial.read(); // Get next char Buf[Idx] = 0; // terminate string } }; Serial.println(Buf);

}

void loop() { while (1);

}

Input = 123456

Waiting for serial input (in setup) Serial.available = 1 Now reading serial input 1st char = 1 Still available = 5 Still available = 4 Still available = 3 Still available = 2 Still available = 1 123456

In setup() as in loop() it retuns always 1 (or 0), regardless of the number of characters entered

This should not be a surprise as serial comms is very slow, particularly at 9600 baud. You check whether at least once character is available then read from the buffer while data is available, but what if more data has not yet arrived ? The while loop will end and you will have an incomplete message.

You would be well advised to read Serial input basics - updated and note the techniques used to determine that a complete message has been received. Such code does not have to be in setup() and, in fact, it belongs in loop() where the function can do the looping instead of using a blocking while.

@OP

For years and years, the educationists have researched on the issue of finding optimum methodology for Institutional Teaching and Self Teaching.

The Self Teaching Methodology emphasizes that a learner should first play around with an example that works. After that he should understand the rationality of the statements of that example and then he should try to devise his own way of providing an alternative solution of the given example.

The way you are trying to understand the 'UART Reception Logic' is very unlikely to bring you to an understanding level. You may look at the 'UART Reception Logic' example of Post#5 where every label of the Flow Chart has been clearly marked in the corresponding coding sketch; you might be immensely helped!

OK, this is it! Right after the statement: "while (Serial.available() == 0);", there's only one character available. The others are still to come. chars = Serial.available() returns 1 After a delay(100) it returns 6 (input was 123456) The Serial.read in the "while (Serial.available()) " works correct now. Thanks for the help!

@ GolamMostafa Yes, You may have a point, but... The internal Serial interface on the Arduino system is a simulation. So I assumed when the "send" or "enter" was pressed the entire string was passed to the interface. Aparently it simulates also the timing of the serial communication, sending charachter per character at the communication speed. I did not expect this... Btw. I used to work a lot with serial communication, some years ago... ;-)

richard_delvaux:
So I assumed when the “send” or “enter” was pressed the entire string was passed to the interface.

1. In the InputBox of the 9600 Bd Serial Monitor, we enter <12345> and then press the Send button. After that the following events occur:

(1) The PC sends the ASCII Code of < which is (00111100 = 0x3C) in a 10-bit frame called UART Frame where LSBit is transferred first. The frame transmission time from PC to UNO is about : 10x1/9600 = 1041 us. The PC will send 7 such frames one after another at 1041 us apart.

(2) The UNO receives a frame and finishes the processing far less that 1041 us. The 8-bit character enters into the Receiver Section of the UART Module of the MCU and interrupts the processor. The processor goes to the ISR (Interrupt Sub Routine) and saves the data (character) byte into a 64-byte wide FIFO Buffer.

(Assume that we not reading data/character from the FIFO Buffer)

(3) The next character 1 enters into the next location of the FIFO Buffer and so on.

(4) The Arduino Platform has given us (that’s why, the Arduino is so popular to the Logicians) us a function called byte x = Serial.available(); by which we can check how many charcaters so far been accumulated in the buffer.

(5) Arduino has also given us another function called byte y = Serial.read(); by which we can transfer a data/character from the FIFO buffer (the character that has entered first in the FIFO will come out first) into the variable x for processing.

2. It is hoped that the above points are enough to clarify the meanings of the following commands:

if(Serial.available()>0)
{
   //do as needed
}
//--------------------
if(Serial.available() == 3)
{
   //do as needed
}

The internal Serial interface on the Arduino system is a simulation.

I don't know what you mean by this. If you are referring to the Serial monitor it is neither internal or a simulation

After a delay(100) it returns 6 (input was 123456)

Using a delay() to wait for serial data, and then assuming that it has arrived, is the poorest way of handling serial data.

Don't use delay to wait for characters. Implement a buffer and wait for any one of (or combination of) expected number of bytes received, known end-of-message delimiter character/byte, character timeout etc.

What you use will be based in part on the structure of the message you're sending.

One way is to call the receive routine from loop(), add characters (if any) to the buffer there and look and EOM conditions. Return a flag so loop() knows a complete serial message is waiting.

Perhaps something like this simple framework:

#define BUF_SIZE    32
char
    RxBuffer[BUF_SIZE];
byte
    bufHeadPtr,
    bufTailPtr;
bool
    bOverrun;
       
void setup() 
{
    Serial.begin(9600);
    bufHeadPtr = 0;
    bufTailPtr = 0;
    bOverrun = false;
    
}

void loop() 
{
    RxSerChars();
    while( bufHeadPtr != bufTailPtr )
    {
        Serial.print( RxBuffer[bufTailPtr] );
        bufTailPtr = (bufTailPtr + 1) & (BUF_SIZE-1);
    }
    
}

bool RxSerChars( void )
{
    static byte
        bCount=0;
    static unsigned long
        timeRXOut=0;

    if( bCount > 0 )
    {
        if( (millis() - timeRXOut > 50 ) )
        {
            //sender has stopped sending characters
            bCount = 0;
            return true;
            
        }//if
        
    }//if
    
    if( Serial.available() > 0 )
    {
        timeRXOut = millis();
        RxBuffer[bufHeadPtr] = Serial.read();
        bufHeadPtr = (bufHeadPtr + 1) & (BUF_SIZE-1);
        if( bufHeadPtr == bufTailPtr )
            bOverrun = true;
        return false;    
    }//if
    
}//if