Auto serial baudrate detector/selector

In another posting the thought came up that having a automatic baud rate detector function could be a useful thing to have in ones bag of tools. I'm still learning C, so I know this is not a bullet proof function, but rather something to get those interested in such a function to look it over and offer improvements or come up with a better version.

This sketch contains the function and a simple demo that runs in the setup section of the sketch. The function when called measures data bit widths and then selects the most likely standard speed for that width. I tested it using the 'U' character as it contains alternating ones and zero data bits. No doubt this function could be fooled by characters starting with multiple consecutive zero bits. The new line character also seems to work reliably as a sensing character.

Feedback welcome.

/*
 First stab at a auto baud rate detection function. This uses the pulseIn command
 to determine what speed an unknown serial link is running at. Detects and sets the standard baud 
 rates supported by the Arduino IDE serial monitor. Uses character "U" as a test character
 
 I'm sure characters with multiple zero bits in a row may fake out proper detection. If data is
 garbled in the serial monitor then the auto baud rate function failed.
 
 This is just a demo sketch to show concept. After uploading the sketch, open the serial monitor
 and pick a random baud rate and then start sending U characters. The monitor will print out
 what baud rate it detected. It will default to 9600 baud if found a bit
 width too long for a standard rate used by the serial monitor. Not that as written, this is a 
 'blocking' function that will wait forever if no character are being sent.
 
 Note that while the serial monitor has a 300 baud option, the Arduino hardware serial library 
 does not seem to support that baud rate, at least for version 22, in my testing.
 
 By "retrolefty" 1/22/11
 */
 
int recPin = 0;  //the pin receiving the serial input data
long baudRate;   // global in case useful elsewhere in a sketch

void setup()
 {
  pinMode(recPin, INPUT);      // make sure serial in is a input pin
  digitalWrite (recPin, HIGH); // pull up enabled just for noise protection
  
  baudRate = detRate(recPin);  // Function finds a standard baudrate of either
                               // 1200,2400,4800,9600,14400,19200,28800,38400,57600,115200 
                               // by having sending circuit send "U" characters.
                               // Returns 0 if none or under 1200 baud  
  Serial.begin(baudRate);
  Serial.println();
  Serial.print("Detected baudrate at ");
  Serial.println(baudRate);
 }

void loop()
 {
  // nothing to do here
 }

long detRate(int recpin)  // function to return valid received baud rate
                          // Note that the serial monitor has no 600 baud option and 300 baud
                          // doesn't seem to work with version 22 hardware serial library
  {
   while(digitalRead(recpin) == 1){} // wait for low bit to start
   long baud;
   long rate = pulseIn(recpin,LOW);   // measure zero bit width from character 'U'
     if (rate < 12)
      baud = 115200;
      else if (rate < 20)
      baud = 57600;
      else if (rate < 29)
      baud = 38400;
      else if (rate < 40)
      baud = 28800;
      else if (rate < 60)
      baud = 19200;
      else if (rate < 80)
      baud = 14400;
      else if (rate < 150)
      baud = 9600;
      else if (rate < 300)
      baud = 4800;
      else if (rate < 600)
      baud = 2400;
      else if (rate < 1200)
      baud = 1200;
      else 
      baud = 0;  
   return baud; 
  }

Lefty

As mentioned in the other post I think you should test for a few pulses, so here's my version of the detRate() function (I don't have access to my hardware at present to test this).

long detRate(int recpin)  // function to return valid received baud rate
                          // Note that the serial monitor has no 600 baud option and 300 baud
                          // doesn't seem to work with version 22 hardware serial library
  {
  long baud, rate = 10000, x;
  for (int i = 0; i < 10; i++) {
      x = pulseIn(recpin,LOW);   // measure the next zero bit width
      rate = x < rate ? x : rate;
  }
   
  if (rate < 12)
      baud = 115200;
      else if (rate < 20)
      baud = 57600;
      else if (rate < 29)
      baud = 38400;
      else if (rate < 40)
      baud = 28800;
      else if (rate < 60)
      baud = 19200;
      else if (rate < 80)
      baud = 14400;
      else if (rate < 150)
      baud = 9600;
      else if (rate < 300)
      baud = 4800;
      else if (rate < 600)
      baud = 2400;
      else if (rate < 1200)
      baud = 1200;
      else
      baud = 0;  
   return baud;
  }

This will need 2-3 bytes to get a result, and can still get the wrong answer of course.

Another problem is that the 10th pulse could occur anywhere in a byte, so the main code probably should wait a short time then flush the rx buffer.

If the data stream is constant this may never sync.


Rob

There's quite a good autobaud detector in the TellyMate source code. It uses Timer1 input capture interrupt to count the number of clock ticks between state changes on the serial line. It times a few transitions and assumes the smallest time to be the width of one bit. (You need to link the serial in signal with the timer1 input capture pin to get it to work, I think it's arduino pin 8)

Rob;

Thanks for the ideas. I did just add the serial.flush after detection as it can't hurt. I also added a simple character echo function in the loop() section, just for grins.

So far, using a 'U' character as the detection signal I haven't seen a false rate detection yet. A 'U' has no double or greater zero bits in a row so I'm sure that is why it's been stable so far. A new line character has also been OK. Later I will research some worst case characters to see if I can force a false speed detection.

I also want to play around with having a continuous data stream already being sent to the arduino prior to even starting it's sketch to see how bad that will effect it. My brey terminal monitor can send from a test file so I will see how that works with this auto detect scheme. Later I may play around with a read multiple bit widths and pick the shortest feature. Not sure how many widths would be a good trade off from a how many characters needed to be read Vs statistical probability of not finding the shortest one.

Stemmer:

Is that TellyMate source written in C or assembly? I think that the Arduino pulseIn() function works in a similar manner if you look at the source code from the arduino core library.

Lefty

Not sure how many widths would be a good trade off from a how many characters needed to be read Vs statistical probability of not finding the shortest one.

I doubt there is a correct answer for that, I'd start by sampling for a week and see how that goes. :slight_smile:

As for the continuous data stream, I don't think it's possible to sync on that reliably. There has to be a break somewhere to start the ball rolling at the right place.

BTW, it's the 24th, this thread (and all other active ones) will probably get lost in the big changover.


Rob