Emptying (Flushing) Serial1 INPUT Buffer

I'm using Pro Micro Leonardos and an HC-12 RF link via Serial1 ports, to control a walking robot.

I have good comms, with data packets "boxed" between STX & ETB markers, and generally good reception.

Problem is leg movements take several seconds. When the Mobile ProMicro returns from each leg action the Serial1 input buffer is full of "old" data from the Base Station (sending joystick & switch packets at approx 10 Hz rate).

I want to flush out the old stuff and start reading what the Base Station is sending "NOW" , but I have an indeterminate number of packets to read and clear before getting to current data.

I know the "Serial.flush" works on OUTPUT - is there anything similar for Serial INPUT ?

How much code do you think it would take for you to write something to read and toss what is in the buffer?
Paul

Serial read removes one byte from the receive buffer. Maybe repeatably read while there are bytes available in the buffer?

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

while (Serial1.read() > 0) {}

Warning: If characters are still arriving this will not guarantee that the buffer will be empty next time you look. To get closer to that:

while (Serial1.available()) {Serial1.read(); delay(10);}

while (Serial1.read() > 0) {delay(10);}

Thanks for all your responses - there is still a problem. The RF data packet is 18 ASCII chars "boxed" in with STX & ETB, so 20 chars total, arriving about every 100 mS.

If the "Leg Action" delay is less than 100 mS all is fine, If delay is greater then there are multiple poor reads, of 13 chars, 26 chars, etc, with the odd good one, but not really usable.

As far as I can see, once "GetAllRFText" has seen an STX, all activity should be local to this routine until the ETB is seen and the routine Returns to the main Loop, so it should get a good Data Packet.

It looks to me as if the Serial1 buffer is getting corrupted, overwritten or suchlike.

My "minimal code" trial is below:-


// for ProMicro Leonardo & HC-12 RF link
//  RF Serial port uses default Serial1 pins  ( Monitor uses USB serial port )

byte RF_CMD = 21;  // For RF Mode Changes, Normal HIGH, take LOW for commands
boolean GOT_RF_DATA, READING_DATA, GOOD_DATA;  // flags RF data

// following are RF Data Block delimiters
char RFStart    = 0x02;     // "STX" = Start of RF data block
char RFEnd      = 0x17;     // "ETB" = End of RF data block
char RFText[32];
byte NumChars, MaxChars = 31;
byte RF_MessageLength = 18;  // "Normal" RF message data length

// Simulated Leg Movement delay before next search for RF data
int LegDelay = 1000;

//--------------------- INITIAL SETUP 
void setup()
{
  Serial.begin(115200);              // start Leonardo Monitor Serial Port
  while (!Serial && millis() < 5000) // wait up to 5 seconds for Serial Port

 // Initialise HC-12 RF link for Serial Data reception via Serial1 port
  Serial1.begin(9600);
  pinMode(RF_CMD, OUTPUT);
  digitalWrite(RF_CMD, HIGH);    // HC-12 normal, transparent mode

  GOT_RF_DATA = false;  // clear flag for more data
  NumChars = 0;         // Empty the RF Character Array

}  //------- END OF INITIAL SET UP 

void loop()
{

  GetAllRFText();  // if any RF data available, read it

  if (GOT_RF_DATA == true)  // when we have a Data Packet from Base Station
  {
    PrintRFText();        // show RF Data Packet on Monitor

    // now finished with this data, clear Flags for next transmission
    GOT_RF_DATA = false;    // clear flag for more data
    NumChars = 0;           // Empty the Character Buffer
  }
  // now waste some time, e.g. moving our Legs
  Serial.print(" .... Start Delay ... ");
  delay(LegDelay);
  Serial.println(" ##### end delay ###");
}
//======== end of MAIN loop ===================================================

//##############################################################################
//  Read whole RF character string, "boxed" by RFStart, RFEnd codes.

void GetAllRFText(void)
{
  byte RF_Char;

  // first FLUSH the Input Buffer to clear "old" data
  while (Serial.available() > 0)
  {
    Serial.read();
  }



  // if any RF data now available,look for start character, then stay here until
  // whole packet is read

  if (Serial1.available())
  {
    RF_Char = Serial1.read();
    if (RF_Char == RFStart)
    { // Start of data seen, Flag this, then read till end of data
      READING_DATA = true;
      NumChars = 0;

      while (GOT_RF_DATA == false) // now read rest of packet
      {
        if (Serial1.available())
        {
          RF_Char = Serial1.read();

          if (RF_Char != RFEnd)  // this char is content of packet
          {
            RFText[NumChars] = RF_Char;
            NumChars++;
            if (NumChars >= MaxChars) NumChars = MaxChars - 1;
          }
          else                  // indicates end of data packet
          {
            RFText[NumChars] = '\0'; // terminate the data string
            READING_DATA = false;
            GOT_RF_DATA = true;
          }
        }
      }  // end of while(GOT_RF_DATA == false)
    }
  }
}
//##############################################################################
// print the RF text on Monitor
//##############################################################################
void PrintRFText(void)
{
  if (GOT_RF_DATA == true)
  {
    // Display on Local Monitor
    Serial.print(" Received Text >");
    Serial.print(RFText);
    Serial.print("< ");

    Serial.print(" > "); Serial.print(NumChars);
    if (NumChars != RF_MessageLength)
    {
      Serial.println(" <!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    }
    Serial.println();
  }
}
//=====  END OF ALL CODE ======================================================

Oops - in last posted code I missed the "1" in Serial1 for the Flush operation, but the code I tested was correct and failed as I noted. Further experiment has given an "acceptable" performance with a delay of 95 mS after the Flush operation.

I may explore UART Interrupts to improve things. Final code below.

t
// for ProMicro Leonardo & HC-12 RF link
//  RF Serial port uses default Serial1 pins  ( Monitor uses USB serial port )

byte RF_CMD = 21;  // For RF Mode Changes, Normal HIGH, take LOW for commands
boolean GOT_RF_DATA, READING_DATA, GOOD_DATA;  // flags RF data

// following are RF Data Block delimiters
char RFStart    = 0x02;     // "STX" = Start of RF data block
char RFEnd      = 0x17;     // "ETB" = End of RF data block
char RFText[32];
byte NumChars, MaxChars = 31;
byte RF_MessageLength = 18;  // "Normal" RF message data length

// Simulated Leg Movement delay before next search for RF data
int LegDelay = 2000;

//--------------------- INITIAL SETUP
void setup()
{
  Serial.begin(115200);              // start Leonardo Monitor Serial Port
  while (!Serial && millis() < 5000) // wait up to 5 seconds for Serial Port

    // Initialise HC-12 RF link for Serial Data reception via Serial1 port
    Serial1.begin(9600);
  pinMode(RF_CMD, OUTPUT);
  digitalWrite(RF_CMD, HIGH);    // HC-12 normal, transparent mode

  GOT_RF_DATA = false;  // clear flag for more data
  NumChars = 0;         // Empty the RF Character Array

}  //------- END OF INITIAL SET UP

void loop()
{

  GetAllRFText();  // if any RF data available, read it

  if (GOT_RF_DATA == true)  // when we have a Data Packet from Base Station
  {
    PrintRFText();        // show RF Data Packet on Monitor

    // now finished with this data, clear Flags for next transmission
    GOT_RF_DATA = false;    // clear flag for more data
    NumChars = 0;           // Empty the Character Buffer
  }
  // now waste some time, e.g. moving our Legs
  Serial.print(" .... Start Delay ... ");
  delay(LegDelay);
  Serial.println(" ##### end delay ###");
}
//======== end of MAIN loop ===================================================

//##############################################################################
//  Read whole RF character string, "boxed" by RFStart, RFEnd codes.

void GetAllRFText(void)
{
  byte RF_Char;

  // first FLUSH the Input Buffer to clear "old" data
  while (Serial1.available() > 0) {
    Serial1.read();
  }
  delay(95);

  // if any RF data now available,look for start character, then stay here until
  // whole packet is read

  if (Serial1.available())
  {
    RF_Char = Serial1.read();
    if (RF_Char == RFStart)
    { // Start of data seen, Flag this, then read till end of data
      READING_DATA = true;
      NumChars = 0;

      while (GOT_RF_DATA == false) // now read rest of packet
      {
        if (Serial1.available())
        {
          RF_Char = Serial1.read();

          if (RF_Char != RFEnd)  // this char is content of packet
          {
            RFText[NumChars] = RF_Char;
            NumChars++;
            if (NumChars >= MaxChars) NumChars = MaxChars - 1;
          }
          else                  // indicates end of data packet
          {
            RFText[NumChars] = '\0'; // terminate the data string
            READING_DATA = false;
            GOT_RF_DATA = true;
          }
        }
      }  // end of while(GOT_RF_DATA == false)
    }
  }
}
//##############################################################################
// print the RF text on Monitor
//##############################################################################
void PrintRFText(void)
{
  if (GOT_RF_DATA == true)
  {
    // Display on Local Monitor
    Serial.print(" Received Text >");
    Serial.print(RFText);
    Serial.print("< ");

    Serial.print(" > "); Serial.print(NumChars);
    if (NumChars != RF_MessageLength)
    {
      Serial.println(" <!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    }
    Serial.println();
  }
}
//=====  END OF ALL CODE ======================================================ype or paste code here

Nope, wrong approach. Too many newbies think interrupts are a get out of jail free card after they've painted themselves into a corner with poorly-structured blocking code. That's the case here. The solution is to keep the loop() function "looping" by breaking things up into small chunks and only doing what needs to be done (and can be done) on each iteration.

For example, if you don't yet have a complete message from the HC-12 and no more characters are yet available() from Serial1, then don't waste your time in that while() loop. Move on and try again the next time the loop() function iterates. Same thing with moving the legs. Don't "waste some time, e.g. moving our Legs". Do it in a non-blocking manner instead.

Thanks for these comments. This code is for a simple robot for teaching Primay School kids age 10/11.

The major activity is fore/aft and up/down movements of 6 legs (12 servos) which by the nature of the mass, inertia and servo resonance are relatively slow and require a lot of I2C messages and timing delays. The walking action is a "tripod" of legs balancing the robot so I don't want it to fall over.

Basically, every few seconds the requirement is to read the RF data arriving "NOW" and set off the next 2 - 4 second leg movements.

It would be a major exercise to keep reading and discarding the RF data when a simple Flush & read is required.

Here is an example of reading your RF data that doesn't block. It reads as much as it can and then returns. The main code deals with the packet when GOT_RF_DATA is true. You need to to similar things with your movement.

//##############################################################################
//  Read whole RF character string, "boxed" by RFStart, RFEnd codes.

void GetAllRFText(void) {
  // following are RF Data Block delimiters
  const char RFStart    = 0x02;     // "STX" = Start of RF data block
  const char RFEnd      = 0x17;     // "ETB" = End of RF data block

  static bool waiting = true;   // true = waiting for start, false = in packet reception
  static byte idx = 0;

  byte RF_Char;

  // if any RF data now available,look for start character
  // whole packet is read

  while (Serial1.available()) {
    byte RF_Char = Serial1.read();

    if ( waiting ) {
      if (RF_Char == RFStart) {
        // Start of data seen, Flag this, then read till end of data
        waiting = false;
        idx = 0;
        GOT_RF_DATA = false;
      }
    }
    else {
      if (RF_Char != RFEnd) {
        // this char is content of packet
        RFText[idx] = RF_Char;
        idx++;
        if (idx >= MaxChars) idx = MaxChars - 1;
      }
      else {
        // indicates end of data packet
        RFText[idx] = '\0'; // terminate the data string
        waiting = true;
        GOT_RF_DATA = true;
      }
    }
  }
}

You probably need some sort of flag to indicate you are busy moving and then, if the flag is set, just discard the RF packet else use it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.