2

I am trying to read data sent from the tty/USB0 and print it out with byte format.

Question:

  1. I expect to print out the data once the reading bytes reach 40 However, the time takes much longer than I expect. The read() system call hangs and I believe the data should already be larger than 40. The data will finally be printed out but it should not take so long. Did I make anything wrong in this programming ?

thanks

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

#define BAUDRATE B9600            
#define MODEMDEVICE "/dev/ttyUSB0"

#define FALSE 0
#define TRUE 1

main()
{
int fd,c, res;
struct termios oldtio,newtio;
unsigned char buf[40];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }

tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));

newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 1;
newtio.c_lflag = ICANON;

tcflush(fd, TCIOFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

int i;
while (1) {
    res = read(fd,buf,40);
    if(res==40){
        printf("res reaches 40 \n");
    }
    printf("res: %d\n",res);
    for(i=0;i<res;++i){
    printf("%02x ", buf[i]);
    }
    return;
    }
}

--------------------raw mode code------------------------

  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <termios.h>
  #include <stdio.h>

  #define BAUDRATE B9600
  #define MODEMDEVICE "/dev/ttyUSB0"
  #define _POSIX_SOURCE 1 /* POSIX compliant source */
  #define FALSE 0
  #define TRUE 1

  volatile int STOP=FALSE;

  main()
  {
    int fd,c, res;
    struct termios oldtio,newtio;
    unsigned char buf[255];

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
    if (fd <0) {perror(MODEMDEVICE); exit(-1); }

    tcgetattr(fd,&oldtio); /* save current port settings */

    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;

    /* set input mode (non-canonical, no echo,...) */
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 0;   
    newtio.c_cc[VMIN]     = 40;   

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newtio);

    int i;
    while (STOP==FALSE) {       
      res = read(fd,buf,255);   
      for( i=0;i<res;++i){
        printf("%02x \n", buf[i]);
      }
    }
    tcsetattr(fd,TCSANOW,&oldtio);
  }

It now can print out the data once buffer capacity is full ( which is 40 ). 1 question:

  1. When I modified the printf

    printf("%02x ", buf[i]); ( remove "\n" ) It will not print out when the buffer is full until more bytes are received. Why this happens?

Thanks

Sam
  • 3,889
  • 10
  • 40
  • 74
  • What is "too long"? What makes you think the data should already be larger than 40 bytes? as far as I can tell from the code, it behaves as it's suppose to. ``read`` blocks until data is available. If your modem device is slow, then I would guess so is ``read``. – Fred Oct 01 '15 at 09:36
  • @Fred, I have a remote controller and every time I press the button it will send the command to my host through serial port. Say if I added one line of code newtio.c_cc[VEOF] = 1 and remove return in the while loop, once the button is pressed, the command display on the terminal immediately and the command size is larger than 40 bytes. – Sam Oct 01 '15 at 09:51
  • Does the situation improve whan you use `stty -F /dev/ttyUSB0 raw` command? – vlp Oct 01 '15 at 10:08
  • @vlp yea, by using stty, everytime I press the button the command displays on the terminal. – Sam Oct 01 '15 at 10:11
  • Then have a look at e.g. [here](http://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c) how to do the same programmatically. – vlp Oct 01 '15 at 10:21

1 Answers1

2

You need to switch the terminal to raw mode to disable line buffering.

Citing this answer:

The terms raw and cooked only apply to terminal drivers. "Cooked" is called canonical and "raw" is called non-canonical mode.

The terminal driver is, by default a line-based system: characters are buffered internally until a carriage return (Enter or Return) before it is passed to the program - this is called "cooked". This allows certain characters to be processed (see stty(1)), such as Cntl-D, Cntl-S, Ctrl-U Backspace); essentially rudimentary line-editing. The terminal driver "cooks" the characters before serving them up.

The terminal can be placed into "raw" mode where the characters are not processed by the terminal driver, but are sent straight through (it can be set that INTR and QUIT characters are still processed). This allows programs like emacs and vi to use the entire screen more easily.

You can read more about this in the "Canonical mode" section of the termios(3) manpage.

See e.g. this or this how to achieve that programmatically (did not check the code, but it should be easy to find it).

Alternatively you could use e.g. strace or ltrace to check what stty -F /dev/ttyUSB0 raw does (or read the manual page where it is described).

EDIT>

Regarding printf without a newline -- fflush(stdout); immediately after it should help (another line-buffering is taking place).

You might consider reading this and maybe this.

Community
  • 1
  • 1
vlp
  • 6,063
  • 2
  • 18
  • 44
  • thanks. your solution works to me. however, I've updated the post and please take a look and let me know if you have any idea. – Sam Oct 02 '15 at 02:25