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

Merging boostless into master

This commit is contained in:
William Woodall 2012-06-09 18:10:46 -05:00
commit 061c184d00
21 changed files with 4814 additions and 1026 deletions

2
.gitignore vendored
View File

@ -7,11 +7,11 @@
*.pyc *.pyc
*.pyo *.pyo
*.zip *.zip
*.cc
*/files/* */files/*
*/tmp/* */tmp/*
*.hwm* *.hwm*
*.cfg *.cfg
*.out
.svn .svn
build build
bin bin

View File

@ -1,6 +1,8 @@
find_path(serial_INCLUDE_DIRS serial.h /usr/include/serial "$ENV{NAMER_ROOT}") find_path(serial_INCLUDE_DIRS serial/serial.h /usr/include
/usr/local/include "$ENV{NAMER_ROOT}")
find_library(serial_LIBRARIES serial /usr/lib "$ENV{NAMER_ROOT}") find_library(serial_LIBRARIES serial /usr/lib /usr/local/lib
"$ENV{NAMER_ROOT}")
set(serial_FOUND TRUE) set(serial_FOUND TRUE)

View File

@ -1,5 +1,5 @@
ifdef ROS_ROOT ifdef ROS_ROOT
include $(shell rospack find mk)/cmake.mk include $(shell rospack find mk)/cmake.mk
else else
include serial.mk include serial.makefile
endif endif

View File

@ -7,7 +7,6 @@ Coming Soon!
## Dependencies ## Dependencies
* CMake, for the build system: http://www.cmake.org/ * CMake, for the build system: http://www.cmake.org/
* Boost, for threading: http://www.boost.org/
* (Optional) ROS * (Optional) ROS
## Stand Alone Installation ## Stand Alone Installation

1716
doc/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

147
examples/serial_example.cc Normal file
View File

@ -0,0 +1,147 @@
/***
* This example expects the serial port has a loopback on it.
*
* Alternatively, you could use an Arduino:
*
* <pre>
* setup() {
* Serial.begin(<insert your baudrate here>);
* }
*
* loop() {
* if (Serial.available()) {
* Serial.write(Serial.read());
* }
* }
* </pre>
*/
#include <string>
#include <iostream>
#include <cstdio>
// OS Specific sleep
#ifdef __WIN32__
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "serial/serial.h"
using std::string;
using std::exception;
using std::cout;
using std::cerr;
using std::endl;
void my_sleep(unsigned long milliseconds) {
#ifdef __WIN32__
Sleep(milliseconds); // 100 ms
#else
usleep(milliseconds*1000); // 100 ms
#endif
}
int run(int argc, char **argv)
{
if(argc < 3) {
cerr << "Usage: test_serial <serial port address> ";
cerr << "<baudrate> [test string]" << endl;
return 0;
}
// Argument 1 is the serial port
string port(argv[1]);
// Argument 2 is the baudrate
unsigned long baud = 0;
sscanf(argv[2], "%lu", &baud);
// port, baudrate, timeout in milliseconds
serial::Serial my_serial(port, baud, 1000);
cout << "Is the serial port open?";
if(my_serial.isOpen())
cout << " Yes." << endl;
else
cout << " No." << endl;
// Get the Test string
int count = 0;
string test_string;
if (argc == 4) {
test_string = argv[3];
} else {
test_string = "Testing.";
}
// Test the timeout, there should be 1 second between prints
cout << "Timeout == 1000ms, asking for 1 more byte than written." << endl;
while (count < 10) {
size_t bytes_wrote = my_serial.write(test_string);
string result = my_serial.read(test_string.length()+1);
cout << "Iteration: " << count << ", Bytes written: ";
cout << bytes_wrote << ", Bytes read: ";
cout << result.length() << ", String read: " << result << endl;
count += 1;
}
// Test the timeout at 250ms
my_serial.setTimeout(250);
count = 0;
cout << "Timeout == 250ms, asking for 1 more byte than written." << endl;
while (count < 10) {
size_t bytes_wrote = my_serial.write(test_string);
string result = my_serial.read(test_string.length()+1);
cout << "Iteration: " << count << ", Bytes written: ";
cout << bytes_wrote << ", Bytes read: ";
cout << result.length() << ", String read: " << result << endl;
count += 1;
}
// Test the timeout at 250ms, but asking exactly for what was written
count = 0;
cout << "Timeout == 250ms, asking for exactly what was written." << endl;
while (count < 10) {
size_t bytes_wrote = my_serial.write(test_string);
string result = my_serial.read(test_string.length());
cout << "Iteration: " << count << ", Bytes written: ";
cout << bytes_wrote << ", Bytes read: ";
cout << result.length() << ", String read: " << result << endl;
count += 1;
}
// Test the timeout at 250ms, but asking for 1 less than what was written
count = 0;
cout << "Timeout == 250ms, asking for 1 less than was written." << endl;
while (count < 10) {
size_t bytes_wrote = my_serial.write(test_string);
string result = my_serial.read(test_string.length()-1);
cout << "Iteration: " << count << ", Bytes written: ";
cout << bytes_wrote << ", Bytes read: ";
cout << result.length() << ", String read: " << result << endl;
count += 1;
}
return 0;
}
int main(int argc, char **argv) {
try {
return run(argc, argv);
} catch (exception &e) {
cerr << "Unhandled Exception: " << e.what() << endl;
}
}

View File

@ -1,33 +0,0 @@
#include <string>
#include <iostream>
#include "serial/serial.h"
int main(int argc, char **argv)
{
if(argc < 2) {
std::cerr << "Usage: test_serial <serial port address>" << std::endl;
return 0;
}
std::string port(argv[1]);
serial::Serial serial(port, 115200, 250);
std::cout << "Is the serial port open?";
if(serial.isOpen())
std::cout << " Yes." << std::endl;
else
std::cout << " No." << std::endl;
int count = 0;
while (count >= 0) {
int bytes_wrote = serial.write("Testing.");
std::string result = serial.read(8);
if(count % 10 == 0)
std::cout << ">" << count << ">" << bytes_wrote << ">" << result << std::endl;
count += 1;
}
return 0;
}

197
include/serial/impl/unix.h Normal file
View File

@ -0,0 +1,197 @@
/*!
* \file serial/impl/unix.h
* \author William Woodall <wjwwood@gmail.com>
* \author John Harrison <ash@greaterthaninfinity.com>
* \version 0.1
*
* \section LICENSE
*
* The MIT License
*
* Copyright (c) 2011 William Woodall, John Harrison
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* \section DESCRIPTION
*
* This provides a unix based pimpl for the Serial class. This implementation is
* based off termios.h and uses select for multiplexing the IO ports.
*
*/
#ifndef SERIAL_IMPL_UNIX_H
#define SERIAL_IMPL_UNIX_H
#include "serial/serial.h"
#include <pthread.h>
namespace serial {
using std::string;
using std::invalid_argument;
using serial::SerialExecption;
using serial::IOException;
class serial::Serial::SerialImpl {
public:
SerialImpl (const string &port,
unsigned long baudrate,
long timeout,
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 (unsigned char* buf, size_t size = 1);
size_t
write (const string &data);
void
flush ();
void
flushInput ();
void
flushOutput ();
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;
void
setTimeout (long timeout);
long
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:
void reconfigurePort ();
private:
string port_; // Path to the file descriptor
int fd_; // The current file descriptor
bool is_open_;
bool xonxoff_;
bool rtscts_;
long timeout_; // Timeout for read operations
unsigned long baudrate_; // Baudrate
parity_t parity_; // Parity
bytesize_t bytesize_; // Size of the bytes
stopbits_t stopbits_; // Stop Bits
flowcontrol_t flowcontrol_; // Flow Control
// Mutex used to lock the read functions
pthread_mutex_t read_mutex;
// Mutex used to lock the write functions
pthread_mutex_t write_mutex;
};
}
#endif // SERIAL_IMPL_UNIX_H

View File

@ -0,0 +1,177 @@
/*!
* \file serial/impl/windows.h
* \author William Woodall <wjwwood@gmail.com>
* \author John Harrison <ash@greaterthaninfinity.com>
* \version 0.1
*
* \section LICENSE
*
* The MIT License
*
* Copyright (c) 2011 William Woodall, John Harrison
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* \section DESCRIPTION
*
* This provides a windows implementation of the Serial class interface.
*
*/
#ifndef SERIAL_IMPL_WINDOWS_H
#define SERIAL_IMPL_WINDOWS_H
#include "serial/serial.h"
namespace serial {
using std::string;
using std::invalid_argument;
using serial::SerialExecption;
using serial::IOException;
class serial::Serial::SerialImpl {
public:
SerialImpl (const string &port,
unsigned long baudrate,
long timeout,
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 (char* buf, size_t size = 1);
size_t
write (const string &data);
void
flush ();
void
flushInput ();
void
flushOutput ();
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;
void
setTimeout (long timeout);
long
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;
protected:
void reconfigurePort ();
private:
string port_; // Path to the file descriptor
int fd_; // The current file descriptor
bool isOpen_;
bool xonxoff_;
bool rtscts_;
long timeout_; // Timeout for read operations
unsigned long baudrate_; // Baudrate
parity_t parity_; // Parity
bytesize_t bytesize_; // Size of the bytes
stopbits_t stopbits_; // Stop Bits
flowcontrol_t flowcontrol_; // Flow Control
};
}
#endif // SERIAL_IMPL_WINDOWS_H

View File

@ -1,21 +1,21 @@
/** /*!
* @file serial.h * \file serial/serial.h
* @author William Woodall <wjwwood@gmail.com> * \author William Woodall <wjwwood@gmail.com>
* @author John Harrison <ash.gti@gmail.com> * \author John Harrison <ash.gti@gmail.com>
* @version 0.1 * \version 0.1
* *
* @section LICENSE * \section LICENSE
* *
* The MIT License * The MIT License
* *
* Copyright (c) 2011 William Woodall * Copyright (c) 2011 William Woodall
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a
* of this software and associated documentation files (the "Software"), to deal * copy of this software and associated documentation files (the "Software"),
* in the Software without restriction, including without limitation the rights * to deal in the Software without restriction, including without limitation
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * the rights to use, copy, modify, merge, publish, distribute, sublicense,
* copies of the Software, and to permit persons to whom the Software is * and/or sell copies of the Software, and to permit persons to whom the
* furnished to do so, subject to the following conditions: * Software is furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
@ -24,423 +24,473 @@
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* THE SOFTWARE. * DEALINGS IN THE SOFTWARE.
* *
* @section DESCRIPTION * \section DESCRIPTION
* *
* This provides a cross platform interface for interacting with Serial Ports. * This provides a cross platform interface for interacting with Serial Ports.
*/ */
#ifndef SERIAL_H #ifndef SERIAL_H
#define SERIAL_H #define SERIAL_H
#include <iostream> #include <limits>
#include <vector>
#include <string.h>
#include <sstream> #include <sstream>
#include <string> #include <exception>
#include <stdexcept>
#include <boost/asio.hpp> #define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )
#include <boost/asio/serial_port.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/scoped_ptr.hpp>
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// If on Windows undefine the PARITY_* defines that are in winbase.h
#ifdef _WIN32
#undef PARITY_NONE
#undef PARITY_ODD
#undef PARITY_EVEN
#endif
// DEFINES
#ifndef DEFAULT_BAUDRATE
#define DEFAULT_BAUDRATE 9600
#endif
#ifndef DEFAULT_TIMEOUT
#define DEFAULT_TIMEOUT 0
#endif
#ifndef DEFAULT_BYTESIZE
#define DEFAULT_BYTESIZE EIGHTBITS
#endif
#ifndef DEFAULT_PARITY
#define DEFAULT_PARITY PARITY_NONE
#endif
#ifndef DEFAULT_STOPBITS
#define DEFAULT_STOPBITS STOPBITS_ONE
#endif
#ifndef DEFAULT_FLOWCONTROL
#define DEFAULT_FLOWCONTROL FLOWCONTROL_NONE
#endif
namespace serial { namespace serial {
// Serial Port settings CONSTANTS /*!
enum bytesize_t { FIVEBITS = 5, SIXBITS = 6, SEVENBITS = 7, EIGHTBITS = 8 }; * Enumeration defines the possible bytesizes for the serial port.
enum parity_t { PARITY_NONE, PARITY_ODD, PARITY_EVEN }; */
enum stopbits_t { STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO }; typedef enum {
enum flowcontrol_t { FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE }; fivebits = 5,
sixbits = 6,
sevenbits = 7,
eightbits = 8
} bytesize_t;
/*!
* Enumeration defines the possible parity types for the serial port.
*/
typedef enum {
parity_none = 0,
parity_odd = 1,
parity_even = 2
} parity_t;
/*!
* Enumeration defines the possible stopbit types for the serial port.
*/
typedef enum {
stopbits_one = 1,
stopbits_one_point_five,
stopbits_two = 2
} stopbits_t;
/*!
* Enumeration defines the possible flowcontrol types for the serial port.
*/
typedef enum {
flowcontrol_none = 0,
flowcontrol_software
} flowcontrol_t;
/*!
* Class that provides a portable serial port interface.
*/
class Serial { class Serial {
public: public:
/** Constructor, Creates a Serial object but doesn't open the serial port. */ /*!
Serial(); * Constructor, creates a SerialPortBoost object and opens the port.
*
* \param port A std::string containing the address of the serial port,
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
* on Linux.
*
* \param baudrate An integer that represents the buadrate
*
* \param timeout A long that represents the time (in milliseconds) until a
* timeout on reads occur. Setting this to zero (0) will cause reading to
* be non-blocking, i.e. the available data will be returned immediately,
* but it will not block to wait for more. Setting this to a number less
* than zero (-1) will result in infinite blocking behaviour, i.e. the
* serial port will block until either size bytes have been read or an
* exception has occured.
*
* \param bytesize Size of each byte in the serial transmission of data,
* default is eightbits, possible values are: fivebits, sixbits, sevenbits,
* eightbits
*
* \param parity Method of parity, default is parity_none, possible values
* are: parity_none, parity_odd, parity_even
*
* \param stopbits Number of stop bits used, default is stopbits_one,
* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
*
* \param flowcontrol Type of flowcontrol used, default is
* flowcontrol_none, possible values are: flowcontrol_none,
* flowcontrol_software, flowcontrol_hardware
*
* \param buffer_size The maximum size of the internal buffer, defaults
* to 256 bytes (2^8).
*
* \throw PortNotOpenedException
*/
Serial (const std::string &port = "",
unsigned long baudrate = 9600,
long timeout = 0,
bytesize_t bytesize = eightbits,
parity_t parity = parity_none,
stopbits_t stopbits = stopbits_one,
flowcontrol_t flowcontrol = flowcontrol_none);
/** /*! Destructor */
* Constructor, creates a SerialPortBoost object and opens the port. virtual ~Serial ();
*
* @param port A std::string containing the address of the serial port,
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
* on Linux.
*
* @param baudrate An integer that represents the buadrate
*
* @param timeout A long that represents the time (in milliseconds) until a
* timeout on reads occur. Setting this to zero (0) will cause reading
* to be non-blocking, i.e. the available data will be returned immediately,
* but it will not block to wait for more. Setting this to a number less than
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will
* block until either size bytes have been read or an exception has occured.
*
* @param bytesize Size of each byte in the serial transmission of data,
* default is EIGHTBITS, possible values are: FIVEBITS,
* SIXBITS, SEVENBITS, EIGHTBITS
*
* @param parity Method of parity, default is PARITY_NONE, possible values
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN
*
* @param stopbits Number of stop bits used, default is STOPBITS_ONE, possible
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
*
* @param flowcontrol Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE
*
* @throw SerialPortAlreadyOpenException
* @throw SerialPortFailedToOpenException
*/
Serial(std::string port,
int baudrate = DEFAULT_BAUDRATE,
long timeout = DEFAULT_TIMEOUT,
bytesize_t bytesize = DEFAULT_BYTESIZE,
parity_t parity = DEFAULT_PARITY,
stopbits_t stopbits = DEFAULT_STOPBITS,
flowcontrol_t flowcontrol = DEFAULT_FLOWCONTROL);
/** Destructor */ /*!
~Serial(); * Opens the serial port as long as the portname is set and the port isn't
* alreay open.
*
* If the port is provided to the constructor then an explicit call to open
* is not needed.
*
* \see Serial::Serial
*
* \throw std::invalid_argument
* \throw serial::SerialExecption
* \throw serial::IOException
*/
void
open ();
/** /*! Gets the open status of the serial port.
* Opens the serial port as long as the portname is set and the port isn't alreay open. *
* * \return Returns true if the port is open, false otherwise.
* @throw SerialPortAlreadyOpenException */
* @throw SerialPortFailedToOpenException bool
*/ isOpen () const;
void open();
/** Gets the status of the serial port. /*! Closes the serial port. */
* void
* @return A boolean value that represents whether or not the serial port is open. close ();
*/
bool isOpen();
/** Closes the serial port and terminates threads. */ /*! Return the number of characters in the buffer. */
void close(); size_t
available();
/** Read size bytes from the serial port. /*! Read a given amount of bytes from the serial port.
* If a timeout is set it may return less characters than requested. With no timeout *
* it will block until the requested number of bytes have been read. * If a timeout is set it may return less characters than requested. With
* * no timeout it will block until the requested number of bytes have been
* @param buffer A char[] of length >= the size parameter to hold incoming data. * read or until an exception occurs.
* *
* @param size An integer defining how many bytes to be read. * \param size A size_t defining how many bytes to be read.
* *
* @return An integer representing the number of bytes read. * \return A std::string containing the data read.
*/ */
int read(char* buffer, int size = 1); size_t
read (unsigned char *buffer, size_t size);
size_t
read (std::vector<unsigned char> &buffer, size_t size = 1);
size_t
read (std::string &buffer, size_t size = 1);
std::string
read (size_t size = 1);
/** Read size bytes from the serial port. /*! Reads in a line or until a given delimiter has been processed
* If a timeout is set it may return less characters than requested. With no timeout *
* it will block until the requested number of bytes have been read. * Reads from the serial port until a single line has been read.
* *
* @param size An integer defining how many bytes to be read. * \param size A maximum length of a line, defaults to 65536 (2^16)
* * \param eol A string to match against for the EOL.
* @return A std::string containing the data read. *
*/ * \return A std::string containing the line.
std::string read(int size = 1); */
size_t
readline (std::string &buffer,
size_t size = 65536,
std::string eol = "\n");
std::string
readline (size_t size = 65536,
std::string eol = "\n");
std::string read_until(char delim, size_t size = -1); /*! Reads in multiple lines until the serail port times out.
std::string read_until(std::string delim, size_t size = -1); *
* This requires a timeout > 0 before it can be run. It will read until a
* timeout occurs and return a list of strings.
*
* \param size A maximum length of combined lines, defaults to 65536 (2^16)
*
* \param eol A string to match against for the EOL.
*
* \return A vector<string> containing the lines.
*/
std::vector<std::string>
readlines (size_t size = 65536, std::string eol = "\n");
/** Write length bytes from buffer to the serial port. /*! Write a string to the serial port.
* *
* @param data A char[] with data to be written to the serial port. * \param data A const reference containg the data to be written
* * to the serial port.
* @param length An integer representing the number of bytes to be written. *
* * \return A size_t representing the number of bytes actually written to
* @return An integer representing the number of bytes written. * the serial port.
*/ */
int write(char data[], int length); size_t
write (const unsigned char *data, size_t size);
size_t
write (const std::vector<unsigned char> &data);
size_t
write (const std::string &data);
/** Write a string to the serial port. /*! Sets the serial port identifier.
* *
* @param data A std::string to be written to the serial port. (must be null terminated) * \param port A const std::string reference containing the address of the
* * serial port, which would be something like 'COM1' on Windows and
* @return An integer representing the number of bytes written to the serial port. * '/dev/ttyS0' on Linux.
*/ *
int write(std::string data); * \throw InvalidConfigurationException
*/
void
setPort (const std::string &port);
/** Sets the logic level of the RTS line. /*! Gets the serial port identifier.
* *
* @param level The logic level to set the RTS to. Defaults to true. * \see Serial::setPort
*/ *
void setRTS(bool level = true); * \throw InvalidConfigurationException
*/
std::string
getPort () const;
/** Sets the logic level of the DTR line. /*! Sets the timeout for reads in milliseconds.
* *
* @param level The logic level to set the DTR to. Defaults to true. * \param timeout A long that represents the time (in milliseconds) until a
*/ * timeout on reads occur. Setting this to zero (0) will cause reading to be
void setDTR(bool level = true); * non-blocking, i.e. the available data will be returned immediately, but it
* will not block to wait for more. Setting this to a number less than
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port
* will block until either size bytes have been read or an exception has
* occured.
*/
void
setTimeout (long timeout);
/** Gets the status of the CTS line. /*! Gets the timeout for reads in seconds.
* *
* @return A boolean value that represents the current logic level of the CTS line. * \see Serial::setTimeout
*/ */
bool getCTS() const; long
getTimeout () const;
/** Gets the status of the DSR line. /*! Sets the baudrate for the serial port.
* *
* @return A boolean value that represents the current logic level of the DSR line. * Possible baudrates depends on the system but some safe baudrates include:
*/ * 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000,
bool getDSR() const; * 57600, 115200
* Some other baudrates that are supported by some comports:
* 128000, 153600, 230400, 256000, 460800, 921600
*
* \param baudrate An integer that sets the baud rate for the serial port.
*
* \throw InvalidConfigurationException
*/
void
setBaudrate (unsigned long baudrate);
/** Sets the serial port identifier. /*! Gets the baudrate for the serial port.
* *
* @param port A std::string containing the address of the serial port, * \return An integer that sets the baud rate for the serial port.
* which would be something like 'COM1' on Windows and '/dev/ttyS0' *
* on Linux. * \see Serial::setBaudrate
*/ *
void setPort(std::string port); * \throw InvalidConfigurationException
*/
unsigned long
getBaudrate () const;
/** Gets the serial port identifier. /*! Sets the bytesize for the serial port.
* *
* @return A std::string containing the address of the serial port, * \param bytesize Size of each byte in the serial transmission of data,
* which would be something like 'COM1' on Windows and '/dev/ttyS0' * default is eightbits, possible values are: fivebits, sixbits, sevenbits,
* on Linux. * eightbits
*/ *
std::string getPort() const; * \throw InvalidConfigurationException
*/
void
setBytesize (bytesize_t bytesize);
/** Sets the timeout for reads in seconds. /*! Gets the bytesize for the serial port.
* *
* @param timeout A long that represents the time (in milliseconds) until a * \see Serial::setBytesize
* timeout on reads occur. Setting this to zero (0) will cause reading *
* to be non-blocking, i.e. the available data will be returned immediately, * \throw InvalidConfigurationException
* but it will not block to wait for more. Setting this to a number less than */
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will bytesize_t
* block until either size bytes have been read or an exception has occured. getBytesize () const;
*/
void setTimeoutMilliseconds(long timeout);
/** Gets the timeout for reads in seconds. /*! Sets the parity for the serial port.
* *
* @param timeout A long that represents the time (in milliseconds) until a * \param parity Method of parity, default is parity_none, possible values
* timeout on reads occur. Setting this to zero (0) will cause reading * are: parity_none, parity_odd, parity_even
* to be non-blocking, i.e. the available data will be returned immediately, *
* but it will not block to wait for more. Setting this to a number less than * \throw InvalidConfigurationException
* zero (-1) will result in infinite blocking behaviour, i.e. the serial port will */
* block until either size bytes have been read or an exception has occured. void
*/ setParity (parity_t parity);
long getTimeoutMilliseconds() const;
/** Sets the baudrate for the serial port. /*! Gets the parity for the serial port.
* *
* @param baudrate An integer that sets the baud rate for the serial port. * \see Serial::setParity
*/ *
void setBaudrate(int baudrate); * \throw InvalidConfigurationException
*/
parity_t
getParity () const;
/** Gets the baudrate for the serial port. /*! Sets the stopbits for the serial port.
* *
* @return An integer that sets the baud rate for the serial port. * \param stopbits Number of stop bits used, default is stopbits_one,
*/ * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
int getBaudrate() const; *
* \throw InvalidConfigurationException
*/
void
setStopbits (stopbits_t stopbits);
/** Sets the bytesize for the serial port. /*! Gets the stopbits for the serial port.
* *
* @param bytesize Size of each byte in the serial transmission of data, * \see Serial::setStopbits
* default is EIGHTBITS, possible values are: FIVEBITS, *
* SIXBITS, SEVENBITS, EIGHTBITS * \throw InvalidConfigurationException
* */
* @throw InvalidBytesizeException stopbits_t
*/ getStopbits () const;
void setBytesize(bytesize_t bytesize);
/** Gets the bytesize for the serial port. /*! Sets the flow control for the serial port.
* *
* @return Size of each byte in the serial transmission of data, * \param flowcontrol Type of flowcontrol used, default is flowcontrol_none,
* default is EIGHTBITS, possible values are: FIVEBITS, * possible values are: flowcontrol_none, flowcontrol_software,
* SIXBITS, SEVENBITS, EIGHTBITS * flowcontrol_hardware
* *
* @throw InvalidBytesizeException * \throw InvalidConfigurationException
*/ */
bytesize_t getBytesize() const; void
setFlowcontrol (flowcontrol_t flowcontrol);
/** Sets the parity for the serial port. /*! Gets the flow control for the serial port.
* *
* @param parity Method of parity, default is PARITY_NONE, possible values * \see Serial::setFlowcontrol
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN *
* * \throw InvalidConfigurationException
* @throw InvalidParityException */
*/ flowcontrol_t
void setParity(parity_t parity); getFlowcontrol () const;
/** Gets the parity for the serial port. /*! Flush the input and output buffers */
* void
* @return Method of parity, default is PARITY_NONE, possible values flush ();
* are: PARITY_NONE, PARITY_ODD, PARITY_EVEN
*
* @throw InvalidParityException
*/
parity_t getParity() const;
/** Sets the stopbits for the serial port. /*! Flush only the input buffer */
* void
* @param stopbits Number of stop bits used, default is STOPBITS_ONE, possible flushInput ();
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
*
* @throw InvalidStopbitsException
*/
void setStopbits(stopbits_t stopbits);
/** Gets the stopbits for the serial port. /*! Flush only the output buffer */
* void
* @return Number of stop bits used, default is STOPBITS_ONE, possible flushOutput ();
* values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO
*
* @throw InvalidStopbitsException
*/
stopbits_t getStopbits() const;
/** Sets the flow control for the serial port. void
* sendBreak (int duration);
* @param flowcontrol Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE void
* setBreak (bool level = true);
* @throw InvalidFlowcontrolException
*/ void
void setFlowcontrol(flowcontrol_t flowcontrol); setRTS (bool level = true);
void
setDTR (bool level = true);
bool
getCTS ();
bool
getDSR ();
bool
getRI ();
bool
getCD ();
/** Gets the flow control for the serial port.
*
* @return Type of flowcontrol used, default is FLOWCONTROL_NONE, possible
* values are: FLOWCONTROL_NONE, FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE
*
* @throw InvalidFlowcontrolException
*/
flowcontrol_t getFlowcontrol() const;
private: private:
DISALLOW_COPY_AND_ASSIGN(Serial); // Disable copy constructors
void init(); Serial(const Serial&);
void read_complete(const boost::system::error_code& error, std::size_t bytes_transferred); void operator=(const Serial&);
void timeout_callback(const boost::system::error_code& error); const Serial& operator=(Serial);
boost::asio::io_service io_service; std::string read_cache_; //!< Cache for doing reads in chunks.
boost::asio::io_service::work work; // Pimpl idiom, d_pointer
class SerialImpl;
SerialImpl *pimpl_;
boost::scoped_ptr<boost::asio::serial_port> serial_port; // Scoped Lock Classes
class ScopedReadLock;
class ScopedWriteLock;
boost::asio::deadline_timer timeout_timer; // Read common function
size_t
read_ (unsigned char *buffer, size_t size);
std::string port;
boost::asio::serial_port_base::baud_rate baudrate;
boost::posix_time::time_duration timeout;
boost::asio::serial_port_base::character_size bytesize;
boost::asio::serial_port_base::parity parity;
boost::asio::serial_port_base::stop_bits stopbits;
boost::asio::serial_port_base::flow_control flowcontrol;
int bytes_read;
int bytes_to_read;
bool reading;
bool nonblocking;
}; };
class SerialPortAlreadyOpenException : public std::exception { class SerialExecption : public std::exception
const char * port; {
const char* e_what_;
public: public:
SerialPortAlreadyOpenException(const char * port) {this->port = port;} SerialExecption (const char *description) : e_what_ (description) {}
virtual const char* what() const throw() { virtual const char* what () const throw ()
std::stringstream ss; {
ss << "Serial Port already open: " << this->port; std::stringstream ss;
return ss.str().c_str(); ss << "SerialException " << e_what_ << " failed.";
} return ss.str ().c_str ();
}
}; };
class SerialPortFailedToOpenException : public std::exception { class IOException : public std::exception
const char * e_what; {
std::string file_;
int line_;
const char* e_what_;
int errno_;
public: public:
SerialPortFailedToOpenException(const char * e_what) {this->e_what = e_what;} explicit IOException (std::string file, int line, int errnum)
: file_(file), line_(line), e_what_ (strerror (errnum)), errno_(errnum) {}
explicit IOException (std::string file, int line, const char * description)
: file_(file), line_(line), e_what_ (description), errno_(0) {}
virtual ~IOException() throw() {}
virtual const char* what() const throw() { int getErrorNumber () { return errno_; }
std::stringstream ss;
ss << "Serial Port failed to open: " << this->e_what; virtual const char* what () const throw ()
return ss.str().c_str(); {
} std::stringstream ss;
if (errno_ == 0)
ss << "IO Exception: " << e_what_;
else
ss << "IO Exception (" << errno_ << "): " << e_what_;
ss << ", file " << file_ << ", line " << line_ << ".";
return ss.str ().c_str ();
}
}; };
class InvalidBytesizeException : public std::exception { class PortNotOpenedException : public std::exception
int bytesize; {
const char * e_what_;
public: public:
InvalidBytesizeException(int bytesize) {this->bytesize = bytesize;} PortNotOpenedException (const char * description) : e_what_ (description) {}
virtual const char* what() const throw() { virtual const char* what () const throw ()
std::stringstream ss; {
ss << "Invalid bytesize provided: " << this->bytesize; std::stringstream ss;
return ss.str().c_str(); ss << e_what_ << " called before port was opened.";
} return ss.str ().c_str ();
}
}; };
class InvalidParityException : public std::exception { class SerialExceptionBase : public std::exception
int parity; {
public:
InvalidParityException(int parity) {this->parity = parity;}
virtual const char* what() const throw() {
std::stringstream ss;
ss << "Invalid parity provided: " << this->parity;
return ss.str().c_str();
}
};
class InvalidStopbitsException : public std::exception {
int stopbits;
public:
InvalidStopbitsException(int stopbits) {this->stopbits = stopbits;}
virtual const char* what() const throw() {
std::stringstream ss;
ss << "Invalid stopbits provided: " << this->stopbits;
return ss.str().c_str();
}
};
class InvalidFlowcontrolException : public std::exception {
int flowcontrol;
public:
InvalidFlowcontrolException(int flowcontrol) {this->flowcontrol = flowcontrol;}
virtual const char* what() const throw() {
std::stringstream ss;
ss << "Invalid flowcontrol provided: " << this->flowcontrol;
return ss.str().c_str();
}
}; };
} // namespace serial } // namespace serial

View File

@ -1,122 +1,150 @@
macro(build_serial) macro(build_serial)
## Project Setup
cmake_minimum_required(VERSION 2.4.6)
if(COMMAND cmake_policy) ## Project Setup
cmake_policy(SET CMP0003 NEW) cmake_minimum_required(VERSION 2.4.6)
endif(COMMAND cmake_policy)
project(Serial) if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
## Configurations project(Serial)
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF) ## Configurations
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF) # Enable warnings
# Assuming unix means a gcc style compiler, eg. g++ or clang++.
IF(UNIX)
set(CMAKE_CXX_FLAGS "-Wall -Weffc++ -pedantic -pedantic-errors -Wextra -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimplicit -Wimport -Winit-self -Winline -Winvalid-pch -Wlong-long -Wmissing-braces -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wstack-protector -Wstrict-aliasing -Wstrict-aliasing=2 -Wswitch -Wswitch-default -Wswitch-enum -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wvariadic-macros -Wvolatile-register-var -Wwrite-strings")
ELSEIF(WIN32)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
endif(UNIX)
# Allow for building shared libs override # Use clang if available
IF(NOT BUILD_SHARED_LIBS) IF(EXISTS /usr/bin/clang)
set(BUILD_SHARED_LIBS OFF) set(CMAKE_CXX_COMPILER /usr/bin/clang++)
ENDIF(NOT BUILD_SHARED_LIBS) set(CMAKE_OSX_DEPLOYMENT_TARGET "")
set(SERIAL_BUILD_WARNINGS TRUE)
set(CMAKE_BUILD_TYPE Debug)
ENDIF(EXISTS /usr/bin/clang)
# Set the default path for built executables to the "bin" directory option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH)) option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
# set the default path for built libraries to the "lib" directory
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
## Configure the build system # Allow for building shared libs override
IF(NOT BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS OFF)
ENDIF(NOT BUILD_SHARED_LIBS)
# Add the include folder to the include path # Threading libraries added for mutexs
include_directories(${PROJECT_SOURCE_DIR}/include) FIND_PACKAGE (Threads)
# Add default source files # Set the default path for built executables to the "bin" directory
set(SERIAL_SRCS src/serial.cpp) IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
# Add default header files set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(SERIAL_HEADERS include/serial/serial.h) ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
# set the default path for built libraries to the "lib" directory
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
# Find Boost, if it hasn't already been found ## Configure the build system
IF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
ENDIF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
link_directories(${Boost_LIBRARY_DIRS}) # Add the include folder to the include path
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${PROJECT_SOURCE_DIR}/include)
set(SERIAL_LINK_LIBS ${Boost_SYSTEM_LIBRARY} # Add default source files
${Boost_FILESYSTEM_LIBRARY} set(SERIAL_SRCS src/serial.cc)
${Boost_THREAD_LIBRARY}) IF(WIN32)
list(APPEND SERIAL_SRCS src/impl/windows.cc)
ELSE(WIN32)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
ENDIF(WIN32)
# Add default header files
set(SERIAL_HEADERS include/serial/serial.h)
## Build the Serial Library
# Compile the Library set(OTHER_LIBS "")
add_library(serial ${SERIAL_SRCS} ${SERIAL_HEADERS}) if(UNIX)
target_link_libraries(serial ${SERIAL_LINK_LIBS}) set(OTHER_LIBS util)
IF( WIN32 ) endif(UNIX)
target_link_libraries(serial wsock32)
ENDIF( )
# Check for OS X and if so disable kqueue support in asio ## Build the Serial Library
IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE)
ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin)
## Build Examples # Compile the Library
add_library(serial ${SERIAL_SRCS})
target_link_libraries(serial ${CMAKE_THREAD_LIBS_INIT} ${OTHER_LIBS})
# If asked to ## Build Examples
IF(SERIAL_BUILD_EXAMPLES)
# Compile the Test program
add_executable(serial_example examples/serial_example.cpp)
# Link the Test program to the Serial library
target_link_libraries(serial_example serial)
ENDIF(SERIAL_BUILD_EXAMPLES)
## Build tests # If asked to
IF(SERIAL_BUILD_EXAMPLES)
# Compile the Serial Test program
add_executable(serial_example examples/serial_example.cc)
# Link the Test program to the Serial library
target_link_libraries(serial_example serial)
ENDIF(SERIAL_BUILD_EXAMPLES)
# If asked to ## Build tests
IF(SERIAL_BUILD_TESTS)
# none yet...
ENDIF(SERIAL_BUILD_TESTS)
## Setup install and uninstall # If asked to
IF(SERIAL_BUILD_TESTS)
# Find Google Test
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# Unless asked not to... # Compile the Serial Test program
IF(NOT SERIAL_DONT_CONFIGURE_INSTALL) add_executable(serial_tests tests/serial_tests.cc)
# Configure make install # Link the Test program to the serial library
IF(NOT CMAKE_INSTALL_PREFIX) target_link_libraries(serial_tests ${GTEST_BOTH_LIBRARIES}
SET(CMAKE_INSTALL_PREFIX /usr/local) serial)
ENDIF(NOT CMAKE_INSTALL_PREFIX)
INSTALL(TARGETS serial add_test(AllTestsIntest_serial serial_tests)
RUNTIME DESTINATION bin ENDIF(SERIAL_BUILD_TESTS)
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(FILES include/serial/serial.h DESTINATION include/serial) ## Setup install and uninstall
IF(NOT CMAKE_FIND_INSTALL_PATH) # Unless asked not to...
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT}) IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
ENDIF(NOT CMAKE_FIND_INSTALL_PATH) # Configure make install
IF(NOT CMAKE_INSTALL_PREFIX)
SET(CMAKE_INSTALL_PREFIX /usr/local)
ENDIF(NOT CMAKE_INSTALL_PREFIX)
INSTALL(FILES Findserial.cmake DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/) INSTALL(TARGETS serial
RUNTIME DESTINATION bin
ADD_CUSTOM_TARGET(uninstall @echo uninstall package) LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall package"
COMMAND xargs ARGS rm < install_manifest.txt
TARGET uninstall
) )
ELSE(UNIX)
ADD_CUSTOM_COMMAND( INSTALL(FILES include/serial/serial.h
COMMENT "uninstall only implemented in unix" DESTINATION include/serial)
TARGET uninstall
) IF(NOT CMAKE_FIND_INSTALL_PATH)
ENDIF(UNIX) set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL) ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
INSTALL(FILES Findserial.cmake
DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/)
ADD_CUSTOM_TARGET(uninstall @echo uninstall package)
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall package"
COMMAND xargs ARGS rm < install_manifest.txt
TARGET uninstall
)
ELSE(UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall only implemented in unix"
TARGET uninstall
)
ENDIF(UNIX)
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
endmacro(build_serial) endmacro(build_serial)

View File

@ -20,6 +20,13 @@ clean:
-cd build && make clean -cd build && make clean
rm -rf build bin lib rm -rf build bin lib
.PHONY: doc
doc:
@doxygen doc/Doxyfile
ifeq ($(UNAME),Darwin)
@open doc/html/index.html
endif
.PHONY: test .PHONY: test
test: test:
@mkdir -p build @mkdir -p build
@ -30,4 +37,4 @@ ifneq ($(MAKE),)
else else
cd build && make cd build && make
endif endif
# cd bin && ./serial_tests cd bin && ./serial_tests

View File

@ -1,36 +1,42 @@
macro(build_serial) macro(build_serial)
cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
# Set the build type. Options are: cmake_minimum_required(VERSION 2.4.6)
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
# Debug : w/ debug symbols, w/o optimization
# Release : w/o debug symbols, w/ optimization
# RelWithDebInfo : w/ debug symbols, w/ optimization
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
set(ROS_BUILD_TYPE RelWithDebInfo)
rosbuild_init() # Set the build type. Options are:
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
# Debug : w/ debug symbols, w/o optimization
# Release : w/o debug symbols, w/ optimization
# RelWithDebInfo : w/ debug symbols, w/ optimization
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
set(ROS_BUILD_TYPE Debug)
#set the default path for built executables to the "bin" directory rosbuild_init()
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#set the default path for built libraries to the "lib" directory
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# Check for OS X and if so disable kqueue support in asio #set the default path for built executables to the "bin" directory
IF(CMAKE_SYSTEM_NAME MATCHES Darwin) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE) #set the default path for built libraries to the "lib" directory
ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin) set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# Build the serial library include_directories(include)
rosbuild_add_library(serial src/serial.cpp include/serial/serial.h) include_directories(vendor)
# Add boost dependencies set(SERIAL_SRCS src/serial.cc)
rosbuild_add_boost_directories() if(UNIX)
rosbuild_link_boost(serial system filesystem thread) list(APPEND SERIAL_SRCS src/impl/unix.cc)
else(UNIX)
list(APPEND SERIAL_SRCS src/impl/winows.cc)
endif(UNIX)
# Build example # Build the serial library
rosbuild_add_executable(serial_example examples/serial_example.cpp) rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
target_link_libraries(serial_example serial)
# Build example
rosbuild_add_executable(serial_example examples/serial_example.cc)
target_link_libraries(serial_example ${PROJECT_NAME})
# Create unit tests
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
target_link_libraries(serial_tests ${PROJECT_NAME})
endmacro(build_serial) endmacro(build_serial)

761
src/impl/unix.cc Normal file
View File

@ -0,0 +1,761 @@
/* Copyright 2012 William Woodall and John Harrison */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <paths.h>
#include <sysexits.h>
#include <termios.h>
#include <sys/param.h>
#include <pthread.h>
#if defined(__linux__)
# include <linux/serial.h>
#endif
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
#include "serial/impl/unix.h"
#ifndef TIOCINQ
#ifdef FIONREAD
#define TIOCINQ FIONREAD
#else
#define TIOCINQ 0x541B
#endif
#endif
using std::string;
using std::invalid_argument;
using serial::Serial;
using serial::SerialExecption;
using serial::PortNotOpenedException;
using serial::IOException;
Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
long timeout, bytesize_t bytesize,
parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (true), rtscts_ (false),
timeout_ (timeout), 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 ()
{
close();
pthread_mutex_destroy(&this->read_mutex);
pthread_mutex_destroy(&this->write_mutex);
}
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;
}
void
Serial::SerialImpl::reconfigurePort ()
{
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 |= (unsigned long) (CLOCAL|CREAD);
options.c_lflag &= (unsigned long) ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|
ISIG|IEXTEN); //|ECHOPRT
options.c_oflag &= (unsigned long) ~(OPOST);
options.c_iflag &= (unsigned long) ~(INLCR|IGNCR|ICRNL|IGNBRK);
#ifdef IUCLC
options.c_iflag &= (unsigned long) ~IUCLC;
#endif
#ifdef PARMRK
options.c_iflag &= (unsigned long) ~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
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__)
struct serial_struct ser;
ioctl (fd_, TIOCGSERIAL, &ser);
// set custom divisor
ser.custom_divisor = ser.baud_base / 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 &= (unsigned long) ~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 &= (unsigned long) ~(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 &= (unsigned long) ~(INPCK|ISTRIP);
if (parity_ == parity_none)
{
options.c_cflag &= (unsigned long) ~(PARENB|PARODD);
}
else if (parity_ == parity_even)
{
options.c_cflag &= (unsigned long) ~(PARODD);
options.c_cflag |= (PARENB);
}
else if (parity_ == parity_odd)
{
options.c_cflag |= (PARENB|PARODD);
}
else
{
throw invalid_argument ("invalid parity");
}
// setup flow control
// xonxoff
#ifdef IXANY
if (xonxoff_)
options.c_iflag |= (IXON|IXOFF); //|IXANY)
else
options.c_iflag &= (unsigned long) ~(IXON|IXOFF|IXANY);
#else
if (xonxoff_)
options.c_iflag |= (IXON|IXOFF);
else
options.c_iflag &= (unsigned long) ~(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::SerialImpl::close ()
{
if (is_open_ == true)
{
if (fd_ != -1)
{
::close (fd_); // Ignoring the outcome
fd_ = -1;
}
is_open_ = false;
}
}
bool
Serial::SerialImpl::isOpen () const
{
return is_open_;
}
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);
}
}
inline void get_time_now(struct timespec &time) {
# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
time.tv_sec = mts.tv_sec;
time.tv_nsec = mts.tv_nsec;
# else
clock_gettime(CLOCK_REALTIME, &time);
# endif
}
size_t
Serial::SerialImpl::read (unsigned char* buf, size_t size)
{
if (!is_open_)
{
throw PortNotOpenedException ("Serial::read");
}
fd_set readfds;
size_t bytes_read = 0;
struct timeval timeout;
timeout.tv_sec = timeout_ / 1000;
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
while (bytes_read < size)
{
FD_ZERO (&readfds);
FD_SET (fd_, &readfds);
// 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, &readfds, NULL, 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'
/** Error **/
if (r < 0) {
// Select was interrupted, try again
if (errno == EINTR) {
continue;
}
// Otherwise there was some error
THROW (IOException, errno);
}
/** Timeout **/
if (r == 0) {
break;
}
/** Something ready to read **/
if (r > 0) {
// Make sure our file descriptor is in the ready to read list
if (FD_ISSET (fd_, &readfds)) {
// This should be non-blocking returning only what is avaialble now
// Then returning so that select can block again.
ssize_t bytes_read_now =
::read (fd_, buf+bytes_read, size-bytes_read);
// read should always return some data as select reported it was
// ready to read when we get to this point.
if (bytes_read_now < 1)
{
// Disconnected devices, at least on Linux, show the
// behavior that they are always ready to read immediately
// but reading returns nothing.
throw SerialExecption ("device reports readiness to read but "
"returned no data (device disconnected?)");
}
// Update bytes_read
bytes_read += static_cast<size_t> (bytes_read_now);
// If bytes_read == size then we have read everything we need
if (bytes_read == size) {
break;
}
// If bytes_read < size then we have more to read
if (bytes_read < size) {
continue;
}
// If bytes_read > size then we have over read, which shouldn't happen
if (bytes_read > size) {
throw SerialExecption ("read over read, too many bytes where "
"read, 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 read, but our fd isn't"
" in the list, this shouldn't happen!");
}
}
return bytes_read;
}
size_t
Serial::SerialImpl::write (const string &data)
{
if (is_open_ == false)
{
throw PortNotOpenedException ("Serial::write");
}
return static_cast<size_t> (::write (fd_, data.c_str (), data.length ()));
}
void
Serial::SerialImpl::setPort (const string &port)
{
port_ = port;
}
string
Serial::SerialImpl::getPort () const
{
return port_;
}
void
Serial::SerialImpl::setTimeout (long timeout)
{
timeout_ = timeout;
}
long
Serial::SerialImpl::getTimeout () const
{
return timeout_;
}
void
Serial::SerialImpl::setBaudrate (unsigned long baudrate)
{
baudrate_ = baudrate;
if (is_open_)
reconfigurePort ();
}
unsigned long
Serial::SerialImpl::getBaudrate () const
{
return baudrate_;
}
void
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
{
bytesize_ = bytesize;
if (is_open_)
reconfigurePort ();
}
serial::bytesize_t
Serial::SerialImpl::getBytesize () const
{
return bytesize_;
}
void
Serial::SerialImpl::setParity (serial::parity_t parity)
{
parity_ = parity;
if (is_open_)
reconfigurePort ();
}
serial::parity_t
Serial::SerialImpl::getParity () const
{
return parity_;
}
void
Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
{
stopbits_ = stopbits;
if (is_open_)
reconfigurePort ();
}
serial::stopbits_t
Serial::SerialImpl::getStopbits () const
{
return stopbits_;
}
void
Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
{
flowcontrol_ = flowcontrol;
if (is_open_)
reconfigurePort ();
}
serial::flowcontrol_t
Serial::SerialImpl::getFlowcontrol () const
{
return flowcontrol_;
}
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);
}
void
Serial::SerialImpl::sendBreak (int duration)
{
if (is_open_ == false)
{
throw PortNotOpenedException ("Serial::sendBreak");
}
tcsendbreak (fd_, static_cast<int> (duration/4));
}
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::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::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);
}
}
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;
}
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::SerialImpl::getCD()
{
if (is_open_ == false)
{
throw PortNotOpenedException ("Serial::getCD");
}
int s = ioctl (fd_, TIOCMGET, 0);
return (s & TIOCM_CD) != 0;
}
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::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);
}
}

670
src/impl/windows.cc Normal file
View File

@ -0,0 +1,670 @@
/* Copyright 2012 William Woodall and John Harrison */
#include "serial/impl/windows.h"
using std::string;
using std::invalid_argument;
using serial::Serial;
using serial::SerialExecption;
using serial::PortNotOpenedException;
using serial::IOException;
Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
long timeout, bytesize_t bytesize,
parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: port_ (port), fd_ (-1), isOpen_ (false), xonxoff_ (true), rtscts_ (false),
timeout_ (timeout), baudrate_ (baudrate), parity_ (parity), bytesize_ (bytesize),
stopbits_ (stopbits), flowcontrol_ (flowcontrol)
{
if (port_.empty () == false)
open ();
}
Serial::SerialImpl::~SerialImpl ()
{
close();
}
void
Serial::SerialImpl::open ()
{
if (port_.empty())
{
throw invalid_argument ("bad port specified");
}
if (isOpen_ == true)
{
throw SerialExecption ("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 ("to many file handles open");
break;
default:
throw IOException (errno);
}
}
reconfigurePort();
isOpen_ = true;
}
void
Serial::SerialImpl::reconfigurePort ()
{
if (fd_ == -1)
{
// Can only operate on a valid file descriptor
throw IOException ("invalid file descriptor");
}
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 |= (unsigned long) (CLOCAL|CREAD);
options.c_lflag &= (unsigned long) ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|
ISIG|IEXTEN); //|ECHOPRT
options.c_oflag &= (unsigned long) ~(OPOST);
options.c_iflag &= (unsigned long) ~(INLCR|IGNCR|ICRNL|IGNBRK);
#ifdef IUCLC
options.c_iflag &= (unsigned long) ~IUCLC;
#endif
#ifdef PARMRK
options.c_iflag &= (unsigned long) ~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
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__)
struct serial_struct ser;
ioctl(fd_, TIOCGSERIAL, &ser);
// set custom divisor
ser.custom_divisor = ser.baud_base / 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 &= (unsigned long) ~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 &= (unsigned long) ~(CSTOPB);
else if (stopbits_ == STOPBITS_ONE_POINT_FIVE)
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
throw invalid_argument ("invalid stop bit");
// setup parity
options.c_iflag &= (unsigned long) ~(INPCK|ISTRIP);
if (parity_ == PARITY_NONE)
{
options.c_cflag &= (unsigned long) ~(PARENB|PARODD);
}
else if (parity_ == PARITY_EVEN)
{
options.c_cflag &= (unsigned long) ~(PARODD);
options.c_cflag |= (PARENB);
}
else if (parity_ == PARITY_ODD)
{
options.c_cflag |= (PARENB|PARODD);
}
else
{
throw invalid_argument ("invalid parity");
}
// setup flow control
// xonxoff
#ifdef IXANY
if (xonxoff_)
options.c_iflag |= (IXON|IXOFF); //|IXANY)
else
options.c_iflag &= (unsigned long) ~(IXON|IXOFF|IXANY);
#else
if (xonxoff_)
options.c_iflag |= (IXON|IXOFF);
else
options.c_iflag &= (unsigned long) ~(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
options.c_cc[VMIN] = 1; // Minimum of 1 character in the buffer
options.c_cc[VTIME] = 0; // timeout on waiting for new data
// activate settings
::tcsetattr (fd_, TCSANOW, &options);
}
void
Serial::SerialImpl::close ()
{
if (isOpen_ == true)
{
if (fd_ != -1)
{
::close (fd_); // Ignoring the outcome
fd_ = -1;
}
isOpen_ = false;
}
}
bool
Serial::SerialImpl::isOpen () const
{
return isOpen_;
}
size_t
Serial::SerialImpl::available ()
{
if (!isOpen_)
{
return 0;
}
int count = 0;
int result = ioctl (fd_, TIOCINQ, &count);
if (result == 0)
{
return static_cast<size_t> (count);
}
else
{
throw IOException (errno);
}
}
size_t
Serial::SerialImpl::read (char* buf, size_t size)
{
if (!isOpen_)
{
throw PortNotOpenedException ("Serial::read");
}
fd_set readfds;
ssize_t bytes_read = 0;
int count = 0;
while (true)
{
count++;
if (timeout_ != -1)
{
FD_ZERO (&readfds);
FD_SET (fd_, &readfds);
struct timeval timeout;
timeout.tv_sec = timeout_ / 1000;
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
int r = select (fd_ + 1, &readfds, NULL, NULL, &timeout);
if (r == -1 && errno == EINTR)
continue;
if (r == -1)
{
throw IOException (errno);
}
}
if (timeout_ == -1 || FD_ISSET (fd_, &readfds))
{
bytes_read = ::read (fd_, buf, size);
// 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 SerialExecption ("device reports readiness to read but "
"returned no data (device disconnected?)");
}
break;
}
else
{
break;
}
}
return static_cast<size_t> (bytes_read);
}
size_t
Serial::SerialImpl::write (const string &data)
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::write");
}
fd_set writefds;
ssize_t bytes_written = 0;
while (true)
{
if (timeout_ != -1)
{
FD_ZERO (&writefds);
FD_SET (fd_, &writefds);
struct timeval timeout;
timeout.tv_sec = timeout_ / 1000;
timeout.tv_usec = static_cast<int> (timeout_ % 1000) * 1000;
int r = select (fd_ + 1, NULL, &writefds, NULL, &timeout);
if (r == -1 && errno == EINTR)
continue;
if (r == -1)
{
throw IOException (errno);
}
}
if (timeout_ == -1 || FD_ISSET (fd_, &writefds))
{
bytes_written = ::write (fd_, data.c_str (), data.length ());
// read should always return some data as select reported it was
// ready to read when we get to this point.
if (bytes_written < 1)
{
// Disconnected devices, at least on Linux, show the
// behavior that they are always ready to read immediately
// but reading returns nothing.
throw SerialExecption ("device reports readiness to read but "
"returned no data (device disconnected?)");
}
break;
}
else
{
break;
}
}
return static_cast<size_t> (bytes_written);
}
void
Serial::SerialImpl::setPort (const string &port)
{
port_ = port;
}
string
Serial::SerialImpl::getPort () const
{
return port_;
}
void
Serial::SerialImpl::setTimeout (long timeout)
{
timeout_ = timeout;
}
long
Serial::SerialImpl::getTimeout () const
{
return timeout_;
}
void
Serial::SerialImpl::setBaudrate (unsigned long baudrate)
{
baudrate_ = baudrate;
if (isOpen_)
reconfigurePort ();
}
unsigned long
Serial::SerialImpl::getBaudrate () const
{
return baudrate_;
}
void
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
{
bytesize_ = bytesize;
if (isOpen_)
reconfigurePort ();
}
serial::bytesize_t
Serial::SerialImpl::getBytesize () const
{
return bytesize_;
}
void
Serial::SerialImpl::setParity (serial::parity_t parity)
{
parity_ = parity;
if (isOpen_)
reconfigurePort ();
}
serial::parity_t
Serial::SerialImpl::getParity () const
{
return parity_;
}
void
Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
{
stopbits_ = stopbits;
if (isOpen_)
reconfigurePort ();
}
serial::stopbits_t
Serial::SerialImpl::getStopbits () const
{
return stopbits_;
}
void
Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
{
flowcontrol_ = flowcontrol;
if (isOpen_)
reconfigurePort ();
}
serial::flowcontrol_t
Serial::SerialImpl::getFlowcontrol () const
{
return flowcontrol_;
}
void
Serial::SerialImpl::flush ()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::flush");
}
tcdrain (fd_);
}
void
Serial::SerialImpl::flushInput ()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::flushInput");
}
tcflush (fd_, TCIFLUSH);
}
void
Serial::SerialImpl::flushOutput ()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::flushOutput");
}
tcflush (fd_, TCOFLUSH);
}
void
Serial::SerialImpl::sendBreak (int duration)
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::sendBreak");
}
tcsendbreak (fd_, static_cast<int> (duration/4));
}
void
Serial::SerialImpl::setBreak (bool level)
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::setBreak");
}
if (level)
{
ioctl (fd_, TIOCSBRK);
}
else {
ioctl (fd_, TIOCCBRK);
}
}
void
Serial::SerialImpl::setRTS (bool level)
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::setRTS");
}
if (level)
{
ioctl (fd_, TIOCMBIS, TIOCM_RTS);
}
else {
ioctl (fd_, TIOCMBIC, TIOCM_RTS);
}
}
void
Serial::SerialImpl::setDTR (bool level)
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::setDTR");
}
if (level)
{
ioctl (fd_, TIOCMBIS, TIOCM_DTR);
}
else
{
ioctl (fd_, TIOCMBIC, TIOCM_DTR);
}
}
bool
Serial::SerialImpl::getCTS ()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::getCTS");
}
int s = ioctl (fd_, TIOCMGET, 0);
return (s & TIOCM_CTS) != 0;
}
bool
Serial::SerialImpl::getDSR()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::getDSR");
}
int s = ioctl(fd_, TIOCMGET, 0);
return (s & TIOCM_DSR) != 0;
}
bool
Serial::SerialImpl::getRI()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::getRI");
}
int s = ioctl (fd_, TIOCMGET, 0);
return (s & TIOCM_RI) != 0;
}
bool
Serial::SerialImpl::getCD()
{
if (isOpen_ == false)
{
throw PortNotOpenedException ("Serial::getCD");
}
int s = ioctl (fd_, TIOCMGET, 0);
return (s & TIOCM_CD) != 0;
}

362
src/serial.cc Normal file
View File

@ -0,0 +1,362 @@
/* Copyright 2012 William Woodall and John Harrison */
#include <alloca.h>
#include "serial/serial.h"
#ifdef _WIN32
#include "serial/impl/win.h"
#else
#include "serial/impl/unix.h"
#endif
using std::invalid_argument;
using std::min;
using std::numeric_limits;
using std::vector;
using std::size_t;
using std::string;
using serial::Serial;
using serial::SerialExecption;
using serial::IOException;
using serial::bytesize_t;
using serial::parity_t;
using serial::stopbits_t;
using serial::flowcontrol_t;
class Serial::ScopedReadLock {
public:
ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->readLock();
}
~ScopedReadLock() {
this->pimpl_->readUnlock();
}
private:
SerialImpl *pimpl_;
};
class Serial::ScopedWriteLock {
public:
ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->writeLock();
}
~ScopedWriteLock() {
this->pimpl_->writeUnlock();
}
private:
SerialImpl *pimpl_;
};
Serial::Serial (const string &port, unsigned long baudrate, long timeout,
bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: read_cache_("")
{
pimpl_ = new SerialImpl (port, baudrate, timeout, bytesize, parity,
stopbits, flowcontrol);
}
Serial::~Serial ()
{
delete pimpl_;
}
void
Serial::open ()
{
pimpl_->open ();
}
void
Serial::close ()
{
pimpl_->close ();
}
bool
Serial::isOpen () const
{
return pimpl_->isOpen ();
}
size_t
Serial::available ()
{
return pimpl_->available ();
}
size_t
Serial::read_ (unsigned char *buffer, size_t size)
{
return this->pimpl_->read (buffer, size);
}
size_t
Serial::read (unsigned char *buffer, size_t size)
{
ScopedReadLock (this->pimpl_);
return this->pimpl_->read (buffer, size);
}
size_t
Serial::read (std::vector<unsigned char> &buffer, size_t size)
{
ScopedReadLock (this->pimpl_);
unsigned char *buffer_ = new unsigned char[size];
size_t bytes_read = this->pimpl_->read (buffer_, size);
buffer.insert (buffer.end (), buffer_, buffer_+bytes_read);
delete[] buffer_;
return bytes_read;
}
size_t
Serial::read (std::string &buffer, size_t size)
{
ScopedReadLock (this->pimpl_);
unsigned char *buffer_ = new unsigned char[size];
size_t bytes_read = this->pimpl_->read (buffer_, size);
buffer.append (reinterpret_cast<const char*>(buffer_), bytes_read);
delete[] buffer_;
return bytes_read;
}
string
Serial::read (size_t size)
{
std::string buffer;
this->read (buffer, size);
return buffer;
}
size_t
Serial::readline (string &buffer, size_t size, string eol)
{
ScopedReadLock (this->pimpl_);
size_t eol_len = eol.length ();
unsigned char *buffer_ = static_cast<unsigned char*>
(alloca (size * sizeof (unsigned char)));
size_t read_so_far = 0;
while (true)
{
size_t bytes_read = this->read_ (buffer_ + read_so_far, 1);
read_so_far += bytes_read;
if (bytes_read == 0) {
break; // Timeout occured on reading 1 byte
}
if (string (reinterpret_cast<const char*>
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
break; // EOL found
}
if (read_so_far == size) {
break; // Reached the maximum read length
}
}
buffer.append(reinterpret_cast<const char*> (buffer_), read_so_far);
return read_so_far;
}
string
Serial::readline (size_t size, string eol)
{
std::string buffer;
this->readline (buffer, size, eol);
return buffer;
}
vector<string>
Serial::readlines (size_t size, string eol)
{
ScopedReadLock (this->pimpl_);
std::vector<std::string> lines;
size_t eol_len = eol.length ();
unsigned char *buffer_ = static_cast<unsigned char*>
(alloca (size * sizeof (unsigned char)));
size_t read_so_far = 0;
size_t start_of_line = 0;
while (read_so_far < size) {
size_t bytes_read = this->read_ (buffer_+read_so_far, 1);
read_so_far += bytes_read;
if (bytes_read == 0) {
if (start_of_line != read_so_far) {
lines.push_back (
string (reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Timeout occured on reading 1 byte
}
if (string (reinterpret_cast<const char*>
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
// EOL found
lines.push_back(
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
start_of_line = read_so_far;
}
if (read_so_far == size) {
if (start_of_line != read_so_far) {
lines.push_back(
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Reached the maximum read length
}
}
return lines;
}
size_t
Serial::write (const string &data)
{
ScopedWriteLock(this->pimpl_);
return pimpl_->write (data);
}
void
Serial::setPort (const string &port)
{
ScopedReadLock(this->pimpl_);
ScopedWriteLock(this->pimpl_);
bool was_open = pimpl_->isOpen ();
if (was_open) close();
pimpl_->setPort (port);
if (was_open) open ();
}
string
Serial::getPort () const
{
return pimpl_->getPort ();
}
void
Serial::setTimeout (long timeout)
{
pimpl_->setTimeout (timeout);
}
long
Serial::getTimeout () const {
return pimpl_->getTimeout ();
}
void
Serial::setBaudrate (unsigned long baudrate)
{
pimpl_->setBaudrate (baudrate);
}
unsigned long
Serial::getBaudrate () const
{
return pimpl_->getBaudrate ();
}
void
Serial::setBytesize (bytesize_t bytesize)
{
pimpl_->setBytesize (bytesize);
}
bytesize_t
Serial::getBytesize () const
{
return pimpl_->getBytesize ();
}
void
Serial::setParity (parity_t parity)
{
pimpl_->setParity (parity);
}
parity_t
Serial::getParity () const
{
return pimpl_->getParity ();
}
void
Serial::setStopbits (stopbits_t stopbits)
{
pimpl_->setStopbits (stopbits);
}
stopbits_t
Serial::getStopbits () const
{
return pimpl_->getStopbits ();
}
void
Serial::setFlowcontrol (flowcontrol_t flowcontrol)
{
pimpl_->setFlowcontrol (flowcontrol);
}
flowcontrol_t
Serial::getFlowcontrol () const
{
return pimpl_->getFlowcontrol ();
}
void Serial::flush ()
{
ScopedReadLock(this->pimpl_);
ScopedWriteLock(this->pimpl_);
pimpl_->flush ();
read_cache_.clear ();
}
void Serial::flushInput ()
{
ScopedReadLock(this->pimpl_);
pimpl_->flushInput ();
}
void Serial::flushOutput ()
{
ScopedWriteLock(this->pimpl_);
pimpl_->flushOutput ();
read_cache_.clear ();
}
void Serial::sendBreak (int duration)
{
pimpl_->sendBreak (duration);
}
void Serial::setBreak (bool level)
{
pimpl_->setBreak (level);
}
void Serial::setRTS (bool level)
{
pimpl_->setRTS (level);
}
void Serial::setDTR (bool level)
{
pimpl_->setDTR (level);
}
bool Serial::getCTS ()
{
return pimpl_->getCTS ();
}
bool Serial::getDSR ()
{
return pimpl_->getDSR ();
}
bool Serial::getRI ()
{
return pimpl_->getRI ();
}
bool Serial::getCD ()
{
return pimpl_->getCD ();
}

View File

@ -1,466 +0,0 @@
#include "serial/serial.h"
#include <iostream>
using namespace serial;
/** Completion Conditions **/
class transfer_at_least_ignore_invalid_argument {
public:
typedef bool result_type;
explicit transfer_at_least_ignore_invalid_argument(std::size_t minimum) : minimum_(minimum) {}
template <typename Error>
bool operator()(const Error& err, std::size_t bytes_transferred) {
if(err) {// There is an Error
if(err == boost::asio::error::invalid_argument)
std::cout << "Invalid Argument Error" << std::endl;
if(err == boost::asio::error::operation_aborted) {
return 1;
}
if(err != boost::asio::error::invalid_argument) {// The Error is not invalid argument
return 1; // Stop reading
}
}
if(bytes_transferred >= minimum_) {// We have all the bytes we need
return 1; // Stop
} else {
return 0; // Continue
}
}
private:
std::size_t minimum_;
};
/** Classes for Handshaking control **/
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
# define BOOST_ASIO_OPTION_STORAGE DCB
#else
# define BOOST_ASIO_OPTION_STORAGE termios
#endif
class DTRControl {
public:
explicit DTRControl(bool enable = false) : m_enable(enable) {};
boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
boost::system::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if(m_enable)
storage.fDtrControl = DTR_CONTROL_ENABLE;
else
storage.fDtrControl = DTR_CONTROL_DISABLE;
#else
ec = boost::asio::error::operation_not_supported;
ec = boost::system::error_code();
#endif
return ec;
};
boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
boost::system::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if(storage.fDtrControl == DTR_CONTROL_ENABLE)
m_enable = true;
else
m_enable = true;
#else
#endif
return ec;
};
private:
bool m_enable;
};
class RTSControl {
public:
explicit RTSControl(bool enable = false) : m_enable(enable) {};
boost::system::error_code store(BOOST_ASIO_OPTION_STORAGE& storage,
boost::system::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if(m_enable)
storage.fRtsControl = RTS_CONTROL_ENABLE;
else
storage.fRtsControl = RTS_CONTROL_DISABLE;
#else
ec = boost::asio::error::operation_not_supported;
ec = boost::system::error_code();
#endif
return ec;
};
boost::system::error_code load(const BOOST_ASIO_OPTION_STORAGE& storage,
boost::system::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if(storage.fRtsControl == RTS_CONTROL_ENABLE)
m_enable = true;
else
m_enable = true;
#else
#endif
return ec;
};
private:
bool m_enable;
};
/** Serial Class Implementation **/
Serial::Serial() : io_service(), work(io_service), timeout_timer(io_service) {
this->init();
}
Serial::Serial(std::string port,
int baudrate,
long timeout,
bytesize_t bytesize,
parity_t parity,
stopbits_t stopbits,
flowcontrol_t flowcontrol)
: io_service(), work(io_service), timeout_timer(io_service)
{
// Call default constructor to initialize variables
this->init();
// Write provided settings
this->port = port;
this->setBaudrate(baudrate);
this->setTimeoutMilliseconds(timeout);
this->setBytesize(bytesize);
this->setParity(parity);
this->setStopbits(stopbits);
this->setFlowcontrol(flowcontrol);
// Open the serial port
this->open();
}
void Serial::init() {
// Boost asio variables
this->serial_port.reset();
// Serial Port settings
this->port = "";
this->setBaudrate(DEFAULT_BAUDRATE);
this->setTimeoutMilliseconds(DEFAULT_TIMEOUT);
// Private variables
this->bytes_read = 0;
this->bytes_to_read = 0;
this->reading = false;
this->nonblocking = false;
}
Serial::~Serial() {
this->close();
}
void Serial::open() {
// Make sure the Serial port is not already open.
if(this->serial_port != NULL && this->serial_port->is_open()) {
throw(SerialPortAlreadyOpenException(this->port.c_str()));
}
// Try to open the serial port
try {
this->serial_port.reset(new boost::asio::serial_port(this->io_service, this->port));
this->serial_port->set_option(this->baudrate);
this->serial_port->set_option(this->flowcontrol);
this->serial_port->set_option(this->parity);
this->serial_port->set_option(this->stopbits);
this->serial_port->set_option(this->bytesize);
} catch(std::exception &e) {
this->serial_port.reset();
throw(SerialPortFailedToOpenException(e.what()));
}
}
bool Serial::isOpen() {
if(this->serial_port != NULL)
return this->serial_port->is_open();
return false;
}
void Serial::close() {
// Cancel the current timeout timer and async reads
this->timeout_timer.cancel();
if(this->serial_port != NULL) {
this->serial_port->cancel();
this->serial_port->close();
this->serial_port.reset();
}
}
static const boost::posix_time::time_duration timeout_zero_comparison(boost::posix_time::milliseconds(0));
int Serial::read(char* buffer, int size) {
this->reading = true;
if(this->nonblocking) {// Do not wait for data
this->serial_port->async_read_some(boost::asio::buffer(buffer, size),
boost::bind(&Serial::read_complete, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else { // Wait for data until size is read or timeout occurs
boost::asio::async_read(*this->serial_port, boost::asio::buffer(buffer, size), transfer_at_least_ignore_invalid_argument(size),
boost::bind(&Serial::read_complete, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
if(this->timeout > timeout_zero_comparison) { // Only set a timeout_timer if there is a valid timeout
this->timeout_timer.expires_from_now(this->timeout);
this->timeout_timer.async_wait(boost::bind(&Serial::timeout_callback, this,
boost::asio::placeholders::error));
} else if(this->nonblocking) {
this->timeout_timer.expires_from_now(boost::posix_time::milliseconds(1));
this->timeout_timer.async_wait(boost::bind(&Serial::timeout_callback, this,
boost::asio::placeholders::error));
}
while(this->reading)
this->io_service.run_one();
this->bytes_to_read = size;
return this->bytes_read;
}
std::string Serial::read(int size) {
char *serial_buffer = new char[size];
int bytes_read_ = this->read(serial_buffer, size);
std::string return_str(serial_buffer, (std::size_t)bytes_read_);
delete[] serial_buffer;
return return_str;
}
std::string
Serial::read_until(char delim, size_t size) {
using namespace std;
string r = "";
while (r.find(delim) == string::npos) {
string s = read(1);
if (s.length() > 0)
r += s;
}
return r;
}
std::string
Serial::read_until(std::string delim, size_t size) {
using namespace std;
string r = "";
while (r.find(delim) == string::npos) {
string s = read(1);
if (s.length() > 0)
r += s;
}
return r;
}
void Serial::read_complete(const boost::system::error_code& error, std::size_t bytes_transferred) {
if(!error || error != boost::asio::error::operation_aborted) { // If there was no error OR the error wasn't operation aborted (canceled), Cancel the timer
this->timeout_timer.cancel(); // will cause timeout_callback to fire with an error
}
this->bytes_read = bytes_transferred;
this->reading = false;
}
void Serial::timeout_callback(const boost::system::error_code& error) {
if (!error) {
// The timeout wasn't canceled, so cancel the async read
this->serial_port->cancel();
}
}
int Serial::write(char data[], int length) {
return boost::asio::write(*this->serial_port, boost::asio::buffer(data, length), boost::asio::transfer_all());
}
int Serial::write(std::string data) {
char *cstr = new char[data.size()+1];
std::strcpy(cstr, data.c_str());
int bytes_wrote = this->write(cstr, data.length());
delete[] cstr;
return bytes_wrote;
}
void Serial::setRTS(bool level) {
this->serial_port->set_option(RTSControl(level));
}
void Serial::setDTR(bool level) {
this->serial_port->set_option(DTRControl(level));
}
bool Serial::getCTS() const {
throw(boost::asio::error::operation_not_supported);
return false;
}
bool Serial::getDSR() const {
throw(boost::asio::error::operation_not_supported);
return false;
}
void Serial::setPort(std::string port) {
this->port = port;
}
std::string Serial::getPort() const {
return this->port;
}
void Serial::setTimeoutMilliseconds(long timeout) {
// If timeout > 0 then read until size or timeout occurs
// If timeout == 0 then read nonblocking, return data available immediately up to size
// If timeout < 0 then read blocking, until size is read, period.
if(timeout > 0) {
this->timeout = boost::posix_time::time_duration(boost::posix_time::milliseconds(timeout));
} else {
this->timeout = boost::posix_time::time_duration(boost::posix_time::milliseconds(0));
}
if(timeout == 0)
this->nonblocking = true;
else // Must be negative
this->nonblocking = false;
}
long Serial::getTimeoutMilliseconds() const {
return this->timeout.total_milliseconds();
}
void Serial::setBaudrate(int baudrate) {
this->baudrate = boost::asio::serial_port_base::baud_rate(baudrate);
}
int Serial::getBaudrate() const {
return this->baudrate.value();
}
void Serial::setBytesize(bytesize_t bytesize) {
switch(bytesize) {
case FIVEBITS:
this->bytesize = boost::asio::serial_port_base::character_size(5);
break;
case SIXBITS:
this->bytesize = boost::asio::serial_port_base::character_size(6);
break;
case SEVENBITS:
this->bytesize = boost::asio::serial_port_base::character_size(7);
break;
case EIGHTBITS:
this->bytesize = boost::asio::serial_port_base::character_size(8);
break;
default:
throw(InvalidBytesizeException(bytesize));
break;
}
}
bytesize_t Serial::getBytesize() const {
return bytesize_t(this->bytesize.value());
}
void Serial::setParity(parity_t parity) {
switch(parity) {
case PARITY_NONE:
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none);
break;
case PARITY_ODD:
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd);
break;
case PARITY_EVEN:
this->parity = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::even);
break;
default:
throw(InvalidParityException(parity));
break;
}
}
parity_t Serial::getParity() const {
switch(this->parity.value()) {
case boost::asio::serial_port_base::parity::none:
return parity_t(PARITY_NONE);
case boost::asio::serial_port_base::parity::odd:
return parity_t(PARITY_ODD);
case boost::asio::serial_port_base::parity::even:
return parity_t(PARITY_EVEN);
default:
throw(InvalidParityException(this->parity.value()));
}
}
void Serial::setStopbits(stopbits_t stopbits) {
switch(stopbits) {
case STOPBITS_ONE:
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one);
break;
case STOPBITS_ONE_POINT_FIVE:
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::onepointfive);
break;
case STOPBITS_TWO:
this->stopbits = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::two);
break;
default:
throw(InvalidStopbitsException(stopbits));
break;
}
}
stopbits_t Serial::getStopbits() const {
switch(this->stopbits.value()) {
case boost::asio::serial_port_base::stop_bits::one:
return stopbits_t(STOPBITS_ONE);
case boost::asio::serial_port_base::stop_bits::onepointfive:
return stopbits_t(STOPBITS_ONE_POINT_FIVE);
case boost::asio::serial_port_base::stop_bits::two:
return stopbits_t(STOPBITS_TWO);
default:
throw(InvalidStopbitsException(this->stopbits.value()));
}
}
void Serial::setFlowcontrol(flowcontrol_t flowcontrol) {
switch(flowcontrol) {
case FLOWCONTROL_NONE:
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none);
break;
case FLOWCONTROL_SOFTWARE:
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::software);
break;
case FLOWCONTROL_HARDWARE:
this->flowcontrol = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware);
break;
default:
throw(InvalidFlowcontrolException(flowcontrol));
break;
}
}
flowcontrol_t Serial::getFlowcontrol() const {
switch(this->flowcontrol.value()) {
case boost::asio::serial_port_base::flow_control::none:
return flowcontrol_t(FLOWCONTROL_NONE);
case boost::asio::serial_port_base::flow_control::software:
return flowcontrol_t(FLOWCONTROL_SOFTWARE);
case boost::asio::serial_port_base::flow_control::hardware:
return flowcontrol_t(FLOWCONTROL_HARDWARE);
default:
throw(InvalidFlowcontrolException(this->flowcontrol.value()));
}
}

View File

@ -0,0 +1 @@
#include ""

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
import serial, sys
if len(sys.argv) != 2:
print "python: Usage_serial_test <port name like: /dev/ttyUSB0>"
sys.exit(1)
sio = serial.Serial(sys.argv[1], 115200)
sio.timeout = 250
while True:
sio.write("Testing.")
print sio.read(8)

View File

@ -0,0 +1,31 @@
#include <iostream>
#include <string>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
void
_delimeter_tokenizer (std::string &data, std::vector<std::string> &tokens,
std::string delimeter)
{
boost::split(tokens, data, boost::is_any_of(delimeter));
}
typedef boost::function<void(std::string&,std::vector<std::string>&)> TokenizerType;
int main(void) {
std::string data = "a\rb\rc\r";
std::vector<std::string> tokens;
std::string delimeter = "\r";
TokenizerType f = boost::bind(_delimeter_tokenizer, _1, _2, delimeter);
f(data, tokens);
BOOST_FOREACH(std::string token, tokens)
std::cout << token << std::endl;
return 0;
}

118
tests/serial_tests.cc Normal file
View File

@ -0,0 +1,118 @@
/* To run these tests you need to change the define below to the serial port
* with a loop back device attached.
*
* Alternatively you could use an Arduino:
void setup()
{
Serial.begin(115200);
}
void loop()
{
while (Serial.available() > 0) {
Serial.write(Serial.read());
}
}
*/
#include <string>
#include "gtest/gtest.h"
#include <boost/bind.hpp>
// Use FRIEND_TEST... its not as nasty, thats what friends are for
// // OMG this is so nasty...
// #define private public
// #define protected public
#include "serial/serial.h"
#ifdef __linux__
#include <pty.h>
#else
#include <util.h>
#endif
using namespace serial;
using std::string;
namespace {
class SerialTests : public ::testing::Test {
protected:
virtual void SetUp() {
if (openpty(&master_fd, &slave_fd, name, NULL, NULL) == -1) {
perror("openpty");
exit(127);
}
ASSERT_TRUE(master_fd > 0);
ASSERT_TRUE(slave_fd > 0);
ASSERT_TRUE(string(name).length() > 0);
port1 = new Serial(string(name), 115200, 250);
}
virtual void TearDown() {
port1->close();
delete port1;
}
Serial * port1;
int master_fd;
int slave_fd;
char name[100];
};
TEST_F(SerialTests, readWorks) {
write(master_fd, "abc\n", 4);
string r = port1->read(4);
EXPECT_EQ(r, string("abc\n"));
}
TEST_F(SerialTests, writeWorks) {
char buf[5] = "";
port1->write("abc\n");
read(master_fd, buf, 4);
EXPECT_EQ(string(buf, 4), string("abc\n"));
}
TEST_F(SerialTests, timeoutWorks) {
// Timeout a read, returns an empty string
string empty = port1->read();
EXPECT_EQ(empty, string(""));
// Ensure that writing/reading still works after a timeout.
write(master_fd, "abc\n", 4);
string r = port1->read(4);
EXPECT_EQ(r, string("abc\n"));
}
TEST_F(SerialTests, partialRead) {
// Write some data, but request more than was written.
write(master_fd, "abc\n", 4);
// Should timeout, but return what was in the buffer.
string empty = port1->read(10);
EXPECT_EQ(empty, string("abc\n"));
// Ensure that writing/reading still works after a timeout.
write(master_fd, "abc\n", 4);
string r = port1->read(4);
EXPECT_EQ(r, string("abc\n"));
}
} // namespace
int main(int argc, char **argv) {
try {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
} catch (std::exception &e) {
std::cerr << "Unhandled Exception: " << e.what() << std::endl;
}
return 1;
}