Serial 57600 bps with GPIO not possible?

I am using AltSoftSerial to receive data from GPS receiver at 57600 bps with Arduino UNO. GPS is set to 5Hz with 6 message types. Real data amount is ~ 24000 bps. Here is how I calculate, hope correct.

1 message is roughly 100 bytes(exaggerated). I have 6 of messages. So 100x6=600 bytes. I am receiving this with 5Hz so in 1 second I get 5x600 = 3000 bytes, which is 24000 bps.

In the main loop, I’ve removed any UART coomunication, to save time. So the following is my sketch:

void setup() {
  pinMode(LED_PIN_RED, OUTPUT);  
  pinMode(LED_PIN_GREEN, OUTPUT);  
  altSerial.begin(57600);
}

unsigned short prevFailed = 0;

void loop() {
  char c;
  unsigned long chars;
  unsigned short sentences, failed; 

  while(altSerial.available())
  {
     c = altSerial.read();
     if(c != -1)
     {
        gps.stats(&chars, &sentences, &failed);
        if (gps.encode(c)) 
        {
          if(failed > prevFailed)
          {
             digitalWrite(LED_PIN_RED, HIGH); 
             delay(1);
             digitalWrite(LED_PIN_RED, LOW);
             prevFailed = failed;
          }
          else
          {
             digitalWrite(LED_PIN_GREEN, HIGH); 
             delay(1);
             digitalWrite(LED_PIN_GREEN, LOW);
          }
        }
     }
  }

}

TinyGPS used to parse NMEA sentences. LEDs are used to indicate success and errors. I got red LED flashing couple times in 10 second, while green LED is flashing very intensively, so good data has bigger proportion.

I know that I could connect GPS receiver to single UART on UNO, but first of all I need to debug my program, which is sensitive about data accuracy. Besides, documentation of AltSoftSerial says:

A commonly asked question is the maximum baud rate these library can support. Both can work with approximately 1 bit time of interrupt latency from OTHER code. So if other interrupts take a maximum of 15 µs (eg, some libraries), then a baud rate of 57600 ought to be possible. Without other libraries, on Teensy or Arduino (with the issue 776 fix), interrupt latency is about 3 to 4 µs. 115200 baud is possible.

However, the maximum baud rate is often not the most important question. Each library imposes interrupt latency on other libraries. AltSoftSerial causes approximately 2-3 µs latency. NewSoftSerial causes 10 bit times of latency for other libraries. Running at 57600 baud, that’s 174 µs! This latency is the primary difference between AltSoftSerial and NewSoftSerial.
I have tried to increase RX_BUFFER_SIZE in AltSoftSerial.cpp to different values. No good.

So how to achieve this 57600 kbps?

Does the GPS actually support output at 57.6K? Most start at 4800 default, you have to send it a command to change the data rate.

Yes it does. It is LS20031 based on mt3339. Connecting GPS to PC with 57600 will produce no error.

Have you tried something like this, with Serial set to 115200?

void loop() {
  if (altSerial.available()) {
    char c = altSerial.read();
    Serial.write(c);
  }
}

Does all the data appear in the serial monitor, or is something clearly something missing?

Also, does your program work with slower update rates, like 4 Hz or 1 Hz ?

Another question would be if TinyGPS is capable of 5 Hz operation on 8 bit AVR? It does a lot of float and 32 bit long math. Can you try driving a pin high right before the call to TinyGPS stuff and low right after, like this:

        digitalWrite(2, HIGH);
        gps.stats(&chars, &sentences, &failed);
        boolean result = gps.encode(c);
        digitalWrite(2, LOW);

Just measure pin 2 with a multimeter. If you have a duty cycle measurement, use it. Otherwise, just look at the approximate average voltage. If it's tiny, then not much CPU time is being spent inside TinyGPS. But it it's something like 3 volts, then the number crunching might be too much for AVR-based Arduino.

[quote author=Paul Stoffregen link=topic=131774.msg991225#msg991225 date=1352598302] Have you tried something like this, with Serial set to 115200?

void loop() {
  if (altSerial.available()) {
    char c = altSerial.read();
    Serial.write(c);
  }
}

Does all the data appear in the serial monitor, or is something clearly something missing? [/quote] I have tried that and that's why I eliminated HW serial output to see if this is causing issue. Data is corrupting once in 3-10 second. Receiving $@PGSA,A,1,,,,,,,,,,,,,,,*1E instead of $GPGSA,A,1,,,,,,,,,,,,,,,*1E Receiving $@PRMC,073405.491,V,,,,,0.00,0.00,111112,,,N*47 insted of $GPRMC... Strage, that only G character gets overwrited with @.

Also, does your program work with slower update rates, like 4 Hz or 1 Hz ?

Another question would be if TinyGPS is capable of 5 Hz operation on 8 bit AVR? It does a lot of float and 32 bit long math. Can you try driving a pin high right before the call to TinyGPS stuff and low right after, like this:

        digitalWrite(2, HIGH);
        gps.stats(&chars, &sentences, &failed);
        boolean result = gps.encode(c);
        digitalWrite(2, LOW);

Just measure pin 2 with a multimeter. If you have a duty cycle measurement, use it. Otherwise, just look at the approximate average voltage. If it's tiny, then not much CPU time is being spent inside TinyGPS. But it it's something like 3 volts, then the number crunching might be too much for AVR-based Arduino.

I've measured with multimeter. Frequency is about 3Khz, duty cycle is 4.5%. Not sure if this is fine.

maybe the wrong part for the application, use a 644 or any of the other handful of arduino compatible chips with more than one hardware uart

Osgeld: maybe the wrong part for the application, use a 644 or any of the other handful of arduino compatible chips with more than one hardware uart

Price-wise 644 is much more expensive comparing with 328. Also I want to make sure that it's impossible to get 57600 bps on 328.

I’ve just tested with 1Hz, with the same simple program presented in first post. Error rate decreased, but still can see the red LED blinking like once per 10 sec. I just guess that this is related to the strange ‘@’ char.

Update:
Indeed. Tested with Serial output (GPS 1Hz) and ‘@’ character appears occasionally in the sentence(this time at random locations), overwriting the right character.

This is the list off erroneous sentences received through serial:
http://pastebin.com/DgDkygPR

Ouch. Maybe it's a bug in AltSoftSerial?

Could you try sending the data from a PC to AltSoftSerial's receive pin at 57600, with the sketch sending it back to the serial monitor at 115200?

If that also shows the problem, could you please send me the exact code and specific details of the hardware, operating system, software versions and everything else you're using, so I can try to set up the same system here and see if I can reproduce the problem?

I'm trying to do it now. I see you are trying to eliminate the possibility of GPS of sending wrong data, but again, if I connect GPS directly to PC, there is no error at all.

Actually, I do believe the GPS is sending good data.

I simply don't have that GPS unit.... so I'm hoping for a test that I can reproduce here with the equipment I have.

Got it. Will get you some result shortly.

Paul, I think I have good news for you :) I was able to reproduce it with Serial2Serial scenario like you described and hit to same '@' symbol. Let me summarize shortly.

  1. Arduino UNO as prototype platform with Arduino 1.0.1 IDE + Latest AltSoftSerial library
  2. Exact code I copied from sketch
#include <AltSoftSerial.h>

AltSoftSerial altSerial;

void setup() {
  Serial.begin(115200);
  altSerial.begin(57600);
}

void loop() {
if (altSerial.available()) {
    char c = altSerial.read();
    Serial.write(c);
  }
}
  1. Windows 7 is operating system, but not sure how it may be related.
  2. For Serial2Serial test I was using simple C# app I made (let me know if you will need it) which is sending the following pattern every 1 second.
            serialPort.WriteLine("$Lorem ipsum dolor sit amet, consectetur adipiscing elit *EA");
            serialPort.WriteLine("$Ut enim ad minim veniam, quis nostrud exerc *EB");
            serialPort.WriteLine("$Tia non ob ea soluad incommod quae egen ium improb fugiend *EC");

This time the characters are randomly replaced with ‘`’ as you can see here
Note, that if text is little bit shorter, longer or maybe different, then ‘@’ character appears.

Let me know if you need more information.
Thanks!

After testing more, the problem is not consistent. Each time I reset the board, I get new type of data corruption. It's no more only '' and '@'. Randomly bytes are corrupted...

Hope you can solve it.

I've updated AltSoftSerial with a new, faster pair of receive interrupts.

You may need to refresh the page to download it. The new zip file is 7553 bytes.

http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html

The original code stored the captured input edges to an array, then did all the analysis during the stop bit to recover the byte from the timing of the edges. Sadly, the AVR at 16 MHz just isn't fast enough to complete the analysis for bytes with a pattern of 0101010 before well into the next start bit. Sometimes if the LSB of the next byte was 1 (serial sends LSB first), the 2nd edge defining the LSB would occur before the interrupt could respond to the leading edge of the start bit, because the interrupt response was delayed by the analysis happening from the prior stop bit.

I redesigned the interrupt handlers. Now it does the analysis incrementally on each interrupt. I also made the edge detect interrupt able to put the detected byte into the buffer, so the timeout interrupt doesn't need to run. Of course, that only applies when the MSB is zero, so the stop bit begins with a rising edge. When the MSB is one, the stop bit is detected by the timeout interrupt instead. Previously the edge detect interrupt would put that final rising edge into the buffer, then the timeout interrupt would do the analysis. The AVR is terribly slow getting into and out of interrupt handlers, so never needing to run 2 interrupt routines per bit saves a lot of time. This test case didn't trigger that failure mode, but if bytes with the MSB set are received at 57600, they should now work and not corrupt subsequent bytes.

The stop bit interrupt now (hopefully) always completes before the edge of the next start bit, at 57600 baud. However, there's not a huge amount of timing margin, so any other interrupts or code adding more than about 9 us bit time interrupt latency can cause AltSoftSerial to receive incorrectly at 57600. Many interrupts are several us, so check that data carefully while you're doing other stuff generating interrupts.

This indeed fixed the problem. My original code with TinyGPS works without single data corruption so far. I was just wondering, if the data amount is not that big actually, would it be better to decrease baudrate to 38400 or lower and gain more margin for other calculations? I would need to stick with 5Hz from GPS.

Thanks

Great. Glad it's working.

It's unfortunate the Arduino SoftwareSerial page can't link to AltSoftSerial.