1
0
mirror of https://github.com/wjwwood/serial.git synced 2026-01-22 19:54:57 +08:00

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}

This commit is contained in:
John Harrison 2012-01-12 12:46:08 -06:00
parent 99bf74faae
commit 11807e407b
6 changed files with 316 additions and 144 deletions

View File

@ -1,5 +1,11 @@
ifdef ROS_ROOT CXX=clang++
include $(shell rospack find mk)/cmake.mk CXXFLAGS=-g -I./include
else
include serial.makefile test: tests/serial_tests.o src/serial.o src/impl/unix.o
endif $(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

View File

@ -58,22 +58,22 @@ public:
void close (); void close ();
bool isOpen (); bool isOpen ();
size_t available (); size_t available ();
string read (size_t size = 1); string read (size_t size = 1);
size_t write (const string &data); size_t write (const string &data);
void flush (); void flush ();
void flushInput (); void flushInput ();
void flushOutput (); void flushOutput ();
void sendBreak(); void sendBreak(int duration);
void setBreak(); void setBreak(bool level);
void setRTS(); void setRTS(bool level);
void setDTR(); void setDTR(bool level);
void getCTS(); bool getCTS();
void getDSR(); bool getDSR();
void getRI(); bool getRI();
void getCD(); bool getCD();
void setPort (const string &port); void setPort (const string &port);
string getPort () const; string getPort () const;
@ -97,25 +97,25 @@ public:
flowcontrol_t getFlowcontrol () const; flowcontrol_t getFlowcontrol () const;
protected: protected:
void reconfigurePort (); void reconfigurePort ();
private: private:
int fd_; // The current file descriptor. int fd_; // The current file descriptor.
bool isOpen_; bool isOpen_;
int interCharTimeout_; int interCharTimeout_;
int writeTimeout_; int writeTimeout_;
int xonxoff_; int xonxoff_;
int rtscts_; int rtscts_;
string port_; // Path to the file descriptor string port_; // Path to the file descriptor
int baudrate_; // Baudrate int baudrate_; // Baudrate
long timeout_; // Timeout for read operations long timeout_; // Timeout for read operations
bytesize_t bytesize_; // Size of the bytes bytesize_t bytesize_; // Size of the bytes
parity_t parity_; // Parity parity_t parity_; // Parity
stopbits_t stopbits_; // Stop Bits stopbits_t stopbits_; // Stop Bits
flowcontrol_t flowcontrol_; // Flow Control flowcontrol_t flowcontrol_; // Flow Control
}; };
} }

View File

@ -373,6 +373,18 @@ public:
flowcontrol_t flowcontrol_t
getFlowcontrol () const; 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: private:
// Disable copy constructors // Disable copy constructors
Serial(const Serial&); Serial(const Serial&);

View File

@ -28,14 +28,15 @@ using std::string;
Serial::SerialImpl::SerialImpl (const string &port, int baudrate, Serial::SerialImpl::SerialImpl (const string &port, int baudrate,
long timeout, bytesize_t bytesize, long timeout, bytesize_t bytesize,
parity_t parity, stopbits_t stopbits, parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol) flowcontrol_t flowcontrol)
: fd_(-1), isOpen_(false), interCharTimeout_(-1), port_(port), baudrate_(baudrate), : fd_(-1), isOpen_(false), interCharTimeout_(-1), port_(port), baudrate_(baudrate),
timeout_(timeout), bytesize_(bytesize), parity_(parity), stopbits_(stopbits), timeout_(timeout), bytesize_(bytesize), parity_(parity), stopbits_(stopbits),
flowcontrol_(flowcontrol) flowcontrol_(flowcontrol)
{ {
if (port_.empty() == false) { printf("Got port: %s\n", port.c_str());
this->open(); if (!port_.empty()) {
} this->open();
}
} }
Serial::SerialImpl::~SerialImpl () { Serial::SerialImpl::~SerialImpl () {
@ -44,46 +45,48 @@ Serial::SerialImpl::~SerialImpl () {
void void
Serial::SerialImpl::open () { Serial::SerialImpl::open () {
if (port_.empty() == false) { if (port_.empty()) {
throw "error"; printf("Port was empty\n");
} throw "error";
if (isOpen_ == false) { }
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) { fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
// printf("Error opening serial port %s - %s(%d).\n",
// port.c_str(), strerror(errno), errno); if (fd_ == -1) {
throw "Error"; // Error printf("Error opening serial port %s - %s(%d).\n",
} port_.c_str(), strerror(errno), errno);
throw "Error"; // Error
reconfigurePort(); }
isOpen_ = true;
reconfigurePort();
isOpen_ = true;
} }
void void
Serial::SerialImpl::reconfigurePort () { Serial::SerialImpl::reconfigurePort () {
if (fd_ == -1) { if (fd_ == -1) {
throw "Error"; // Can only operate on a valid file descriptor throw "Error"; // Can only operate on a valid file descriptor
} }
struct termios options; // The current options for the file descriptor struct termios options; // The current options for the file descriptor
struct termios originalTTYAttrs; // The orignal file descriptor options struct termios originalTTYAttrs; // The orignal file descriptor options
uint8_t vmin = 0, vtime = 0; // timeout is done via select uint8_t vmin = 0, vtime = 0; // timeout is done via select
if (interCharTimeout_ == -1) { if (interCharTimeout_ == -1) {
vmin = 1; vmin = 1;
vtime = int(interCharTimeout_ * 10); vtime = int(interCharTimeout_ * 10);
} }
if (tcgetattr(fd_, &originalTTYAttrs) == -1) { if (tcgetattr(fd_, &originalTTYAttrs) == -1) {
throw "Error"; throw "Error";
} }
options = originalTTYAttrs; options = originalTTYAttrs;
// set up raw mode / no echo / binary // set up raw mode / no echo / binary
options.c_cflag |= (CLOCAL|CREAD); options.c_cflag |= (CLOCAL|CREAD);
options.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL| options.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|
@ -95,12 +98,12 @@ Serial::SerialImpl::reconfigurePort () {
options.c_iflag &= ~IUCLC; options.c_iflag &= ~IUCLC;
#endif #endif
#ifdef PARMRK #ifdef PARMRK
options.c_iflag &= ~PARMRK; options.c_iflag &= ~PARMRK;
#endif #endif
// setup baud rate // setup baud rate
// TODO(ash_git): validate baud rate // TODO(ash_git): validate baud rate
cfsetspeed(&options, baudrate_); cfsetspeed(&options, baudrate_);
// setup char len // setup char len
options.c_cflag &= ~CSIZE; 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 options.c_cflag |= (CSTOPB); // XXX same as TWO.. there is no POSIX support for 1.5
else if (stopbits_ == STOPBITS_TWO) else if (stopbits_ == STOPBITS_TWO)
options.c_cflag |= (CSTOPB); options.c_cflag |= (CSTOPB);
else else
throw "ValueError(Invalid stop bit specification:)"; throw "ValueError(Invalid stop bit specification:)";
// setup parity // setup parity
options.c_iflag &= ~(INPCK|ISTRIP); options.c_iflag &= ~(INPCK|ISTRIP);
if (parity_ == PARITY_NONE) { if (parity_ == PARITY_NONE) {
options.c_cflag &= ~(PARENB|PARODD); options.c_cflag &= ~(PARENB|PARODD);
} }
else if (parity_ == PARITY_EVEN) { else if (parity_ == PARITY_EVEN) {
options.c_cflag &= ~(PARODD); options.c_cflag &= ~(PARODD);
options.c_cflag |= (PARENB); options.c_cflag |= (PARENB);
} }
else if (parity_ == PARITY_ODD) { else if (parity_ == PARITY_ODD) {
options.c_cflag |= (PARENB|PARODD); options.c_cflag |= (PARENB|PARODD);
} }
else { else {
throw "ValueError(Invalid parity:"; throw "ValueError(Invalid parity:";
} }
// setup flow control // setup flow control
// xonxoff // xonxoff
#ifdef IXANY #ifdef IXANY
@ -159,7 +162,7 @@ Serial::SerialImpl::reconfigurePort () {
options.c_cflag &= ~(CRTSCTS); options.c_cflag &= ~(CRTSCTS);
#elif defined CNEW_RTSCTS #elif defined CNEW_RTSCTS
if (rtscts_) if (rtscts_)
options.c_cflag |= (CNEW_RTSCTS); options.c_cflag |= (CNEW_RTSCTS);
else else
options.c_cflag &= ~(CNEW_RTSCTS); options.c_cflag &= ~(CNEW_RTSCTS);
#else #else
@ -173,18 +176,18 @@ Serial::SerialImpl::reconfigurePort () {
options.c_cc[VTIME] = vtime; options.c_cc[VTIME] = vtime;
// activate settings // activate settings
::tcsetattr(fd_, TCSANOW, &options); ::tcsetattr(fd_, TCSANOW, &options);
} }
void void
Serial::SerialImpl::close () { Serial::SerialImpl::close () {
if (isOpen_ == true) { if (isOpen_ == true) {
if (fd_ != -1) { if (fd_ != -1) {
::close(fd_); ::close(fd_);
fd_ = -1; fd_ = -1;
} }
isOpen_ = false; isOpen_ = false;
} }
} }
bool bool
@ -194,73 +197,73 @@ Serial::SerialImpl::isOpen () {
size_t size_t
Serial::SerialImpl::available () { Serial::SerialImpl::available () {
if (!isOpen_) { if (!isOpen_) {
return 0; return 0;
} }
int count = 0; int count = 0;
int result = ioctl(fd_, TIOCINQ, &count); int result = ioctl(fd_, TIOCINQ, &count);
if (result == 0) { if (result == 0) {
return count; return count;
} }
else { else {
throw "Error"; throw "Error";
} }
} }
string string
Serial::SerialImpl::read (size_t size) { Serial::SerialImpl::read (size_t size) {
if (!isOpen_) { if (!isOpen_) {
throw "PortNotOpenError()"; // throw "PortNotOpenError()"; //
} }
string message = ""; string message = "";
char buf[1024]; char buf[1024]; // TODO(ash_gti): Should this be 1024? or...?
fd_set readfds; fd_set readfds;
while (message.length() < size) { while (message.length() < size) {
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fd_, &readfds); FD_SET(fd_, &readfds);
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = timeout_ / 1000; timeout.tv_sec = timeout_ / 1000;
timeout.tv_usec = timeout_ % 1000; timeout.tv_usec = timeout_ % 1000;
int r = select(1, &readfds, NULL, NULL, &timeout); int r = select(fd_ + 1, &readfds, NULL, NULL, &timeout);
if (r == -1 && errno == EINTR)
continue;
if (r == -1) { if (r == -1 && errno == EINTR)
continue;
if (r == -1) {
perror("select()"); perror("select()");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (FD_ISSET(fd_, &readfds)) { if (FD_ISSET(fd_, &readfds)) {
memset(buf, 0, 1024); memset(buf, 0, 1024);
size_t bytes_read = ::read(fd_, buf, 1024); size_t bytes_read = ::read(fd_, buf, size-strlen(buf));
// read should always return some data as select reported it was // read should always return some data as select reported it was
// ready to read when we get to this point. // ready to read when we get to this point.
if (bytes_read < 1) { if (bytes_read < 1) {
// Disconnected devices, at least on Linux, show the // Disconnected devices, at least on Linux, show the
// behavior that they are always ready to read immediately // behavior that they are always ready to read immediately
// but reading returns nothing. // but reading returns nothing.
throw "SerialException('device reports readiness to read but returned no data (device disconnected?)')"; throw "SerialException('device reports readiness to read but returned no data (device disconnected?)')";
} }
message.append(buf, bytes_read); message.append(buf, bytes_read);
} }
else { else {
break; // Timeout break; // Timeout
} }
} }
return message; return message;
} }
size_t size_t
Serial::SerialImpl::write (const string &data) { Serial::SerialImpl::write (const string &data) {
if (isOpen_ == false) { if (isOpen_ == false) {
throw "portNotOpenError"; throw "portNotOpenError";
} }
size_t t = data.length(); size_t t = data.length();
size_t n = ::write(fd_, data.c_str(), data.length()); size_t n = ::write(fd_, data.c_str(), data.length());
if (n == -1) { if (n == -1) {
throw "Write error"; throw "Write error";
} }
return n; return n;
} }
@ -286,8 +289,8 @@ Serial::SerialImpl::getTimeout () const {
void void
Serial::SerialImpl::setBaudrate (int baudrate) { Serial::SerialImpl::setBaudrate (int baudrate) {
baudrate_ = baudrate; baudrate_ = baudrate;
reconfigurePort(); reconfigurePort();
} }
int int
@ -297,7 +300,7 @@ Serial::SerialImpl::getBaudrate () const {
void void
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) { Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) {
bytesize_ = bytesize; bytesize_ = bytesize;
} }
serial::bytesize_t 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;
}

View File

@ -23,7 +23,6 @@ Serial::Serial (const string &port, int baudrate,
: pimpl(new SerialImpl(port,baudrate,timeout,bytesize,parity,stopbits, : pimpl(new SerialImpl(port,baudrate,timeout,bytesize,parity,stopbits,
flowcontrol)) flowcontrol))
{ {
} }
Serial::~Serial () { Serial::~Serial () {
@ -62,12 +61,12 @@ Serial::read (size_t size) {
string string
Serial::readline(size_t size, string eol) { Serial::readline(size_t size, string eol) {
size_t leneol = eol.length(); size_t leneol = eol.length();
string line; string line = "";
while (true) { while (true) {
string c = read(1); string c = pimpl->read(1);
if (c.empty()) { if (!c.empty()) {
line += c; line.append(c);
if (line.substr(line.length() - leneol, leneol) == eol) { if (line.length() > leneol && line.substr(line.length() - leneol, leneol) == eol) {
break; break;
} }
if (line.length() >= size) { if (line.length() >= size) {
@ -186,6 +185,39 @@ Serial::getFlowcontrol () const {
return this->pimpl->getFlowcontrol (); 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();
}

29
tests/serial_tests.cc Normal file
View File

@ -0,0 +1,29 @@
#include "gtest/gtest.h"
#include <boost/bind.hpp>
// OMG this is so nasty...
// #define private public
// #define protected public
#include <string>
#include <iostream>
#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++;
}
}