Serial: putc works but printf causes problems

09 Feb 2012

Hi,

I'm trying to read GPS data through a uart connection and print it to an SD card. To test, I just printed out all of the data to Tera Term and ran into a problem using printf.

Code with putc:

        if (serialGPS.readable()) {
            
            if (serialGPS.getc() == '$') {
                memset(gpsTEMP,' ',strlen(gpsTEMP));
                a=1;  
                gpsTEMP[0] = '$';         
                while (1) {                  
                    gpsTEMP[a] = serialGPS.getc();
                    pc.putc(gpsTEMP[a]);              //******** un commented

                    if (gpsTEMP[a] == '\n')
                    {
                       // pc.printf(gpsTEMP);         //********commented out
                        pc.printf("\n\r");
                        break;
                    } 
                    
                    a++;
                }
            }     
        }

output:

GPRMC,000634.964,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*76

GPRMC,000635.014,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*79

GPVTG,051.5,T,,M,000.0,N,000.0,K,A*0C

GPRMC,000635.064,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7E

GPRMC,000635.114,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*78

GPRMC,000635.164,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7F

GPRMC,000635.214,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7B

GPRMC,000635.264,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7C

GPRMC,000635.314,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7A

GPRMC,000635.364,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7D

GPRMC,000635.414,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7D

GPRMC,000635.464,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7A

GPRMC,000635.514,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7C

GPRMC,000635.564,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7B

GPRMC,000635.614,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7F

GPRMC,000635.664,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*78

GPRMC,000635.714,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7E

GPRMC,000635.764,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*79

GPRMC,000635.814,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*71

GPRMC,000635.864,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*76

GPRMC,000635.914,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*70

GPRMC,000635.964,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*77

GPRMC,000636.014,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7A

GPVTG,051.5,T,,M,000.0,N,000.0,K,A*0C

GPRMC,000636.064,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7D

GPRMC,000636.114,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7B

GPRMC,000636.164,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7C

GPRMC,000636.214,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*78

Code with printf:

        if (serialGPS.readable()) {
            
            if (serialGPS.getc() == '$') {
                memset(gpsTEMP,' ',strlen(gpsTEMP));
                a=1;  
                gpsTEMP[0] = '$';         
                while (1) {                  
                    gpsTEMP[a] = serialGPS.getc();
                   // pc.putc(gpsTEMP[a]);       //********commented out  

                    if (gpsTEMP[a] == '\n')
                    {
                        pc.printf(gpsTEMP);   
                        pc.printf("\n\r");       //******** un commented
                        break;
                    } 
                    
                    a++;
                }
            }     
        }

output

$GPRMC,001425.964,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*75

$GPRMC,001426.010842,W,000.0,051.5,090212,,,A*78

$GPVTG,051.5,T,,C

$GPRMC,001426.064,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7F

$GPRMC,001426.110842,W,000.0,051.5,090212,,,A*79

$GPRMC,001426.164,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7E

$GPRMC,001426.210842,W,000.0,051.5,090212,,,A*7A

$GPRMC,001426.264,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7D

$GPRMC,001426.31.0842,W,000.0,051.5,090212,,,A*7B

$GPRMC,001426.364,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7C

$GPRMC,001426.410842,W,000.0,051.5,090212,,,A*7C

$GPRMC,001426.464,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7B

$GPRMC,001426.515.0842,W,000.0,051.5,090212,,,A*7D

$GPRMC,001426.564,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7A

$GPRMC,001426.610842,W,000.0,051.5,090212,,,A*7E

$GPRMC,001426.664,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*79

$GPRMC,001426.71.0842,W,000.0,051.5,090212,,,A*7F

$GPRMC,001426.764,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*78

$GPRMC,001426.81842,W,000.0,051.5,090212,,,A*70

$GPRMC,001426.864,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*77

$GPRMC,001426.910842,W,000.0,051.5,090212,,,A*71

$GPRMC,001426.964,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*76

$GPRMC,001427.010842,W,000.0,051.5,090212,,,A*79

$GPVTG,051.5,T,,

$GPRMC,001427.064,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7E

$GPRMC,001427.110842,W,000.0,051.5,090212,,,A*78

$GPRMC,001427.164,A,4517.3274,N,07555.0842,W,000.0,051.5,090212,,,A*7F

$GPRMC,001427.21.0842,W,000.0,051.5,090212,,,A*7B

Any ideas as to why it starts to chop off or lose characters when I output the data with printf?

Thanks for the help, Sebastian

09 Feb 2012

Hello, Sebastian.

If you are going to use printf inside the while loop, then use:

pc.printf("%c", gpsTEMP[a]);    //print one character at a time

But putc is simpler.

Otherwise, take printf out of the while loop, like this:

while(1) {
    gpsTEMP[a] = serialGPS.getc();
    if(gpsTEMP[a] == '/n')
    {
        gpsTEMP[a] = '/0';    //overwrite newline with end-of-string char
        break;
    }
    a++;
}
pc.printf("%s\n", gpsTEMP);   //print entire line

Also, where are you allocating storage for gpsTEMP, and is the storage large enough for an entire line?

09 Feb 2012

Hi Bob,

Did not work:

$GPRMC,031848.491,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*78
$GPRMC,031848.545.0916,W,000.0,000.0,090212,,,A*74
$GPRMC,031848.591,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*79
$GPRMC,031848.64.0916,W,000.0,000.0,090212,,,A*77
$GPRMC,031848.691,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7A
$GPRMC,031848.7455.0916,W,000.0,000.0,090212,,,A*76
$GPRMC,031848.791,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7B
$GPRMC,031848.845.0916,W,000.0,000.0,090212,,,A*79
$GPRMC,031848.891,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*74
$GPRMC,031848.945.0916,W,000.0,000.0,090212,,,A*78
$GPRMC,031848.991,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*75
$GPRMC,031849.045.0916,W,000.0,000.0,090212,,,A*70
$GPVTG,000.0,T,,,000.0,K,A*0D
$GPRMC,031849.091,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7D
$GPRMC,031849.1455.0916,W,000.0,000.0,090212,,,A*71
$GPRMC,031849.191,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7C
$GPRMC,031849.245.0916,W,000.0,000.0,090212,,,A*72
$GPRMC,031849.291,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7F
$GPRMC,031849.345.0916,W,000.0,000.0,090212,,,A*73
$GPRMC,031849.391,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7E
$GPRMC,031849.445.0916,W,000.0,000.0,090212,,,A*74
$GPRMC,031849.491,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*79
$GPRMC,031849.545.0916,W,000.0,000.0,090212,,,A*75
$GPRMC,031849.591,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*78
$GPRMC,031849.64.0916,W,000.0,000.0,090212,,,A*76
$GPRMC,031849.691,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7B
$GPRMC,031849.745.0916,W,000.0,000.0,090212,,,A*77
$GPRMC,031849.791,A,4517.3284,N,07555.0916,W,000.0,000.0,090212,,,A*7A
$GPRMC,031849.845.0916,W,000.0,000.0,090212,,,A*78

The char array is 100 characters long, I think that the biggest line is only 72 chars long.

If I moved the printf statement outside of the while loop like you suggested above wouldnt that do the exact same thing as keeping the printf inside of the if(char == '\n') and calling break right after printing since it would only print the whole string once and jump out?

Thanks Sebastian

09 Feb 2012

Use '\n' and '\0' instead of '/n' and '/0'. The compiler should be generating errors/warnings with the current code.

e.g.

while(1) {
    gpsTEMP[a] = serialGPS.getc();
    if(gpsTEMP[a] == '\n')
    {
        gpsTEMP[a] = '\0';    //overwrite newline with end-of-string char
        break;
    }
    a++;
}
pc.printf("%s\n", gpsTEMP);   //print entire line
09 Feb 2012

Sebastian,

Consider the possibility that you are seeing a buffering problem. After printf() outputs a long line, the next line gets the first 15 characters (the input buffer filled while printf() was busy?), then misses about 20 characters (that didn't make it into the already full input buffer). With putc() your incoming and outgoing serial streams tend to stay in synch on a character-by-character basis, so no incoming buffer over-runs/losses. Also, is the output's baud rate greater than the input's?

The alternative for keeping printf() is to implement some sort of 'handshaking' dialogue with the input stream (to delay any input character generation until printf() has finished).

10 Feb 2012

Sebastian,

Yes, I see now what you were trying to do with break; it would print the string and jump out, but you still need to put an end-of-string character in there, as I indicated. I incorrectly used a forward slash instead of a backslash in the newline and end-of-string characters. As Paul M. pointed out, those should be '\n' and '\0'. (Well, I got it right in the final line.)

Having said that printf() followed by break will work, I wouldn't do it that way. It is better to let the loop do just one thing...build the output string. Then print the string.

A problem that I did not see before is the use of strlen() inside the call to memset(). Consider the following code (which I did compile and run, sending output to TeraTerm):

    char line[100];
    
    // Find size of a statically allocated array
    printf("The array has storage for %d characters.\n", sizeof(line)/sizeof(line[0]) );
    
    
    // Find size of string using strlen()
    int i;
    for(i = 0;  i < 23;  ++i)
        line[i] = ' ';
    line[i] = '\0';
    
    printf("The string named \"line\" is %d characters long.\n", strlen(line) );

Here's the output:

The array has storage for 100 characters.
The string named "line" is 23 characters long.

The top version is what you want. In your code strlen() counts until the first '\0', which should be really interesting the first time through when memory is full of random bits.

Also, why isn't the '$' that is stored in gpsTEMP[0] printing at the beginning of the output line? What am I missing?

Edit: By "top version" I mean using the sizeof method, listed at the top of my code, instead of using strlen. Actually, strlen() would work after the first pass, because then gpsTEMP would contain a string. But you would need to use strlen + 1 to also set the '\0' to a space.

14 Feb 2012

Thanks guys for the help, so it turns out that the GPS does not output data every 1/20 of a second, instead it would output 2 messages every 1/10 of a second and when it had output all 20 messages (which covered 1 second) it would then output another line which had the speed etc... Something like:

No serial (repeated about 80 times)
$GPR......
No serial
$GPR.....

No serial (repeated about 80 times)
$GPR......
No serial
$GPR.....
No serial
$GP.....

No serial (repeated about 80 times)
$GPR......
No serial
$GPR.....

So I created an array of buffers and accesed/stored the data when it was in the "No Serial" loops while waiting for the next batch of GPS data to be sent to the mbed.

Cheers, Sebastian