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 
22 namespace 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  {
83  Debuggable::operator=(other);
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 
int getDataBits() const
Gets the current number of data bits.
Definition: SerialPort.cpp:481
int getStopBits() const
Gets the current number of stop bits.
Definition: SerialPort.cpp:537
bool setBaudRate(int baudrate)
Sets the current baud rate.
Definition: SerialPort.cpp:308
~SerialPort()
Destroys a SerialPort object.
Definition: SerialPort.cpp:53
bool setDataBits(int databits)
Sets the current number of data bits.
Definition: SerialPort.cpp:486
Debuggable & operator=(const Debuggable &other)
Assignment operator for Debuggable objects.
Definition: Debuggable.cpp:59
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.
Definition: SerialPort.cpp:25
Class that manages communications with a serial port.
Definition: SerialPort.h:28
bool write(const std::string &data)
Writes data to the serial port.
Definition: SerialPort.cpp:271
char getParity() const
Gets the current parity setting.
Definition: SerialPort.cpp:432
Class that supports debug output.
Definition: Debuggable.h:38
bool setStopBits(int stopbits)
Sets the current number of stop bits.
Definition: SerialPort.cpp:542
bool open()
Open the serial port.
Definition: SerialPort.cpp:104
std::string getLastError() const
Gets the last error message.
Definition: SerialPort.cpp:659
bool enableXonXoffFlowControl(bool enabled)
Enables XON/XOFF (software) flow control on the serial port.
Definition: SerialPort.cpp:586
SerialPort & operator=(const SerialPort &other)
Assignment operator for SerialPort objects.
Definition: SerialPort.cpp:81
virtual int debug(const char *format,...)
Outputs debug information.
Definition: Debuggable.cpp:95
BASIC_LIST_CONTAINER< std::string > BasicStringList
Type representing a list of strings.
Definition: BasicList.h:25
Defines functionality for LibCyberRadio applications.
Definition: App.h:23
bool usesRtsCtsFlowControl() const
Gets whether the serial port currently uses RTS/CTS (hardware) flow control.
Definition: SerialPort.cpp:620
virtual const char * debugBool(bool x)
Gets a debug output string for a Boolean value.
Definition: Debuggable.cpp:126
bool setParity(char parity)
Sets the current parity setting.
Definition: SerialPort.cpp:437
bool close()
Close the serial port.
Definition: SerialPort.cpp:212
bool usesXonXoffFlowControl() const
Gets whether the serial port currently uses XON/XOFF (software) flow control.
Definition: SerialPort.cpp:581
bool enableRtsCtsFlowControl(bool enabled)
Enables RTS/CTS (hardware) flow control on the serial port.
Definition: SerialPort.cpp:625
static std::string Join(const BasicStringList &vec, const std::string &sep)
Joins a list of string tokens, concatenating them into a single string.
Definition: Pythonesque.cpp:98
std::string read()
Reads data from the serial port.
Definition: SerialPort.cpp:231
int getBaudRate() const
Gets the current baud rate.
Definition: SerialPort.cpp:303