title: Why does this serial communication code hang when talking to a USB-to-serial adapter?
body: <p>I am writing an application that is supposed to control a projector from the desktop of a Mac Mini. I am writing the application in Objective-C using Xcode and termios.h to talk to the serial port. I am connecting from a USB-to-Serial adapter and I know the drivers are installed correctly because I can send HEX commands to the projector to control its functions. </p>
  
  <p>I have code written that I believe should open the serial port, connect to the projector, send codes, and disconnect:</p>
  
  <pre><code>@implementation Projector
  
  @synthesize helpUrl = _helpUrl;
  @synthesize projectorConnected;
  
  - (id)init {
  
      //[self connectProjector];
  
      if (TRUE == projectorConnected) {
          // Success!
      }
      else {
          // Error!
      }
  
      return self;
  }
  - (id)initWithHelpUrl:(NSString *)helpUrlString {
  
      [self setHelpUrl:helpUrlString];
  
      return [self init];
  }
  
  - (void)connectProjector {
  
      [self setProjectorConnected:FALSE];
  
      NSString *deviceNameString = @"/dev/tty.usbserial";
      speed_t baudRate = B19200;
  
      serialFileDescriptor = [self openSerialPort:[deviceNameString cStringUsingEncoding:NSASCIIStringEncoding] baud:baudRate];
      if (serialFileDescriptor == -1) {
          NSLog(@"Error opening serial port file!");
      }
  
      _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: serialFileDescriptor];
  
      //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveSerialMessage:) name:NSFileHandleReadCompletionNotification object:_fileHandle];
      //[_fileHandle readInBackgroundAndNotify];
  
      // [self closeSerialPort];
  
  }
  
  - (int)openSerialPort:(const char *)serialPortFile baud:(speed_t)baudRate {
  
      struct termios toptions;
      int fd;
  
      fd = open(serialPortFile, O_RDWR | O_NOCTTY | O_NDELAY);
      if (fd == -1)  {
          perror("init_serialport: Unable to open port ");
          return -1;
      }
  
      if (tcgetattr(fd, &amp;gOriginalTTYAttrs) &lt; 0) {
          perror("init_serialport: Couldn't get term attributes");
          return -1;
      }
  
      toptions = gOriginalTTYAttrs;
  
      cfsetispeed(&amp;toptions, baudRate);
      cfsetospeed(&amp;toptions, baudRate);
  
      toptions.c_cflag &amp;= PARENB;
      toptions.c_cflag &amp;= CSTOPB;
      toptions.c_cflag &amp;= CSIZE;
      toptions.c_cflag |= CS8;
  
      toptions.c_cflag &amp;= CRTSCTS;
  
      toptions.c_cflag |= CREAD | CLOCAL;  
      toptions.c_iflag &amp;= (IXON | IXOFF | IXANY); 
  
      toptions.c_lflag &amp;= (ICANON | ECHO | ECHOE | ISIG); 
      toptions.c_oflag &amp;= OPOST; 
  
      // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
      toptions.c_cc[VMIN]  = 0;
      toptions.c_cc[VTIME] = 20;
  
      if( tcsetattr(fd, TCSANOW, &amp;toptions) &lt; 0) {
          perror("init_serialport: Couldn't set term attributes");
          return -1;
      }
  
      return fd;
  
  }
  
  - (void)sendSerialMessage:(NSString *)message {
  
      NSString *deviceNameString = @"/dev/tty.usbserial";
      speed_t baudRate = B19200;
  
      serialFileDescriptor = [self openSerialPort:[deviceNameString cStringUsingEncoding:NSASCIIStringEncoding] baud:baudRate];
      if (serialFileDescriptor == -1) {
          NSLog(@"Error opening serial port file!");
      }
  
      _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: serialFileDescriptor];
  
      //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveSerialMessage:) name:NSFileHandleReadCompletionNotification object:_fileHandle];
      //[_fileHandle readInBackgroundAndNotify];
  
      // convert message string into NSData
      NSString *dataString = [message stringByReplacingOccurrencesOfString:@" " withString:@""];
      NSMutableData *dataToSend = [[NSMutableData alloc] init];
      unsigned char whole_byte;
      char byte_chars[3] = {'\0','\0','\0'};
      int i;
      for (i=0; i &lt; 6; i++) {
          byte_chars[0] = [dataString characterAtIndex:i*2];
          byte_chars[1] = [dataString characterAtIndex:i*2+1];
          whole_byte = strtol(byte_chars, NULL, 16);
          [dataToSend appendBytes:&amp;whole_byte length:1];
      }
  
      // write to the serial port file
      [_fileHandle writeData:dataToSend];
  
      sleep(10);
  
      [self closeSerialPort];
  }
  
  - (void)receiveSerialMessage:(NSNotification *)notification {
      NSData* messageData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
      if ( [messageData length] == 0 ) {
          [_fileHandle readInBackgroundAndNotify];
          return;
      }
  
      NSString* receivedMessage = [[NSString alloc] initWithData:messageData encoding:NSASCIIStringEncoding];
  
      [[NSNotificationCenter defaultCenter] postNotificationName:@"serialMessageReceived" object:receivedMessage];
      [_fileHandle readInBackgroundAndNotify];
  
  }
  
  // Given the file descriptor for a serial device, close that device.
  - (void)closeSerialPort {
  
      ///*
      // Block until all written output has been sent from the device.
      // Note that this call is simply passed on to the serial device driver. 
      // See tcsendbreak(3) ("man 3 tcsendbreak") for details.
      if (tcdrain(serialFileDescriptor) == -1) {
          NSLog(@"Error waiting for drain - %s(%d).", strerror(errno), errno);
      }
  
      // Traditionally it is good practice to reset a serial port back to
      // the state in which you found it. This is why the original termios struct
      // was saved.
      if (tcsetattr(serialFileDescriptor, TCSANOW, &amp;gOriginalTTYAttrs) == -1) {
          NSLog(@"Error resetting tty attributes - %s(%d).\n", strerror(errno), errno);
      }
      //*/
  
      close(serialFileDescriptor);
      serialFileDescriptor = -1;
  
      _fileHandle = nil;
  }
  
  - (BOOL)powerOn {
      [self sendSerialMessage:@"02 00 00 00 00 02"];
      // TODO: change to success/failure
      return TRUE;
  }
  
  - (BOOL)powerOff {
      [self sendSerialMessage:@"02 01 00 00 00 03"];
      // TODO: change to success/failure
      return TRUE;
  }
  
  - (BOOL)blankScreen {
      [self sendSerialMessage:@"02 10 00 00 00 12"];
      // TODO: change to success/failure
      return TRUE;
  }
  
  - (BOOL)showScreen {
      [self sendSerialMessage:@"02 11 00 00 00 13"];
      // TODO: change to success/failure
      return TRUE;
  }
  
  - (BOOL)requestHelp {
  
      NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:_helpUrl]];
  
      // Perform request and get data back
      NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
  
      if (nil != responseData) {
  
          // TODO change this to check for response code is 200?
          return TRUE;
  
          /*
           // get String value of response
           NSString *returnString = [[NSString alloc] initWithData:responseData            encoding:NSUTF8StringEncoding];
  
           // DEBUG
           NSLog(@"Return string: %@", returnString);
  
           // TODO: better way to do this?
           NSString *successString = @"{\"status\":true}";
  
           if ([returnString isEqualToString:successString]) {
           // success
           NSLog(@"Success!");
  
           return TRUE;
           }
           else {
           NSLog(@"Error bad response");
           }
           */
  
      }
      else {
          NSLog(@"Error: no response");
      }
  
      // return faliure!
      return FALSE;
  
  }
  
  @end
  </code></pre>
  
  <p>I was wondering where I am going wrong though because when I run the program it hangs.</p>