Commit 462341902f9acaf557ba49f60afe664592363e06

Authored by Geoffrey Hunter
1 parent 9ed00102

Basic custom baud rate functionality working.

.vscode/c_cpp_properties.json
... ... @@ -60,7 +60,8 @@
60 60 },
61 61 "compilerPath": "/usr/bin/gcc",
62 62 "cStandard": "c11",
63   - "cppStandard": "c++17"
  63 + "cppStandard": "c++17",
  64 + "configurationProvider": "ms-vscode.cmake-tools"
64 65 },
65 66 {
66 67 "name": "Win32",
... ...
README.md
... ... @@ -66,18 +66,19 @@ using namespace mn::CppLinuxSerial;
66 66  
67 67 int main() {
68 68 // Create serial port object and open serial port
69   - SerialPort serialPort0("/dev/ttyUSB0", BaudRate::B_57600);
70   - serialPort0.Open();
  69 + SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_57600);
  70 + serialPort.SetTimeout(-1); // Block when reading until any data is received
  71 + serialPort.Open();
71 72  
72 73 // Write some ASCII datae
73   - serialPort0.Write("Hello");
  74 + serialPort.Write("Hello");
74 75  
75 76 // Read some data back
76 77 std::string readData;
77   - serialPort0.Read(readData);
  78 + serialPort.Read(readData);
78 79  
79 80 // Close the serial port
80   - serialPort0.Close();
  81 + serialPort.Close();
81 82 }
82 83 ```
83 84  
... ...
include/CppLinuxSerial/SerialPort.hpp
... ... @@ -15,27 +15,66 @@
15 15 #include <string>
16 16 #include <fstream> // For file I/O (reading/writing to COM port)
17 17 #include <sstream>
18   -#include <termios.h> // POSIX terminal control definitions (struct termios)
  18 +// #include <termios.h> // POSIX terminal control definitions (struct termios)
  19 +// #include <asm/termios.h> // Terminal control definitions (struct termios)
19 20 #include <vector>
  21 +#include <asm/ioctls.h>
  22 +#include <asm/termbits.h>
20 23  
21 24 // User headers
22 25 #include "Exception.hpp"
23 26  
  27 +
  28 + typedef unsigned int speed_t;
  29 +// typedef struct termios2 {
  30 +// tcflag_t c_iflag; /* input mode flags */
  31 +// tcflag_t c_oflag; /* output mode flags */
  32 +// tcflag_t c_cflag; /* control mode flags */
  33 +// tcflag_t c_lflag; /* local mode flags */
  34 +// cc_t c_line; /* line discipline */
  35 +// cc_t c_cc[NCCS]; /* control characters */
  36 +// speed_t c_ispeed; /* input speed */
  37 +// speed_t c_ospeed; /* output speed */
  38 +// } termios2_t;
  39 +
24 40 namespace mn {
25 41 namespace CppLinuxSerial {
26 42  
  43 + enum class BaudRateType {
  44 + STANDARD,
  45 + CUSTOM,
  46 + };
  47 +
27 48 /// \brief Strongly-typed enumeration of baud rates for use with the SerialPort class
  49 + /// \details Specifies all the same baud rates as UNIX, as well as B_CUSTOM to specify your
  50 + /// own. See https://linux.die.net/man/3/cfsetispeed for list of supported UNIX baud rates.
28 51 enum class BaudRate {
  52 + B_0,
  53 + B_50,
  54 + B_75,
  55 + B_110,
  56 + B_134,
  57 + B_150,
  58 + B_200,
  59 + B_300,
  60 + B_600,
  61 + B_1200,
  62 + B_1800,
  63 + B_2400,
  64 + B_4800,
29 65 B_9600,
  66 + B_19200,
30 67 B_38400,
31 68 B_57600,
32 69 B_115200,
33   - CUSTOM
  70 + B_230400,
  71 + B_460800,
  72 + B_CUSTOM, // Placeholder
34 73 };
35 74  
36 75 enum class State {
37 76 CLOSED,
38   - OPEN
  77 + OPEN,
39 78 };
40 79  
41 80 /// \brief SerialPort object is used to perform rx/tx serial communication.
... ... @@ -48,6 +87,9 @@ namespace mn {
48 87 /// \brief Constructor that sets up serial port with the basic (required) parameters.
49 88 SerialPort(const std::string &device, BaudRate baudRate);
50 89  
  90 + /// \brief Constructor that sets up serial port with the basic (required) parameters.
  91 + SerialPort(const std::string &device, speed_t baudRate);
  92 +
51 93 //! @brief Destructor. Closes serial port if still open.
52 94 virtual ~SerialPort();
53 95  
... ... @@ -57,6 +99,9 @@ namespace mn {
57 99  
58 100 void SetBaudRate(BaudRate baudRate);
59 101  
  102 + /// \brief Allows the user to set a custom baud rate.
  103 + void SetBaudRate(speed_t baudRate);
  104 +
60 105 /// \brief Sets the read timeout (in milliseconds)/blocking mode.
61 106 /// \details Only call when state != OPEN. This method manupulates VMIN and VTIME.
62 107 /// \param timeout_ms Set to -1 to infinite timeout, 0 to return immediately with any data (non
... ... @@ -92,13 +137,18 @@ namespace mn {
92 137 private:
93 138  
94 139 /// \brief Returns a populated termios structure for the passed in file descriptor.
95   - termios GetTermios();
  140 + // termios GetTermios();
  141 +
  142 +
96 143  
97 144 /// \brief Configures the tty device as a serial port.
98 145 /// \warning Device must be open (valid file descriptor) when this is called.
99 146 void ConfigureTermios();
100 147  
101   - void SetTermios(termios myTermios);
  148 + // void SetTermios(termios myTermios);
  149 +
  150 + termios2 GetTermios2();
  151 + void SetTermios2(termios2 tty);
102 152  
103 153 /// \brief Keeps track of the serial port's state.
104 154 State state_;
... ... @@ -106,8 +156,14 @@ namespace mn {
106 156 /// \brief The file path to the serial port device (e.g. "/dev/ttyUSB0").
107 157 std::string device_;
108 158  
109   - /// \brief The current baud rate.
110   - BaudRate baudRate_;
  159 + /// \brief The type of baud rate that the user has specified.
  160 + BaudRateType baudRateType_;
  161 +
  162 + /// \brief The current baud rate if baudRateType_ == STANDARD.
  163 + BaudRate baudRateStandard_;
  164 +
  165 + /// \brief The current baud rate if baudRateType_ == CUSTOM.
  166 + speed_t baudRateCustom_;
111 167  
112 168 /// \brief The file descriptor for the open file. This gets written to when Open() is called.
113 169 int fileDesc_;
... ...
src/SerialPort.cpp
... ... @@ -15,20 +15,28 @@
15 15 #include <unistd.h> // UNIX standard function definitions
16 16 #include <fcntl.h> // File control definitions
17 17 #include <errno.h> // Error number definitions
18   -#include <termios.h> // POSIX terminal control definitions (struct termios)
  18 +// #include <termios.h> // POSIX terminal control definitions (struct termios)
19 19 #include <system_error> // For throwing std::system_error
  20 +#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
  21 +#include <cassert>
  22 +// #include <asm/termios.h> // Terminal control definitions (struct termios)
  23 +#include <asm/ioctls.h>
  24 +#include <asm/termbits.h>
20 25  
21 26 // User includes
22 27 #include "CppLinuxSerial/Exception.hpp"
23 28 #include "CppLinuxSerial/SerialPort.hpp"
24 29  
  30 +#define BOTHER 0010000
  31 +
25 32 namespace mn {
26 33 namespace CppLinuxSerial {
27 34  
28 35 SerialPort::SerialPort() {
29 36 echo_ = false;
30 37 timeout_ms_ = defaultTimeout_ms_;
31   - baudRate_ = defaultBaudRate_;
  38 + baudRateType_ = BaudRateType::STANDARD;
  39 + baudRateStandard_ = defaultBaudRate_;
32 40 readBufferSize_B_ = defaultReadBufferSize_B_;
33 41 readBuffer_.reserve(readBufferSize_B_);
34 42 state_ = State::CLOSED;
... ... @@ -37,7 +45,15 @@ namespace CppLinuxSerial {
37 45 SerialPort::SerialPort(const std::string& device, BaudRate baudRate) :
38 46 SerialPort() {
39 47 device_ = device;
40   - baudRate_ = baudRate;
  48 + baudRateType_ = BaudRateType::STANDARD;
  49 + baudRateStandard_ = baudRate;
  50 + }
  51 +
  52 + SerialPort::SerialPort(const std::string& device, speed_t baudRate) :
  53 + SerialPort() {
  54 + device_ = device;
  55 + baudRateType_ = BaudRateType::CUSTOM;
  56 + baudRateCustom_ = baudRate;
41 57 }
42 58  
43 59 SerialPort::~SerialPort() {
... ... @@ -52,13 +68,21 @@ namespace CppLinuxSerial {
52 68 void SerialPort::SetDevice(const std::string& device) {
53 69 device_ = device;
54 70 if(state_ == State::OPEN)
55   -
56   -
57   - ConfigureTermios();
  71 + ConfigureTermios();
58 72 }
59 73  
60 74 void SerialPort::SetBaudRate(BaudRate baudRate) {
61   - baudRate_ = baudRate;
  75 + std::cout << "standard called\n";
  76 + baudRateType_ = BaudRateType::STANDARD;
  77 + baudRateStandard_ = baudRate;
  78 + if(state_ == State::OPEN)
  79 + ConfigureTermios();
  80 + }
  81 +
  82 + void SerialPort::SetBaudRate(speed_t baudRate) {
  83 + std::cout << " custom called\n";
  84 + baudRateType_ = BaudRateType::CUSTOM;
  85 + baudRateCustom_ = baudRate;
62 86 if(state_ == State::OPEN)
63 87 ConfigureTermios();
64 88 }
... ... @@ -66,7 +90,7 @@ namespace CppLinuxSerial {
66 90 void SerialPort::Open()
67 91 {
68 92  
69   - std::cout << "Attempting to open COM port \"" << device_ << "\"." << std::endl;
  93 + // std::cout << "Attempting to open COM port \"" << device_ << "\"." << std::endl;
70 94  
71 95 if(device_.empty()) {
72 96 THROW_EXCEPT("Attempted to open file when file path has not been assigned to.");
... ... @@ -86,7 +110,7 @@ namespace CppLinuxSerial {
86 110  
87 111 ConfigureTermios();
88 112  
89   - std::cout << "COM port opened successfully." << std::endl;
  113 + // std::cout << "COM port opened successfully." << std::endl;
90 114 state_ = State::OPEN;
91 115 }
92 116  
... ... @@ -97,11 +121,12 @@ namespace CppLinuxSerial {
97 121  
98 122 void SerialPort::ConfigureTermios()
99 123 {
100   - std::cout << "Configuring COM port \"" << device_ << "\"." << std::endl;
  124 + // std::cout << "Configuring COM port \"" << device_ << "\"." << std::endl;
101 125  
102 126 //================== CONFIGURE ==================//
103 127  
104   - termios tty = GetTermios();
  128 + // termios tty = GetTermios();
  129 + termios2 tty = GetTermios2();
105 130  
106 131 //================= (.c_cflag) ===============//
107 132  
... ... @@ -115,29 +140,134 @@ namespace CppLinuxSerial {
115 140  
116 141 //===================== BAUD RATE =================//
117 142  
118   - switch(baudRate_) {
119   - case BaudRate::B_9600:
120   - cfsetispeed(&tty, B9600);
121   - cfsetospeed(&tty, B9600);
122   - break;
123   - case BaudRate::B_38400:
124   - cfsetispeed(&tty, B38400);
125   - cfsetospeed(&tty, B38400);
126   - break;
127   - case BaudRate::B_57600:
128   - cfsetispeed(&tty, B57600);
129   - cfsetospeed(&tty, B57600);
130   - break;
131   - case BaudRate::B_115200:
132   - cfsetispeed(&tty, B115200);
133   - cfsetospeed(&tty, B115200);
134   - break;
135   - case BaudRate::CUSTOM:
136   - // See https://gist.github.com/kennethryerson/f7d1abcf2633b7c03cf0
137   - throw std::runtime_error("Custom baud rate not yet supported.");
138   - default:
139   - throw std::runtime_error(std::string() + "baudRate passed to " + __PRETTY_FUNCTION__ + " unrecognized.");
140   - }
  143 + // We used to use cfsetispeed() cand cfsetospeed(), but this didn't allow
  144 + // us to set custom baud rates
  145 + if (baudRateType_ == BaudRateType::STANDARD) {
  146 + switch(baudRateStandard_) {
  147 + // case BaudRate::B_0:
  148 + // cfsetispeed(&tty, B0);
  149 + // cfsetospeed(&tty, B0);
  150 + // tty.c_ispeed = 0;
  151 + // tty.c_ospeed = 0;
  152 + // break;
  153 + // case BaudRate::B_50:
  154 + // cfsetispeed(&tty, B50);
  155 + // cfsetospeed(&tty, B50);
  156 + // tty.c_ispeed = 50;
  157 + // tty.c_ospeed = 50;
  158 + // break;
  159 + // case BaudRate::B_75:
  160 + // cfsetispeed(&tty, B75);
  161 + // cfsetospeed(&tty, B75);
  162 + // break;
  163 + // case BaudRate::B_110:
  164 + // cfsetispeed(&tty, B110);
  165 + // cfsetospeed(&tty, B110);
  166 + // break;
  167 + // case BaudRate::B_134:
  168 + // cfsetispeed(&tty, B134);
  169 + // cfsetospeed(&tty, B134);
  170 + // break;
  171 + // case BaudRate::B_150:
  172 + // cfsetispeed(&tty, B150);
  173 + // cfsetospeed(&tty, B150);
  174 + // break;
  175 + // case BaudRate::B_200:
  176 + // cfsetispeed(&tty, B200);
  177 + // cfsetospeed(&tty, B200);
  178 + // break;
  179 + // case BaudRate::B_300:
  180 + // cfsetispeed(&tty, B300);
  181 + // cfsetospeed(&tty, B300);
  182 + // break;
  183 + // case BaudRate::B_600:
  184 + // cfsetispeed(&tty, B600);
  185 + // cfsetospeed(&tty, B600);
  186 + // break;
  187 + // case BaudRate::B_1200:
  188 + // cfsetispeed(&tty, B1200);
  189 + // cfsetospeed(&tty, B1200);
  190 + // break;
  191 + // case BaudRate::B_1800:
  192 + // cfsetispeed(&tty, B1800);
  193 + // cfsetospeed(&tty, B1800);
  194 + // break;
  195 + // case BaudRate::B_2400:
  196 + // cfsetispeed(&tty, B2400);
  197 + // cfsetospeed(&tty, B2400);
  198 + // break;
  199 + // case BaudRate::B_4800:
  200 + // cfsetispeed(&tty, B4800);
  201 + // cfsetospeed(&tty, B4800);
  202 + // break;
  203 + // case BaudRate::B_9600:
  204 + // cfsetispeed(&tty, B9600);
  205 + // cfsetospeed(&tty, B9600);
  206 + // tty.c_ispeed = 9600;
  207 + // tty.c_ospeed = 9600;
  208 + // break;
  209 + // case BaudRate::B_19200:
  210 + // cfsetispeed(&tty, B19200);
  211 + // cfsetospeed(&tty, B19200);
  212 + // break;
  213 + // case BaudRate::B_38400:
  214 + // cfsetispeed(&tty, B38400);
  215 + // cfsetospeed(&tty, B38400);
  216 + // break;
  217 + // case BaudRate::B_57600:
  218 + // cfsetispeed(&tty, B57600);
  219 + // cfsetospeed(&tty, B57600);
  220 + // break;
  221 + // case BaudRate::B_115200:
  222 + // cfsetispeed(&tty, B115200);
  223 + // cfsetospeed(&tty, B115200);
  224 + // break;
  225 + // case BaudRate::B_230400:
  226 + // cfsetispeed(&tty, B230400);
  227 + // cfsetospeed(&tty, B230400);
  228 + // break;
  229 + // case BaudRate::B_460800:
  230 + // cfsetispeed(&tty, B460800);
  231 + // cfsetospeed(&tty, B460800);
  232 + // break;
  233 + // case BaudRate::CUSTOM:
  234 + // // See https://gist.github.com/kennethryerson/f7d1abcf2633b7c03cf0
  235 + // throw std::runtime_error("Custom baud rate not yet supported.");
  236 + default:
  237 + throw std::runtime_error(std::string() + "baudRate passed to " + __PRETTY_FUNCTION__ + " unrecognized.");
  238 + }
  239 + }
  240 + else if (baudRateType_ == BaudRateType::CUSTOM)
  241 + {
  242 + tty.c_cflag &= ~CBAUD;
  243 + tty.c_cflag |= CBAUDEX;
  244 + // tty.c_cflag |= BOTHER;
  245 + tty.c_ispeed = baudRateCustom_;
  246 + tty.c_ospeed = baudRateCustom_;
  247 +
  248 +
  249 + // #include <linux/serial.h>
  250 + // // configure port to use custom speed instead of 38400
  251 + // struct serial_struct ss;
  252 + // ioctl(fileDesc_, TIOCGSERIAL, &ss);
  253 + // ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
  254 + // ss.custom_divisor = (ss.baud_base + (baudRateCustom_ / 2)) / baudRateCustom_;
  255 + // int closestSpeed = ss.baud_base / ss.custom_divisor;
  256 +
  257 + // if (closestSpeed < baudRateCustom_ * 98 / 100 || closestSpeed > baudRateCustom_ * 102 / 100) {
  258 + // printf("Cannot set serial port speed to %d. Closest possible is %d\n", baudRateCustom_, closestSpeed);
  259 + // }
  260 +
  261 + // ioctl(fileDesc_, TIOCSSERIAL, &ss);
  262 +
  263 + // cfsetispeed(&tty, B38400);
  264 + // cfsetospeed(&tty, B38400);
  265 + }
  266 + else
  267 + {
  268 + // Should never get here, bug in this libraries code!
  269 + assert(false);
  270 + }
141 271  
142 272 //===================== (.c_oflag) =================//
143 273  
... ... @@ -193,7 +323,8 @@ namespace CppLinuxSerial {
193 323 // Try and use raw function call
194 324 //cfmakeraw(&tty);
195 325  
196   - this->SetTermios(tty);
  326 + // this->SetTermios(tty);
  327 + this->SetTermios2(tty);
197 328  
198 329 /*
199 330 // Flush port, then apply attributes
... ... @@ -262,39 +393,60 @@ namespace CppLinuxSerial {
262 393 // If code reaches here, read must of been successful
263 394 }
264 395  
265   - termios SerialPort::GetTermios() {
266   - if(fileDesc_ == -1)
267   - throw std::runtime_error("GetTermios() called but file descriptor was not valid.");
  396 + // termios SerialPort::GetTermios() {
  397 + // if(fileDesc_ == -1)
  398 + // throw std::runtime_error("GetTermios() called but file descriptor was not valid.");
268 399  
269   - struct termios tty;
270   - memset(&tty, 0, sizeof(tty));
  400 + // struct termios tty;
  401 + // memset(&tty, 0, sizeof(tty));
271 402  
272   - // Get current settings (will be stored in termios structure)
273   - if(tcgetattr(fileDesc_, &tty) != 0)
274   - {
275   - // Error occurred
276   - std::cout << "Could not get terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl;
277   - throw std::system_error(EFAULT, std::system_category());
278   - //return false;
279   - }
  403 + // // Get current settings (will be stored in termios structure)
  404 + // if(tcgetattr(fileDesc_, &tty) != 0)
  405 + // {
  406 + // // Error occurred
  407 + // std::cout << "Could not get terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl;
  408 + // throw std::system_error(EFAULT, std::system_category());
  409 + // //return false;
  410 + // }
280 411  
281   - return tty;
282   - }
  412 + // return tty;
  413 + // }
283 414  
284   - void SerialPort::SetTermios(termios myTermios)
285   - {
286   - // Flush port, then apply attributes
287   - tcflush(fileDesc_, TCIFLUSH);
  415 + // void SerialPort::SetTermios(termios myTermios)
  416 + // {
  417 + // // Flush port, then apply attributes
  418 + // tcflush(fileDesc_, TCIFLUSH);
288 419  
289   - if(tcsetattr(fileDesc_, TCSANOW, &myTermios) != 0)
290   - {
291   - // Error occurred
292   - std::cout << "Could not apply terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl;
293   - throw std::system_error(EFAULT, std::system_category());
  420 + // if(tcsetattr(fileDesc_, TCSANOW, &myTermios) != 0)
  421 + // {
  422 + // // Error occurred
  423 + // std::cout << "Could not apply terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl;
  424 + // throw std::system_error(EFAULT, std::system_category());
294 425  
295   - }
  426 + // }
  427 +
  428 + // // Successful!
  429 + // }
296 430  
297   - // Successful!
  431 + termios2 SerialPort::GetTermios2()
  432 + {
  433 + struct termios2 term2;
  434 +
  435 + ioctl(fileDesc_, TCGETS2, &term2);
  436 +
  437 + return term2;
  438 +
  439 + // term2.c_cflag &= ~CBAUD; /* Remove current BAUD rate */
  440 + // term2.c_cflag |= BOTHER; /* Allow custom BAUD rate using int input */
  441 + // term2.c_ispeed = speed; /* Set the input BAUD rate */
  442 + // term2.c_ospeed = speed; /* Set the output BAUD rate */
  443 +
  444 + // ioctl(fd, TCSETS2, &term2);
  445 + }
  446 +
  447 + void SerialPort::SetTermios2(termios2 tty)
  448 + {
  449 + ioctl(fileDesc_, TCSETS2, &tty);
298 450 }
299 451  
300 452 void SerialPort::Close() {
... ...
test/arduino/Basic/Basic.ino 0 → 100644
  1 +/*
  2 + AnalogReadSerial
  3 +
  4 + Reads an analog input on pin 0, prints the result to the Serial Monitor.
  5 + Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu).
  6 + Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.
  7 +
  8 + This example code is in the public domain.
  9 +
  10 + http://www.arduino.cc/en/Tutorial/AnalogReadSerial
  11 +*/
  12 +
  13 +// the setup routine runs once when you press reset:
  14 +void setup() {
  15 + // initialize serial communication at 9600 bits per second:
  16 + Serial.begin(13000);
  17 +// Serial.begin(81234); // Used to test custom baud rates
  18 +}
  19 +
  20 +// the loop routine runs over and over again forever:
  21 +void loop() {
  22 + Serial.println("Hello");
  23 + delay(100); // delay in between reads for stability
  24 +}
... ...
test/arduino/main.cpp 0 → 100644
  1 +#include <CppLinuxSerial/SerialPort.hpp>
  2 +
  3 +using namespace mn::CppLinuxSerial;
  4 +
  5 +int main() {
  6 + // Create serial port object and open serial port
  7 + // SerialPort serialPort("/dev/ttyACM0", BaudRate::B_9600);
  8 + SerialPort serialPort("/dev/ttyACM0", 13000);
  9 + serialPort.SetTimeout(-1); // Block when reading until any data is received
  10 + serialPort.Open();
  11 +
  12 + // Write some ASCII datae
  13 + // serialPort0.Write("Hello");
  14 +
  15 + // Read some data back
  16 + while(1) {
  17 + std::string readData;
  18 + serialPort.Read(readData);
  19 + std::cout << "Received data: " << readData;
  20 + }
  21 +
  22 + // Close the serial port
  23 + serialPort.Close();
  24 +}
0 25 \ No newline at end of file
... ...