libcyberradio 22.01.24
SerialPort.cpp
1/***************************************************************************
2 * \file SerialPort.cpp
3 *
4 * \brief Implements a serial port connection manager.
5 *
6 * \author DA
7 * \copyright Copyright (c) 2015-2021 CyberRadio Solutions, Inc.
8 *
9 */
10
11#include "LibCyberRadio/Common/SerialPort.h"
12#include "LibCyberRadio/Common/BasicList.h"
13#include "LibCyberRadio/Common/Pythonesque.h"
14#include <unistd.h>
15#include <fcntl.h>
16#include <errno.h>
17#include <time.h>
18#include <string.h>
19#include <sys/select.h>
20
21
22namespace LibCyberRadio
23{
24
26 const std::string& device,
27 int baudrate,
28 char parity,
29 int databits,
30 int stopbits,
31 bool xonxoff,
32 bool rtscts,
33 bool debug
34 ) :
35 Debuggable(debug, "SerialPort"),
36 _device(device),
37 _baudrate(baudrate),
38 _parity(parity),
39 _databits(databits),
40 _stopbits(stopbits),
41 _useXonXoff(xonxoff),
42 _useRtsCts(rtscts),
43 _useDtsDtr(false),
44 _fd(-1),
45 _lastError(""),
46 _recvBuf(NULL),
47 _recvBufSize(1024)
48 {
49 memset(&_settings, 0, sizeof(struct termios));
50 this->debug("CONSTRUCTED\n");
51 }
52
54 {
55 // Close the serial port if it is open
56 close();
57 // Destroy the receive buffer
58 if ( _recvBuf != NULL )
59 delete _recvBuf;
60 this->debug("DESTROYED\n");
61 }
62
64 Debuggable(other),
65 _device(other._device),
66 _baudrate(other._baudrate),
67 _parity(other._parity),
68 _databits(other._databits),
69 _stopbits(other._stopbits),
70 _useXonXoff(other._useXonXoff),
71 _useRtsCts(other._useRtsCts),
72 _useDtsDtr(other._useDtsDtr),
73 _fd(other._fd),
74 _lastError(other._lastError),
75 _recvBuf(other._recvBuf),
76 _recvBufSize(other._recvBufSize)
77 {
78 memcpy(&_settings, &(other._settings), sizeof(struct termios));
79 }
80
82 {
84 // Prevent self-assignment
85 if ( this != &other )
86 {
87 _device = other._device;
88 _baudrate = other._baudrate;
89 _parity = other._parity;
90 _databits = other._databits;
91 _stopbits = other._stopbits;
92 _useXonXoff = other._useXonXoff;
93 _useRtsCts = other._useRtsCts;
94 _useDtsDtr = other._useDtsDtr;
95 _fd = other._fd;
96 _lastError = other._lastError;
97 _recvBuf = other._recvBuf;
98 _recvBufSize = other._recvBufSize;
99 memcpy(&_settings, &(other._settings), sizeof(struct termios));
100 }
101 return *this;
102 }
103
105 {
106 this->debug("[open] Called\n");
107 bool ret = true;
108 _lastError = "";
109 BasicStringList errors;
110 // Open the serial port device file
111 this->debug("[open] Opening port\n");
112 _fd = ::open(_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
113 this->debug("[open] -- FD = %d\n", _fd);
114 if ( _fd != -1 )
115 {
116 // Set flags on serial port device file
117 ::fcntl(_fd, F_SETFL, 0);
118 // Get the current port settings
119 this->debug("[open] Getting port settings\n");
120 if ( tcgetattr(_fd, &_settings) == 0 )
121 {
122 this->debug("[open] -- SUCCESS\n");
123 this->debug("[open] Setting initial port settings\n");
124 // Put port in "raw" mode (non-canonical N-8-1, no special processing)
125 cfmakeraw(&_settings);
126 // Turn on ability to read the port
127 _settings.c_cflag |= CREAD;
128 // Ignore modem control settings
129 _settings.c_cflag |= CLOCAL;
130 // Set port to retrieve bytes as they are available
131 _settings.c_cc[VMIN] = 0; // don't block if no bytes are available
132 _settings.c_cc[VTIME] = 1; // 1/10 second inter-byte timeout
133 // Apply settings to the port
134 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
135 {
136 this->debug("[open] -- SUCCESS\n");
137 }
138 else
139 {
140 this->debug("[open] -- FAILED\n");
141 errors.push_back( strerror(errno) );
142 }
143 // Apply terminal settings
144 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
145 {
146 this->debug("[open] -- SUCCESS\n");
147 }
148 else
149 {
150 this->debug("[open] -- FAILED\n");
151 errors.push_back( strerror(errno) );
152 }
153 // Set baud rate
154 if ( !setBaudRate(_baudrate) )
155 {
156 errors.push_back( _lastError );
157 }
158 // Set parity
159 if ( !setParity(_parity) )
160 {
161 errors.push_back( _lastError );
162 }
163 // Set data bits
164 if ( !setDataBits(_databits) )
165 {
166 errors.push_back( _lastError );
167 }
168 // Set stop bits
169 if ( !setStopBits(_stopbits) )
170 {
171 errors.push_back( _lastError );
172 }
173 // Set XON/XOFF software flow control
174 if ( !enableXonXoffFlowControl(_useXonXoff) )
175 {
176 errors.push_back( _lastError );
177 }
178 // Set RTS/CTS hardware flow control
179 if ( !enableRtsCtsFlowControl(_useRtsCts) )
180 {
181 errors.push_back( _lastError );
182 }
183 // Set DTS/DTR hardware flow control (not supported on Linux)
184 }
185 else
186 {
187 this->debug("[open] -- FAILED\n");
188 errors.push_back( strerror(errno) );
189 }
190 }
191 // Create the receive buffer on success
192 if ( errors.size() == 0 )
193 {
194 _recvBuf = new char[_recvBufSize];
195 }
196 // Set last error if there was a failure
197 if ( errors.size() > 0 )
198 {
199 _lastError = Pythonesque::Join(errors, ", ");
200 this->debug("[open] ERROR: %s\n", _lastError.c_str());
201 ret = false;
202 if ( _fd != -1 )
203 {
204 ::close(_fd);
205 _fd = -1;
206 }
207 }
208 this->debug("[open] Returning %s\n", this->debugBool(ret));
209 return ret;
210 }
211
213 {
214 this->debug("[close] Called\n");
215 bool ret = false;
216 _lastError = "";
217 if ( _fd != -1 )
218 {
219 ::close(_fd);
220 ret = true;
221 }
222 else
223 _lastError = "Port already closed";
224 _fd = -1;
225 if ( _lastError != "" )
226 this->debug("[close] ERROR: %s\n", _lastError.c_str());
227 this->debug("[close] Returning %s\n", this->debugBool(ret));
228 return ret;
229 }
230
231 std::string SerialPort::read()
232 {
233 this->debug("[read] Called\n");
234 std::string ret;
235 _lastError = "";
236 // Check for waiting data
237 ssize_t bytes = 1;
238 this->debug("[read] Entering read loop\n");
239 while ( (bytes > 0) && (_lastError == "") )
240 {
241 bytes = 0;
242 memset(_recvBuf, 0, _recvBufSize);
243 this->debug("[read] -- Reading data\n");
244 bytes = ::read(_fd, _recvBuf, _recvBufSize);
245 this->debug("[read] -- Bytes = %d\n", bytes);
246 if ( bytes > 0 )
247 {
248 // Data was read
249 for (int i = 0; i < bytes; i++)
250 {
251 this->debug("[read] -- %02x\n", (int)_recvBuf[i]);
252 }
253 ret += _recvBuf;
254 }
255 else if ( bytes < 0 )
256 {
257 // Error condition
258 _lastError = strerror(errno);
259 }
260 }
261 // Identify timeout condition (no error up to this point, but
262 // no data received, either)
263 if ( (ret == "") && (_lastError == "") )
264 _lastError = "Timeout";
265 if ( _lastError != "" )
266 this->debug("[read] ERROR: %s\n", _lastError.c_str());
267 this->debug("[read] Returning \"%s\"\n", ret.c_str());
268 return ret;
269 }
270
271 bool SerialPort::write(const std::string& data)
272 {
273 this->debug("[write] Called\n");
274 bool ret = false;
275 _lastError = "";
276 this->debug("[write] Writing data\n");
277 ssize_t bytes = ::write(_fd, data.c_str(), data.size());
278 this->debug("[write] -- Bytes = %d\n", bytes);
279 if ( bytes > 0 )
280 {
281 // Data was written
282 ret = true;
283 // Delay slightly to allow the data to be written and
284 // to receive a response
285 usleep(100);
286 }
287 else if ( bytes == 0 )
288 {
289 // No data was written
290 _lastError = "No data written";
291 }
292 else
293 {
294 // Error condition
295 _lastError = strerror(errno);
296 }
297 if ( !ret )
298 this->debug("[write] ERROR: %s\n", _lastError.c_str());
299 this->debug("[write] Returning %s\n", this->debugBool(ret));
300 return ret;
301 }
302
304 {
305 return _baudrate;
306 }
307
308 bool SerialPort::setBaudRate(int baudrate)
309 {
310 this->debug("[setBaudRate] Called, baudrate=%d\n", baudrate);
311 bool ret = true;
312 _lastError = "";
313 // Translate incoming baud rate into a termios baud rate constant
314 int baud = -1;
315 switch( baudrate )
316 {
317 case 50:
318 baud = B50;
319 break;
320 case 75:
321 baud = B75;
322 break;
323 case 110:
324 baud = B110;
325 break;
326 case 134:
327 baud = B134;
328 break;
329 case 150:
330 baud = B150;
331 break;
332 case 200:
333 baud = B200;
334 break;
335 case 300:
336 baud = B300;
337 break;
338 case 600:
339 baud = B600;
340 break;
341 case 1200:
342 baud = B1200;
343 break;
344 case 2400:
345 baud = B2400;
346 break;
347 case 4800:
348 baud = B4800;
349 break;
350 case 9600:
351 baud = B9600;
352 break;
353 case 19200:
354 baud = B19200;
355 break;
356 case 38400:
357 baud = B38400;
358 break;
359 case 57600:
360 baud = B57600;
361 break;
362 case 115200:
363 baud = B115200;
364 break;
365 case 230400:
366 baud = B230400;
367 break;
368 case 460800:
369 baud = B460800;
370 break;
371 case 500000:
372 baud = B500000;
373 break;
374 case 576000:
375 baud = B576000;
376 break;
377 case 921600:
378 baud = B921600;
379 break;
380 case 1000000:
381 baud = B1000000;
382 break;
383 case 1152000:
384 baud = B1152000;
385 break;
386 case 1500000:
387 baud = B1500000;
388 break;
389 case 2000000:
390 baud = B2000000;
391 break;
392 case 2500000:
393 baud = B2500000;
394 break;
395 case 3000000:
396 baud = B3000000;
397 break;
398 case 3500000:
399 baud = B3500000;
400 break;
401 case 4000000:
402 baud = B4000000;
403 break;
404 default:
405 _lastError = "Unsupported baud rate";
406 ret = false;
407 }
408 // If successful, apply current settings
409 if (ret)
410 {
411 this->debug("[getBaudRate] Setting port speed settings\n");
412 if ( (cfsetispeed(&_settings, baud) == 0) &&
413 (cfsetospeed(&_settings, baud) == 0) &&
414 (tcsetattr(_fd, TCSANOW, &_settings) == 0) )
415 {
416 this->debug("[getBaudRate] -- SUCCESS\n");
417 _baudrate = baudrate;
418 }
419 else
420 {
421 this->debug("[getBaudRate] -- FAILED\n");
422 _lastError = strerror(errno);
423 ret = false;
424 }
425 }
426 if ( !ret )
427 this->debug("[getBaudRate] ERROR: %s\n", _lastError.c_str());
428 this->debug("[getBaudRate] Returning %s\n", this->debugBool(ret));
429 return ret;
430 }
431
433 {
434 return _parity;
435 }
436
437 bool SerialPort::setParity(char parity)
438 {
439 this->debug("[setParity] Called, parity=%c\n", parity);
440 bool ret = true;
441 _lastError = "";
442 // Validate parity setting
443 if ( (parity != 'N') && (parity != 'O') && (parity != 'E') )
444 {
445 _lastError = "Unsupported parity setting";
446 ret = false;
447 }
448 // If successful, apply current settings
449 if (ret)
450 {
451 this->debug("[setParity] Setting port parity settings\n");
452 // Clear parity on and odd bits (parity None)
453 _settings.c_cflag &= ~PARENB;
454 _settings.c_cflag &= ~PARODD;
455 if ( parity != 'N' )
456 {
457 // Set parity on bit
458 _settings.c_cflag |= PARENB;
459 if ( parity == 'O' )
460 // Set parity odd bit
461 _settings.c_cflag |= PARODD;
462 }
463 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
464 {
465 this->debug("[setParity] -- SUCCESS\n");
466 _parity = parity;
467 }
468 else
469 {
470 this->debug("[setParity] -- FAILED\n");
471 _lastError = strerror(errno);
472 ret = false;
473 }
474 }
475 if ( !ret )
476 this->debug("[setParity] ERROR: %s\n", _lastError.c_str());
477 this->debug("[setParity] Returning %s\n", this->debugBool(ret));
478 return ret;
479 }
480
482 {
483 return _databits;
484 }
485
486 bool SerialPort::setDataBits(int databits)
487 {
488 this->debug("[setDataBits] Called, databits=%d\n", databits);
489 bool ret = true;
490 _lastError = "";
491 // Translate incoming data bits into a termios data bits constant
492 int dbits = -1;
493 switch ( databits )
494 {
495 case 5:
496 dbits = CS5;
497 break;
498 case 6:
499 dbits = CS6;
500 break;
501 case 7:
502 dbits = CS7;
503 break;
504 case 8:
505 dbits = CS8;
506 break;
507 default:
508 _lastError = "Unsupported data bits size";
509 ret = false;
510 }
511 // If successful, apply current settings
512 if (ret)
513 {
514 this->debug("[setDataBits] Setting port data bit settings\n");
515 // Clear existing size bits
516 _settings.c_cflag &= ~CSIZE;
517 // Apply new size bits
518 _settings.c_cflag |= dbits;
519 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
520 {
521 this->debug("[setDataBits] -- SUCCESS\n");
522 _databits = databits;
523 }
524 else
525 {
526 this->debug("[setDataBits] -- FAILED\n");
527 _lastError = strerror(errno);
528 ret = false;
529 }
530 }
531 if ( !ret )
532 this->debug("[setDataBits] ERROR: %s\n", _lastError.c_str());
533 this->debug("[setDataBits] Returning %s\n", this->debugBool(ret));
534 return ret;
535 }
536
538 {
539 return _stopbits;
540 }
541
542 bool SerialPort::setStopBits(int stopbits)
543 {
544 this->debug("[setStopBits] Called, stopbits=%d\n", stopbits);
545 bool ret = true;
546 _lastError = "";
547 // Validate incoming stop bits
548 if ( (stopbits != 1) && (stopbits != 2) )
549 {
550 _lastError = "Unsupported stop bits size";
551 ret = false;
552 }
553 // If successful, apply current settings
554 if (ret)
555 {
556 this->debug("[setStopBits] Setting port data bit settings\n");
557 if ( stopbits == 2 )
558 // Set two stop bits bit
559 _settings.c_cflag |= CSTOPB;
560 else
561 // Clear two stop bits bit
562 _settings.c_cflag &= ~CSTOPB;
563 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
564 {
565 this->debug("[setStopBits] -- SUCCESS\n");
566 _stopbits = stopbits;
567 }
568 else
569 {
570 this->debug("[setStopBits] -- FAILED\n");
571 _lastError = strerror(errno);
572 ret = false;
573 }
574 }
575 if ( !ret )
576 this->debug("[setStopBits] ERROR: %s\n", _lastError.c_str());
577 this->debug("[setStopBits] Returning %s\n", this->debugBool(ret));
578 return ret;
579 }
580
582 {
583 return _useXonXoff;
584 }
585
587 {
588 this->debug("[enableXonXoffFlowControl] Called, enabled=%s\n",
589 this->debugBool(enabled));
590 bool ret = true;
591 _lastError = "";
592 // Apply current settings
593 if (ret)
594 {
595 this->debug("[enableXonXoffFlowControl] Setting port XON/XOFF settings\n");
596 if ( enabled )
597 // Turn ON XON/XOFF software flow control
598 _settings.c_iflag |= (IXON | IXOFF | IXANY);
599 else
600 // Turn OFF XON/XOFF software flow control
601 _settings.c_iflag &= ~(IXON | IXOFF | IXANY);
602 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
603 {
604 this->debug("[enableXonXoffFlowControl] -- SUCCESS\n");
605 _useXonXoff = enabled;
606 }
607 else
608 {
609 this->debug("[enableXonXoffFlowControl] -- FAILED\n");
610 _lastError = strerror(errno);
611 ret = false;
612 }
613 }
614 if ( !ret )
615 this->debug("[enableXonXoffFlowControl] ERROR: %s\n", _lastError.c_str());
616 this->debug("[enableXonXoffFlowControl] Returning %s\n", this->debugBool(ret));
617 return ret;
618 }
619
621 {
622 return _useRtsCts;
623 }
624
626 {
627 this->debug("[enableRtsCtsFlowControl] Called, enabled=%s\n",
628 this->debugBool(enabled));
629 bool ret = true;
630 _lastError = "";
631 // Apply current settings
632 if (ret)
633 {
634 this->debug("[enableRtsCtsFlowControl] Setting port XON/XOFF settings\n");
635 if ( enabled )
636 // Turn ON RTS/CTS hardware flow control
637 _settings.c_cflag |= CRTSCTS;
638 else
639 // Turn OFF RTS/CTS hardware flow control
640 _settings.c_cflag &= ~CRTSCTS;
641 if ( tcsetattr(_fd, TCSANOW, &_settings) == 0 )
642 {
643 this->debug("[enableRtsCtsFlowControl] -- SUCCESS\n");
644 _useRtsCts = enabled;
645 }
646 else
647 {
648 this->debug("[enableRtsCtsFlowControl] -- FAILED\n");
649 _lastError = strerror(errno);
650 ret = false;
651 }
652 }
653 if ( !ret )
654 this->debug("[enableRtsCtsFlowControl] ERROR: %s\n", _lastError.c_str());
655 this->debug("[enableRtsCtsFlowControl] Returning %s\n", this->debugBool(ret));
656 return ret;
657 }
658
659 std::string SerialPort::getLastError() const
660 {
661 return _lastError;
662 }
663
664} // namespace LibCyberRadio
665
Debuggable & operator=(const Debuggable &other)
Assignment operator for Debuggable objects.
virtual const char * debugBool(bool x)
Gets a debug output string for a Boolean value.
virtual int debug(const char *format,...)
Outputs debug information.
Debuggable(bool debug=false, const std::string &debug_name="", FILE *debug_fp=DEBUG_FP, const std::string &debug_timefmt=DEBUG_TIME_FMT)
Constructs a Debuggable object.
static std::string Join(const BasicStringList &vec, const std::string &sep)
Joins a list of string tokens, concatenating them into a single string.
std::string read()
Reads data from the serial port.
SerialPort & operator=(const SerialPort &other)
Assignment operator for SerialPort objects.
~SerialPort()
Destroys a SerialPort object.
bool usesXonXoffFlowControl() const
Gets whether the serial port currently uses XON/XOFF (software) flow control.
std::string getLastError() const
Gets the last error message.
bool close()
Close the serial port.
int getStopBits() const
Gets the current number of stop bits.
bool write(const std::string &data)
Writes data to the serial port.
bool setStopBits(int stopbits)
Sets the current number of stop bits.
bool setDataBits(int databits)
Sets the current number of data bits.
bool enableRtsCtsFlowControl(bool enabled)
Enables RTS/CTS (hardware) flow control on the serial port.
int getBaudRate() const
Gets the current baud rate.
bool enableXonXoffFlowControl(bool enabled)
Enables XON/XOFF (software) flow control on the serial port.
char getParity() const
Gets the current parity setting.
int getDataBits() const
Gets the current number of data bits.
bool setBaudRate(int baudrate)
Sets the current baud rate.
bool setParity(char parity)
Sets the current parity setting.
bool open()
Open the serial port.
bool usesRtsCtsFlowControl() const
Gets whether the serial port currently uses RTS/CTS (hardware) flow control.
SerialPort(const std::string &device, int baudrate=115200, char parity='N', int databits=8, int stopbits=1, bool xonxoff=false, bool rtscts=false, bool debug=false)
Constructs a SerialPort object.
Defines functionality for LibCyberRadio applications.
Definition App.h:24
BASIC_LIST_CONTAINER< std::string > BasicStringList
Type representing a list of strings.
Definition BasicList.h:25