Trouble with while(serial.available<=0){} function with timeout

I setup MEGA 2560 as master and have 3 slaves. I get slave ID from MEGA serial monitor and send it to slaves via rs485 bus from softserial port. And waiting for the right slave to respond. It works fine if I don’t have the timeout function in the while loop.
But when I try
ok=0;
while (mySerial.available()<=0){ // wait here to be sure to read and clear buffer
ok++;
if (ok>50000) break;
}
It will response to the last slaveID instead of the current Id I type in the serial monitor.
here is the complete code:

/*-----( Import needed libraries )-----*/ 
#include <SoftwareSerial.h>
/*-----( Constants and Pins  )---------*/
int cx = 8;   //DE/RE Controling pin of RS-485
int ledPin = 13;
/*-----( Declare objects )-------------*/
SoftwareSerial mySerial(10, 11); // RX, TX
/*-----( Declare Variables )-----------*/
unsigned int slave_id;
String incoming;   //Declare a String variable to hold your name
unsigned int ok;

void setup() {
  mySerial.begin(9600);//Using mySerial Port
  Serial.begin(9600);
  pinMode(cx, OUTPUT);
  pinMode(ledPin, OUTPUT);//Led Connected
}

void loop() {
  Serial.println("Please enter the slave ID ");        //Prompt User for input
  while (Serial.available()<=0)  {}
  slave_id=Serial.parseInt();                      //Read user input into slave ID
  digitalWrite(cx,HIGH);//DE/RE=HIGH Transmit Enabled M1
  mySerial.print(slave_id);//Write outch and Fetch Data From slave
  delay(3);                  //allows last 2 char send out 
                             // before turn off control pin
  digitalWrite(cx,LOW);//DE/RE=LOW Receive Enabled M1
  Serial.println(slave_id);
  getdata();
  
  Serial.println(ok);
  
  delay(2000);
  digitalWrite(ledPin,HIGH);//Led ON
  delay(2000);
 } // end loop
 
void getdata() {
ok=0;
  while (mySerial.available()<=0){   // wait here to be sure to read and clear buffer
  //ok++;
  //if (ok>50000) break;
  }
  if (ok<50000){
    incoming=mySerial.readString();       //Read slave
    Serial.println(incoming);
    Serial.print('\n');
   }else{
   Serial.println("There is something wrong "); 
   Serial.print('\n');
   } 
}

here is the image that works fine:

here is the image that not work properly:

Hope if any one interested to help. thanks
Felix

Mega_Code4.ino (1.55 KB)

setup.jpg

Images from Original Post so we don’t have to download them. See this Image Guide

setup.jpg

good.jpg

bad.jpg

…R

The first picture is attractive but no help for diagnostics.

Don't post text as an image - just copy and paste the messages so we can read them easily.

There is no need to use SoftwareSerial on a Mega. It has 3 spare HardwareSerial ports that perform properly.

And have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

...R

The output shown in “here is the image that not work properly:” can’t be from the sketch you posted so you must have changed something between the two pictures. The code would be easier to follow if the ‘ok’ variable was local to each function and not global. Try this:

/*-----( Import needed libraries )-----*/
#include <SoftwareSerial.h>
/*-----( Constants and Pins  )---------*/
const int cx = 8;   //DE/RE Controling pin of RS-485
const int ledPin = 13;
/*-----( Declare objects )-------------*/
SoftwareSerial mySerial(10, 11); // RX, TX
/*-----( Declare Variables )-----------*/
unsigned int slave_id;

void setup() {
  mySerial.begin(9600);//Using mySerial Port
  Serial.begin(9600);
  pinMode(cx, OUTPUT);
  pinMode(ledPin, OUTPUT);//Led Connected
}

void loop() {
  Serial.println("Please enter the slave ID ");        //Prompt User for input

  while (Serial.available() <= 0)  {}

  slave_id = Serial.parseInt();         //Read user input into slave ID

  Serial.print(F("Addressing slave ID: "));
  Serial.println(slave_id);

  digitalWrite(cx, HIGH); //DE/RE=HIGH Transmit Enabled M1

  mySerial.print(slave_id); //Write outch and Fetch Data From slave
  delay(3);                  //allows last 2 char send out
  // before turn off control pin

  digitalWrite(cx, LOW); //DE/RE=LOW Receive Enabled M1

  getdata();

  // Turn on an LED for two seconds.
  digitalWrite(ledPin, HIGH); //Led ON
  delay(2000);
  digitalWrite(ledPin, LOW); //Led ON
} // end loop

void getdata() {
  String incoming;   //Declare a String variable to hold your name
  int ok = 0;

  while (mySerial.available() <= 0) { // wait here to be sure to read and clear buffer
    ok++;
    if (ok > 50000) break;  // Timeout
  }

  if (ok < 50000) {
    incoming = mySerial.readString();     //Read slave
    Serial.print(F("Received \""));
    Serial.print(incoming);
    Serial.println(F("\" from slave."));
  } else {
    Serial.println("Input timeout from slave");
  }
}

John Wasser; The code you suggest is exactly what it doesn't work properly. The code that I post is with out the i++ and break statement. which respond the right slave. It doesn't the incoming local or not. I even try serial.flush in difference places and have the same not properly result. Still thanks for the help. Need to think more to solve this issue. Felix

Ditto Robin2' suggestion - get rid of SoftSerial, and use another hardware serial port.

Lose the (S)tring variable.

Ideally (later) learn to work without delay() after setup gas completed.

Testing the value of .available() only needs to handle ==0. it doesn't go negative. Blocking with .available() is ok for development, but not good style for reliability & recoverability.

Hi Mr. lastchance name; Why get rid of softserial? it was originally use serial1 in MEGA. It Only make things worse. And I am not sure what you suggested. I come to think of it after my first post. The problem seems lie in the variable slave_id get stack away when I use i++ and break statement. When I issue the right slave_id the next time. It use the one that it stack away. Do you think is it the case? the reason to use this timeout function is I don't want to wait forever if I ask the device that doesn't exit. If the device does exit. I don't need the 2 line of timing function and always work. I test that so many times. Thanks for help anyway. Felix

These suggestions weren’t specifically to address your program symptoms…

If you have hardware serial - it is a complete waste of cycles and reliability to implement a bit-bashing ‘soft’ serial interface.
There are zero benefits I can think of.
If Serial1 makes your program run worse, then you have a completely different problem than the choice of serial implementation. There is a code issue.

For non-blocking timeout - I suggest you look at timing your serial reception with millis(), and give up after the appropriate period as needed.

— OK here are some specific criticisms —

 while (Serial.available() <= 0)  {}
  slave_id = Serial.parseInt();         //Read user input into slave ID

Is fundamentally flawed. As soon as the user presses the first key, the parser will try to make an integer from that first character. It may work on two or more characters, but only due to the random alignment of the planets.

delay(3)

Simply means you are praying the remote device doesn’t respond in less than 3ms

 while (mySerial.available() <= 0) { // wait here to be sure to read and clear buffer
    ok++;
    if (ok > 50000) break;  // Timeout
  }

Is asking your program to wait for 50,000 numtySecs (undefined)

if (ok < 50000) {
    incoming = mySerial.readString();     //Read slave
    Serial.print(F("Received \""));
    Serial.print(incoming);
    Serial.println(F("\" from slave."));
  } else {
    Serial.println("Input timeout from slave");
  }

This is the afterthought to see if any characters were received.
No idea how many - but let’s process them anyway.

This general coding style is making an incorrect assumption that everything is synchronous - or at the least extremely deterministic.

The use of millis() tickers will ensure you always get the right data when it is full formed and ready to process.
Will also allow you to determine timeouts and break your code - when needed… No delay()s

Hi Mr. astchancename; thanks for the reply. Yes, I am sure I just want single char from the moment the key pressed and store it as int slave_id. The delay (3) is to ensure the serial port can send out the last char to the RS485 bus before the RS485 chip's get disable. In my screen picture, it shows every slave will respond(send back) with a string if it detect the right slave_id. I look at so many post in the pass few month. about the use of millis() funcction. RS485 hardware design consideration, RS485 programming tricks. Arduino hardware seril port interrupt, soft serial port pin interrupt, How serial buffer get filled up to 64 bytes, and how hardware interrupt take place first before serial buffer available. Yes, my code doesn't work. It's not what I think the way it suppose to work. I try millis() function in many place in my code. or beter, I try many logical way that I can think of. And did not solve the problem, or the result that a want to get. May be my programming way is completely wrong. there is a right way to get the result I want to see. That is way a post my problem and ask for help. Felix

OK, you're open to working through it. Now I understand that you're only expecting a [u]single[/u] character reply. Your existing RX code will work, but is inherently fragile.

The delay(3) is ok for the reason you describe - but is risky, as the slave may respond within that period. Luckily, the RxBuffer will absorb the early characters, but it would probably be better to monitor UCSRA: Bit 6: TxC – USART Transmit Complete Flag

Check out this page AVR MEGA UART registers

The issue of millis() over delay or timeouts is really important. The idea of blocking or non-blocking code is fundamental to asynchronous operation.

Hi Mr. lastchancename;
Thanks for the quick reply.
I may change to use the UNO as a master device which doesn’t have a second hardware serial port.
That is the reason to use sot serial to handle RS485 bus sen/receive.
Easy copy and paste the code later.
I would be happy and welcome to change my code if you see what is wrong or have a better way to do it.
May be it is easier I post the slave code too for you to debug it.
The slave check for serial input, then check for valid ID, if so respond with a string, or just flesh to LED and loop again.

/*-----( Import needed libraries )-----*/
#include <SoftwareSerial.h>
/*-----( Declare objects )-------------*/
SoftwareSerial mySerial(10, 11); // RX, TX
/*-----( Constants and Pins  )---------*/
int cx = 8;   //DE/RE Controling pin of RS-485
int ledPin = 13;
/*-----( Declare Variables )-----------*/
int inChar;

void setup() {
  mySerial.begin(9600);
  pinMode(ledPin, OUTPUT);//Led Connected
  pinMode(cx, OUTPUT);//DE/RE Controling pin of RS-485
}

void loop() { // run over and over
  digitalWrite(ledPin,LOW);//Led OFF
  digitalWrite(cx,LOW);//DE/RE=LOW Receive Enabled

   if(mySerial.available()){
    inChar=mySerial.parseInt();  // this is read in Integer 
   	if (inChar == 1){
	// send string:
	digitalWrite(cx,HIGH);//DE/RE=HIGH Transmit Enabled 
      mySerial.print("UNO Felix ");
      mySerial.print("Wong");
	  mySerial.print('\n');
	  delay(3);                  //allows last 2 char send out 
                             // before turn off control pin
	  digitalWrite(cx,LOW);//DE/RE=LOW Receive Enabled
     }
   } 
delay(2000);
digitalWrite(ledPin,HIGH);//Led ON
delay(2000);  
}

Thanks

Felix

I guess it all boils down to doing it, or doing it right.
You already have the MEGA board, and have been suggested some ways to make the code more responsive and reliable.
The next decision is up to you.

‘Bit bashing’ is exactly as it sounds… ‘bashing’ not an elegant solution to a high-resolution requirement.
It works for hobby applications, and in semi-pro solutions at lower bit-rates, but I wouldn’t use it.

Hardware-serial uses a crystal or resonator to determine the bit-durations.
The software-serial approach is susceptible to temperature variation, ISRs and other low-level ‘blocking’ code that may introduce jitter in the serial stream (or interrupt it all together).
Even your

while (mySerial.available() <= 0)

will interfere with the soft-serial clock

felixwmc:
I would be happy and welcome to change my code if you see what is wrong or have a better way to do it.

Have you carefully studied the examples in the link I gave you in Reply #2 ?

…R

Hi Guys; Problem solved. What a relief. Thanks Felix