Using Serial.Begin() in void loop()

Hello everyone,
TL;DR: Is it okay to use Serial.Begin() and serial end in every void loop?

ill describe the problem in detail now.
For my finals project im making a simple electronic parking lot gate system.
One of the options to open the gate is using bluetooth(im using the HC-05 model).
When the parking lot is full all the systems responsible for opening the gate need to be paused until a parking space is cleared, my problem was whenever nothing was functional and i press the button to open the gate using bluetooth, so the second the space was cleared and it got back to working it would open the gate, my guess was the data was accumulating in the serial buffer, so i stopped the serial communication using Serial.end(Since serial.flush doesnt clear the buffer anymore) and for some reason if i would just use Serial.begin() just once afterward the communication wouldnt start, it worked when i put it in the start of void loop(). is there any better way to clear the serial.buffer? or is it okay to stop communication and start it again in every cycle of the loop?

thanks in advance for all the answers, sorry for the long read, ill add the snippets in my code that are relevant, feel free to ask for more information.

void bluetoothGate()
{
  if(Serial3.available() > 0)           //Wait for user input
  { 
    int DataReceived = Serial3.read();  //Read user input and hold it in a variable // Bluetooth data variable
    if(char(DataReceived)=='1')//if the input is '1'
    {
      Serial.print(DataReceived
      );
      gateOpenSequence();
      Serial3.end();
    }
    Serial.print("AfterBluetooth");
  }      

}
void loop()
{

  Serial3.begin(9600);//starting Bluetooth serial each time, because we shut it off once the parking lot is full.
  EEPROM.write(carCountAdress, carCount);//write to eeprom in every loop how much cars are in the parking, so we can keep track incase the power goes down

   if(digitalRead(IrExit)==LOW){//when the exit ir detects a car, it opens the leave gate.
      leaveGate();
    }
  if(carCount==-1){
    carCount=0;
  }
  if(carCount==12)//when parking lot is full. first do this.
  {
    Serial3.end();//stop bluetooth communication to prevent information coming in while system in unavailable
    
    //I use this few functions to edit and decorate the LCD i will explain them once here.
    lcd.setCursor(0,0);//setting the cursor(where we are typing at the moment) to the 0,0 meaning the first collumn in the first row.
    lcd.print("|------------------|");//<<--printing this on the LCD, this will appear on the entire first row
    lcd.setCursor(0,1);//setting the cursor(where we are typing at the moment) to the 0,1 meaning the first collumn in the second row.
    lcd.print("|      SORRY       |");//<<--printing this on the LCD, this will appear on the entire second row. etc etc
    lcd.setCursor(0,2);
    lcd.print("|  PARKING IS FULL |");
    lcd.setCursor(0, 3);
    lcd.print("|------------------|");
    while(carCount==12){// and as long as its full do nothing other than wait for the ir to sense someone leaving.
      
      if(digitalRead(IrExit)==LOW){
        leaveGate();
      }
    }
  }

If no space is available just read the request and ignore it or read it and send back a suitable message. No need to stop/start Serial

I dont think i quite understand what you mean.
I use a while loop to stop the whole code from working while the parking lot is full. the second it goes back to the normal loop and goes to check whats up with the bluetooth, it sees there is data waiting at the buffer and start reading each one, which is a big problem.

writing again cause i forgot to press reply :sweat_smile:

There might be a suitable alternative but without your entire ketch it is hard to tell. Can you please post your entire sketch?

1 Like

My entire sketch is quite long, but i will add it, this is my first project, if you find any flaws other than the subject , i will be happy to accept all the notes i can.

//including libraries.
#include <LiquidCrystal.h>  //Lcd functions library
#include <Servo.h>          //Servo Motor
#include <TM1637.h>        //4 digit display functions library
#include <SPI.h>          //Serial Peripheral interface protocol library. needed for rfid
#include <MFRC522.h>      //rfid functions library.
#include <EEPROM.h>






//DEFINING PINS
//pins are declared as CONST since they need to stay constant throughout the code
const int rs = 38, en = 39, d4 = 36 ,d5 = 37, d6 = 41, d7 = 40;//Defining LCD PINS
const int DA=31, D0=30,D1=33,D2=32,D3=35;//Defining KEYPAD data PINS
const int servoEntrancePin=43, servoExitPin=34,buzzer=42, CLK=49,DIO=48;//Defining(By order): servo pin, buzzer pin, 4DD pins
const int IrEntrance=29,IrExit=28,IrExitHold=27;
const int SS_PIN=45,RST_PIN=44;
const int carCountAdress=5;

//Declaring variables that are later on used to count several things. I will not
//im declaring most here since they need to be global/they are used in the loop()
//and i do not want them to declare themselved on every run of the loop..

int keysPressedIndex = 0;//Index for keysPressed array
int passwordCorrectCounter=0;//this cnt is used later on to make sure all 5 chars are equal.
int cnt2=0;//used for switch case to count how many chars were already received
int carCount=0;//Keeps track of the amount of cars that are in the lot.
int rfidCnt=0;//Used to check if UID matches UID in AllowedUID array.
char charKey=0;//this is where we place the char received from the keypad
int key;  //variable to store what key was pressed from 0-15, 


char password[5] = {'8', '5', '2', '0', '#'};
char keysPressed[5] = "";

//this is for beauty purposes only, can be removed easily and wont affect the code*
//(this is the heart and smiley in our lcd)*-considering you remove the lines that refer
//to that...

byte smileyChar[] = {
  B00000,
  B00000,
  B01010,
  B00000,
  B10001,
  B01110,
  B00000,
  B00000
};


byte heartChar[] = {//Custom Char for lcd screen, in this case; a heart.  
  B00000,
  B00000,
  B01010,
  B11111,
  B11111,
  B01110,
  B00100,
  B00000
};

//an object is an instance(מופע) of a class(מחלקה) so using LiquidCrystal 
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);//Creating LCD object
MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 object.

Servo servoEntrance;//creating Servo object for the entrance servo
Servo servoExit;//creating Servo object for the exit servo

TM1637 fourDigit(CLK, DIO);//creating a TM1637 4DD object

byte allowedUID[4][4]={{0xA3,0xF3,0x3E,0x94},{0xBA,0xFA,0x22,0x28},{2,2,2,2},{3,3,3,3}};//array of UID(rfid chips) that can open the gate
void setup()
{
  Serial.begin(9600);//starting Serial communication for serial monitor
  Serial3.begin(9600);//Starting Serial3 communication to communicate with bluetooth w UART protocol
  SPI.begin();//Starting SPI communication protocol
  pinMode(DA,INPUT);//setting all keypad pins to input since we want to get information from those pins(what key was pressed?) DA is interrupt pin D0-3 Are data pins.
  pinMode(D0,INPUT);
  pinMode(D1,INPUT);
  pinMode(D2,INPUT);
  pinMode(D3,INPUT);

  pinMode(IrEntrance,INPUT);//setting all IR pins as input, since we want to get information from them(Is something infront of them?)
  pinMode(IrExit,INPUT);
  pinMode(IrExitHold, INPUT);

  mfrc522.PCD_Init();   // Initiate MFRC522
  
  

  lcd.begin(20,4);//intializes the lcd interface, giving the size of our LCD (20x4)
  lcdSetup();//Self Created function that turns lcd to our preffered default screen "Home screen"

  servoEntrance.attach(servoEntrancePin);//Attaching the servo object to the pin. A built in function from the Servo.h library
  servoExit.attach(servoExitPin);
  
  servoEntrance.write(10);//giving both servos setup degree, where the servos start when the 
  servoExit.write(105);
  
  fourDigit.init();//intializing four digit
  fourDigit.set(7);//setting 4DD brightness to max


  pinMode(buzzer,OUTPUT);//setting buzzer as output, so we can tell it what to do.
  
  lcd.createChar(2, smileyChar);//creating custom char for the LCD. The lcd can hold up to 8 custom chars.
  lcd.createChar(1, heartChar);
  
  carCount=EEPROM.read(carCountAdress);//Reading from eeprom memory, getting how much cars were in the parking before power down

  digitalWrite(buzzer, HIGH); //running a quick beep to hear we finished the setup.
  delay(200);          
  digitalWrite(buzzer, LOW);

}

void loop()
{

  Serial3.begin(9600);//starting Bluetooth serial each time, because we shut it off once the parking lot is full.
  EEPROM.write(carCountAdress, carCount);//write to eeprom in every loop how much cars are in the parking, so we can keep track incase the power goes down

  
  fourDigit.display(0,((12-carCount)/10));//we can write to each 7 segment display a digit, so we split our number of avaliable spots and unavaliable and display in each segment
  fourDigit.display(1,((12-carCount)%10)); 
  fourDigit.display(2,carCount/10);
  fourDigit.display(3,carCount%10);
 

    if(digitalRead(IrExit)==LOW){//when the exit ir detects a car, it opens the leave gate.
      leaveGate();
    }
  if(carCount==-1){
    carCount=0;
  }
  if(carCount==12)//when parking lot is full. first do this.
  {
    Serial3.end();//stop bluetooth communication to prevent information coming in while system in unavailable
    
    //I use this few functions to edit and decorate the LCD i will explain them once here.
    lcd.setCursor(0,0);//setting the cursor(where we are typing at the moment) to the 0,0 meaning the first collumn in the first row.
    lcd.print("|------------------|");//<<--printing this on the LCD, this will appear on the entire first row
    lcd.setCursor(0,1);//setting the cursor(where we are typing at the moment) to the 0,1 meaning the first collumn in the second row.
    lcd.print("|      SORRY       |");//<<--printing this on the LCD, this will appear on the entire second row. etc etc
    lcd.setCursor(0,2);
    lcd.print("|  PARKING IS FULL |");
    lcd.setCursor(0, 3);
    lcd.print("|------------------|");
    while(carCount==12){// and as long as its full do nothing other than wait for the ir to sense someone leaving.
      
      if(digitalRead(IrExit)==LOW){
        leaveGate();
      }
    }
  }
  

  bluetoothGate();//A function to check if someone has opened the gate using bluetooth

  rfidGate();//A function to check if someone has opened the gate using RFID

  while(digitalRead(DA)==1){         //While recieving characters from keypad.
     key =digitalRead(D3)*8 + digitalRead(D2)*4 +digitalRead(D1)*2 + digitalRead(D0);//We get from the keypad data send in parallel as a binary number, we convert that to decimal and give it to our variable
    returnKey(key);//We give the decimal number of the key pressed from 0-15 indicating location of key pressed, this function converts key pressed to actual value of key pressed and puts in the variable charKey.
    
    if(charKey!='D'){//if the key pressed isnt the D button
      keysPressed[keysPressedIndex]=charKey;//Placing key pressed into an array
      keysPressedIndex++;//raising array index
    }
    else{//if it is D
      keysPressed[keysPressedIndex]="";//delete the key pressed before
      keysPressedIndex--;//deduct one from the arrayIndex
     }

    switch(cnt2){//Switch case just to print on LCD the key pressed and * beforehand
      case 0:
        if(charKey != 'D'){
          lcd.clear();
          lcd.setCursor(9, 0);
          lcd.print(keysPressed[keysPressedIndex-1]);
          cnt2++;
          Beep();
          break;
        }
        else break;
      case 1:
        if(charKey!='D'){
          lcd.setCursor(8,0);
          lcd.print("*");
          lcd.print(keysPressed[keysPressedIndex-1]);
          cnt2++;
          Beep();
          break;
        }
        else{
          lcd.clear();
          lcdSetup();
          cnt2--;
          break;
        }

      case 2:
        if(charKey!='D'){
          lcd.setCursor(8, 0);
          lcd.print("**");
          lcd.print(keysPressed[keysPressedIndex-1]);
          cnt2++;
          Beep();
          break;
        }
        else
        {
          lcd.clear();
          lcd.setCursor(9,0);
          cnt2--;
          lcd.print(keysPressed[keysPressedIndex-1]);
           break;
        }

      case 3:
        if(charKey!='D')
        {
          lcd.setCursor(8, 0);
          lcd.print("***");
          lcd.print(keysPressed[keysPressedIndex-1]);
          cnt2++;
          Beep();
          break;
        }
        else{
          lcd.clear();
          lcd.setCursor(9,0);
          lcd.print("*");
          cnt2--;
          lcd.print(keysPressed[keysPressedIndex-1]);
          break;
        }
      case 4:
        if(charKey!='D')
        {
          cnt2=0;
        }
        else
        {
          lcd.clear();
          lcd.setCursor(9, 0);
          lcd.print("**");
          cnt2--;
          lcd.print(keysPressed[keysPressedIndex-1]); 
        }
      break;
    }
    while(digitalRead(DA)==1);//Making sure we wont get double input from one press, forcing the loop to stay there as long as a key is pressed

    


    if (keysPressedIndex == 5)//when count reaches 5, meaning array is full check if array 'password' is equal to the 5 keys the user just entered.
    {

      
      for (int i = 0; i < 5; i++)
      {
        if (password[i] == keysPressed[i])//if keys are the same add one to pasSwordCorrectCounter, if pasSwordCorrectCounter equals to 5, it means all keys were the same
        {
          passwordCorrectCounter++;
        }
      }

        //
        if(passwordCorrectCounter==5){
          gateOpenSequence();
          passwordCorrectCounter=0;
        }
        else{
          wrongPassword();
          passwordCorrectCounter=0;
        }

        }
      }
      
  
}


void returnKey(int key)//gets what key location was pressed and returns actual key pressed
{ 
  char arrkey[16]={ '1','2','3','A','4','5','6','B','7','8','9','C','*','0','#','D' };
  charKey=arrkey[key];
  
}

void lcdSetup(){
  lcd.setCursor(0,0);
  lcd.write(2);
  lcd.setCursor(1, 0);
  lcd.write(1);
  lcd.setCursor(18,0);
  lcd.write(1);
  lcd.setCursor(19,0);
  lcd.write(2);
  lcd.setCursor(2,0);
  lcd.print("----Welcome-----");
  lcd.setCursor(0,1);
  lcd.print("|  Enter password  |");
  lcd.setCursor(0,2);
  lcd.print("|and then press '#'|");
  lcd.setCursor(0, 3);
  lcd.print("|------------------|");
}

void leaveGate()//opens gate and changes the amount of cars in the lot
{
  

  lcd.print("|------------------|");
  lcd.setCursor(0,1);
  lcd.print("|     GoodBye!     |");
  lcd.setCursor(0,2);
  lcd.print("|                  |");
  lcd.setCursor(0, 3);
  lcd.print("|------------------|");

  keysPressedIndex = 0;
  cnt2=0;
  
  servoOpen("Exit");
  delay(500);
  carCount--;
  while(digitalRead(IrExitHold)==LOW);
  servoDown("Exit");
  lcdSetup();


}

void gateOpenSequence()
{
  carCount++;

  int start = 0;
  int desired = 105;

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("|------------------|");
  lcd.setCursor(0,1);
  lcd.print("| Correct Password |");
  lcd.setCursor(0, 2);
  lcd.print("|     Welcome      |");
  lcd.setCursor(0, 3);
  lcd.print("|------------------|");

  keysPressedIndex = 0;
  cnt2=0;
  digitalWrite(buzzer,HIGH);
  servoOpen("Entrance");
  delay(500);
  servoDown("Entrance");
  lcdSetup();
  delay(10);
  
}
void wrongPassword()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("|------------------|");
  lcd.setCursor(0,1);
  lcd.print("|  Wrong Password  |");
  lcd.setCursor(0, 2);
  lcd.print("|     Try Again    |");
  lcd.setCursor(0, 3);
  lcd.print("|------------------|");
  keysPressedIndex = 0;
  for(int jj=0;jj<3;jj++)
  {
    Beep();
  }
  delay(200);
  lcdSetup();

}

void Beep(){
  digitalWrite(buzzer,HIGH);
  delay(200);
  digitalWrite(buzzer,LOW);
  delay(200);
}
void servoOpen(char gate[10])//Function that opens servo gate depending on the string it gets. if its gets "Exit" it opens the exit gate and if it gets "Entrance" the other opens.
{  
  if(gate=="Exit")
  {
    int start=105;
    servoExit.write(start);
    while(start>10)
    {
      servoExit.write(--start);
      delay(10);
    }   
  }
  if(gate=="Entrance")
  {
    Serial.print("servoOpen entrance: ");
    int start=5;
    servoEntrance.write(start);
    while(start<105)
    {
      servoEntrance.write(++start);
      delay(10);
    }  
  }
}
void servoDown(char gate[10])
{  
  if(gate=="Exit"){
    int start=10;
    servoExit.write(start);
    while(start<105){
      servoExit.write(start);
      if(digitalRead(IrExitHold)==LOW){
        servoExit.write(10);
        start=10;
      }
     servoExit.write(++start);
    delay(10);
    }
  }

  if(gate=="Entrance"){
    int start=105;
    servoEntrance.write(start);
    while(start>5){
      servoEntrance.write(start);
      if(digitalRead(IrEntrance)==LOW){
        servoEntrance.write(105);
        start=105;
      }
      servoEntrance.write(--start);
      delay(10);
    }
  }
  digitalWrite(buzzer,LOW);
}

void bluetoothGate()
{
  if(Serial3.available() > 0)           //Wait for user input
  { 
    int DataReceived = Serial3.read();  //Read user input and hold it in a variable // Bluetooth data variable
    if(char(DataReceived)=='1')//if the input is '1'
    {
      Serial.print(DataReceived
      );
      gateOpenSequence();
      Serial3.end();
    }
    Serial.print("AfterBluetooth");
  }      

}
void rfidGate()
{
  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
    byte buffer[4];
    int i,j;
    for (i = 0; i < 4; i++) {
      buffer[i] = mfrc522.uid.uidByte[i];
    }
    mfrc522.PICC_HaltA();
    Serial.print("UID: ");
    for (byte i = 0; i < 4; i++) {
      Serial.print(buffer[i],HEX);
      Serial.print(" ");
    }
    Serial.println("Hello");
    for(i=0;i<5;i++){
      for(j=0;j<4;j++){
        // Serial.print("Allowed: ");
        // Serial.println((int)allowedUID[i][j]);
        // Serial.print("Buffer: ");
        // Serial.println((int)buffer[j]);
        if(buffer[j]==allowedUID[i][j]){
          rfidCnt++;
        }
        
      }
   }
    if(rfidCnt==4){
      gateOpenSequence();
      rfidCnt=0;
    }
    else{
      wrongPassword();
      rfidCnt=0;
    }
  }
}

You call the begin() method every time, without regard to whether you did or did not previously call end().

That may not be a problem, but it would look better if you tracked whether you had ended the connection, and only begin it again if it had ended.

You can use a variable as a "flag" that just indicates the state of the connection. If it is already, don't. Don't either begin or end it.

You stick in a while loop, as @UKHeliBob suggests why not just soak up anything that you get over the serial connection and throw it out or respond appropriate like "request refused"?

You code looks very nice so far. Ppl will tell you all kinds of ways to do it differently (and by implication better).

Sticky while loops and the use of delay() to perform all timing limits the flexibility of your sketch. You may find this the hard way when the "just one more" desirable feature is nearly impossible to accommodate.

When that happens, we here to steer you onto the next level. :wink:

HTH

a7

So don't do that. Read the Bluetooth data whether or not there are any free spaces. If there are no free spaces then ignore the data

Tbh it is my full intention to block the entire code, there should be nothing else functioning while the parking is full, except the part where it detects someone leaving and free up space. My actual problem is the Buffer filling up while that happening, I'll try to resolve it as Alto suggested with a flag.

First of all I really appreciate the feedback !!
The reason I didnt really use non blocking delay since other than the gates working each for their own I didnt have the reason to bother with it since the project didnt require perfect efficiency and usage, so I didnt bother myself with the logic that looked a bit tough :dizzy_face:

I used your suggestion with the flag and it obviously worked.
I feel extremely dumb for not thinking about it beforehand :laughing:
Ill keep the topic open just to get extra feedback on the code from people who drop by.

I appreciate the help and the feedback, thanks alot alto !

Whatever works! There is no right or wrong in the end. I am obsessed with not doing anything that is already done, even turning on an LED that is already on. So the idea was near the front of my brain.

Sooner later you will run yourself into circumstances where it will be essential to tackle the alternate logic. It can appear a bit tough, and may well be, but there comes an Aha! moment after the third tutorial or maybe the first time you get something going right in front of you.

Truly the key to getting the most out of these wimpy micros.

a7

Yep, but I'm sorry to say that I suspect it's exactly the reason of a code becoming hard to change and/or expand in its functionalities. An efficiently structured code is a more simple way to make changes and add functionality. Just as an example, the behaviour you described should be better implemented by using a "finite state machine" approach. It's a tough topic to handle at first times, but as soon as you'll be "mentally set" you'll find everything extremely simpler than you now have.
And this include avoid the use of almost any delay() and internal while() loops, just define your states, add some "timers" (using millis()) when needed, and you're on the go!

PS: you said this is your first project, and it's a quite long sketch: usually a project with both of those conditions isn't a good thing. :wink: If you want to build a large project, it's better if it's not your first one, or, if it's the first one, start with simpler projects and proceed increasing the complexity (and/or make some smaller projects, one for each of the items involved (e.g. a sensor, a stepper, etc.). And at the end start building the code, adding one feature at the time. I don't know if you followed that process, anyway it's a good thing to remember for anyone. :sunglasses:

1 Like

how to clear the Serial buffer w/o ending Serial.

Note that Serial.flush() only waits until the outgoing serial buffer is empty before returning. If you want to clear the incoming serial buffer, you have to read from it until there's no more bytes to read. while (Serial.read() >= 0); will do the trick.

So flush blocks too.

Well, that will tell you when the receive buffer is empty, but not that you have likely stopped receiving input. You really way to ensure no characters are received for at least several character times, to ensure the sender is really done. Otherwise, you'll empty the receive buffer, and immediately exit the while loop, even though another character may well be in the process of BEING received.

1 Like

Yep, which makes it no worse than calling Serial.begin() each time around.

Personally, I would always read serial data and process it (even if it's to say "sorry, no more spots"), as @Delta_G mentioned, than to go through the hassle of throwing away incoming data like that.

I always find that the easiest way. And, wherever possible, use a simple, logical protocol that makes is EASY to locate the END of each command, be it CR, NL, or some other special character. You can then trivially discard one or more unwanted messages with very little overhead.

It is worse than not ending and beginning Serial in loop() as that likely involves dynamic memory allocation on an MCU.

I'm not sure what you're suggesting involves dynamic memory allocation, but while (Serial.read() >= 0); certainly does not. All that does is read a byte from the serial input buffer (or return -1 if it's empty).

Serial.begin() involves 32-bit division, so it's likely slower than calling Serial.read() many times, which makes Serial.read() "no worse" than Serial.begin() for clearing the serial input buffer.

By the way, from reading the AVR core source code, I noticed that Serial.begin() actually does not clear the input buffer, but Serial.end() does. So calling Serial.read() to clear the serial input buffer is infinitely better than Serial.begin() (unless you also call Serial.end() beforehand).

As long as you can ignore data then sure, whatever.