From 11807e407b544a74906f7862d8c3f59b2839b05a Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 12 Jan 2012 12:46:08 -0600 Subject: [PATCH] Fixed some issues I found while testing my code, also implemented the drain, flush, set/send Break, get {CTS, RI, CD, DSR}, set {RTS, DTR} --- Makefile | 16 +- include/serial/impl/unix.h | 54 +++---- include/serial/serial.h | 12 ++ src/impl/unix.cc | 305 ++++++++++++++++++++++++------------- src/serial.cc | 44 +++++- tests/serial_tests.cc | 29 ++++ 6 files changed, 316 insertions(+), 144 deletions(-) create mode 100644 tests/serial_tests.cc diff --git a/Makefile b/Makefile index 5df6eab..3f1ccf4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ -ifdef ROS_ROOT -include $(shell rospack find mk)/cmake.mk -else -include serial.makefile -endif +CXX=clang++ +CXXFLAGS=-g -I./include + +test: tests/serial_tests.o src/serial.o src/impl/unix.o + $(CXX) -o test tests/serial_tests.o src/serial.o src/impl/unix.o + +# ifdef ROS_ROOT +# include $(shell rospack find mk)/cmake.mk +# else +# include serial.makefile +# endif diff --git a/include/serial/impl/unix.h b/include/serial/impl/unix.h index 9fe739a..4590ee6 100644 --- a/include/serial/impl/unix.h +++ b/include/serial/impl/unix.h @@ -58,22 +58,22 @@ public: void close (); bool isOpen (); - size_t available (); + size_t available (); string read (size_t size = 1); size_t write (const string &data); - - void flush (); - void flushInput (); - void flushOutput (); + + void flush (); + void flushInput (); + void flushOutput (); - void sendBreak(); - void setBreak(); - void setRTS(); - void setDTR(); - void getCTS(); - void getDSR(); - void getRI(); - void getCD(); + void sendBreak(int duration); + void setBreak(bool level); + void setRTS(bool level); + void setDTR(bool level); + bool getCTS(); + bool getDSR(); + bool getRI(); + bool getCD(); void setPort (const string &port); string getPort () const; @@ -97,25 +97,25 @@ public: flowcontrol_t getFlowcontrol () const; protected: - void reconfigurePort (); + void reconfigurePort (); private: int fd_; // The current file descriptor. - bool isOpen_; - - int interCharTimeout_; - int writeTimeout_; - int xonxoff_; - int rtscts_; + bool isOpen_; + + int interCharTimeout_; + int writeTimeout_; + int xonxoff_; + int rtscts_; - string port_; // Path to the file descriptor - int baudrate_; // Baudrate - long timeout_; // Timeout for read operations - bytesize_t bytesize_; // Size of the bytes - parity_t parity_; // Parity - stopbits_t stopbits_; // Stop Bits - flowcontrol_t flowcontrol_; // Flow Control + string port_; // Path to the file descriptor + int baudrate_; // Baudrate + long timeout_; // Timeout for read operations + bytesize_t bytesize_; // Size of the bytes + parity_t parity_; // Parity + stopbits_t stopbits_; // Stop Bits + flowcontrol_t flowcontrol_; // Flow Control }; } diff --git a/include/serial/serial.h b/include/serial/serial.h index b4bca19..5ec3d85 100644 --- a/include/serial/serial.h +++ b/include/serial/serial.h @@ -373,6 +373,18 @@ public: flowcontrol_t getFlowcontrol () const; + void flush(); + void flushInput(); + void flushOutput(); + void sendBreak(int duration); + void setBreak(bool level = true); + void setRTS(bool level = true); + void setDTR(bool level = true); + bool getCTS(); + bool getDSR(); + bool getRI(); + bool getCD(); + private: // Disable copy constructors Serial(const Serial&); diff --git a/src/impl/unix.cc b/src/impl/unix.cc index 766aba4..6b94f69 100644 --- a/src/impl/unix.cc +++ b/src/impl/unix.cc @@ -28,14 +28,15 @@ using std::string; Serial::SerialImpl::SerialImpl (const string &port, int baudrate, long timeout, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, - flowcontrol_t flowcontrol) + flowcontrol_t flowcontrol) : fd_(-1), isOpen_(false), interCharTimeout_(-1), port_(port), baudrate_(baudrate), timeout_(timeout), bytesize_(bytesize), parity_(parity), stopbits_(stopbits), - flowcontrol_(flowcontrol) + flowcontrol_(flowcontrol) { - if (port_.empty() == false) { - this->open(); - } + printf("Got port: %s\n", port.c_str()); + if (!port_.empty()) { + this->open(); + } } Serial::SerialImpl::~SerialImpl () { @@ -44,46 +45,48 @@ Serial::SerialImpl::~SerialImpl () { void Serial::SerialImpl::open () { - if (port_.empty() == false) { - throw "error"; - } - if (isOpen_ == false) { - throw "error"; - } - - fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); - - if (fd_ == -1) { -// printf("Error opening serial port %s - %s(%d).\n", -// port.c_str(), strerror(errno), errno); - throw "Error"; // Error - } - - reconfigurePort(); - isOpen_ = true; + if (port_.empty()) { + printf("Port was empty\n"); + throw "error"; + } + if (isOpen_ == true) { + printf("Port already opened\n"); + throw "error"; + } + + fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd_ == -1) { + printf("Error opening serial port %s - %s(%d).\n", + port_.c_str(), strerror(errno), errno); + throw "Error"; // Error + } + + reconfigurePort(); + isOpen_ = true; } void Serial::SerialImpl::reconfigurePort () { - if (fd_ == -1) { - throw "Error"; // Can only operate on a valid file descriptor - } - - struct termios options; // The current options for the file descriptor - struct termios originalTTYAttrs; // The orignal file descriptor options - + if (fd_ == -1) { + throw "Error"; // Can only operate on a valid file descriptor + } + + struct termios options; // The current options for the file descriptor + struct termios originalTTYAttrs; // The orignal file descriptor options + uint8_t vmin = 0, vtime = 0; // timeout is done via select if (interCharTimeout_ == -1) { - vmin = 1; - vtime = int(interCharTimeout_ * 10); - } - - if (tcgetattr(fd_, &originalTTYAttrs) == -1) { - throw "Error"; - } - - options = originalTTYAttrs; - + vmin = 1; + vtime = int(interCharTimeout_ * 10); + } + + if (tcgetattr(fd_, &originalTTYAttrs) == -1) { + throw "Error"; + } + + options = originalTTYAttrs; + // set up raw mode / no echo / binary options.c_cflag |= (CLOCAL|CREAD); options.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL| @@ -95,12 +98,12 @@ Serial::SerialImpl::reconfigurePort () { options.c_iflag &= ~IUCLC; #endif #ifdef PARMRK - options.c_iflag &= ~PARMRK; + options.c_iflag &= ~PARMRK; #endif // setup baud rate - // TODO(ash_git): validate baud rate - cfsetspeed(&options, baudrate_); + // TODO(ash_git): validate baud rate + cfsetspeed(&options, baudrate_); // setup char len options.c_cflag &= ~CSIZE; @@ -121,23 +124,23 @@ Serial::SerialImpl::reconfigurePort () { options.c_cflag |= (CSTOPB); // XXX same as TWO.. there is no POSIX support for 1.5 else if (stopbits_ == STOPBITS_TWO) options.c_cflag |= (CSTOPB); - else + else throw "ValueError(Invalid stop bit specification:)"; // setup parity options.c_iflag &= ~(INPCK|ISTRIP); if (parity_ == PARITY_NONE) { options.c_cflag &= ~(PARENB|PARODD); } - else if (parity_ == PARITY_EVEN) { + else if (parity_ == PARITY_EVEN) { options.c_cflag &= ~(PARODD); options.c_cflag |= (PARENB); } - else if (parity_ == PARITY_ODD) { + else if (parity_ == PARITY_ODD) { options.c_cflag |= (PARENB|PARODD); } - else { + else { throw "ValueError(Invalid parity:"; - } + } // setup flow control // xonxoff #ifdef IXANY @@ -159,7 +162,7 @@ Serial::SerialImpl::reconfigurePort () { options.c_cflag &= ~(CRTSCTS); #elif defined CNEW_RTSCTS if (rtscts_) - options.c_cflag |= (CNEW_RTSCTS); + options.c_cflag |= (CNEW_RTSCTS); else options.c_cflag &= ~(CNEW_RTSCTS); #else @@ -173,18 +176,18 @@ Serial::SerialImpl::reconfigurePort () { options.c_cc[VTIME] = vtime; // activate settings - ::tcsetattr(fd_, TCSANOW, &options); + ::tcsetattr(fd_, TCSANOW, &options); } void Serial::SerialImpl::close () { - if (isOpen_ == true) { - if (fd_ != -1) { - ::close(fd_); - fd_ = -1; - } - isOpen_ = false; - } + if (isOpen_ == true) { + if (fd_ != -1) { + ::close(fd_); + fd_ = -1; + } + isOpen_ = false; + } } bool @@ -194,73 +197,73 @@ Serial::SerialImpl::isOpen () { size_t Serial::SerialImpl::available () { - if (!isOpen_) { - return 0; - } - int count = 0; - int result = ioctl(fd_, TIOCINQ, &count); - if (result == 0) { - return count; - } - else { - throw "Error"; - } + if (!isOpen_) { + return 0; + } + int count = 0; + int result = ioctl(fd_, TIOCINQ, &count); + if (result == 0) { + return count; + } + else { + throw "Error"; + } } string Serial::SerialImpl::read (size_t size) { if (!isOpen_) { - throw "PortNotOpenError()"; // + throw "PortNotOpenError()"; // } - string message = ""; - char buf[1024]; - fd_set readfds; + string message = ""; + char buf[1024]; // TODO(ash_gti): Should this be 1024? or...? + fd_set readfds; while (message.length() < size) { - FD_ZERO(&readfds); - FD_SET(fd_, &readfds); - struct timeval timeout; - timeout.tv_sec = timeout_ / 1000; - timeout.tv_usec = timeout_ % 1000; - int r = select(1, &readfds, NULL, NULL, &timeout); - - if (r == -1 && errno == EINTR) - continue; + FD_ZERO(&readfds); + FD_SET(fd_, &readfds); + struct timeval timeout; + timeout.tv_sec = timeout_ / 1000; + timeout.tv_usec = timeout_ % 1000; + int r = select(fd_ + 1, &readfds, NULL, NULL, &timeout); - if (r == -1) { + if (r == -1 && errno == EINTR) + continue; + + if (r == -1) { perror("select()"); exit(EXIT_FAILURE); } if (FD_ISSET(fd_, &readfds)) { - memset(buf, 0, 1024); - size_t bytes_read = ::read(fd_, buf, 1024); - // read should always return some data as select reported it was - // ready to read when we get to this point. - if (bytes_read < 1) { - // Disconnected devices, at least on Linux, show the - // behavior that they are always ready to read immediately - // but reading returns nothing. - throw "SerialException('device reports readiness to read but returned no data (device disconnected?)')"; - } - message.append(buf, bytes_read); - } - else { - break; // Timeout - } - } + memset(buf, 0, 1024); + size_t bytes_read = ::read(fd_, buf, size-strlen(buf)); + // read should always return some data as select reported it was + // ready to read when we get to this point. + if (bytes_read < 1) { + // Disconnected devices, at least on Linux, show the + // behavior that they are always ready to read immediately + // but reading returns nothing. + throw "SerialException('device reports readiness to read but returned no data (device disconnected?)')"; + } + message.append(buf, bytes_read); + } + else { + break; // Timeout + } + } return message; } size_t Serial::SerialImpl::write (const string &data) { if (isOpen_ == false) { - throw "portNotOpenError"; - } + throw "portNotOpenError"; + } size_t t = data.length(); - size_t n = ::write(fd_, data.c_str(), data.length()); - if (n == -1) { - throw "Write error"; - } + size_t n = ::write(fd_, data.c_str(), data.length()); + if (n == -1) { + throw "Write error"; + } return n; } @@ -286,8 +289,8 @@ Serial::SerialImpl::getTimeout () const { void Serial::SerialImpl::setBaudrate (int baudrate) { - baudrate_ = baudrate; - reconfigurePort(); + baudrate_ = baudrate; + reconfigurePort(); } int @@ -297,7 +300,7 @@ Serial::SerialImpl::getBaudrate () const { void Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) { - bytesize_ = bytesize; + bytesize_ = bytesize; } serial::bytesize_t @@ -336,6 +339,96 @@ Serial::SerialImpl::getFlowcontrol () const { } +void Serial::SerialImpl::flush () { + if (isOpen_ == false) { + throw "portNotOpen"; + } + tcdrain(fd_); +} +void Serial::SerialImpl::flushInput () { + if (isOpen_ == false) { + throw "portNotOpen"; + } + tcflush(fd_, TCIFLUSH); +} +void Serial::SerialImpl::flushOutput () { + if (isOpen_ == false) { + throw "portNotOpen"; + } + tcflush(fd_, TCOFLUSH); +} + +void Serial::SerialImpl::sendBreak(int duration) { + if (isOpen_ == false) { + throw "portNotOpen"; + } + tcsendbreak(fd_, int(duration/4)); +} +void Serial::SerialImpl::setBreak(bool level) { + if (isOpen_ == false) { + throw "portNotOpen"; + } + if (level) { + ioctl(fd_, TIOCSBRK); + } + else { + ioctl(fd_, TIOCCBRK); + } +} +void Serial::SerialImpl::setRTS(bool level) { + if (isOpen_ == false) { + throw "portNotOpen"; + } + if (level) { + ioctl(fd_, TIOCMBIS, TIOCM_RTS); + } + else { + ioctl(fd_, TIOCMBIC, TIOCM_RTS); + } +} +void Serial::SerialImpl::setDTR(bool level) { + if (isOpen_ == false) { + throw "portNotOpen"; + } + if (level) { + ioctl(fd_, TIOCMBIS, TIOCM_DTR); + } + else { + ioctl(fd_, TIOCMBIC, TIOCM_DTR); + } + +} +bool Serial::SerialImpl::getCTS() { + if (isOpen_ == false) { + throw "portNotOpen"; + } + int s = ioctl(fd_, TIOCMGET, 0); + return (s & TIOCM_CTS) != 0; +} +bool Serial::SerialImpl::getDSR() { + if (isOpen_ == false) { + throw "portNotOpen"; + } + int s = ioctl(fd_, TIOCMGET, 0); + return (s & TIOCM_DSR) != 0; +} +bool Serial::SerialImpl::getRI() { + if (isOpen_ == false) { + throw "portNotOpen"; + } + int s = ioctl(fd_, TIOCMGET, 0); + return (s & TIOCM_RI) != 0; +} +bool Serial::SerialImpl::getCD() { + if (isOpen_ == false) { + throw "portNotOpen"; + } + int s = ioctl(fd_, TIOCMGET, 0); + return (s & TIOCM_CD) != 0; +} + + + diff --git a/src/serial.cc b/src/serial.cc index 3462019..c631b51 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -23,7 +23,6 @@ Serial::Serial (const string &port, int baudrate, : pimpl(new SerialImpl(port,baudrate,timeout,bytesize,parity,stopbits, flowcontrol)) { - } Serial::~Serial () { @@ -62,12 +61,12 @@ Serial::read (size_t size) { string Serial::readline(size_t size, string eol) { size_t leneol = eol.length(); - string line; + string line = ""; while (true) { - string c = read(1); - if (c.empty()) { - line += c; - if (line.substr(line.length() - leneol, leneol) == eol) { + string c = pimpl->read(1); + if (!c.empty()) { + line.append(c); + if (line.length() > leneol && line.substr(line.length() - leneol, leneol) == eol) { break; } if (line.length() >= size) { @@ -186,6 +185,39 @@ Serial::getFlowcontrol () const { return this->pimpl->getFlowcontrol (); } +void Serial::flush() { + this->pimpl->flush(); +} +void Serial::flushInput() { + this->pimpl->flushInput(); +} +void Serial::flushOutput() { + this->pimpl->flushOutput(); +} +void Serial::sendBreak(int duration) { + this->pimpl->sendBreak(duration); +} +void Serial::setBreak(bool level) { + this->pimpl->setBreak(level); +} +void Serial::setRTS(bool level) { + this->pimpl->setRTS(level); +} +void Serial::setDTR(bool level) { + this->pimpl->setDTR(level); +} +bool Serial::getCTS() { + return this->pimpl->getCTS(); +} +bool Serial::getDSR() { + return this->pimpl->getDSR(); +} +bool Serial::getRI() { + return this->pimpl->getRI(); +} +bool Serial::getCD() { + return this->pimpl->getCD(); +} diff --git a/tests/serial_tests.cc b/tests/serial_tests.cc new file mode 100644 index 0000000..663a333 --- /dev/null +++ b/tests/serial_tests.cc @@ -0,0 +1,29 @@ +#include "gtest/gtest.h" + +#include + +// OMG this is so nasty... +// #define private public +// #define protected public +#include +#include + +#include "serial/serial.h" + +using std::string; +using std::cout; +using std::endl; +using serial::Serial; + +int main(int argc, char **argv) { + Serial s("/dev/tty.usbserial-A900adHq", 9600, 2000); + s.flush(); + int count = 0; + while (count < 10) { + size_t available = s.available(); + cout << "avialable: " << available << endl; + string line = s.readline(); + cout << count << ": " << line; + count++; + } +}