Reading and writing to serial port in C on Linux Reading and writing to serial port in C on Linux linux linux

Reading and writing to serial port in C on Linux


I've solved my problems, so I post here the correct code in case someone needs similar stuff.

Open Port

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

Set parameters

struct termios tty;struct termios tty_old;memset (&tty, 0, sizeof tty);/* Error Handling */if ( tcgetattr ( USB, &tty ) != 0 ) {   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;}/* Save old tty parameters */tty_old = tty;/* Set Baud Rate */cfsetospeed (&tty, (speed_t)B9600);cfsetispeed (&tty, (speed_t)B9600);/* Setting other Port Stuff */tty.c_cflag     &=  ~PARENB;            // Make 8n1tty.c_cflag     &=  ~CSTOPB;tty.c_cflag     &=  ~CSIZE;tty.c_cflag     |=  CS8;tty.c_cflag     &=  ~CRTSCTS;           // no flow controltty.c_cc[VMIN]   =  1;                  // read doesn't blocktty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeouttty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines/* Make raw */cfmakeraw(&tty);/* Flush Port, then applies attributes */tcflush( USB, TCIFLUSH );if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {   std::cout << "Error " << errno << " from tcsetattr" << std::endl;}

Write

unsigned char cmd[] = "INIT \r";int n_written = 0,    spot = 0;do {    n_written = write( USB, &cmd[spot], 1 );    spot += n_written;} while (cmd[spot-1] != '\r' && n_written > 0);

It was definitely not necessary to write byte per byte, also int n_written = write( USB, cmd, sizeof(cmd) -1) worked fine.

At last, read:

int n = 0,    spot = 0;char buf = '\0';/* Whole response*/char response[1024];memset(response, '\0', sizeof response);do {    n = read( USB, &buf, 1 );    sprintf( &response[spot], "%c", buf );    spot += n;} while( buf != '\r' && n > 0);if (n < 0) {    std::cout << "Error reading: " << strerror(errno) << std::endl;}else if (n == 0) {    std::cout << "Read nothing!" << std::endl;}else {    std::cout << "Response: " << response << std::endl;}

This one worked for me. Thank you all!


Some receivers expect EOL sequence, which is typically two characters \r\n, so try in your code replace the line

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};

with

unsigned char cmd[] = "INIT\r\n";

BTW, the above way is probably more efficient. There is no need to quote every character.


1) I'd add a /n after init. i.e. write( USB, "init\n", 5);

2) Double check the serial port configuration. Odds are something is incorrect in there. Just because you don't use ^Q/^S or hardware flow control doesn't mean the other side isn't expecting it.

3) Most likely: Add a "usleep(100000); after the write(). The file-descriptor is set not to block or wait, right? How long does it take to get a response back before you can call read? (It has to be received and buffered by the kernel, through system hardware interrupts, before you can read() it.) Have you considered using select() to wait for something to read()? Perhaps with a timeout?

Edited to Add:

Do you need the DTR/RTS lines? Hardware flow control that tells the other side to send the computer data? e.g.

int tmp, serialLines;cout << "Dropping Reading DTR and RTS\n";ioctl ( readFd, TIOCMGET, & serialLines );serialLines &= ~TIOCM_DTR;serialLines &= ~TIOCM_RTS;ioctl ( readFd, TIOCMSET, & serialLines );usleep(100000);ioctl ( readFd, TIOCMGET, & tmp );cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;sleep (2);cout << "Setting Reading DTR and RTS\n";serialLines |= TIOCM_DTR;serialLines |= TIOCM_RTS;ioctl ( readFd, TIOCMSET, & serialLines );ioctl ( readFd, TIOCMGET, & tmp );cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;