c++boostserial-portboost-asio

boost::asio::serial_port reading after reconnecting Device


I have a problem with the boost::asio::serial_port class reading from a GPS device (USB-Serial). Connecting the device and reading from it works fine, but when I disconnect and reconnect the device, read_some doesn't read any bytes from the port.

As boost doesn't seam to detect that the serial port is gone ( is_open() returns true ), I periodically cancel(), close() and open( GPS_PORT ) the device when I don't get data, resetting the port options on the way. But this doesn't help either, the input buffer stays empty.

Am I missing something, or doing something wrong, or is this a bug in asio? Is there a standard way to detect that the port is gone?


Solution

  • It's hard to say what is the exact reason in your case, but practice shows that you often need to disable RTS sensitivity on your serial port.

    RTS is a pin on real RS-232 interface that is on when a device on the other side is on.

    serial_port::read_some invokes underlying Windows API function that looks on this signal.

    As you don't have the real RS-232 device, you need to rely on the driver emulation of this signal which may be faulty (and unfortunately often is).

    To disable it, invoke serial_port::set_option(DCB) with RTSControl set to RTS_CONTROL_DISABLE.

    If close()'ing your handle doesn't help, it may be a problem with boost. Source code for close() looks like this:

      boost::system::error_code close(implementation_type& impl,
          boost::system::error_code& ec)
      {
        if (is_open(impl))
        {
          if (!::CloseHandle(impl.handle_))
          {
            DWORD last_error = ::GetLastError();
            ec = boost::system::error_code(last_error,
                boost::asio::error::get_system_category());
            return ec;
          }
    
          impl.handle_ = INVALID_HANDLE_VALUE;
          impl.safe_cancellation_thread_id_ = 0;
        }
    
        ec = boost::system::error_code();
        return ec;
      }
    

    , i. e. if CloseHandle() fails for some reason (or hangs), the internal handle value is not beign assigned to INVALID_HANDLE_VALUE and is_open() will always return true.

    To work around this, check is_open() right after close()'ing, and if it returns true, destroy whole instance of boost::asio::serial_port and create it again.