Auto Detect Baud Rate

You don't need to detect the 1010 pattern. All you need is to measure the shortest interval between two flanks (rising -> falling, or falling -> rising) for some amount of traffic. Assuming the communications wiring is good, the shortest duration between two flanks will be the duration of a single bit. The baud rate will then be 1 divided by that duration (in seconds.) You probably want to round/clamp/adjust that to be the closest "standard" baud rate once measured.

For a device that transmits continuously (like a GPS) I've found the easiest way to detect the baud rate is to simply try each possibility until something recognizable arrives. If the baud rates are tested in a logical order (e.g. 4800 first) then, in most cases, the detection takes just a few frames. On success, saving the baud rate to EEPROM makes detection even faster the next time around.

tnovak:
I have been searching for a solution to auto detect a baud rate of a serial connection with a gps reciever. Lefty has an old post showing interest in this concept, but I had no luck finding a finished solution. Any thoughts??

My memory from my attempt was that I was only able to 100% reliably detect the proper baudrate if I could control what character(s) were being sent to base the detection method on using the pulseIn() method, as some characters would falsely cause that method to think the baudrate is slower then actual as there was no single bit length avalible in certain characters. That means a reliable algorithm when using arbitrary input characters would have to look at multiple characters and search for a minimum bit length. That was too complex for me to continue playing with, but certainly possible. On using a single character baudrate detection using the 'U' character I think, did work reliably as did several other characters, but several characters would 'spoof' the detection method, it's been while.

Lefty

jwatte:
You don't need to detect the 1010 pattern. All you need is to measure the shortest interval between two flanks (rising -> falling, or falling -> rising) for some amount of traffic. Assuming the communications wiring is good, the shortest duration between two flanks will be the duration of a single bit. The baud rate will then be 1 divided by that duration (in seconds.) You probably want to round/clamp/adjust that to be the closest "standard" baud rate once measured.

The flaw in that method is that there can be characters received that will not contain a valid single bit length. So one has to make a statistical guess at how many consecutive characters to analyze before deciding that you have 100% certainly captured the lowest single bit timing value.

Lefty

One option is to connect the GPS directly to a serial port on your computer and capture the initial character after a known operation, such as GPS power cycle, reset, or some other easy function. Then, use the above mentioned bit timing on the, now, known first character. This method is limited to that specific GPS but you didn't indicate that you wanted a universal method, which is, obviously, harder as you don't know what data is being sent. That said, however, a universal method would be to assume that there is a quiet period between data - reasonable with a stationary GPS, and time for the shortest start bit only. A start bit followed by a "high" in data bit zero. would be what you would time for. Hopefully some character in a stream will have this case and all data will not have a "low" in data bit zero. You would time some quantity of characters, only evaluating the time of the initial start bit. Each character ends with a stop of two "high" bits, so a continuous stream of data will always have a "low" start bit following two "high" bits.

8051 assembly code at the link below provides a similar method, in this case measuring the character length:
http://www.programmersheaven.com/download/3733/1/ZipView.aspx

This may help:

/* Determine the baud rate to operate at by getting characters until
we find something recognizable. Return with the global 'rate' set and
Novation type modems put in transparent mode. The initial delay is because
the answer modem returns connect info much earlier than the originate. */

connect() {

unsigned n;

        delay(100);                    /* delay */
        flush(1);                            /* flush garbage */
        cd_flag= 0;                    /* watch carrier */

        while (1) {
                rate= 300;
                baud(rate);               /* test at 300 baud */
                n= mrconin();         /* get a raw key */

/* At 1200 and above, one byte (CR or anything) fits into 1/4th a character,
so the rest of it is ones. If we find f0h, try more or less the same thing
as the 300 vs. 600 case, but at 1200 baud. */

                if ((n & 0xf0) == 0xf0) {
                        rate= 1200;
                        baud(rate);
                        n= mrconin();      /* RAW Modem conin() */
                        if ((n & 0x7f) == CR) break;
                        if ((n & 0x7f) == ' ') break;
                        if ((n & 0xf0) == 0xf0) {
                                rate= 2400;
                                baud(rate);
                                break;
                        }

/* For 300 baud, we should get a CR. If so, get another to make sure. It
might have been garbage or a leftover result code. Baud rate is already
set to 300. */

                } else {
                        n= mrconin();
                        if ((n & 0x7f) == CR) break;
                        if ((n & 0x7f) == ' ') break;
                }

/* None of the above. Now we need to flush before we try again, as we
somehow might have ended up receiving 300 baud chars at 1200, and that
generates multiple garbage bytes. */

                flush(5);                     /* 50 mS quiet, flush garbage */
        }

/* All set. Put Novation modems to sleep, and flush the software ring buffer
and things. */

        if (mdmtype == SMARTCAT) atp("\016U 0",CR);
        if (mdmtype == IBMJR) atp("\016T 0",CR);
        if ((mdmtype == IBMJR) || (mdmtype == SMARTCAT)) delay(100);
        mconflush();
}

I recall from NetworkClass that you should not look at the individual bits only but to the whole frame first to be able to detect the number of databits stopbits.
The advantage of checking the whole frame is that you know for sure that the startbit and stop bit are HIGH

If you would only measure the framelength that would be typical be 1 start, 8 data and 1 or 2 stopbits, so that are 10 or 11 bits. 10% margin.

The default speeds differ mostly a factor 1.5-2 . 300, 600, 1200,2400,4800,9600,14400,19200 etc

The formula is similar than with reading one bit. baudrate = (10/time); // or 11/time
and find the nearest value.

my 2 bits

robtillaart:
I recall from NetworkClass that you should not look at the individual bits only but to the whole frame first to be able to detect the number of databits stopbits.
The advantage of checking the whole frame is that you know for sure that the startbit and stop bit are HIGH

But for sure the start bit is LOW. Finding the true 'frame length' is no straight forward task either without knowledge of the character(s) being sent, and especially if called with a continuous serial stream already being sent.
If you would only measure the framelength that would be typical be 1 start, 8 data and 1 or 2 stopbits, so that are 10 or 11 bits. 10% margin.

Again I think you might be understating how straight forward the task may be.

The default speeds differ mostly a factor 1.5-2 . 300, 600, 1200,2400,4800,9600,14400,19200 etc

Some auto-baud rate algorithms might assume 'standard' rates must be being used, but that is both an assumption and a limitation, no?

The formula is similar than with reading one bit. baudrate = (10/time); // or 11/time
and find the nearest value.

my 2 bits

Worth what you are charging. :wink:
Lefty

But for sure the start bit is LOW.

Is it? In fact I think it depends ==> the soft serial lib has an inverted flag, so that is a thing you should know too.

Other idea is to measure the timing of all flanks within a frame (sofar as possible) and determine the greatest common denominator

if you have only 2 distances 20 and 50 then the GCD is 10. This is the best estimator for a single bit you have by using the multibit info
(drawback is that if you have 20 and 40 it wont work).

Think this can become a late night project....==> todo list

I think it could be done with a reasonable amount of data. Putting aside whether or not the data is inverted (hopefully you know that?). In short bursts you might not be able to tell the difference between 001100 at 9600 baud and 010 at 4800 baud. Also if the data comes in long, close-together, bursts, the stop-bit/start-bit transition might look like data.

Personally I would sample a few dozen bits, and look for the shortest transition. Presumably that is a single bit length. Then try to find the nearest "standard" baud rate to that. You might also guess that any long periods of a single value are the idle state (usually that would be 1 bits). Knowing the idle state, and timing between one idle state and another should give you the character time. However that could be problematic, as something like this could be valid:

1111101111111111111111111

The 0 is the start bit, the rest is sending the character 0xFF. You can't really tell much from that ... except that by measuring the length of the zero you have one bit time. But if they sent 0xFE you would see:

11111001111111111111111111

Now that single bit time looks twice as long. So, not totally reliable.

BTW the Salaea logic analyzer has an "auto-baud" mode which is quite useful for plugging in and seeing what it comes up with. I think with enough data it makes a reasonable guess. Out of interest I found it got 9600 exactly right, but when I sent 115200 it reported 117647 baud. Quite possibly it was correct, a couple of percentage points out could be accounted for by the processor clock not being exactly right.

Is it? In fact I think it depends ==> the soft serial lib has an inverted flag, so that is a thing you should know too.

No, it doesn't depend on anything, no matter inverted or non-inverted, with asynchronous serial data the start bit and the stop bit(s) are always of opposite state of each other. A start bit is consider a SPACE bit condition no matter what the logic polarity is and a stop bit(s) are always considered a MARK bit condition.

Bottom line is that auto-baud detection can be as easy or difficult as one can imagine depending on the how robust and reliable one requires and if there is any assumptions or not one can make about the incoming serial stream. For instance what if there is no data arriving, does your detection function just block all further processing waiting for it to possibly be coming? Or should it be interrupt driven so as to auto-detect only kicks in when activity starts on the serial stream and does a one time check and never run again until reset or power cycled? Does on make the assumption that parity even or odd isn't being sent or that the stop bits are one or two (there is even a ancient 1 1/2 stop bit option). If parity is detected should the transmit response be made to match? It's one of those applications that the more you look into possible 'difficulties' the deeper the hole appears to be. I think most auto-baud rate detection methods just resign to be 'good enough' for the overall application and use whatever assumptions that the specific application allows one to take. It's a hard one to make bullet proof perfect under all possible conditions internal and external to the sketch.

My company purchased for us in around 1979 a state of the art hardware based asynchronous serial data analyzer, a model called the Data Hawk but I forget the company name. It had a small 9" crt screen and could be configured to display and or send at all the standard baud rates, show serial control signal statues, flag parity errors if configured, detect framing errors, display in ascii, hex, binary, octal data format, able to monitor in series with an existing serial circuit and display data from both directions, and many other features and functions that I can't even specifically recall. As I do recall it cost $12,000 and was invaluable in troubleshooting the many many mixed vendor systems we had in the refinery. One thing it didn't even try to do was auto-baud rate detect, one just had to keep trying a various standard rates until you could recognize a valid data stream. It's amazing that today just a standard arduino board and a little RS-232 voltage converter chips could probably duplicate the same functions, but the sketch would not be trival by any means.

Lefty

It's a hard one to make bullet proof perfect.

If it weren't, there would be no need to ever set a baud rate.

Agreed that fully robust algorithm will be very difficult, but a fairly good system should be easy enough.

As has been mentioned timing between edges should to the job, after say 50 edges you would be very unlucky not to have a valid reading with any normal data. Sure if you received 50 bytes with only the lower bits set to 0 you would be fooled because they would merge with the start bit, but that won't happen with a GPS data stream.

As for stop/parity/inversion etc that's a different problem that doesn't affect the bit rate detection.


Rob

Just to give the thread a update to help future searchers, below is the sketch I played with over a year ago to do auto-baud rate, based on the assumption that the first detected incoming character does contain a valid detectable single bit width pulse, such as a 'U' suggested.

/*
 First stab at a auto baudrate 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 multible 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 baudrate and then start sending U charaters. The monitor will print out
 what baudrate it detected. It will default to 9600 baud if found a bit
 width too long for a standard rate used by the serial monitor. Note 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
long rate=10000;

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();
  delay(100);
  Serial.flush();
  Serial.print("Detected baudrate at ");
  Serial.println(baudRate);
  Serial.println();
  Serial.print("rate in usec =  ");
  Serial.println(rate);
   


 }

void loop()
 {
  
  if (Serial.available() > 0)
   {
    // get incoming byte:
    int inByte = Serial.read();
    Serial.print(inByte,BYTE);           
   }
 }

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;

    
    */
   long baud,  x;
   
   for (int i = 0; i < 5; i++)
     {
      while(digitalRead(recpin) == 1){} // wait for low bit to start
      x = pulseIn(recpin,LOW);   // measure the next zero bit width
      rate = x < rate ? x : rate;
     }
  // 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

Graynomad:
Sure if you received 50 bytes with only the lower bits set to 0 you would be fooled because they would merge with the start bit, but that won't happen with a GPS data stream.

No, that would be the best case, because the low-then-stop-then-start-then-high bit combination will generate a one-bit pattern. But there are certain worst-case patterns. As you say, they are extremely unlikely.

Maybe I should have said "only the upper bits set to 0", I always forget if it's LSB or MSB first.


Rob

It's LSB first and here is why: You don't necessarily send all 8 bits. So it makes sense to start with the least significant, and stop when the required numbers of bits are sent.

Yes, so my original description was correct, if you have all bytes with lower bits = 0 then they are merged with the start bit and will fool the algorithm.


Rob

Graynomad:
Yes, so my original description was correct, if you have all bytes with lower bits = 0 then they are merged with the start bit and will fool the algorithm.

Yes, any attempt to assume that there is a valid single bit width avalible for measurement with any single arbitrary character received is simply a flawed algorithm. Trying to measure the complete frame time is also subject to spoofing if using arbitrary characters.
Lefty


Rob