Tutorial - CTS flow control when uploading data

I have a project in a testing environment where configure tables need to be uploaded to the Arduino before a test can be started. I implemented a super-simple recursive descent command parser that takes in a byte and then goes down an if-else tree to decide which character may come next.

One example of a command is like this:

se0 0a 0f 21 … <128 entries>;

For now, I don’t have a PC script yet that will automatically uploads different configuration tables before a test, so I’m using a terminal, PyTTY, and cut-and-paste these long configuration strings in the terminal.

As many have found out before: this doesn’t work if you’re not careful because there is no flow control in HardwareSerial. After a while, characters are dropped and the whole thing falls to pieces…

I didn’t have experience with this kind of stuff and it took me a lot of hours to find out how to make this work, so I’m filing it here in case it’s of use to others.

At the end of the day, my solution is simple.

The key to the solution is to tell the terminal (PC) to stop sending more characters. The contemporary RS232 convention is to use RTS/CTS for this.

RTS is a way for the PC to tell the Arduino to stop sending data. (Not needed in my case.)
CTS is the opposite, and what I need.

On my Arduino Duemilanove, the CTS# signal that’s connected to the FT232R pin is routed to a solder island X3, pin 1. I soldered a wire to this and connected it to ctsPin (Pin 4, PD4).

Then I added the following lines to my program:

setup()
{

pinMode(ctsPin, OUTPUT);
SET_PORTD_PIN(ctsPin, 0);

}

Note that CTS# is active low. When it’s zero, you tell it that it’s find to send characters to the Arduino.

And in my parser code:

byte getNextByte(byte skipBlanks)
{
byte inByte;

do{
while(Serial.available() == 0) ;

inByte = Serial.read();
} while(skipBlanks && (inByte == ’ ’ || inByte == ‘\t’));

if (Serial.available() > 64){
SET_PORTD_PIN(ctsPin, 1);
}
else if (Serial.available() <32 ){
SET_PORTD_PIN(ctsPin, 0);
}

return inByte;
}

All that remains now, is to instruct PyTTY to take into account RTS/CTS flow control: under the Connection → Serial configuration category, select Flow control ‘RTS/CTS’.

That’s it: Data is being uploaded beautifully without any data drop!

Note 1: I’m using SET_PORTD_PIN instead of digitalWrite(), a macro that looks like this:

#define SET_PORTD_PIN(pinNr, x) (PORTD = ((PORTD & (0xFF ^ (1<<(pinNr)))) | ((x) << (pinNr))))
#define GET_PORTD_PIN(pinNr) ((PIND >> (pinNr)) & 0x01)

The reason for this is that digitalWrite is ridiculously slow if you need to work in a environment where microseconds count. However, for this particular case, digitalWrite() would have worked just as well.

Note 2: why not use XON/XOFF protocol? I tried that by replacing the CTS toggling statements with this:

if (Serial.available() > 64){
//SET_PORTD_PIN(ctsPin, 1);
Serial.print(19); // Send XOFF
}
else if (Serial.available() <32 ){
//SET_PORTD_PIN(ctsPin, 0);
Serial.print(17); // Send XON
}

This didn’t do it… Any suggestions on how to make it work are welcome.

Thanks,
Tom

 if (Serial.available() > 64){
    //SET_PORTD_PIN(ctsPin, 1);
    Serial.print(19); // Send XOFF
  }
  else if (Serial.available() <32 ){
    //SET_PORTD_PIN(ctsPin, 0);
    Serial.print(17); // Send XON
  }

This didn’t do it… Any suggestions on how to make it work are welcome.

Xon/Xoff would be nicer (if the terminal software supports it and no hardware changes needed) if it can be made to work. One thing in your above code, how do you ensure you just send Xoff once rather then each time through the loop until <32?

Possibly this Xon/Xoff code should be part of a modification to the hardware serial library rather then trying to do it in your sketch code? Also depending on how large your configuration files are and how much SRAM your sketch utilizes, it’s possible to increase the buffer size used in the hardware serial library code? This might be more possible if you utilized a mega board with it’s 4k SRAM.

Lefty

Lefty,

You're right, I'm probably sending out way too many XOFF's. But in a separate experiment where all I did was sending XOFF, characters kept on pouring in also. This is why I abandoned this method.

Ultimately, it would be great to see both XON/XOFF and RTS/CTS flow control in HardwareSerial, it should be very easy to do so.

Thanks, Tom

What are you using on the PC end to receive serial data? Is it configured for hardware flow control?

I use teraterm for as it's free and easy to use. I use it when dealing with serial problems. http://hp.vector.co.jp/authors/VA002416/teraterm.html

I'm using putty as a terminal. But I don't have problems with grooming data on the PC side that was sent by the Arduino, so I haven't implemented anything that looks at the RTS signal.

It should be easy to add.

For automation, I'm now using a python script and the pyserial library, also with rtscts enabled. This works fine too.

Tom