0

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 | O_SYNC | O_NONBLOCK );

if(fd < 0)
{
    perror("open comport error.\n");
    exit(EXIT_FAILURE);
}
else
 {

    if(tcgetattr(fd, &tty_attributes) == -1)
    {
        perror("tcgetattr termios function error.\n");
        exit(EXIT_FAILURE);
    }

    tty_attributes.c_lflag = 0;
    tty_attributes.c_oflag = 0;
    tty_attributes.c_iflag = 0;
    tty_attributes.c_cflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
    tty_attributes.c_cflag |= CS8;
    tty_attributes.c_cflag |= CLOCAL;
    tty_attributes.c_cflag &= ~CREAD;
    tty_attributes.c_oflag &= ~OPOST;
    tty_attributes.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
    tty_attributes.c_cc[VMIN] = SIZE_STR_FRAME;
    cfsetospeed(&tty_attributes, BAUDRATE);       //setting communication speed and other attributes
    cfsetispeed(&tty_attributes, BAUDRATE);
    tcflush(fd, TCIOFLUSH);
    tcsetattr(fd, TCSANOW, &tty_attributes);     //change immediately
    return fd;
}

}

And below is my code for Reading the frame

char* frame_read(int fd)
{
    char *ret = NULL;
    int read_ret_val;
    struct timeval time_val;
    if (fd < 0)
    {
        printf("Before read over comm channel, channel must be initialize\n");
        exit(EXIT_FAILURE);
    }
    memset(frame, 0, SIZE);
    fd_set rfds;        //read file discriptors
    int return_val;
    FD_SET(fd, &rfds);

    setReceiveMode(fd, TRUE);
    tcflush(fd, TCIFLUSH);
    tcflush(fd, TCOFLUSH);    //flush previous values
    return_val = select((fd) + 1, &rfds, NULL, NULL, &time_val);
    if (return_val == -1)
    {
        perror("select");
        exit(EXIT_FAILURE);
    }

    else if (return_val)
    {
        usleep(100 * 1000);
        read_ret_val = read(fd, frame, SIZE);
        if (read_ret_val < 0)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }
        ret = frame;
        //printf("inside else if of read\n");
    }
}

I have one gps module is connected with the UART and when i check with minicom I am getting full frame but when i receive over uart(using this code) I am getting first 16 bytes only. Can anyone point my mistake.? Here baud is 9600 , frame is of 64 bytes and SIZE is of 64 bytes.,buffer i took is also 64 bytes. Please forgive me for formating errors if any.

My main.c file

int main(int argc, char *argv[])
{
  int i=0,j=0;
  char *readb;
  unsigned char data[34];
  static int fd = -1;
  struct struct_base_gps *gps;
  int command=0;
  char COMM_PORTNAME[13];
  strcpy( COMM_PORTNAME, argv[1] );// give the first port number for GPS receiving
  if((fd = init_comm_channel(COMM_PORTNAME)) < 0 )
  {
       exit(EXIT_FAILURE);
       printf("port is not opened\n");
  }
  else
  {

     printf("port is open for communication:\n");
     readb = frame_read(fd);
     for (i=0;i<=34;i++)
      {
          data[i] = *(readb +j);
         printf("the data is %x\n",data[i]);
         j++;
     }
  }
  close (fd);

}

for SIZE is #define SIZE 64 and frame is char frame[64];

Thank you for feedback, I have updated the code.

Also Updating the Frame pics which I am getting on terminal as well as with program. Might It will clear more.

Received the data from UART by program

minicom recived

Ax Cool
  • 115
  • 1
  • 6
  • Unrelated to your problem, but you need to clear out the descriptor set `rfds` before you use it. A set is basically a structure containing an array, and that data will be uninitialized and therefore have *indeterminate* values if you do not use `FD_ZERO` on the set the first thing you do. Same problem with `time_val`, it must be initialized to the timeout you want. – Some programmer dude Dec 19 '16 at 14:17
  • As for your problem, you need to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve) and show us. Including how you call these functions and the definitions of the varaibles you use. For example, what is `frame`? What is `SIZE`? – Some programmer dude Dec 19 '16 at 14:19
  • Lastly, data send through serial communication is *streaming*. There's no fixed message boundaries or packets. If you need that you need to implement it yourself. That also means that one call to `read` my not read a full message ("frame"), or that it might read *more* than a single message (if there is more than one buffered). You need to read in a loop until you receive at least one message, and be able to handle multiple messages (and with the last message possibly being partial) in a single `read` call. – Some programmer dude Dec 19 '16 at 14:24

4 Answers4

1

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: ");
    }
}
LPs
  • 16,045
  • 8
  • 30
  • 61
  • :- thanks for feedback, So I should loop `read` till expected chars are received? – Ax Cool Dec 19 '16 at 14:28
  • Look at my little example. – LPs Dec 19 '16 at 14:29
  • Yup., I will modify mine as per this – Ax Cool Dec 19 '16 at 14:31
  • Yup., I will modify mine as per this. I could give +1 here. As I am not able to tick up the answer, ( low points :-) ). Thanks – Ax Cool Dec 19 '16 at 14:34
  • 1
    The answer is incomplete, because the terminal is opened in non-blocking mode. Looping will retrieve more data, but at the expense of wasted CPU cycles. I.E. the program is busy *polling* the system for data. – sawdust Dec 19 '16 at 19:27
0

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;
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
awahl
  • 24
  • 2
  • Thank you for the feedback. and Will try after @LPs example above if not solved :-) – Ax Cool Dec 19 '16 at 14:32
  • -1 for suggesting non-POSIX code. See [Setting Terminal Modes Properly](http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_12.html#SEC237) and [Serial Programming Guide for POSIX Operating Systems](http://www.cmrr.umn.edu/~strupp/serial.html) – sawdust Dec 19 '16 at 19:07
0

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'.

Community
  • 1
  • 1
pbn
  • 2,406
  • 2
  • 26
  • 39
  • Canonical mode doesn't use VMIN and VTIME. The memset() is superfluous before a tcgetattr(). – sawdust Dec 19 '16 at 22:28
  • hi @sawdust :- Thanks for answers. Can you give a example code so that I can try with it. – Ax Cool Dec 20 '16 at 03:51
  • hi @pbn :- thx for answer, I have tried this code. But it's not working. Also It will be good to use `tcflush(fd, TCOFLUSH);` to flush prev. values. Thank you once again for replay. – Ax Cool Dec 20 '16 at 04:51
  • @AxCool Ok, one other thing that I noticed is that you are not setting `struct timeval` in frame read code. Take a look at example section in [select manual](http://man7.org/linux/man-pages/man2/select.2.html). Note that you are also missing a branch for select timeout. It's also explained in manual. – pbn Dec 20 '16 at 08:51
  • I will improve my answer based on the comments once it solves the problem. – pbn Dec 20 '16 at 08:52
  • Hi @pbn : Thanks for noticing, Actually I have used the VMIN and VTIME settings. But it didn't worked. Now to be precise I am using ublox gps. I am updating questions with frame received with terminal, and also code. – Ax Cool Dec 20 '16 at 10:50
0

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);
}
sawdust
  • 16,103
  • 3
  • 40
  • 50
  • Hi @sawdust : Thank you for sharing the answer. I am trying this answer and will share the result. +1 here for Answer :-) . – Ax Cool Dec 21 '16 at 04:35
  • Hi @sawdust :- I know your busy, And I have tried this code. And I am sorry as the result I am getting is different from terminal received data. terminal received data is :- b5 62 01 07 5c 00 48 0b 8d 15 e0 07 0c 1d 04 19 30 37 61 00 00 00 dc 98 04 00 03 01 0b 05 a1 23 37 2e 5e 9d c7 07 08 31 0c 00 3f 81 0d 00 ad cd 00 00 3b aa 00 00 7b 01 00 00 e5 ff ff ff 23 00 00 00 7c 01 00 00 24 55 cd 00 01 0a 00 00 3a 81 84 00 06 01 00 e0 86 4c 22 00 00 00 02 00 00 00 00 80 28 1e b5 62. – Ax Cool Dec 29 '16 at 06:30
  • But code out is :- b5 62 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 69 c 0 0 fd a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 different from actual. Can you suggest edits. – Ax Cool Dec 29 '16 at 06:30
  • In your original question, you posted a link to the data that you're trying to read, which is ASCII text. The code for canonical mode that I posted will only read lines of ASCII text, which will solve your original question. Apparently you have moved on to another issue, and are trying to read different data (i.e. binary data) with an inappropriate configuration. Binary data has to be read in non-canonical mode. See http://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c/38318768#38318768 **A different problem requires a different answer.** – sawdust Dec 29 '16 at 07:08
  • :- I am sorry, That I am not able to clearly explain Scenarios. Also I am much thankful that You took time from your busy schedule to answer. Thank you :-) – Ax Cool Dec 29 '16 at 09:32
  • @sawdust - is this a bad things? "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."? Can it be setup in this way? – Fra Jul 19 '19 at 17:30
  • @Fra -- You can write a program many different ways, including illogical and/or inefficient ways. But a blocking I/O is optimal with canonical mode, as that lets the OS perform its job effectively and makes your code simpler. – sawdust Jul 19 '19 at 19:00
  • @sawdust - given this programmatic setup - any idea why I would be getting a long series of 0x00 bytes? My application runs for hours and then all of a sudden seizes up with a long string of 0x00 bytes. You can see here: https://portal.u-blox.com/s/question/0D52p00008ePY7RCAW/evkm8n-data-issue – Fra Jul 19 '19 at 19:09