0

I'm trying to write a char array to the serial port twice. For some reason the second time I call write() it fails and I have no idea why. The weird part is that the first write succeeds.

Here's my write function:

void write_port()
{
  unsigned char writeBuffer[5];
  int i;
  size_t len;

  writeBuffer[0] = 0x00;
  writeBuffer[1] = 0x01;
  writeBuffer[2] = 0x02;
  writeBuffer[3] = 0x03;
  writeBuffer[4] = 0x04;  

  len = sizeof(writeBuffer);

  for (i=0; i < len; i++)
  {
    printf("Write: %x\n", writeBuffer[i]);
  }

  //First Write:
  int n = write(fd, writeBuffer, len);
  if (n < 0)
    fputs("Serial Port Write Failed!\n", stderr);
  else
    printf("Wrote %0d bytes to serial port\n", n);

  //Second Write:
  n = write(fd, writeBuffer, len);
  if (n < 0)
    fputs("Serial Port Write Failed!\n", stderr);
  else
    printf("Wrote %0d bytes to serial port\n", n);  
}

Here's where I configure the port:

int open_port(void)
{
  struct termios options;

  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);

  bzero(&options, sizeof(options));   // set everything to 0 

  if (fd != -1)
  {
    printf("Serial Port Open\n");

    tcgetattr(fd, &options_original);
    tcgetattr(fd, &options);
    cfsetispeed(&options, B460800);
    cfsetospeed(&options, B460800);
    options.c_cflag |= (CLOCAL | CREAD); /* Enable the receiver and set local mode */
    options.c_cflag &= ~ECHO; // Disable echoing of input characters
    options.c_cflag &= ~ECHOE;
    options.c_cflag &= ~PARENB; // parity disabled
    options.c_cflag &= ~CSTOPB; // 1 stop bit
    options.c_cflag &= ~CSIZE;  // Mask the character size bits
    options.c_cflag |=  CS8;    // 8 data bits    
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Raw Input Mode */

    if (tcsetattr(fd, TCSANOW, &options))    /* Set the new options for the port */
    {
      perror("could not set the serial settings!");
      return -1;
    }
  }
  else
  {
    /* Could not open the port */
    perror("open_port: Unable to open /dev/ttyUSB0 - ");
  }

  return (fd);
}

Finally here's my console output.

Serial Port Open
Flushing IO Buffers
Write: 0
Write: 1
Write: 2
Write: 3
Write: 4
Wrote 5 bytes to serial port
Serial Port Write Failed!
Serial Port Closed

So for some unknown reason I get -1 back from the second write() and have absolutely no idea why, what in the world could cause that?

lurker
  • 56,987
  • 9
  • 69
  • 103
mrbean
  • 547
  • 5
  • 14
  • I don't know for sure if this matters, but what's the serial port connected to? And how big is the output buffer for the serial write? – lurker May 04 '14 at 22:28
  • 3
    Why don't you use `perror` to get more info about why the second write` failed ? – Grapsus May 04 '14 at 22:33
  • From my computer 'ttyUSB0' is a keyspan USB-Serial converter and from there it's connected to an fpga development board with a rs232 controller. I'm not sure how big the output buffer is. Updated: The error number I get back is `errno = 11` and perror returns `perror = : Resource temporarily unavailable` – mrbean May 04 '14 at 22:33
  • Perhaps the serial port can't take 10 bytes all at once. You could test that notion by putting a `sleep` after the first 5 bytes for a few seconds, just to see if that makes the problem go away. If it does, then check the FPGA dev board information to see what it says about serial comms. Perhaps also the USB/serial driver isn't set for the expected flow control mode. – lurker May 04 '14 at 22:52
  • 1
    Well after some searching around for those errors (thanks @Grapsus) likely cause was me incorrectly setting O_NONBLOCK. I clear it by changing `| O_NONBLOCK` to `& ~O_NONBLOCK` and my problem went away and the second write goes through. Hopefully this fixes it! – mrbean May 04 '14 at 22:52
  • What do you write to this serial port ? It may have a line discipline that buffers one line. So the first write would succeed, but not the second one. – Grapsus May 04 '14 at 22:53
  • I'm hoping to eventually write a large character array to the serial port. Currently I am sending just a small character array. How would a line discipline get set, is that through my configuration? I'll be quite honest I don't fully understand how this stuff works under the hood or areas that should be of concern. Also thanks lurker :-) – mrbean May 04 '14 at 22:57
  • @mrbean: line discipline shouldn't be a concern here, but non-blocking mode definitely will fail at times, requiring you to buffer the data and try again later. Non-blocking is often combined with `select` or `poll` to find out when the port is ready to accept more data. – Ben Voigt May 04 '14 at 22:58
  • The accepted answer of using non-blocking mode is probably only a partial solution. More likely the complete solution includes setting non-canonical mode (for for both input & output). Your program only sets non-canonical mode for input, and may be leaving the output mode to canonical (since it does nothing with respect to output). @BenVoigt -- Line discipline should be a concern because it hasn't been disabled for output, and the OP is sending binary data. – sawdust May 04 '14 at 23:38
  • OT: `write()` returns `ssize_t` not `int`. – alk May 05 '14 at 08:17

2 Answers2

2

I think that your send buffer is getting full and you are using a non-blocking socket which means that instead of waiting for the buffer to clear like in a blocking socket, the process just returns the "Resource temporarily unavailable error". See this thread for more info: What can cause a “Resource temporarily unavailable” on sock send() command

Community
  • 1
  • 1
Elias
  • 1,367
  • 11
  • 25
0

The write system call if fails set errno variable. You can check that to get exact details of why write failed. Additionally you may refer this project's native code for linux to see how write is implemented https://github.com/RishiGupta12/serial-communication-manager

samuel05051980
  • 369
  • 2
  • 2
  • Welcome to SO. I think your first sentence would be clearer as "If the write system call fails, then it sets the errno variable." Is that what you meant? This answer would probably be better as a comment than an answer. Unfortunately, you don't yet have the reputation to post comments, so there's not much that you can end up doing. – Teepeemm Apr 01 '15 at 04:03