如何确定是否有可从boost:asio:serial_port读取的字节

spi*_*epf 3 boost serial-port arduino boost-asio

我正在尝试使用boost在台式机和arduino之间进行串行通信。在arduino空间中,我可以在尝试执行读取操作之前询问串行端口是否有可用字节。

我在查找boost :: asio :: serial_port的等效项时遇到了麻烦。

Tan*_*ury 5

尽管Boost.Asio不为此提供直接支持,但仍然可以通过将串行端口native_handle()与系统特定的调用配合使用来实现此目的。请查阅系统文档以确定如何查询准备读取的可用字节,但是通常ioctl(..., FIONREAD, ...)在Linux和ClearCommError()Windows上使用。


这是一个完整的最小示例,该示例使用系统特定的调用来获取可用的字节数。示例程序将继续查询串行端口,直到有20个以上的可用字节为止,此时它将读取除5个字节以外的所有字节:

#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

/// @brief Returns the number of bytes available for reading from a serial
///        port without blocking.
std::size_t get_bytes_available(
  boost::asio::serial_port& serial_port,
  boost::system::error_code& error)
{
  error = boost::system::error_code();
  int value = 0;
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
  COMSTAT status;
  if (0 != ::ClearCommError(serial_port.lowest_layer().native_handle(),
                            NULL, &status))
  {
    value = status.cbInQue;
  }
  // On error, set the error code.
  else
  {
    error = boost::system::error_code(::GetLastError(),
       boost::asio::error::get_system_category());
  }
#else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
  if (0 == ::ioctl(serial_port.lowest_layer().native_handle(),
                   FIONREAD, &value))
  {
    error = boost::system::error_code(errno,
       boost::asio::error::get_system_category());
  }
#endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)

  return error ? static_cast<std::size_t>(0)
               : static_cast<size_t>(value);

}

/// @brief Returns the number of bytes available for reading from a serial
///        port without blocking.  Throws on error.
std::size_t get_bytes_available(boost::asio::serial_port& serial_port)
{
  boost::system::error_code error;
  std::size_t bytes_available = get_bytes_available(serial_port, error);
  if (error)
  {
    boost::throw_exception((boost::system::system_error(error)));
  }
  return bytes_available;
}

int main(int argc, char* argv[])
{
  if (argc < 2)
  {
    std::cerr << "Usage: " << argv[0] << " <device_name>" << std::endl;
    return 1;
  }

  // Create all I/O objects.
  boost::asio::io_service io_service;
  boost::asio::serial_port serial_port(io_service, argv[1]);

  // Continue quering the serial port until at least 20 bytes are available
  // to be read.
  std::size_t bytes_available = 0;
  while (bytes_available < 20)
  {
    bytes_available = get_bytes_available(serial_port);
    std::cout << "available: " << bytes_available << std::endl;
    boost::this_thread::sleep_for(::boost::chrono::seconds(3));
  }

  // Read all but 5 available bytes.
  std::vector<char> buffer(bytes_available - 5);
  std::size_t bytes_transferred =
      read(serial_port, boost::asio::buffer(buffer));
  bytes_available = get_bytes_available(serial_port);

  // Print results.
  std::cout << "Read " << bytes_transferred << " bytes\n";
  std::cout.write(&buffer[0], bytes_transferred);
  std::cout << "\navailable: " << bytes_available << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

使用以下命令创建虚拟串行端口socat

$ socat -d -d PTY: PTY
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/2
2015/02/01 21:12:31 socat[3056] N PTY is /dev/pts/3
2015/02/01 21:12:31 socat[3056] N starting data transfer loop
                                with FDs [3,3] and [5,5]
Run Code Online (Sandbox Code Playgroud)

在一个终端中启动程序后,我/dev/pts/3在另一终端中写入:

$ echo -n "This is" > /dev/pts/3
$ echo -n " an example" > /dev/pts/3
$ echo -n " with asio." > /dev/pts/3
Run Code Online (Sandbox Code Playgroud)

以及程序的结果输出:

$ ./a.out /dev/pts/2
available: 0
available: 7
available: 18
available: 29
Read 24 bytes
This is an example with
available: 5
Run Code Online (Sandbox Code Playgroud)