From 462341902f9acaf557ba49f60afe664592363e06 Mon Sep 17 00:00:00 2001 From: Geoffrey Hunter Date: Sat, 7 Nov 2020 01:23:18 -0800 Subject: [PATCH] Basic custom baud rate functionality working. --- .vscode/c_cpp_properties.json | 3 ++- README.md | 11 ++++++----- include/CppLinuxSerial/SerialPort.hpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- src/SerialPort.cpp | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------- test/arduino/Basic/Basic.ino | 24 ++++++++++++++++++++++++ test/arduino/main.cpp | 24 ++++++++++++++++++++++++ 6 files changed, 332 insertions(+), 74 deletions(-) create mode 100644 test/arduino/Basic/Basic.ino create mode 100644 test/arduino/main.cpp diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 0ee9eae..81ce4af 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -60,7 +60,8 @@ }, "compilerPath": "/usr/bin/gcc", "cStandard": "c11", - "cppStandard": "c++17" + "cppStandard": "c++17", + "configurationProvider": "ms-vscode.cmake-tools" }, { "name": "Win32", diff --git a/README.md b/README.md index 75f4a19..1a4582c 100644 --- a/README.md +++ b/README.md @@ -66,18 +66,19 @@ using namespace mn::CppLinuxSerial; int main() { // Create serial port object and open serial port - SerialPort serialPort0("/dev/ttyUSB0", BaudRate::B_57600); - serialPort0.Open(); + SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_57600); + serialPort.SetTimeout(-1); // Block when reading until any data is received + serialPort.Open(); // Write some ASCII datae - serialPort0.Write("Hello"); + serialPort.Write("Hello"); // Read some data back std::string readData; - serialPort0.Read(readData); + serialPort.Read(readData); // Close the serial port - serialPort0.Close(); + serialPort.Close(); } ``` diff --git a/include/CppLinuxSerial/SerialPort.hpp b/include/CppLinuxSerial/SerialPort.hpp index 1ece112..afc6d38 100644 --- a/include/CppLinuxSerial/SerialPort.hpp +++ b/include/CppLinuxSerial/SerialPort.hpp @@ -15,27 +15,66 @@ #include #include // For file I/O (reading/writing to COM port) #include -#include // POSIX terminal control definitions (struct termios) +// #include // POSIX terminal control definitions (struct termios) +// #include // Terminal control definitions (struct termios) #include +#include +#include // User headers #include "Exception.hpp" + + typedef unsigned int speed_t; +// typedef struct termios2 { +// tcflag_t c_iflag; /* input mode flags */ +// tcflag_t c_oflag; /* output mode flags */ +// tcflag_t c_cflag; /* control mode flags */ +// tcflag_t c_lflag; /* local mode flags */ +// cc_t c_line; /* line discipline */ +// cc_t c_cc[NCCS]; /* control characters */ +// speed_t c_ispeed; /* input speed */ +// speed_t c_ospeed; /* output speed */ +// } termios2_t; + namespace mn { namespace CppLinuxSerial { + enum class BaudRateType { + STANDARD, + CUSTOM, + }; + /// \brief Strongly-typed enumeration of baud rates for use with the SerialPort class + /// \details Specifies all the same baud rates as UNIX, as well as B_CUSTOM to specify your + /// own. See https://linux.die.net/man/3/cfsetispeed for list of supported UNIX baud rates. enum class BaudRate { + B_0, + B_50, + B_75, + B_110, + B_134, + B_150, + B_200, + B_300, + B_600, + B_1200, + B_1800, + B_2400, + B_4800, B_9600, + B_19200, B_38400, B_57600, B_115200, - CUSTOM + B_230400, + B_460800, + B_CUSTOM, // Placeholder }; enum class State { CLOSED, - OPEN + OPEN, }; /// \brief SerialPort object is used to perform rx/tx serial communication. @@ -48,6 +87,9 @@ namespace mn { /// \brief Constructor that sets up serial port with the basic (required) parameters. SerialPort(const std::string &device, BaudRate baudRate); + /// \brief Constructor that sets up serial port with the basic (required) parameters. + SerialPort(const std::string &device, speed_t baudRate); + //! @brief Destructor. Closes serial port if still open. virtual ~SerialPort(); @@ -57,6 +99,9 @@ namespace mn { void SetBaudRate(BaudRate baudRate); + /// \brief Allows the user to set a custom baud rate. + void SetBaudRate(speed_t baudRate); + /// \brief Sets the read timeout (in milliseconds)/blocking mode. /// \details Only call when state != OPEN. This method manupulates VMIN and VTIME. /// \param timeout_ms Set to -1 to infinite timeout, 0 to return immediately with any data (non @@ -92,13 +137,18 @@ namespace mn { private: /// \brief Returns a populated termios structure for the passed in file descriptor. - termios GetTermios(); + // termios GetTermios(); + + /// \brief Configures the tty device as a serial port. /// \warning Device must be open (valid file descriptor) when this is called. void ConfigureTermios(); - void SetTermios(termios myTermios); + // void SetTermios(termios myTermios); + + termios2 GetTermios2(); + void SetTermios2(termios2 tty); /// \brief Keeps track of the serial port's state. State state_; @@ -106,8 +156,14 @@ namespace mn { /// \brief The file path to the serial port device (e.g. "/dev/ttyUSB0"). std::string device_; - /// \brief The current baud rate. - BaudRate baudRate_; + /// \brief The type of baud rate that the user has specified. + BaudRateType baudRateType_; + + /// \brief The current baud rate if baudRateType_ == STANDARD. + BaudRate baudRateStandard_; + + /// \brief The current baud rate if baudRateType_ == CUSTOM. + speed_t baudRateCustom_; /// \brief The file descriptor for the open file. This gets written to when Open() is called. int fileDesc_; diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp index 9bf0aca..72feedd 100644 --- a/src/SerialPort.cpp +++ b/src/SerialPort.cpp @@ -15,20 +15,28 @@ #include // UNIX standard function definitions #include // File control definitions #include // Error number definitions -#include // POSIX terminal control definitions (struct termios) +// #include // POSIX terminal control definitions (struct termios) #include // For throwing std::system_error +#include // Used for TCGETS2, which is required for custom baud rates +#include +// #include // Terminal control definitions (struct termios) +#include +#include // User includes #include "CppLinuxSerial/Exception.hpp" #include "CppLinuxSerial/SerialPort.hpp" +#define BOTHER 0010000 + namespace mn { namespace CppLinuxSerial { SerialPort::SerialPort() { echo_ = false; timeout_ms_ = defaultTimeout_ms_; - baudRate_ = defaultBaudRate_; + baudRateType_ = BaudRateType::STANDARD; + baudRateStandard_ = defaultBaudRate_; readBufferSize_B_ = defaultReadBufferSize_B_; readBuffer_.reserve(readBufferSize_B_); state_ = State::CLOSED; @@ -37,7 +45,15 @@ namespace CppLinuxSerial { SerialPort::SerialPort(const std::string& device, BaudRate baudRate) : SerialPort() { device_ = device; - baudRate_ = baudRate; + baudRateType_ = BaudRateType::STANDARD; + baudRateStandard_ = baudRate; + } + + SerialPort::SerialPort(const std::string& device, speed_t baudRate) : + SerialPort() { + device_ = device; + baudRateType_ = BaudRateType::CUSTOM; + baudRateCustom_ = baudRate; } SerialPort::~SerialPort() { @@ -52,13 +68,21 @@ namespace CppLinuxSerial { void SerialPort::SetDevice(const std::string& device) { device_ = device; if(state_ == State::OPEN) - - - ConfigureTermios(); + ConfigureTermios(); } void SerialPort::SetBaudRate(BaudRate baudRate) { - baudRate_ = baudRate; + std::cout << "standard called\n"; + baudRateType_ = BaudRateType::STANDARD; + baudRateStandard_ = baudRate; + if(state_ == State::OPEN) + ConfigureTermios(); + } + + void SerialPort::SetBaudRate(speed_t baudRate) { + std::cout << " custom called\n"; + baudRateType_ = BaudRateType::CUSTOM; + baudRateCustom_ = baudRate; if(state_ == State::OPEN) ConfigureTermios(); } @@ -66,7 +90,7 @@ namespace CppLinuxSerial { void SerialPort::Open() { - std::cout << "Attempting to open COM port \"" << device_ << "\"." << std::endl; + // std::cout << "Attempting to open COM port \"" << device_ << "\"." << std::endl; if(device_.empty()) { THROW_EXCEPT("Attempted to open file when file path has not been assigned to."); @@ -86,7 +110,7 @@ namespace CppLinuxSerial { ConfigureTermios(); - std::cout << "COM port opened successfully." << std::endl; + // std::cout << "COM port opened successfully." << std::endl; state_ = State::OPEN; } @@ -97,11 +121,12 @@ namespace CppLinuxSerial { void SerialPort::ConfigureTermios() { - std::cout << "Configuring COM port \"" << device_ << "\"." << std::endl; + // std::cout << "Configuring COM port \"" << device_ << "\"." << std::endl; //================== CONFIGURE ==================// - termios tty = GetTermios(); + // termios tty = GetTermios(); + termios2 tty = GetTermios2(); //================= (.c_cflag) ===============// @@ -115,29 +140,134 @@ namespace CppLinuxSerial { //===================== BAUD RATE =================// - switch(baudRate_) { - case BaudRate::B_9600: - cfsetispeed(&tty, B9600); - cfsetospeed(&tty, B9600); - break; - case BaudRate::B_38400: - cfsetispeed(&tty, B38400); - cfsetospeed(&tty, B38400); - break; - case BaudRate::B_57600: - cfsetispeed(&tty, B57600); - cfsetospeed(&tty, B57600); - break; - case BaudRate::B_115200: - cfsetispeed(&tty, B115200); - cfsetospeed(&tty, B115200); - break; - case BaudRate::CUSTOM: - // See https://gist.github.com/kennethryerson/f7d1abcf2633b7c03cf0 - throw std::runtime_error("Custom baud rate not yet supported."); - default: - throw std::runtime_error(std::string() + "baudRate passed to " + __PRETTY_FUNCTION__ + " unrecognized."); - } + // We used to use cfsetispeed() cand cfsetospeed(), but this didn't allow + // us to set custom baud rates + if (baudRateType_ == BaudRateType::STANDARD) { + switch(baudRateStandard_) { + // case BaudRate::B_0: + // cfsetispeed(&tty, B0); + // cfsetospeed(&tty, B0); + // tty.c_ispeed = 0; + // tty.c_ospeed = 0; + // break; + // case BaudRate::B_50: + // cfsetispeed(&tty, B50); + // cfsetospeed(&tty, B50); + // tty.c_ispeed = 50; + // tty.c_ospeed = 50; + // break; + // case BaudRate::B_75: + // cfsetispeed(&tty, B75); + // cfsetospeed(&tty, B75); + // break; + // case BaudRate::B_110: + // cfsetispeed(&tty, B110); + // cfsetospeed(&tty, B110); + // break; + // case BaudRate::B_134: + // cfsetispeed(&tty, B134); + // cfsetospeed(&tty, B134); + // break; + // case BaudRate::B_150: + // cfsetispeed(&tty, B150); + // cfsetospeed(&tty, B150); + // break; + // case BaudRate::B_200: + // cfsetispeed(&tty, B200); + // cfsetospeed(&tty, B200); + // break; + // case BaudRate::B_300: + // cfsetispeed(&tty, B300); + // cfsetospeed(&tty, B300); + // break; + // case BaudRate::B_600: + // cfsetispeed(&tty, B600); + // cfsetospeed(&tty, B600); + // break; + // case BaudRate::B_1200: + // cfsetispeed(&tty, B1200); + // cfsetospeed(&tty, B1200); + // break; + // case BaudRate::B_1800: + // cfsetispeed(&tty, B1800); + // cfsetospeed(&tty, B1800); + // break; + // case BaudRate::B_2400: + // cfsetispeed(&tty, B2400); + // cfsetospeed(&tty, B2400); + // break; + // case BaudRate::B_4800: + // cfsetispeed(&tty, B4800); + // cfsetospeed(&tty, B4800); + // break; + // case BaudRate::B_9600: + // cfsetispeed(&tty, B9600); + // cfsetospeed(&tty, B9600); + // tty.c_ispeed = 9600; + // tty.c_ospeed = 9600; + // break; + // case BaudRate::B_19200: + // cfsetispeed(&tty, B19200); + // cfsetospeed(&tty, B19200); + // break; + // case BaudRate::B_38400: + // cfsetispeed(&tty, B38400); + // cfsetospeed(&tty, B38400); + // break; + // case BaudRate::B_57600: + // cfsetispeed(&tty, B57600); + // cfsetospeed(&tty, B57600); + // break; + // case BaudRate::B_115200: + // cfsetispeed(&tty, B115200); + // cfsetospeed(&tty, B115200); + // break; + // case BaudRate::B_230400: + // cfsetispeed(&tty, B230400); + // cfsetospeed(&tty, B230400); + // break; + // case BaudRate::B_460800: + // cfsetispeed(&tty, B460800); + // cfsetospeed(&tty, B460800); + // break; + // case BaudRate::CUSTOM: + // // See https://gist.github.com/kennethryerson/f7d1abcf2633b7c03cf0 + // throw std::runtime_error("Custom baud rate not yet supported."); + default: + throw std::runtime_error(std::string() + "baudRate passed to " + __PRETTY_FUNCTION__ + " unrecognized."); + } + } + else if (baudRateType_ == BaudRateType::CUSTOM) + { + tty.c_cflag &= ~CBAUD; + tty.c_cflag |= CBAUDEX; + // tty.c_cflag |= BOTHER; + tty.c_ispeed = baudRateCustom_; + tty.c_ospeed = baudRateCustom_; + + + // #include + // // configure port to use custom speed instead of 38400 + // struct serial_struct ss; + // ioctl(fileDesc_, TIOCGSERIAL, &ss); + // ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; + // ss.custom_divisor = (ss.baud_base + (baudRateCustom_ / 2)) / baudRateCustom_; + // int closestSpeed = ss.baud_base / ss.custom_divisor; + + // if (closestSpeed < baudRateCustom_ * 98 / 100 || closestSpeed > baudRateCustom_ * 102 / 100) { + // printf("Cannot set serial port speed to %d. Closest possible is %d\n", baudRateCustom_, closestSpeed); + // } + + // ioctl(fileDesc_, TIOCSSERIAL, &ss); + + // cfsetispeed(&tty, B38400); + // cfsetospeed(&tty, B38400); + } + else + { + // Should never get here, bug in this libraries code! + assert(false); + } //===================== (.c_oflag) =================// @@ -193,7 +323,8 @@ namespace CppLinuxSerial { // Try and use raw function call //cfmakeraw(&tty); - this->SetTermios(tty); + // this->SetTermios(tty); + this->SetTermios2(tty); /* // Flush port, then apply attributes @@ -262,39 +393,60 @@ namespace CppLinuxSerial { // If code reaches here, read must of been successful } - termios SerialPort::GetTermios() { - if(fileDesc_ == -1) - throw std::runtime_error("GetTermios() called but file descriptor was not valid."); + // termios SerialPort::GetTermios() { + // if(fileDesc_ == -1) + // throw std::runtime_error("GetTermios() called but file descriptor was not valid."); - struct termios tty; - memset(&tty, 0, sizeof(tty)); + // struct termios tty; + // memset(&tty, 0, sizeof(tty)); - // Get current settings (will be stored in termios structure) - if(tcgetattr(fileDesc_, &tty) != 0) - { - // Error occurred - std::cout << "Could not get terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; - throw std::system_error(EFAULT, std::system_category()); - //return false; - } + // // Get current settings (will be stored in termios structure) + // if(tcgetattr(fileDesc_, &tty) != 0) + // { + // // Error occurred + // std::cout << "Could not get terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; + // throw std::system_error(EFAULT, std::system_category()); + // //return false; + // } - return tty; - } + // return tty; + // } - void SerialPort::SetTermios(termios myTermios) - { - // Flush port, then apply attributes - tcflush(fileDesc_, TCIFLUSH); + // void SerialPort::SetTermios(termios myTermios) + // { + // // Flush port, then apply attributes + // tcflush(fileDesc_, TCIFLUSH); - if(tcsetattr(fileDesc_, TCSANOW, &myTermios) != 0) - { - // Error occurred - std::cout << "Could not apply terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; - throw std::system_error(EFAULT, std::system_category()); + // if(tcsetattr(fileDesc_, TCSANOW, &myTermios) != 0) + // { + // // Error occurred + // std::cout << "Could not apply terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; + // throw std::system_error(EFAULT, std::system_category()); - } + // } + + // // Successful! + // } - // Successful! + termios2 SerialPort::GetTermios2() + { + struct termios2 term2; + + ioctl(fileDesc_, TCGETS2, &term2); + + return term2; + + // term2.c_cflag &= ~CBAUD; /* Remove current BAUD rate */ + // term2.c_cflag |= BOTHER; /* Allow custom BAUD rate using int input */ + // term2.c_ispeed = speed; /* Set the input BAUD rate */ + // term2.c_ospeed = speed; /* Set the output BAUD rate */ + + // ioctl(fd, TCSETS2, &term2); + } + + void SerialPort::SetTermios2(termios2 tty) + { + ioctl(fileDesc_, TCSETS2, &tty); } void SerialPort::Close() { diff --git a/test/arduino/Basic/Basic.ino b/test/arduino/Basic/Basic.ino new file mode 100644 index 0000000..d9a4a20 --- /dev/null +++ b/test/arduino/Basic/Basic.ino @@ -0,0 +1,24 @@ +/* + AnalogReadSerial + + Reads an analog input on pin 0, prints the result to the Serial Monitor. + Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu). + Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground. + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/AnalogReadSerial +*/ + +// the setup routine runs once when you press reset: +void setup() { + // initialize serial communication at 9600 bits per second: + Serial.begin(13000); +// Serial.begin(81234); // Used to test custom baud rates +} + +// the loop routine runs over and over again forever: +void loop() { + Serial.println("Hello"); + delay(100); // delay in between reads for stability +} diff --git a/test/arduino/main.cpp b/test/arduino/main.cpp new file mode 100644 index 0000000..935d756 --- /dev/null +++ b/test/arduino/main.cpp @@ -0,0 +1,24 @@ +#include + +using namespace mn::CppLinuxSerial; + +int main() { + // Create serial port object and open serial port + // SerialPort serialPort("/dev/ttyACM0", BaudRate::B_9600); + SerialPort serialPort("/dev/ttyACM0", 13000); + serialPort.SetTimeout(-1); // Block when reading until any data is received + serialPort.Open(); + + // Write some ASCII datae + // serialPort0.Write("Hello"); + + // Read some data back + while(1) { + std::string readData; + serialPort.Read(readData); + std::cout << "Received data: " << readData; + } + + // Close the serial port + serialPort.Close(); +} \ No newline at end of file -- libgit2 0.21.4