0
votes

I have an inconsistency problem while I read in my serial port using Linux and C.

The code that I use to configure the serial port is this:

serial = open("/dev/ttymxc1", O_RDWR | O_NOCTTY | O_SYNC);      //Open in non blocking read/write mode
    if (serial == -1)
    {
        //ERROR - CAN'T OPEN SERIAL PORT
        printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
    }

    struct termios tty;
    memset (&tty, 0, sizeof tty);
    if (tcgetattr (serial, &tty) != 0)
    {
        printf("error from tcgetattr");
        return -1;
    }

    cfsetospeed (&tty, B115200);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
    // disable IGNBRK for mismatched speed tests; otherwise receive break
    // as \000 chars
    //tty.c_iflag &= ~IGNBRK;         // disable break processing
    tty.c_lflag = 0;                // no signaling chars, no echo,
                                            // no canonical processing
    tty.c_oflag = 0;                // no remapping, no delays
    tty.c_cc[VMIN]  = 0;            // read doesn't block
    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

    tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                            // enable reading
    tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
    tty.c_cflag |= 0;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr (serial, TCSANOW, &tty) != 0)
    {
        printf("error from tcsetattr");
        return -1;
    }

Then I poll the UART (from the same thread which has configured the serial port) with the following code:

while(1)
{
    if (serial != -1)
    {
        //memset(rx, 0, sizeof(rx));
        int rx_length = read(serial, &rx, MAX_TXRX_BUF);        //Filestream, buffer to store in, number of bytes to read (max)
        if(rx_length > 0){
            //Bytes received
            //rx[rx_length] = '\0';

            printf("1) %i bytes read : %s\n", rx_length, rx);
            //forward_data(rx, rx_length);
            printf("2) %i bytes read : %s\n", rx_length, rx);
            //tcflush(serial, TCIOFLUSH);
        }
        // else: NO DATA
    }
    else{
        fprintf(stderr, "TEST: %s SERIAL FAIL\n", __func__);
        releaseUart();
    }

}

The problem is that this print:

printf("1) %i bytes read : %s\n", rx_length, rx);

always WORKS and print the correct data read from serial. While the second print:

printf("2) %i bytes read : %s\n", rx_length, rx);

which is immediately below the first one, sometimes works and other items it just prints an unknown character.

Below I show you the ouput but in the case in which it works and in the case in which it does not work:

Correct:

1) 2 bytes read : gv
2) 2 bytes read gv

Wrong:

1) 2 bytes read : gv
2) 2 bytes read: �

Why even if the two prints are one below the other sometimes I get these kind of inconsistencies in printing the SAME buffer?

Thanks in advance very much for your help.

Best regards, Marco

1
After applying zwol's fixes, you may be trying to output binary that is outside the ASCII range. It may try to interpret it as utf-8 and get confused. That diamond question mark symbol is an indicator of that. Try: void prt(unsigned char *buf,int len) { unsigned int chr; for (; len > 0; --len, ++buf) { chr = *buf; if ((chr >= 0x20) && (chr <= 0x7E)) printf("%c",(char) chr); else printf("{?%2.2X?}",chr); } }Craig Estey
What do you have rx declared as? Is it a char or word or long?Baddack
"Below I show you the ouput ..." -- Your output looks bogus. In the "correct" output, why does the second line have no colon? In the "wrong" output, where did the "SERIAL READ:" come from instead of "bytes read :" that's in the printf()?sawdust
Hello @sawdust I simply made a mistake copying it, I have corrected the question with the same output.Marco Fontana
Hello @Baddack it is declared as char rx[MAX_TXRX_BUF];Marco Fontana

1 Answers

1
votes

I cannot explain why the output changes between the first printf and the second. However, read does not NUL-terminate its buffer, which means, after you do

int rx_length = read(serial, &rx, MAX_TXRX_BUF);

you do not have a valid C string in rx. You need to do something like this instead:

char rx[MAX_TXRX_BUF + 1]; // extra space for terminator
ssize_t rx_length = read(serial, rx, MAX_TXRX_BUF);
if (rx_length >= 0) {
    rx[rx_length] = '\0';
    // use rx here
    // if rx_length == 0, handle EOF
} else {
    // read error
}

Even though I don't know why the output changes, I predict that if you put your two printf statements where I have // use rx here, they will always print the same thing.

N.B. taking the address of an entire array, as you did with &rx, is almost always a mistake. In most cases what you actually want, in order to refer to an array in C, is the address of the first element, which is what you get with just rx. (For reasons too tedious and tangential to get into here, read doesn't care which one of the two you supply it, but in other cases it can lead to subtle bugs. Therefore, best practice in C is to use the address of the first element even in cases where it doesn't matter; reserve &rx for the rare cases where you do specifically need the address of the entire array.)