I am working with Linux Serial port written in C. Below is my UART settings
int fd;
struct termios tty_attributes;
fd = open(comport, O_RDWR | O_NOCTTY |
Looking at The Man
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of- file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. See also NOTES.
Emphasis mine
So you cannot expect that a whole frame can be retrieved by a single read. You should loop until all expected chars are received, for example:
int total_rec = 0;
char temp[SIZE];
while( total_rec < SIZE )
{
read_ret_val = read(fd, temp, SIZE);
if (read_ret_val != -1)
{
if ( (total_rec + read_ret_val) >= SIZE)
{
read_ret_val = SIZE - total_rec;
}
memcpy(&frame[total_rec], temp, read_ret_val);
total_rec += read_ret_val;
}
else
{
perror("error reading serial line: ");
}
}
Try with
memset(&tty_attributes,0,sizeof(tty_attributes));
tty_attributes.c_iflag=0;
tty_attributes.c_oflag=0;
tty_attributes.c_cflag=CS8|CREAD|CLOCAL;
tty_attributes.c_lflag=0;
tty_attributes.c_cc[VMIN]=1;
tty_attributes.c_cc[VTIME]=5;
Your original code has numerous issues which cause it to "getting first 16 bytes only":
The code (as posted) only performs a single read() syscall (rather than continuously loop to read the data from the device).
The input is obviously ASCII text delimited into lines terminated with carriage return and line feed, yet your program uses non-canonical mode to read rather than canonical mode. The assumption by @pbn is confirmed by the minicom output.
Your program uses the serial terminal in non-blocking mode, rather than blocking mode, and resorts to using select() and usleep() calls to wait for the arrival of data.
The termios initialization (besides not being POSIX compliant) has several errors, including improper iflag symbols applied to the cflag member, the character size bits are not cleared with ~CSIZE, and CREAD is not enabled.
Your read routine unnecessarily flushes (i.e. discards) all received but unread data prior to the select() call.
A revised routine for opening and configuring the serial terminal (for blocking canonical mode):
#define BAUDRATE B9600
int init_comm_channel(char *comport)
{
struct termios tty_attributes;
int fd;
fd = open(comport, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("open comport error.\n");
return (-2);
}
if (tcgetattr(fd, &tty_attributes) == -1) {
perror("tcgetattr termios function error.\n");
return (-3);
}
tty_attributes.c_cflag |= CLOCAL | CREAD;
tty_attributes.c_cflag &= ~CSIZE;
tty_attributes.c_cflag |= CS8; /* 8-bit characters */
tty_attributes.c_cflag &= ~PARENB; /* no parity bit */
tty_attributes.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty_attributes.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty_attributes.c_lflag |= ICANON | ISIG; /* canonical input */
tty_attributes.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty_attributes.c_iflag &= ~INPCK;
tty_attributes.c_iflag |= IGNCR;
tty_attributes.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty_attributes.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty_attributes.c_oflag &= ~OPOST;
cfsetospeed(&tty_attributes, BAUDRATE); //setting communication speed and other attributes
cfsetispeed(&tty_attributes, BAUDRATE);
tcflush(fd, TCIOFLUSH);
if (tcsetattr(fd, TCSANOW, &tty_attributes) < 0) {
perror("tcsetattr function error.\n");
return (-4);
}
return fd;
}
The revised routine for reading a line per syscall:
#define SIZE 64
unsigned char frame[SIZE];
char *frame_read(int fd)
{
int read_ret_val;
if (fd < 0) {
printf("Before read over comm channel, channel must be initialize\n");
exit (EXIT_FAILURE);
}
read_ret_val = read(fd, frame, SIZE - 1);
if (read_ret_val < 0) {
perror("read");
exit (EXIT_FAILURE);
}
frame[read_ret_val] = 0; /* terminate string */
return (frame);
}
A revised main() routine that loops forever:
int main(int argc, char *argv[])
{
int fd;
char *readb;
char com_portname[13] = {0};
if (argc > 1)
strcpy(com_portname, argv[1]); // give the first port number for GPS receiving
if ((fd = init_comm_channel(com_portname)) < 0) {
printf("port is not opened\n");
exit (EXIT_FAILURE);
}
printf("port is open for communication:\n");
do {
readb = frame_read(fd);
while (*readb > 0)
printf("the data is 0x%x\n", *readb++);
printf("The line is: %s", frame);
} while (1); /* loop for another line */
close(fd);
}
Most GPS modules and serial interfaces for devices in general send you data line by line. For this you can use canonical mode which you have explicitly disabled.
Canonical mode as stated in manual
In canonical mode:
Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the start of line). Except in the case of EOF, the line delimiter is included in the buffer returned by read(2).
I post code to set serial interface speed and parity with canonical mode enabled:
int set_interface_attribs(int fd, int speed, int parity)
{
// setup based on stty < /dev/ttyACM0 (cfg.txt) output which
// worked for ABSniffer in pyserial implementation
// otherwise module responded only once for every two prompts
struct termios tty;
int rc;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0)
{
log_info("error from tcgetattr %s\r\n", strerror(errno));
return -1;
}
rc = cfsetospeed(&tty, speed);
if (rc == - 1) return -1;
rc = cfsetispeed(&tty, speed);
if (rc == - 1) return -1;
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_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
// tty.c_iflag |= ICRNL | BRKINT; //ICRNL
tty.c_iflag |= IGNCR;
tty.c_cflag &= ~CRTSCTS;
// tty.c_oflag |= OPOST | ONLCR;
// tty.c_iflag |= ISIG | ICANON | IEXTEN;
tty.c_lflag |= ISIG | IEXTEN | ICANON;
tty.c_lflag &= ~ECHO;
tty.c_cc[VEOF] = 0x0;
tty.c_cc[VEOL] = 0x0;
if (tcsetattr(fd, TCSANOW, &tty) != 0)
{
log_info("error from tcsetattr %s\r\n", strerror(errno));
return -1;
}
return 0;
}
Here is how you use it:
rc = set_interface_attribs(fd, B9600, 0);
From now on data should be available line by line. All the errors and possible return values are explained in read manual. Assuming there are no errors, reading a buffer of some arbitrary size should return either EAGAIN (Resource temporarily unavailable) with return code -1 or bytes to the newline character '\n'.