Lukor
Lukor

Reputation: 1707

C - Data not received by serial device

I am using C to open a serial device and send/receive data to/from it. Receiving works without a problem, but any data I send does not reach the device. I open the device like this:

int open_tty() {
    int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_CLOEXEC);  

    struct termios config;
    cfsetispeed(&config, B38400);
    cfsetospeed(&config, B38400);
    config.c_cflag &= ~PARENB;                                                                                                                                          
    config.c_cflag &= ~CSTOPB;                                                                                                                                          
    config.c_cflag &= ~CSIZE;                                                                                                                                           
    config.c_cflag |= CS8;                                                                                                                                              

    tcsetattr(fd, TCSANOW, &config);                                                         

    return fd;
}

...
write(fd, data, length)
...

According to strace, everything works just fine:

openat(AT_FDCWD, "/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_CLOEXEC) = 3
ioctl(3, TCGETS, {B38400 -opost isig icanon echo ...}) = 0
ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost isig icanon echo ...}) = 0
ioctl(3, TCGETS, {B38400 -opost isig icanon echo ...}) = 0
write(3, "some data.......", 16) = 16

However, the device does not receive any data (it should send an ACK packet). If I do the same thing in python, everything works just fine:

s=serial.Serial('/dev/ttyUSB0', baudrate=9600*4)
s.write('some data.......')

strace:

openat(AT_FDCWD, "/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC) = 3
ioctl(3, TCGETS, {B38400 -opost isig icanon echo ...}) = 0
ioctl(3, TCGETS, {B38400 -opost isig icanon echo ...}) = 0
ioctl(3, TCGETS, {B38400 -opost isig icanon echo ...}) = 0
ioctl(3, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(3, TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(3, TIOCMBIS, [TIOCM_DTR])         = 0
ioctl(3, TIOCMBIS, [TIOCM_RTS])         = 0
ioctl(3, TCFLSH, TCIFLUSH)              = 0
write(3, "some data.......", 16) = 16

Any help would be greatly appreciated.

EDIT: As @abarnert suggested, I set the DTR and RTS bits and flushed the buffer:

char rts = TIOCM_RTS;
char dtr = TIOCM_DTR;
ioctl(fd, TIOCMBIS, &dtr);
ioctl(fd, TIOCMBIS, &rts);
tcflush(fd, TCIFLUSH);

This led to the following additional syscalls being dispatched directly before the write call:

ioctl(3, TIOCMBIS, [TIOCM_DTR|TIOCM_DSR|0x200]) = 0
ioctl(3, TIOCMBIS, [[TIOCM_RTS|0x30200}) = 0
ioctl(3, TCFLSH, TCIFLUSH)               = 0

However, I still don't get any ACK packet back from the device.

The device is a VirtualRobotix GPS uBlox 8 (http://www.virtualrobotix.it/index.php/en/shop/gps/3dr-gps-ublox-8-542015-11-30-13-35-34_-detail) connected using a USB to serial converter.

Upvotes: 0

Views: 379

Answers (1)

Luis Colorado
Luis Colorado

Reputation: 12668

Hmmmm, I'm afraid you have not followed the correct procedure in setting termios parameters. First you must use tcgetattrs(3) to get the actual configured parameters (and to initialize the termios structure, otherwise uninitialized to garbage in your code, as you have declared it as an automatic variable), then you change what you want, and finally you set the desired configuration.

The correct procedure is:

 struct termios param;
 res = tcgetattrs(fd, &param); /* VERY IMPORTANT: first get the actual parameters */
 if (res < 0) {
      perror("tegetattrs");
      exit(EXIT_FAILURE);
 }
 cfsetispeed(&param, B38400);
 cfsetospeed(&param, B38400);
 param.c_cflag &= ~PARENB;
 /* ... all the needed configuration. */
 /* and finally. */
 res = tcsetattrs(fd, &param);
 if (res < 0) { /* error */
     perror("tcsetattrs");
     exit(EXIT_FAILURE);
 }

(Also, it's common to check the return value res for errors)

as the manual page termios(3) says in relation to setting the line speed:

Line speed

The baud rate functions are provided for getting and setting the values of the input and output baud rates in the termios structure. The new values do not take effect until tcsetattr() is successfully called.

Setting the speed to B0 instructs the modem to "hang up". The actual bit rate corresponding to B38400 may be altered with setserial(8).

The input and output baud rates are stored in the termios structure.

(quotation is mine)

Upvotes: 1

Related Questions