serial
1.1.0
Cross-platform, serial port library written in C++
|
#include <unix.h>
Public Member Functions | |
SerialImpl (const string &port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, flowcontrol_t flowcontrol) | |
virtual | ~SerialImpl () |
void | open () |
void | close () |
bool | isOpen () const |
size_t | available () |
size_t | read (uint8_t *buf, size_t size=1) |
size_t | write (const uint8_t *data, size_t length) |
void | flush () |
void | flushInput () |
void | flushOutput () |
void | sendBreak (int duration) |
void | setBreak (bool level) |
void | setRTS (bool level) |
void | setDTR (bool level) |
bool | waitForChange () |
bool | getCTS () |
bool | getDSR () |
bool | getRI () |
bool | getCD () |
void | setPort (const string &port) |
string | getPort () const |
void | setTimeout (Timeout &timeout) |
Timeout | getTimeout () const |
void | setBaudrate (unsigned long baudrate) |
unsigned long | getBaudrate () const |
void | setBytesize (bytesize_t bytesize) |
bytesize_t | getBytesize () const |
void | setParity (parity_t parity) |
parity_t | getParity () const |
void | setStopbits (stopbits_t stopbits) |
stopbits_t | getStopbits () const |
void | setFlowcontrol (flowcontrol_t flowcontrol) |
flowcontrol_t | getFlowcontrol () const |
void | readLock () |
void | readUnlock () |
void | writeLock () |
void | writeUnlock () |
SerialImpl (const string &port, unsigned long baudrate, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, flowcontrol_t flowcontrol) | |
virtual | ~SerialImpl () |
void | open () |
void | close () |
bool | isOpen () const |
size_t | available () |
size_t | read (uint8_t *buf, size_t size=1) |
size_t | write (const uint8_t *data, size_t length) |
void | flush () |
void | flushInput () |
void | flushOutput () |
void | sendBreak (int duration) |
void | setBreak (bool level) |
void | setRTS (bool level) |
void | setDTR (bool level) |
bool | waitForChange () |
bool | getCTS () |
bool | getDSR () |
bool | getRI () |
bool | getCD () |
void | setPort (const string &port) |
string | getPort () const |
void | setTimeout (Timeout &timeout) |
Timeout | getTimeout () const |
void | setBaudrate (unsigned long baudrate) |
unsigned long | getBaudrate () const |
void | setBytesize (bytesize_t bytesize) |
bytesize_t | getBytesize () const |
void | setParity (parity_t parity) |
parity_t | getParity () const |
void | setStopbits (stopbits_t stopbits) |
stopbits_t | getStopbits () const |
void | setFlowcontrol (flowcontrol_t flowcontrol) |
flowcontrol_t | getFlowcontrol () const |
void | readLock () |
void | readUnlock () |
void | writeLock () |
void | writeUnlock () |
Protected Member Functions | |
void | reconfigurePort () |
void | reconfigurePort () |
serial::serial::Serial::SerialImpl::SerialImpl | ( | const string & | port, |
unsigned long | baudrate, | ||
bytesize_t | bytesize, | ||
parity_t | parity, | ||
stopbits_t | stopbits, | ||
flowcontrol_t | flowcontrol | ||
) |
virtual serial::serial::Serial::SerialImpl::~SerialImpl | ( | ) | [virtual] |
Serial::SerialImpl::SerialImpl | ( | const string & | port, |
unsigned long | baudrate, | ||
bytesize_t | bytesize, | ||
parity_t | parity, | ||
stopbits_t | stopbits, | ||
flowcontrol_t | flowcontrol | ||
) |
: port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (false), rtscts_ (false), baudrate_ (baudrate), parity_ (parity), bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol) { pthread_mutex_init(&this->read_mutex, NULL); pthread_mutex_init(&this->write_mutex, NULL); if (port_.empty () == false) open (); }
Serial::SerialImpl::~SerialImpl | ( | ) | [virtual] |
{ close(); pthread_mutex_destroy(&this->read_mutex); pthread_mutex_destroy(&this->write_mutex); }
size_t Serial::SerialImpl::available | ( | ) |
{ if (!is_open_) { return 0; } int count = 0; int result = ioctl (fd_, TIOCINQ, &count); if (result == 0) { return static_cast<size_t> (count); } else { THROW (IOException, errno); } }
void Serial::SerialImpl::close | ( | ) |
{ if (is_open_ == true) { if (fd_ != -1) { ::close (fd_); // Ignoring the outcome fd_ = -1; } is_open_ = false; } }
void Serial::SerialImpl::flush | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::flush"); } tcdrain (fd_); }
void Serial::SerialImpl::flushInput | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::flushInput"); } tcflush (fd_, TCIFLUSH); }
void Serial::SerialImpl::flushOutput | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::flushOutput"); } tcflush (fd_, TCOFLUSH); }
unsigned long serial::serial::Serial::SerialImpl::getBaudrate | ( | ) | const |
unsigned long serial::serial::Serial::SerialImpl::getBaudrate | ( | ) | const |
bool Serial::SerialImpl::getCD | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::getCD"); } int s = ioctl (fd_, TIOCMGET, 0); return (s & TIOCM_CD) != 0; }
bool Serial::SerialImpl::getCTS | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::getCTS"); } int s = ioctl (fd_, TIOCMGET, 0); return (s & TIOCM_CTS) != 0; }
bool Serial::SerialImpl::getDSR | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::getDSR"); } int s = ioctl (fd_, TIOCMGET, 0); return (s & TIOCM_DSR) != 0; }
string serial::serial::Serial::SerialImpl::getPort | ( | ) | const |
string serial::serial::Serial::SerialImpl::getPort | ( | ) | const |
bool Serial::SerialImpl::getRI | ( | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::getRI"); } int s = ioctl (fd_, TIOCMGET, 0); return (s & TIOCM_RI) != 0; }
bool serial::serial::Serial::SerialImpl::isOpen | ( | ) | const |
bool serial::serial::Serial::SerialImpl::isOpen | ( | ) | const |
void Serial::SerialImpl::open | ( | ) |
{ if (port_.empty ()) { throw invalid_argument ("Empty port is invalid."); } if (is_open_ == true) { throw SerialExecption ("Serial port already open."); } fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd_ == -1) { switch (errno) { case EINTR: // Recurse because this is a recoverable error. open (); return; case ENFILE: case EMFILE: THROW (IOException, "Too many file handles open."); default: THROW (IOException, errno); } } reconfigurePort(); is_open_ = true; }
size_t serial::serial::Serial::SerialImpl::read | ( | uint8_t * | buf, |
size_t | size = 1 |
||
) |
size_t serial::serial::Serial::SerialImpl::read | ( | uint8_t * | buf, |
size_t | size = 1 |
||
) |
void Serial::SerialImpl::readLock | ( | ) |
{ int result = pthread_mutex_lock(&this->read_mutex); if (result) { THROW (IOException, result); } }
void Serial::SerialImpl::readUnlock | ( | ) |
{ int result = pthread_mutex_unlock(&this->read_mutex); if (result) { THROW (IOException, result); } }
void Serial::SerialImpl::reconfigurePort | ( | ) | [protected] |
{ if (fd_ == -1) { // Can only operate on a valid file descriptor THROW (IOException, "Invalid file descriptor, is the serial port open?"); } struct termios options; // The options for the file descriptor if (tcgetattr(fd_, &options) == -1) { THROW (IOException, "::tcgetattr"); } // set up raw mode / no echo / binary options.c_cflag |= (tcflag_t) (CLOCAL | CREAD); options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN); //|ECHOPRT options.c_oflag &= (tcflag_t) ~(OPOST); options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK); #ifdef IUCLC options.c_iflag &= (tcflag_t) ~IUCLC; #endif #ifdef PARMRK options.c_iflag &= (tcflag_t) ~PARMRK; #endif // setup baud rate bool custom_baud = false; speed_t baud; switch (baudrate_) { #ifdef B0 case 0: baud = B0; break; #endif #ifdef B50 case 50: baud = B50; break; #endif #ifdef B75 case 75: baud = B75; break; #endif #ifdef B110 case 110: baud = B110; break; #endif #ifdef B134 case 134: baud = B134; break; #endif #ifdef B150 case 150: baud = B150; break; #endif #ifdef B200 case 200: baud = B200; break; #endif #ifdef B300 case 300: baud = B300; break; #endif #ifdef B600 case 600: baud = B600; break; #endif #ifdef B1200 case 1200: baud = B1200; break; #endif #ifdef B1800 case 1800: baud = B1800; break; #endif #ifdef B2400 case 2400: baud = B2400; break; #endif #ifdef B4800 case 4800: baud = B4800; break; #endif #ifdef B7200 case 7200: baud = B7200; break; #endif #ifdef B9600 case 9600: baud = B9600; break; #endif #ifdef B14400 case 14400: baud = B14400; break; #endif #ifdef B19200 case 19200: baud = B19200; break; #endif #ifdef B28800 case 28800: baud = B28800; break; #endif #ifdef B57600 case 57600: baud = B57600; break; #endif #ifdef B76800 case 76800: baud = B76800; break; #endif #ifdef B38400 case 38400: baud = B38400; break; #endif #ifdef B115200 case 115200: baud = B115200; break; #endif #ifdef B128000 case 128000: baud = B128000; break; #endif #ifdef B153600 case 153600: baud = B153600; break; #endif #ifdef B230400 case 230400: baud = B230400; break; #endif #ifdef B256000 case 256000: baud = B256000; break; #endif #ifdef B460800 case 460800: baud = B460800; break; #endif #ifdef B921600 case 921600: baud = B921600; break; #endif #ifdef B1000000 case 1000000: baud = B1000000; break; #endif #ifdef B1152000 case 1152000: baud = B1152000; break; #endif #ifdef B1500000 case 1500000: baud = B1500000; break; #endif #ifdef B2000000 case 2000000: baud = B2000000; break; #endif #ifdef B2500000 case 2500000: baud = B2500000; break; #endif #ifdef B3000000 case 3000000: baud = B3000000; break; #endif #ifdef B3500000 case 3500000: baud = B3500000; break; #endif #ifdef B4000000 case 4000000: baud = B4000000; break; #endif default: custom_baud = true; // Mac OS X 10.x Support #if defined(__APPLE__) && defined(__MACH__) #define IOSSIOSPEED _IOW('T', 2, speed_t) int new_baud = static_cast<int> (baudrate_); if (ioctl (fd_, IOSSIOSPEED, &new_baud, 1) < 0) { THROW (IOException, errno); } // Linux Support #elif defined(__linux__) && defined (TIOCSSERIAL) struct serial_struct ser; ioctl (fd_, TIOCGSERIAL, &ser); // set custom divisor ser.custom_divisor = ser.baud_base / (int) baudrate_; // update flags ser.flags &= ~ASYNC_SPD_MASK; ser.flags |= ASYNC_SPD_CUST; if (ioctl (fd_, TIOCSSERIAL, &ser) < 0) { THROW (IOException, errno); } #else throw invalid_argument ("OS does not currently support custom bauds"); #endif } if (custom_baud == false) { #ifdef _BSD_SOURCE ::cfsetspeed(&options, baud); #else ::cfsetispeed(&options, baud); ::cfsetospeed(&options, baud); #endif } // setup char len options.c_cflag &= (tcflag_t) ~CSIZE; if (bytesize_ == eightbits) options.c_cflag |= CS8; else if (bytesize_ == sevenbits) options.c_cflag |= CS7; else if (bytesize_ == sixbits) options.c_cflag |= CS6; else if (bytesize_ == fivebits) options.c_cflag |= CS5; else throw invalid_argument ("invalid char len"); // setup stopbits if (stopbits_ == stopbits_one) options.c_cflag &= (tcflag_t) ~(CSTOPB); else if (stopbits_ == stopbits_one_point_five) // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5 options.c_cflag |= (CSTOPB); else if (stopbits_ == stopbits_two) options.c_cflag |= (CSTOPB); else throw invalid_argument ("invalid stop bit"); // setup parity options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP); if (parity_ == parity_none) { options.c_cflag &= (tcflag_t) ~(PARENB | PARODD); } else if (parity_ == parity_even) { options.c_cflag &= (tcflag_t) ~(PARODD); options.c_cflag |= (PARENB); } else if (parity_ == parity_odd) { options.c_cflag |= (PARENB | PARODD); } else { throw invalid_argument ("invalid parity"); } // setup flow control if (flowcontrol_ == flowcontrol_none) { xonxoff_ = false; rtscts_ = false; } if (flowcontrol_ == flowcontrol_software) { xonxoff_ = true; rtscts_ = false; } if (flowcontrol_ == flowcontrol_hardware) { xonxoff_ = false; rtscts_ = true; } // xonxoff #ifdef IXANY if (xonxoff_) options.c_iflag |= (IXON | IXOFF); //|IXANY) else options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY); #else if (xonxoff_) options.c_iflag |= (IXON | IXOFF); else options.c_iflag &= (tcflag_t) ~(IXON | IXOFF); #endif // rtscts #ifdef CRTSCTS if (rtscts_) options.c_cflag |= (CRTSCTS); else options.c_cflag &= (unsigned long) ~(CRTSCTS); #elif defined CNEW_RTSCTS if (rtscts_) options.c_cflag |= (CNEW_RTSCTS); else options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS); #else #error "OS Support seems wrong." #endif // http://www.unixwiz.net/techtips/termios-vmin-vtime.html // this basically sets the read call up to be a polling read, // but we are using select to ensure there is data available // to read before each call, so we should never needlessly poll options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 0; // activate settings ::tcsetattr (fd_, TCSANOW, &options); }
void serial::serial::Serial::SerialImpl::reconfigurePort | ( | ) | [protected] |
void Serial::SerialImpl::sendBreak | ( | int | duration | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::sendBreak"); } tcsendbreak (fd_, static_cast<int> (duration / 4)); }
void serial::serial::Serial::SerialImpl::sendBreak | ( | int | duration | ) |
void Serial::SerialImpl::setBaudrate | ( | unsigned long | baudrate | ) |
{ baudrate_ = baudrate; if (is_open_) reconfigurePort (); }
void serial::serial::Serial::SerialImpl::setBaudrate | ( | unsigned long | baudrate | ) |
void Serial::SerialImpl::setBreak | ( | bool | level | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::setBreak"); } if (level) { ioctl (fd_, TIOCSBRK); } else { ioctl (fd_, TIOCCBRK); } }
void serial::serial::Serial::SerialImpl::setBreak | ( | bool | level | ) |
void serial::serial::Serial::SerialImpl::setBytesize | ( | bytesize_t | bytesize | ) |
void serial::serial::Serial::SerialImpl::setBytesize | ( | bytesize_t | bytesize | ) |
void Serial::SerialImpl::setDTR | ( | bool | level | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::setDTR"); } if (level) { ioctl (fd_, TIOCMBIS, TIOCM_DTR); } else { ioctl (fd_, TIOCMBIC, TIOCM_DTR); } }
void serial::serial::Serial::SerialImpl::setDTR | ( | bool | level | ) |
void serial::serial::Serial::SerialImpl::setFlowcontrol | ( | flowcontrol_t | flowcontrol | ) |
void serial::serial::Serial::SerialImpl::setFlowcontrol | ( | flowcontrol_t | flowcontrol | ) |
void serial::serial::Serial::SerialImpl::setParity | ( | parity_t | parity | ) |
void serial::serial::Serial::SerialImpl::setParity | ( | parity_t | parity | ) |
void Serial::SerialImpl::setPort | ( | const string & | port | ) |
{ port_ = port; }
void serial::serial::Serial::SerialImpl::setPort | ( | const string & | port | ) |
void Serial::SerialImpl::setRTS | ( | bool | level | ) |
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::setRTS"); } if (level) { ioctl (fd_, TIOCMBIS, TIOCM_RTS); } else { ioctl (fd_, TIOCMBIC, TIOCM_RTS); } }
void serial::serial::Serial::SerialImpl::setRTS | ( | bool | level | ) |
void serial::serial::Serial::SerialImpl::setStopbits | ( | stopbits_t | stopbits | ) |
void serial::serial::Serial::SerialImpl::setStopbits | ( | stopbits_t | stopbits | ) |
void serial::serial::Serial::SerialImpl::setTimeout | ( | Timeout & | timeout | ) |
void serial::serial::Serial::SerialImpl::setTimeout | ( | Timeout & | timeout | ) |
bool Serial::SerialImpl::waitForChange | ( | ) |
{ #ifndef TIOCMIWAIT while (is_open_ == true) { int s = ioctl (fd_, TIOCMGET, 0); if ((s & TIOCM_CTS) != 0) return true; if ((s & TIOCM_DSR) != 0) return true; if ((s & TIOCM_RI) != 0) return true; if ((s & TIOCM_CD) != 0) return true; usleep(1000); } return false; #else if (ioctl(fd_, TIOCMIWAIT, (TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS)) != 0) { stringstream ss; ss << "waitForDSR failed on a call to ioctl(TIOCMIWAIT): " << errno << " " << strerror(errno); throw(SerialExecption(ss.str().c_str())); } return true; #endif }
size_t Serial::SerialImpl::write | ( | const uint8_t * | data, |
size_t | length | ||
) |
Error
Port ready to write
{ if (is_open_ == false) { throw PortNotOpenedException ("Serial::write"); } fd_set writefds; size_t bytes_written = 0; struct timeval timeout; timeout.tv_sec = timeout_.write_timeout_constant / 1000; timeout.tv_usec = static_cast<int> (timeout_.write_timeout_multiplier % 1000); timeout.tv_usec *= 1000; // To convert to micro seconds while (bytes_written < length) { FD_ZERO (&writefds); FD_SET (fd_, &writefds); // On Linux the timeout struct is updated by select to contain the time // left on the timeout to make looping easier, but on other platforms this // does not occur. #if !defined(__linux__) // Begin timing select struct timespec start, end; get_time_now(start); #endif // Do the select int r = select (fd_ + 1, NULL, &writefds, NULL, &timeout); #if !defined(__linux__) // Calculate difference and update the structure get_time_now(end); // Calculate the time select took struct timeval diff; diff.tv_sec = end.tv_sec - start.tv_sec; diff.tv_usec = static_cast<int> ((end.tv_nsec - start.tv_nsec) / 1000); // Update the timeout if (timeout.tv_sec <= diff.tv_sec) { timeout.tv_sec = 0; } else { timeout.tv_sec -= diff.tv_sec; } if (timeout.tv_usec <= diff.tv_usec) { timeout.tv_usec = 0; } else { timeout.tv_usec -= diff.tv_usec; } #endif // Figure out what happened by looking at select's response 'r' if (r < 0) { // Select was interrupted, try again if (errno == EINTR) { continue; } // Otherwise there was some error THROW (IOException, errno); } if (r == 0) { break; } if (r > 0) { // Make sure our file descriptor is in the ready to write list if (FD_ISSET (fd_, &writefds)) { // This will write some ssize_t bytes_written_now = ::write (fd_, data + bytes_written, length - bytes_written); // write should always return some data as select reported it was // ready to write when we get to this point. if (bytes_written_now < 1) { // Disconnected devices, at least on Linux, show the // behavior that they are always ready to write immediately // but writing returns nothing. throw SerialExecption ("device reports readiness to write but " "returned no data (device disconnected?)"); } // Update bytes_written bytes_written += static_cast<size_t> (bytes_written_now); // If bytes_written == size then we have written everything we need to if (bytes_written == length) { break; } // If bytes_written < size then we have more to write if (bytes_written < length) { continue; } // If bytes_written > size then we have over written, which shouldn't happen if (bytes_written > length) { throw SerialExecption ("write over wrote, too many bytes where " "written, this shouldn't happen, might be " "a logical error!"); } } // This shouldn't happen, if r > 0 our fd has to be in the list! THROW (IOException, "select reports ready to write, but our fd isn't" " in the list, this shouldn't happen!"); } } return bytes_written; }
size_t serial::serial::Serial::SerialImpl::write | ( | const uint8_t * | data, |
size_t | length | ||
) |
void Serial::SerialImpl::writeLock | ( | ) |
{ int result = pthread_mutex_lock(&this->write_mutex); if (result) { THROW (IOException, result); } }
void Serial::SerialImpl::writeUnlock | ( | ) |
{ int result = pthread_mutex_unlock(&this->write_mutex); if (result) { THROW (IOException, result); } }