C – Linux serial programming, ttyS0

Linux serial programming, ttyS0… here is a solution to the problem.

Linux serial programming, ttyS0

I’m trying to learn how to program ttyS0 serial port in Linux using C. I have another machine connected to my serial port that sends alternating hexadecimal values 5f and 6f approximately every two seconds. I have verified with other port monitoring applications that these values appear on the port. In my code, I’m blocking read() for a 10-character long buffer. Even though my other machine is still sending data, read() blocks forever. If I include the line fcntl(fd, F_SETFL, FNDELAY); Setting read() to non-blocking read() always returns a value of -1, which means there is no data in the UART buffer, and my for loop code just prints out random values in the buffer. So in short, my assumption is that my code isn’t reading ttyS0, and I don’t know why. Below is my code, hopefully someone can see what is causing my problem and get me straight to the point. By the way, I’m using Scientific Linux, and I believe ttyS0 is com port 1, just like in RedHat and Fedora. The aslo below is the output when I run the code. It seems to be writing to the COM port without problems, but says it’s not available for reading it. Also it’s clear that the buffer I’m printing is just random values, not read in data. Thanks

Console output

hello world
hi again
write error: : Success
 wrote 4 bytes
number of bytes read is -1
read error:: Resource temporarily unavailable
4  8  120  -99  -73  -65  41  -120  4  8  
should of put something out

Code

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>

int main()
{
    printf("hello world\n");
    int n;
    int fd;
    char c;
    int bytes;

char buffer[10];
    char *bufptr;
    int nbytes;
    int tries;
    int x;
    struct termios options;

fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd == -1)
    {
        perror("open_port: Unable to open:");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
        printf("hi again\n");
    }

tcgetattr(fd, &options);

cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag &= ~( ICANON | ECHO | ECHOE | ISIG );
    options.c_iflag &= ~(IXON | IXOFF | IXANY );
    options.c_oflag &= ~OPOST;

tcsetattr(fd, TCSANOW, &options);

write(fd, "ATZ\r",4);
    printf(" wrote\n");
    bufptr = buffer;

fcntl(fd, F_SETFL, FNDELAY);
     bytes = read(fd, bufptr, sizeof(buffer));
    printf("number of bytes read is %d\n", bytes);
    perror ("read error:");

for (x = 0; x < 10 ; x++)
    {
        c = buffer[x];
        printf("%d  ",c);
    }
    close(fd);

puts(buffer[0]);
    printf("\nshould of put something out \n");

return (0);
}

Solution

The following line will be problematic:

options.c_cflag &= CSTOPB;

It resets all other bits of the c_cflag.

If you want to use 1 stop bit, then use:

options.c_cflag &= ~CSTOPB;

If you want to use 2 stop bits, then use:

options.c_cflag |= CSTOPB;

Edit:

The following lines can also cause problems:

fcntl(fd, F_SETFL, 0);

It will reset several important flags.

Related Problems and Solutions