views:

1404

answers:

1

I am trying to write to a bluetooth device on MAC OSX using the unistd.h Linux functions in c. I am connecting fine and writing the first few bytes with success. When I try to write other commands to it (there are bytes added to the write buffer every 15ms), I don't see any results even though the write() function returns 1 (write success).

If you start a write and it doesn't finish by the time you try to start another write (since it is non-blocking), could that possibly screw up the initial write? (If so, is there any way to check if a write has completed?) That is the only thing I can think of since the writes are occurring fairly frequently and the first two are successfully sent.

qwbyte() simply adds a byte to the output array and increments its length

The open port function:

BAMid = -1;
struct termios options;

struct termios originalTTYAttrs;

// Open the serial port read/write, nonblocking, with no controlling terminal, and don't wait for a connection.
BAMid = open(strPath, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (BAMid == -1)
{
    printf("Error opening serial port %s - %s(%d).\n",
           strPath, strerror(errno), errno);
    goto error;
}

// Issue TIOCEXCL ioctl to prevent additional opens except by root-owned processes.
if (ioctl(BAMid, TIOCEXCL) == -1)
{
    printf("Error setting TIOCEXCL on %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// Get the current options and save them so we can restore the default settings later.
if (tcgetattr(BAMid, &originalTTYAttrs) == -1)
{
    printf("Error getting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// The serial port attributes such as timeouts and baud rate are set by modifying the termios
// structure and then calling tcsetattr() to cause the changes to take effect. Note that the
// changes will not become effective without the tcsetattr() call.

options = originalTTYAttrs;

// Set raw input (non-canonical) mode, with reads blocking until either a single character 
// has been received or a one second timeout expires. [should be moot since we are leaving it as nonblocking]

cfmakeraw(&options);

options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 10; cfsetspeed(&options, B57600); // Set 57600 baud
options.c_cflag |= CS8; // Use 8 bit words

// Cause the new options to take effect immediately.
if (tcsetattr(BAMid, TCSANOW, &options) == -1)
{
    printf("Error setting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

//flush old transmissions if (tcflush(BAMid,TCIOFLUSH) == -1) { printf("Error flushing BAM serial port - %s(%d).\n", strerror(errno), errno); }

oBufLength = 0;

// Ask it to start if (! qwbyte(CmdStart) ) { goto error; }

if (! qwbyte(CmdFull) ) { goto error; } //this transmit works txbytes();

printf("success opening port!"); return -1; // Failure path error: if (BAMid != -1) { close(BAMid); } printf("returning an error--%d",errno); return errno; }

The write function (txbytes):

int i, bufSize, numBytes;

if(oBufLength != 0) { //if the output array isn't empty

        //duplicating the output array and its size so it can 
        //be overwritten while this write is occuring
        printf("about to transmit: ");
  for(i = 0; i < oBufLength; i++) {
   printf(" %u",oBuf[i]);
   tempBuf[i] = oBuf[i];
  }
  printf("\n");

bufSize = oBufLength; oBufLength = 0;

numBytes = write(BAMid, &tempBuf, bufSize);

printf("bytes written = %d\n",numBytes);

if (numBytes == -1) { printf("Error writing to port - %s(%d).\n", strerror(errno), errno); }

return (numBytes > 0); } else { return 0; }

+1  A: 

Non-blocking writes don't work how you're expecting them to.

If the write can't be completed immediately, write() returns to your code - but it doesn't keep trying to send the data in the background. All it does is say "I couldn't write at the moment - try again later". It does this by returning -1, with errno set to EAGAIN.

Also, remember that when successful, write() returns the number of bytes successfully written. So if you're getting a return value of 1 when you asked for 2 bytes to be written, that means it was only partially successful, and you need to call write() again some time for the second byte.

Basically, if you're using non-blocking IO, you want your txbytes() function to call write() in a loop until either your buffer is empty, or it returns -1. If it returned -1 you need to check errno - if it's EAGAIN, you'll have to call write() again some other time; anything else is probably a real error. Something like this:

ssize_t written = 0;

while (oBufLength > 0 && written > -1)
{
    size_t i;

    printf("about to transmit %d bytes: ", oBufLength);
    for(i = 0; i < oBufLength; i++) {
            printf(" %u",oBuf[i]);
    }
    printf("\n");

    written = write(BAMid, oBuf, oBufLength);

    printf("Write returned %d\n", written);

    if (written > 0)
    {
        /* The first "written" number of bytes in the buffer have now been sent, so
         * we discard them and move up the remaining bytes (if any) to the start of 
         * the buffer */

        oBufLength -= written;
        memmove(oBuf, oBuf + written, oBufLength);

        printf("Now have %d bytes left to send.\n", oBufLength);
     }
}

if (written > -1 || errno == EAGAIN)
{
    /* No fatal errors... */
    return 0;
} else
    /* error left in errno for caller to inspect */
    return -1;
}

Note that there's no need to duplicate the buffer - since write() doesn't do anything in parallel with your code. If it says its written the bytes, it doesn't need your buffer any more. Hope that helps!

caf
Thanks for the good advice on the function structure, though unfortunately it doesn't solve my problem. I am always getting the number of bytes I sent to write returned from the write() function, indicating that it successfully wrote all of the bytes sent.
OK, well if write() is returning a positive number equal to the number of bytes you asked it to send, then there's no problem with the write()s - the OS has accepted the bytes from you and the kernel now has the job of transmitting them.So the problem is the kernel not doing that after the first few bytes. This sounds like a flow control problem - you need to make sure that your device is using the same flow control settings as you're setting with tcsetattr().As a stab in the dark you could try unsetting CRTSCTS and/or setting CLOCAL in options.c_cflag.
caf