Unexpectedly Slow Serial

I have a network of 20 Nanos all tied together on a serial bus (with a central 20-input NAND gate and bus drivers on the output). A master Nano sends out a token message and then the addressed remote unit replies. The token message is 4 bytes and reply messages can be 4-7 bytes long, with a maximum of 9 messages in one reply. At 9600 baud, a reply with 9 messages (a maximum of 60 bytes) should take 63 msec. To my consternation, it is taking 800 msec.

Here's the code from the responding remote unit. As you can see, the "send data" [S_data()] functions are adjacent lines of code and the S_data() function itself consists of immediately adjacent Serial.write() calls. I expect this code to be in blocking mode for 40-50 msec as the transmit buffer fills and empties, but the whole operation should take maybe 70-80 msec, with a little overhead for the function calls.

case 'K': { // token
	if(Serial.read() == UID) { // for this unit?
		S_data(T_Util) ;
		S_data(T_Utilmax) ;
		S_data(T_Utilmin) ;
		S_data(T_Bflr) ;
		S_data(T_Uflr) ;
		S_data(T_Foot) ;
		S_data(T_Footmax) ;
		S_data(T_Footmin) ;
	}
	S_done() ; // send "I'm finished" msg
}
break ;
--------------------------------
// ----------- Send an Integer -------------------------
void S_data(byte b) {
	Serial.write(0xAA) ; // start
	Serial.write("N") ;  // data (always an int)
	Serial.write(UID) ;  // from this unit
	Serial.write(b) ;    // index to data integer
	Serial.write(lowByte(vars[b])) ; // get byte
	Serial.write(highByte(vars[b])) ; // get byte
	Serial.write(0x04) ; // end of msg
}
// ------------ Send Done Msg ----------------------------
void S_done() {
	Serial.write(0xAA) ; // start
	Serial.write("D") ;  // done msg
	Serial.write(UID) ;  // from this unit
	Serial.write(0x04) ; // end of msg

The result is quite different. I have Mega on the network that watches the serial line and then reports those messages via the IDE serial monitor. Here is what the Mega sees as unit #2 responds to its token:

08:41:22.807 -> --Token-- msg =  --====-- 2  (this is a 4-byte message)
08:41:22.900 -> --Integer-- msg from 2       (this is a 7-byte message)
08:41:22.994 -> --Integer-- msg from 2
08:41:23.088 -> --Integer-- msg from 2
08:41:23.182 -> --Integer-- msg from 2
08:41:23.238 -> --Integer-- msg from 2
08:41:23.335 -> --Integer-- msg from 2
08:41:23.428 -> --Integer-- msg from 2
08:41:23.522 -> --Integer-- msg from 2
08:41:23.616 -> --Done-- 2                  (this is a 4-byte message from unit #2)
08:41:23.616 -> Msec to Done = 792          (total time from the token msg to the "I'm done" msg)

This is about 90 msec per message, more than 10x the time it should take to transmit 7 bytes. The total time of 792 msec has some variation due to the housekeeping loop in the Mega, as well as the housekeeping loop in the remote unit, both of which take 45-60 msec. So worst case, housekeeping can account for maybe 100 msec.

Where is the extra time? Is it in the Serial library in the IDE or in the UART interface in the chip? Is there a way to stuff bytes into the UART buffer so that they are transmitted as one continuous serial message without 80 msec gaps between bytes?

Even when there is only one reply message from a unit, eg, "done," the typical time from when a remote unit sees its token to when it has the full "done" message on the net is 86 msec, of which only 6 msec is the message transmit time. This means it can take 3-5 seconds to poll all the units, when the theoretical limit is on the order of 0.2 seconds. What the heck?

Dr Quark

Can you show the full sketch ? That will be three sketches (Master Nano, other Nano, Mega sniffer).
When someone complains that a sketch is slow, then all we have to do is look for delay(), or a syncing problem of serial input, or \r \n that lock up things. I suppose your sketch does not have those things, but maybe you overlooked something.

If you want to know about gaps between the bytes, then you need a logic analyzer. They can be very cheap (10 dollars), but I'm very fond of the 30 dollar LHT00SU1 with sigrok/PulseView.

Because an Arduino has a 64-byte buffer for the serial input, I can not be sure that your serial data has those timings.
When a single byte at the input causes the Mega to output ten bytes at the same baudrate, then the measured timings are all wrong.

Do you have a Leonardo or (Pro) Micro board ? Those use USB-HID serial communication which uses the USB speed and not the baudrate. That is a better board for a serial sniffer.

In case you wonder why I question so many things: that is what we do ! That is how we solve problems. We question everything that you take for granted :wink:

Thanks. I skinny’d the code down from 3,000 lines to 155 so I could look at just the serial xmit and rec functions. I’m beginning to suspect part (maybe all) of the problem is in the Mega sending plain text or the laptop receiving and displaying the test in the serial monitor.

Here’ the new master code:

//                 "01234567890123456789"
char this_file[] = "xmit" ;

bool Done_fl ;
bool sr_bad ;
unsigned long T_2 ;
// ------------------------------------------------------------------------
void setup() { // ---------------- Setup() --------------------------------

  Serial.begin(9600);  // network comm link
  
}

void loop() { 
	delay(2000) ;

	S_token(2) ;  // token for unit #2
	Done_fl = false ;
	T_2 = millis() ;
	do {
		chk_serial() ;
	// wait a max of 2 sec or valid "done" msg
	} while(((millis() - T_2) < 2000) && !Done_fl) ;

}
// ---------------end of loop() -------------------------------------------

// ------------------ chk_serial() -------------------------------------
void chk_serial() {
// this loop drops serial bytes until it finds a valid start byte
	 while (Serial.available() && !(Serial.peek() == 0xAA)) { //check for valid start bit
		Serial.read() ;      // no, so drop it
	 }
	 if(Serial.available() > 3) {
		sr_bad = false ; 
		Serial.read() ; // drop the start byte
		switch(Serial.read()) { // read the msg type byte
		
		// Done
			 case 'D': { // current UID is done with all messages
				if(Serial.read() == 2) { 
					Done_fl = true ;
				} else {
					sr_bad = true ;
				}
				if(Serial.read() != 0x04) sr_bad = true ; // drop the EOF
			 }
			 break ;
		// Default
			 default: 
				delay(6) ; // get all of the message
				if(Serial.available()) {
					while(Serial.peek() != 0x04) {
						Serial.read() ; // drop all
					}
					Serial.read() ; // drop the EOF
				}
		}
	}
}

// ------------ Send the Token ----------------------------
void S_token(byte b) {
	Serial.write(0xAA) ; // start
	Serial.write("K") ;  // token msg
	Serial.write(b) ;  // to that unit
	Serial.write(0x04) ; // end of msg
}

Here’s the new slave code:

//                 "01234567890123456789"
char this_file[] = "rec_ack" ;
const byte UID = 2 ;

byte index ;
byte hibyte ;
byte lowbyte ;

int vars[] = { 1,500,-2204,87,919} ;
int var ;
// --------------------------------------------
void setup() {
	
	Serial.begin(9600);  

}

void loop() { 
	
	chk_serial() ;   // watches for network messages

}
// ---------------end of loop() -------------------------------------------

// ------------------ chk_serial() -------------------------------------
void chk_serial() {
// this loop drops serial bytes until it finds a valid start byte
	 while (Serial.available() && !(Serial.peek() == 0xAA)) { //check for valid start bit
		Serial.read() ;      // no, so drop it
	 }
	 if(Serial.available() > 3) {
		Serial.read() ; // drop the start byte
		switch(Serial.read()) { // read the msg type byte
		
		// Token			 
			case 'K': { // token
				if(Serial.read() == UID) { // for this unit?
					// then send 8 integers
							S_data(1) ;
							S_data(0) ;
							S_data(3) ;
							S_data(4) ;
							S_data(2) ;
							S_data(1) ;
							S_data(3) ;
							S_data(0) ;
					S_done() ; // send "I'm finished" msg
				}
				Serial.read() ; // drop the eof
			 }
			 break ;

		// Default
			 default: 
				if(Serial.available()) {
					while(Serial.peek() != 0x04) {
						Serial.read() ; // drop all
					}
					Serial.read() ; // drop the EOF
				}
		}
	}
}

// ----------- Send an Integer -------------------------
void S_data(byte b) {
	while(Serial.availableForWrite() < 8) ;
	Serial.write(0xAA) ; // start
	Serial.write("N") ;  // data (always an int)
	Serial.write(UID) ;  // from this unit
	Serial.write(b) ;    // index to data integer
	Serial.write(lowByte(vars[b])) ; // get byte
	Serial.write(highByte(vars[b])) ; // get byte
	Serial.write(0x04) ; // end of msg
}

// ------------ Send Done Msg ----------------------------
void S_done() {
	while(Serial.availableForWrite() < 5) ;
	Serial.write(0xAA) ; // start
	Serial.write("D") ;  // done msg
	Serial.write(UID) ;  // from this unit
	Serial.write(0x04) ; // end of msg
}

serial monitor output in the next message

The output from the Mega monitor to the serial monitor in the IDE is below. The time from token to "done" is reliably 791-792 msec, and then all of a sudden there will be a 2 second stumble. But the time (according to the Mega) between the integer messages is still about 93 msec, no change.

15:31:26.711 -> %% Token-- msg =  --====-- 2
15:31:26.805 ->   Integer-- msg from 2
15:31:26.852 ->   Integer-- msg from 2
15:31:26.946 ->   Integer-- msg from 2
15:31:27.039 ->   Integer-- msg from 2
15:31:27.133 ->   Integer-- msg from 2
15:31:27.227 ->   Integer-- msg from 2
15:31:27.320 ->   Integer-- msg from 2
15:31:27.414 ->   Integer-- msg from 2
15:31:27.461 -> ## Done-- 2
15:31:27.461 ->   Token to Done = 792
15:31:27.555 ->   msec --------------
15:31:28.807 -> %% Token-- msg =  --====-- 2
15:31:28.854 ->   Integer-- msg from 2
15:31:28.948 ->   Integer-- msg from 2
15:31:29.042 ->   Integer-- msg from 2
15:31:29.135 ->   Integer-- msg from 2
15:31:29.229 ->   Integer-- msg from 2
15:31:29.323 ->   Integer-- msg from 2
15:31:29.416 ->   Integer-- msg from 2
15:31:29.510 ->   Integer-- msg from 2
15:31:29.557 -> ## Done-- 2
15:31:29.557 ->   Token to Done = 792
15:31:29.604 ->   msec --------------
15:31:30.914 -> %% Token-- msg =  --====-- 2
15:31:30.961 ->   Integer-- msg from 2
15:31:31.055 ->   Integer-- msg from 2
15:31:31.148 ->   Integer-- msg from 2
15:31:31.242 ->   Integer-- msg from 2
15:31:31.336 ->   Integer-- msg from 2
15:31:31.429 ->   Integer-- msg from 2
15:31:31.523 ->   Integer-- msg from 2
15:31:31.617 ->   Integer-- msg from 2
15:31:32.887 -> ## Done-- 2
15:31:32.887 ->   Token to Done = 1997
15:31:32.934 ->   msec --------------
15:31:32.934 ->   Bad done message, ID=2
15:31:32.980 ->   Integer-- msg from 2
15:31:33.074 ->   Integer-- msg from 2
15:31:33.168 ->   Integer-- msg from 2
15:31:33.262 ->   Integer-- msg from 2
15:31:33.355 ->   Integer-- msg from 2
15:31:33.449 ->   Integer-- msg from 2
15:31:33.543 ->   Integer-- msg from 2
15:31:33.637 ->   Integer-- msg from 2
15:31:33.683 -> ## Done-- 2
15:31:33.683 ->   Token to Done = 2811
15:31:33.777 ->   msec --------------
15:31:35.041 -> %% Token-- msg =  --====-- 2
15:31:35.088 ->   Integer-- msg from 2
15:31:35.182 ->   Integer-- msg from 2
15:31:35.275 ->   Integer-- msg from 2
15:31:35.369 ->   Integer-- msg from 2
15:31:35.463 ->   Integer-- msg from 2
15:31:35.557 ->   Integer-- msg from 2
15:31:35.650 ->   Integer-- msg from 2
15:31:35.744 ->   Integer-- msg from 2
15:31:35.791 -> ## Done-- 2
15:31:35.791 ->   Token to Done = 792
15:31:35.838 ->   msec --------------
15:31:37.011 -> %% Token-- msg =  --====-- 2
15:31:37.105 ->   Integer-- msg from 2
15:31:37.199 ->   Integer-- msg from 2
15:31:37.292 ->   Integer-- msg from 2
15:31:37.386 ->   Integer-- msg from 2
15:31:37.480 ->   Integer-- msg from 2
15:31:37.574 ->   Integer-- msg from 2
15:31:37.621 ->   Integer-- msg from 2
15:31:37.715 ->   Integer-- msg from 2
15:31:37.808 -> ## Done-- 2
15:31:37.808 ->   Token to Done = 787
15:31:37.855 ->   msec --------------
15:31:39.108 -> %% Token-- msg =  --====-- 2
15:31:39.201 ->   Integer-- msg from 2
15:31:39.295 ->   Integer-- msg from 2
15:31:39.391 ->   Integer-- msg from 2
15:31:39.485 ->   Integer-- msg from 2
15:31:39.578 ->   Integer-- msg from 2
15:31:39.663 ->   Integer-- msg from 2
15:31:39.757 ->   Integer-- msg from 2
15:31:39.851 ->   Integer-- msg from 2
15:31:39.898 -> ## Done-- 2
15:31:39.898 ->   Token to Done = 792
15:31:39.944 ->   msec --------------
15:31:41.201 -> %% Token-- msg =  --====-- 2
15:31:41.295 ->   Integer-- msg from 2
15:31:41.388 ->   Integer-- msg from 2
15:31:41.482 ->   Integer-- msg from 2
15:31:41.576 ->   Integer-- msg from 2
15:31:41.669 ->   Integer-- msg from 2
15:31:41.763 ->   Integer-- msg from 2
15:31:41.857 ->   Integer-- msg from 2
15:31:41.904 ->   Integer-- msg from 2
15:31:41.997 -> ## Done-- 2
15:31:41.997 ->   Token to Done = 792
15:31:42.044 ->   msec --------------
15:31:43.218 -> %% Token-- msg =  --====-- 2
15:31:43.311 ->   Integer-- msg from 2
15:31:43.405 ->   Integer-- msg from 2
15:31:43.499 ->   Integer-- msg from 2
15:31:43.593 ->   Integer-- msg from 2
15:31:43.686 ->   Integer-- msg from 2
15:31:43.768 ->   Integer-- msg from 2
15:31:43.862 ->   Integer-- msg from 2
15:31:43.909 ->   Integer-- msg from 2
15:31:44.003 -> ## Done-- 2
15:31:44.003 ->   Token to Done = 791
15:31:44.050 ->   msec --------------
15:31:45.348 -> %% Token-- msg =  --====-- 2
15:31:45.395 ->   Integer-- msg from 2
15:31:45.489 ->   Integer-- msg from 2
15:31:45.583 ->   Integer-- msg from 2
15:31:45.676 ->   Integer-- msg from 2
15:31:45.770 ->   Integer-- msg from 2
15:31:45.864 ->   Integer-- msg from 2
15:31:45.958 ->   Integer-- msg from 2
15:31:46.051 ->   Integer-- msg from 2
15:31:46.098 -> ## Done-- 2
15:31:46.098 ->   Token to Done = 792
15:31:46.145 ->   msec --------------
15:31:47.397 -> %% Token-- msg =  --====-- 2
15:31:47.491 ->   Integer-- msg from 2
15:31:47.584 ->   Integer-- msg from 2
15:31:47.678 ->   Integer-- msg from 2
15:31:47.772 ->   Integer-- msg from 2
15:31:47.866 ->   Integer-- msg from 2
15:31:47.959 ->   Integer-- msg from 2
15:31:48.053 ->   Integer-- msg from 2
15:31:48.100 ->   Integer-- msg from 2
15:31:49.386 -> ## Done-- 2
15:31:49.386 ->   Token to Done = 1996
15:31:49.433 ->   msec --------------
15:31:49.433 ->   Bad done message, ID=2
15:31:49.480 ->   Integer-- msg from 2
15:31:49.621 ->   Integer-- msg from 2
15:31:49.715 ->   Integer-- msg from 2
15:31:49.808 ->   Integer-- msg from 2
15:31:49.902 ->   Integer-- msg from 2
15:31:49.996 ->   Integer-- msg from 2
15:31:50.089 ->   Integer-- msg from 2
15:31:50.136 ->   Integer-- msg from 2
15:31:50.229 -> ## Done-- 2
15:31:50.229 ->   Token to Done = 2810
15:31:50.277 ->   msec --------------
15:31:51.529 -> %% Token-- msg =  --====-- 2
15:31:51.623 ->   Integer-- msg from 2
15:31:51.717 ->   Integer-- msg from 2
15:31:51.810 ->   Integer-- msg from 2
15:31:51.904 ->   Integer-- msg from 2
15:31:51.998 ->   Integer-- msg from 2
15:31:52.091 ->   Integer-- msg from 2
15:31:52.138 ->   Integer-- msg from 2
15:31:52.232 ->   Integer-- msg from 2
15:31:52.312 -> ## Done-- 2
15:31:52.312 ->   Token to Done = 791
15:31:52.359 ->   msec --------------
15:31:53.560 -> %% Token-- msg =  --====-- 2
15:31:53.654 ->   Integer-- msg from 2
15:31:53.748 ->   Integer-- msg from 2
15:31:53.794 ->   Integer-- msg from 2
15:31:53.888 ->   Integer-- msg from 2
15:31:53.982 ->   Integer-- msg from 2
15:31:54.076 ->   Integer-- msg from 2
15:31:54.169 ->   Integer-- msg from 2
15:31:54.263 ->   Integer-- msg from 2
15:31:54.309 -> ## Done-- 2
15:31:54.309 ->   Token to Done = 787
15:31:54.405 ->   msec --------------
15:31:55.654 -> %% Token-- msg =  --====-- 2
15:31:55.748 ->   Integer-- msg from 2
15:31:55.842 ->   Integer-- msg from 2
15:31:55.888 ->   Integer-- msg from 2
15:31:55.982 ->   Integer-- msg from 2
15:31:56.076 ->   Integer-- msg from 2
15:31:56.170 ->   Integer-- msg from 2
15:31:56.263 ->   Integer-- msg from 2
15:31:56.359 ->   Integer-- msg from 2
15:31:56.452 -> ## Done-- 2
15:31:56.452 ->   Token to Done = 791
15:31:56.499 ->   msec --------------
15:31:57.763 -> %% Token-- msg =  --====-- 2
15:31:57.810 ->   Integer-- msg from 2
15:31:57.904 ->   Integer-- msg from 2
15:31:57.998 ->   Integer-- msg from 2
15:31:58.091 ->   Integer-- msg from 2
15:31:58.185 ->   Integer-- msg from 2
15:31:58.279 ->   Integer-- msg from 2
15:31:58.369 ->   Integer-- msg from 2
15:31:58.462 ->   Integer-- msg from 2
15:31:59.756 -> ## Done-- 2
15:31:59.756 ->   Token to Done = 1997
15:31:59.756 ->   msec --------------
15:31:59.756 ->   Bad done message, ID=2
15:31:59.802 ->   Integer-- msg from 2
15:31:59.943 ->   Integer-- msg from 2
15:32:00.037 ->   Integer-- msg from 2
15:32:00.130 ->   Integer-- msg from 2
15:32:00.224 ->   Integer-- msg from 2
15:32:00.318 ->   Integer-- msg from 2
15:32:00.413 ->   Integer-- msg from 2
15:32:00.507 ->   Integer-- msg from 2
15:32:00.553 -> ## Done-- 2
15:32:00.553 ->   Token to Done = 2810
15:32:00.600 ->   msec --------------
15:32:01.875 -> %% Token-- msg =  --====-- 2
15:32:01.969 ->   Integer-- msg from 2
15:32:02.062 ->   Integer-- msg from 2
15:32:02.109 ->   Integer-- msg from 2
15:32:02.203 ->   Integer-- msg from 2
15:32:02.297 ->   Integer-- msg from 2
15:32:02.391 ->   Integer-- msg from 2
15:32:02.484 ->   Integer-- msg from 2
15:32:02.578 ->   Integer-- msg from 2
15:32:02.672 -> ## Done-- 2
15:32:02.672 ->   Token to Done = 792
15:32:02.719 ->   msec --------------

LHT00SU1 on order. Thanks.

Sorry, but I'm going to ignore what you measure with the Mega. I don't know if that is right.

About your master and the slaves: We don't wait in the loop(), we don't do while-loops that take time, we don't stay around in a timout, we don't need to use Serial.peek(), and we don't use delay() in a serious sketch.

Can you reduce the total amount of bytes that is transmitted to be below 64 ? You wait in the sketch when that buffer is full. Can you avoid that it will ever happen ? It can slow down a sketch so bad, that a sketch will be almost useless.

See the Arduino loop() as something that is going very fast and is executed hundreds times per second.
Meanwhile, there is incoming serial data at a slow rate.
As soon as serial data is received, get that byte (or bytes), and store it in a buffer. If the message is complete, then it can be processed. That keeps the loop() going as fast as possible.

It would be nice if the receiver would know the start and the end of the message. However, I think that 0xAA and 0x04 might be in the middle of a message as well.

Do the slaves only respond after they get a command ? Can you make the response with a fixed length ? That would make it easier to synchronize and create a timeout.

The sigrok/PulseView require an odd program called "zadig". Just follow the website and it will be allright: Windows - sigrok.
Turn the analog channel off in PulseView to be able to capture the digital inputs at a high rate.

Absolutely correct: “About your master and the slaves: We don’t wait in the loop(), we don’t do while-loops that take time, we don’t stay around in a timout, we don’t need to use Serial.peek(), and we don’t use delay() in a serious sketch.

Yes, there can be 0xAA and 0x04 inside a message, but (1) the 0xAA is a stab a dumping garbage if a slave restarts at some random time and (2) the 0x04 is only checked at the expected end of a message (all messages are of a specified length, so you know when it should be the next byte) as a pretty good check on message quality. All of the messages (except the alarm) are repeated every 6-10 seconds and they are mostly environmental temperatures, so nothing happens fast and a message can be missed without harm.

Now to the Mega. I took out all the messages to the serial monitor except the “token arrived” and the “done” time tag. In the previous 792 msec loop it decreased it by 1 msec to 791 msec.

More interesting, I set up a Nano with a display and had the Nano record it’s own timing for transmitting the 56 bytes in the 8 integer messages. With an empty transmit buffer, it took just under 1 msec for the eight S_data() functions (which each consist of seven Serial.write() calls) to stuff the write buffer and return to the main code. I also tried to estimate how long it took to empty the transmit buffer with the following two lines:

while(Serial.availableForWrite() < 62) ; // wait here until the buffer is almost empty
T_xmit = millis() - T_Token ; // T_xmit is the time to send 56 bytes

where T_Token = millis() just prior to the first S_data() call. Assuming the transmit buffer is 64 bytes long, this should provide a good, if not perfect, idea of how the UART is operating. Surprise, T_xmit is reported by the Nano to be 54-55 msec! The computed transmit time is 58 msec. So there doesn’t appear to be any delay in the Hardware.serial code or the UART.

The next step is to write similar code for the master and see what it measures from sending the Token to recognizing the “done” reply. Using the Mega for reporting the buss timing has obviously mislead me.

Now, to address the "inelegant" serial data checking: The system consists of two TTL serial networks, one in the house, one in the shop. There are a total of 223 slaves (so far). The TTL works pretty good over 60-70' within each building, but this is thunderstorm country, so I expect an occasional false start bit. The two networks are connected with a full duplex RS485 link that is 200' between the buildings.

I'd rather throw away a corrupted message or an empty message that is caused by a false start bit, than to incorporate bad data into the data array.

Maybe I'll clean it up as I get more confidence in the network. :slight_smile:

Of course there is no delay in the Serial library or in the hardware.
If you put something in the 64-byte software TX buffer with Serial.write() or Serial.print() or Serial.println(), then it will be transmitted in a interrupt.
If something is received, then it will be received in a interrupt and put in a software RX buffer of 64 byte. The Serial.read() reads that buffer.

The Arduino Mega and Arduino Nano use microcontrollers of the AVR family. They have only one byte RX hardware buffer. That means that interrupts may not be turned off for some time. How long it can be turned off depends on the baudrate.

A full software TX buffer (the buffer inside the Serial library) is a real problem.

In the old days, a message started with and with a checksum and a at the end and readable ascii for the data in the middle. That is still a good way to transfer a message.
Today, there is often a CarriageReturn or LineFeed or both at the end. That is with short distances.

There are tutorials and libraries, but you can also write everything yourself.
If you want a real protocol for RS-485, then there are libraries. However, I don’t know which library is good.

To keep the loop() running as fast as possible, you have to read the data. The basic principle is this:

void loop()
{
  // read one byte each loop() iteration
  if( Serial.available() > 0)
  {
    buffer[index] = Serial.read();
    index++;
  }
}

or this:

void loop()
{
  // allow to read multiple bytes per loop() iteration
  while( Serial.available() > 0)
  {
    buffer[index] = Serial.read();
    index++;
  }
}

It is the basic principle, because there must be checks for buffer overflow and a check for the end of message and a timeout.

One advantage of not reading the bytes into a user-defined Rx buffer is that when you read a message from the Arduino-provided buffer, the system keeps track of the pointers. Since my longest message is 60 bytes and the master ensures that there is only one message string at a time, the Rx buffer never gets overrun.

I did some test programs in the master. The housekeeping loops (writing to the lcd, monitoring the keyboard, reading temperatures, checking the Rx buffer) takes 40-55 msec in all the units. The master is reporting 80-220 msec from sending the token to reading the "done" message from the addressed slave. I can account for about 160 msec (50 msec on each end, depending on where the units are in their housekeeping loop, plus another 60 msec for Tx of the longer messages), so I've still got to hunt down the extra 60 msec, probably in some unneeded while(0) or delay(). The bottom line is that I'm stuck with a 3-4 second loop time for polling all the slaves. Not terrible, but can't be improved unless my housekeeping loops have a different strategy, since they all are asynchronous with each other. Using a higher baud rate would only get back 45 msec per slave, at best, so the loop would still be 2.5-3 sec.

Thanks for the heads up on the Mega as the network monitor. It does a good job of showing all the messages on the net, but a really bad job of time tagging them. Believing that timing data, at first, really got me in a tizzy and this has been a needed exercise in looking at the details of how a polling serial network really works.

I'm using the RS-485 as a reliable way to get the RS-232 signals between the two buildings. I don't have to worry about the Tx/Rx switching since it's duplex and I wire-AND all the Tx lines at the concentrator/buffer unit.

You should aim to get the loop() running at hundreds of times per second. That makes the Arduino response.

The liquid crystals of a LCD display is slow. It only needs a update 3 or 4 times per second.
The temperature can probably be read once per 5 seconds or so.
Reading something from the Serial buffer has to be done all the time.
A keyboard can be read 10 times per second.

You are not stuck in a slow loop(), you are stuck in the wrong sketch :wink:

Koepel:
You are not stuck in a slow loop(), you are stuck in the wrong sketch :wink:

I believe in throwing good code after bad. :slight_smile: