Main MRPT website > C++ reference for MRPT 1.9.9
CSerialPort.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "comms-precomp.h" // Precompiled headers
11 
12 #include <mrpt/comms/CSerialPort.h>
13 #include <mrpt/system/os.h>
14 #include <mrpt/utils/utils_defs.h>
15 
16 #if defined(MRPT_OS_LINUX) || defined(MRPT_OS_APPLE)
17 // Linux implementation: refer to
18 // http://www.easysw.com/~mike/serial/serial.html
19 
20 #include <stdio.h> /* Standard input/output definitions */
21 #include <string.h> /* String function definitions */
22 #include <unistd.h> /* UNIX standard function definitions */
23 #include <fcntl.h> /* File control definitions */
24 #include <errno.h> /* Error number definitions */
25 #include <sys/time.h> // gettimeofday
26 
27 #include <termios.h> /* POSIX terminal control definitions */
28 
29 #include <sys/ioctl.h> // FIONREAD,...
30 #include <signal.h>
31 
32 #ifdef HAVE_LINUX_SERIAL_H
33 #include <linux/serial.h>
34 #endif
35 
36 #include <map>
37 
38 #endif // Linux | Apple
39 
40 #ifdef MRPT_OS_WINDOWS
41 #include <windows.h>
42 #endif
43 
44 #include <thread>
45 
46 using namespace mrpt;
47 using namespace mrpt::comms;
48 using namespace mrpt::utils;
49 using namespace std;
50 using namespace std::literals;
51 
52 // ctor
53 CSerialPort::CSerialPort(const string& portName, bool openNow)
54  : m_serialName(portName),
55  m_totalTimeout_ms(0),
56  m_interBytesTimeout_ms(0),
57 #ifdef MRPT_OS_WINDOWS
58  hCOM(nullptr)
59 #else
60  hCOM(-1) // Not connected
61 #endif
62 {
63  if (openNow) open();
64 }
65 
66 // Default ctor
68  : m_serialName(),
69  m_totalTimeout_ms(0),
70  m_interBytesTimeout_ms(0),
71 #ifdef MRPT_OS_WINDOWS
72  hCOM(nullptr)
73 #else
74  hCOM(-1) // Not connected
75 #endif
76 {
77 }
78 
79 // Dtor:
81 {
82  if (isOpen()) close();
83 }
84 
85 /* -----------------------------------------------------
86  Open
87  ----------------------------------------------------- */
89 {
91 #ifdef MRPT_OS_WINDOWS
92  // Check name:
93  if (!m_serialName.size()) THROW_EXCEPTION("Serial port name is empty!!")
94 
95  // Is it COMX, X>4? -> "\\.\COMX"
96  if (tolower(m_serialName[0]) == 'c' && tolower(m_serialName[1]) == 'o' &&
97  tolower(m_serialName[2]) == 'm')
98  {
99  // Need to add "\\.\"?
100  if (m_serialName.size() > 4 || m_serialName[3] > '4')
101  m_serialName = std::string("\\\\.\\") + m_serialName;
102  }
103 
104  // Open the serial port:
105  if (INVALID_HANDLE_VALUE ==
106  (hCOM = CreateFileA(
107  m_serialName.c_str(), // Serial Port name
108  GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)))
109  {
110  hCOM = nullptr;
112  "Error trying to open serial port: %s", m_serialName.c_str());
113  }
114 
115  // Set recommended buffer sizes:
116  SetupComm(hCOM, 4096, 4096);
117 
118 #else
119  // Check name:
120  if (!m_serialName.size()) THROW_EXCEPTION("Serial port name is empty!!")
121  if (m_serialName[0] != '/') m_serialName = string("/dev/") + m_serialName;
122 
123  // Open the serial port:
124  // The O_NOCTTY flag tells UNIX that this program doesn't want to be the
125  // "controlling terminal" for that port.
126  // The O_NDELAY flag tells UNIX that this program doesn't care what state
127  // the DCD signal line is in - whether the other end of the port is up and
128  // running.
129  if (-1 ==
130  (hCOM = ::open(m_serialName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)))
132  "Error trying to open the serial port %s!!", m_serialName.c_str());
133 
134  // Clear flags:
135  fcntl(hCOM, F_SETFL, 0);
136 
137  //
138  // Start assembling the new port settings.
139  //
140  termios port_settings;
141  bzero(&port_settings, sizeof(port_settings));
142 
143  //
144  // Enable the receiver (CREAD) and ignore modem control lines
145  // (CLOCAL).
146  //
147  port_settings.c_cflag |= CREAD | CLOCAL;
148 
149  //
150  // Set the VMIN and VTIME parameters to zero by default. VMIN is
151  // the minimum number of characters for non-canonical read and
152  // VTIME is the timeout in deciseconds for non-canonical
153  // read. Setting both of these parameters to zero implies that a
154  // read will return immediately only giving the currently
155  // available characters.
156  //
157  port_settings.c_cc[VMIN] = 0;
158  port_settings.c_cc[VTIME] = 0;
159 
160  /*
161  * Flush the input buffer associated with the port.
162  */
163  if (tcflush(hCOM, TCIFLUSH) < 0)
164  THROW_EXCEPTION_FMT("Cannot flush serial port: %s", strerror(errno));
165 
166  /*
167  * Write the new settings to the port.
168  */
169  if (tcsetattr(hCOM, TCSANOW, &port_settings) < 0)
171  "Cannot set the new config to the serial port: %s",
172  strerror(errno));
173 
174  // Do NOT block on read.
175  fcntl(hCOM, F_SETFL, FNDELAY);
176 
177 // Success!
178 #endif
179  MRPT_END
180 }
181 
182 /* -----------------------------------------------------
183  isOpen
184  ----------------------------------------------------- */
186 {
187 #ifdef MRPT_OS_WINDOWS
188  return hCOM != nullptr;
189 #else
190  return hCOM != -1;
191 #endif
192 }
193 
194 /* -----------------------------------------------------
195  setConfig
196  ----------------------------------------------------- */
198  int baudRate, int parity, int bits, int nStopBits, bool enableFlowControl)
199 {
200  MRPT_START
201 #ifdef MRPT_OS_WINDOWS
202 
203  DCB dcb_conf;
204  dcb_conf.DCBlength = sizeof(DCB);
205 
206  // Port must be open!
207  if (!isOpen()) THROW_EXCEPTION("The serial port is not open");
208 
209  if (!GetCommState(hCOM, &dcb_conf))
210  THROW_EXCEPTION("Error retrieving COM state");
211 
212  //
213  // Apply baud rate
214  //
215  int BR;
216  switch (baudRate)
217  {
218  case 300:
219  BR = CBR_300;
220  break;
221  case 600:
222  BR = CBR_600;
223  break;
224  case 1200:
225  BR = CBR_1200;
226  break;
227  case 2400:
228  BR = CBR_2400;
229  break;
230  case 4800:
231  BR = CBR_4800;
232  break;
233  case 9600:
234  BR = CBR_9600;
235  break;
236  case 19200:
237  BR = CBR_19200;
238  break;
239  case 38400:
240  BR = CBR_38400;
241  break;
242  case 57600:
243  BR = CBR_57600;
244  break;
245  case 115200:
246  BR = CBR_115200;
247  break;
248  default:
249  BR = baudRate;
250  // THROW_EXCEPTION_FMT("Invalid desired baud rate value:
251  // %i",baudRate ) ;
252  break;
253  }
254 
255  dcb_conf.BaudRate = BR;
256 
257  dcb_conf.ByteSize = (BYTE)bits;
258  dcb_conf.Parity = (BYTE)parity;
259 
260  // stop bits:
261  switch (nStopBits)
262  {
263  case 1:
264  dcb_conf.StopBits = ONESTOPBIT;
265  break;
266  case 2:
267  dcb_conf.StopBits = TWOSTOPBITS;
268  break;
269  default:
270  THROW_EXCEPTION_FMT("Invalid number of stop bits: %i", nStopBits);
271  break;
272  }
273 
274  dcb_conf.fBinary = true;
275  dcb_conf.fParity = parity != 0;
276 
277  dcb_conf.fRtsControl =
278  enableFlowControl ? RTS_CONTROL_HANDSHAKE : RTS_CONTROL_DISABLE;
279  dcb_conf.fOutxCtsFlow = enableFlowControl;
280 
281  // Apply:
282  if (!SetCommState(hCOM, &dcb_conf))
283  THROW_EXCEPTION("Error changing COM state");
284 
285  // Assure:
286  if (!GetCommState(hCOM, &dcb_conf))
287  THROW_EXCEPTION("Error retrieving COM state");
288  if (((int)dcb_conf.BaudRate) != baudRate)
289  THROW_EXCEPTION("COM state verification after writing failed");
290 
291  m_baudRate = baudRate;
292 
293 #else
294  // Port must be open!
295  if (!isOpen()) THROW_EXCEPTION("The serial port is not open!");
296 
297  ASSERT_(baudRate > 0)
298 
299  //
300  // Apply baud rate
301  //
302  int BR;
303  bool special_rate = false;
304  switch (baudRate)
305  {
306  case 50:
307  BR = B50;
308  break;
309  case 75:
310  BR = B75;
311  break;
312  case 110:
313  BR = B110;
314  break;
315  case 134:
316  BR = B134;
317  break;
318  case 200:
319  BR = B200;
320  break;
321  case 300:
322  BR = B300;
323  break;
324  case 600:
325  BR = B600;
326  break;
327  case 1200:
328  BR = B1200;
329  break;
330  case 2400:
331  BR = B2400;
332  break;
333  case 4800:
334  BR = B4800;
335  break;
336  case 9600:
337  BR = B9600;
338  break;
339  case 19200:
340  BR = B19200;
341  break;
342  case 38400:
343  BR = B38400;
344  break;
345  case 57600:
346  BR = B57600;
347  break;
348  case 115200:
349  BR = B115200;
350  break;
351  case 230400:
352  BR = B230400;
353  break;
354 #ifdef B460800
355  case 460800:
356  BR = B460800;
357  break;
358 #endif
359 #ifdef B500000
360  case 500000:
361  BR = B500000;
362  break;
363 #endif
364 #ifdef B4000000
365  case 576000:
366  BR = B576000;
367  break;
368  case 921600:
369  BR = B921600;
370  break;
371  case 1000000:
372  BR = B1000000;
373  break;
374  case 1152000:
375  BR = B1152000;
376  break;
377  case 1500000:
378  BR = B1500000;
379  break;
380  case 2000000:
381  BR = B2000000;
382  break;
383  case 2500000:
384  BR = B2500000;
385  break;
386  case 3000000:
387  BR = B3000000;
388  break;
389  case 3500000:
390  BR = B3500000;
391  break;
392  case 4000000:
393  BR = B4000000;
394  break;
395 #endif
396  default:
397 #ifdef HAVE_LINUX_SERIAL_H
398  special_rate = true;
399 #else
400  BR = baudRate; // This is all we can try in that case...
401 #endif
402  break;
403  }
404 
405  if (special_rate)
406  {
407 #ifdef HAVE_LINUX_SERIAL_H
408  struct serial_struct serial;
409  if (ioctl(hCOM, TIOCGSERIAL, &serial) < 0)
410  THROW_EXCEPTION("error on TIOCGSERIAL ioctl");
411 
412  serial.custom_divisor = serial.baud_base / baudRate;
413  if (!serial.custom_divisor) serial.custom_divisor = 1;
414  const int actual_rate = serial.baud_base / serial.custom_divisor;
415 
416  serial.flags &= ~ASYNC_SPD_MASK;
417  serial.flags |= ASYNC_SPD_CUST; // We want to use our CUSTOM divisor.
418 
419  if (ioctl(hCOM, TIOCSSERIAL, &serial) < 0)
420  THROW_EXCEPTION("error on TIOCSSERIAL ioctl");
421 
422  BR = B38400; // It seems that 38400 means to the driver here to use our
423  // custom divisor
424 
425  if (actual_rate != baudRate)
426  cout << "[CSerialPort::setConfig] Setting custom baud rate to "
427  << actual_rate << ", the closer I can make to " << baudRate
428  << endl;
429 #else
430  THROW_EXCEPTION("Custom serial port baud rates require linux/serial.h");
431 #endif
432  } // end specialRate
433  else
434  {
435  // Normal baudrate: Just in case, undo possible custom divisors:
436  //#ifdef HAVE_LINUX_SERIAL_H
437  // struct serial_struct serial;
438  // if (ioctl(hCOM, TIOCGSERIAL, &serial) < 0)
439  // THROW_EXCEPTION("error on TIOCGSERIAL ioctl");
440  //
441  // serial.flags &= ~ASYNC_SPD_MASK;
442  //
443  // if (ioctl(hCOM, TIOCSSERIAL, &serial) < 0)
444  // THROW_EXCEPTION("error on TIOCSSERIAL ioctl");
445  //#endif
446  }
447 
448  termios port_settings;
449  if (tcgetattr(hCOM, &port_settings) < 0)
451  "Cannot get the current settings: %s", strerror(errno));
452 
453  if ((cfsetispeed(&port_settings, BR) < 0) ||
454  (cfsetospeed(&port_settings, BR) < 0))
456  "Cannot change baudRate in setting structure: %s", strerror(errno));
457 
458  //
459  // Set the character size.
460  //
461  port_settings.c_cflag &= ~CSIZE;
462  switch (bits)
463  {
464  case 5:
465  port_settings.c_cflag |= CS5;
466  break;
467  case 6:
468  port_settings.c_cflag |= CS6;
469  break;
470  case 7:
471  port_settings.c_cflag |= CS7;
472  break;
473  case 8:
474  port_settings.c_cflag |= CS8;
475  break;
476  default:
477  THROW_EXCEPTION_FMT("Invalid character size: %i", bits);
478  break;
479  }
480 
481  // parity 0:No parity, 1:Odd, 2:Even
482  switch (parity)
483  {
484  case 2:
485  port_settings.c_cflag |= PARENB;
486  port_settings.c_cflag &= ~PARODD;
487  port_settings.c_iflag |= INPCK;
488  break;
489  case 1:
490  port_settings.c_cflag |= (PARENB | PARODD);
491  port_settings.c_iflag |= INPCK;
492  break;
493  case 0:
494  port_settings.c_cflag &= ~(PARENB);
495  port_settings.c_iflag |= IGNPAR;
496  break;
497  default:
498  THROW_EXCEPTION_FMT("Invalid parity selection: %i", parity);
499  break;
500  }
501 
502  // stop bits:
503  switch (nStopBits)
504  {
505  case 1:
506  port_settings.c_cflag &= ~(CSTOPB);
507  break;
508  case 2:
509  port_settings.c_cflag |= CSTOPB;
510  break;
511  default:
512  THROW_EXCEPTION_FMT("Invalid number of stop bits: %i", nStopBits);
513  break;
514  }
515 
516  //
517  // Set the flow control.
518  //
519  if (enableFlowControl)
520  {
521  // RTS/CTS ON:
522  port_settings.c_cflag |= CRTSCTS;
523  }
524  else
525  {
526  // none
527  port_settings.c_cflag &= ~(CRTSCTS);
528  }
529 
530  /* Write the new settings to the port.
531  */
532  if (tcsetattr(hCOM, TCSANOW, &port_settings) < 0)
533  THROW_EXCEPTION_FMT("Cannot set the new settings: %s", strerror(errno));
534 
535  // Check:
536  termios port_settings_verif;
537  if (tcgetattr(hCOM, &port_settings_verif) < 0)
539  "Cannot get the settings to verify: %s", strerror(errno));
540 
541 #if 0
542  if (!special_rate)
543  {
544  if (port_settings_verif.c_ispeed != port_settings.c_ispeed)
545  THROW_EXCEPTION("Verification of changed baudrate(i) failed");
546  if (port_settings_verif.c_ospeed != port_settings.c_ospeed)
547  THROW_EXCEPTION("Verification of changed baudrate(i) failed");
548  }
549 
550  if (port_settings_verif.c_cflag != port_settings.c_cflag)
551  THROW_EXCEPTION("Verification of serial port flags failed");
552 #endif
553 
554  m_baudRate = baudRate;
555 #endif
556  MRPT_END
557 }
558 
560  int ReadIntervalTimeout, int ReadTotalTimeoutMultiplier,
561  int ReadTotalTimeoutConstant, int WriteTotalTimeoutMultiplier,
562  int WriteTotalTimeoutConstant)
563 {
564  MRPT_START
565 #ifdef MRPT_OS_WINDOWS
566  COMMTIMEOUTS timeouts;
567 
568  // Port must be open!
569  if (!isOpen()) THROW_EXCEPTION("The COM port is not open");
570 
571  // Config:
572  timeouts.ReadIntervalTimeout =
573  ReadIntervalTimeout; // Milisegundos entre dos bytes recibidos
574  timeouts.ReadTotalTimeoutMultiplier =
575  ReadTotalTimeoutMultiplier; // Milisegundos de espera por cada byte a
576  // recibir
577  timeouts.ReadTotalTimeoutConstant =
578  ReadTotalTimeoutConstant; // Milisegundos de espera en cada operacion
579  // de recepcion
580  timeouts.WriteTotalTimeoutMultiplier =
581  WriteTotalTimeoutMultiplier; // Timeout de escritura no usado
582  timeouts.WriteTotalTimeoutConstant =
583  WriteTotalTimeoutConstant; // Timeout de escritura no usado
584 
585  if (!SetCommTimeouts(hCOM, &timeouts))
586  THROW_EXCEPTION("Error changing COM port timeout config");
587 
588 // Success
589 #else
590  // Port must be open!
591  if (!isOpen()) THROW_EXCEPTION("The serial port is not open!");
592 
593  // Save variables which are used in other methods:
594  m_totalTimeout_ms = ReadTotalTimeoutConstant;
595  m_interBytesTimeout_ms = ReadIntervalTimeout;
596 
597  // http://www.unixwiz.net/techtips/termios-vmin-vtime.html
598  // VMIN & VTIME
599  termios port_settings;
600  if (tcgetattr(hCOM, &port_settings) < 0)
602  "Cannot get the current settings: %s", strerror(errno));
603 
604  // We set VMIN=0 and VTIME=ReadIntervalTimeout (in thenth of seconds)
605  //
606  port_settings.c_cc[VMIN] = 0;
607  port_settings.c_cc[VTIME] = max(1, ReadTotalTimeoutConstant / 100);
608 
609  /* Write the new settings to the port.
610  */
611  if (tcsetattr(hCOM, TCSANOW, &port_settings) < 0)
612  THROW_EXCEPTION_FMT("Cannot set the new settings: %s", strerror(errno));
613 #endif
614  MRPT_END
615 }
616 
617 /* -----------------------------------------------------
618  Close
619  ----------------------------------------------------- */
621 {
622  MRPT_START
623 #ifdef MRPT_OS_WINDOWS
624  if (hCOM) CloseHandle(hCOM);
625  hCOM = nullptr;
626 #else
627  if (hCOM < 0) return; // Already closed
628 
629  // PosixSignalDispatcher& signal_dispatcher =
630  // PosixSignalDispatcher::Instance() ;
631  // signal_dispatcher.DetachHandler( SIGIO, *this ) ;
632  // Close the serial port file descriptor.
633  ::close(hCOM);
634 
635  hCOM = -1; // Means the port is closed
636 #endif
637  MRPT_END
638 }
639 
640 /* -----------------------------------------------------
641  read
642  ----------------------------------------------------- */
643 size_t CSerialPort::Read(void* Buffer, size_t Count)
644 {
645  MRPT_START
646 #ifdef MRPT_OS_WINDOWS
647  // Port must be open!
648  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
649 
650  DWORD actuallyRead;
651 
652  if (!ReadFile(
653  hCOM, // Handle,
654  Buffer, // Buffer
655  (DWORD)Count, // Max expected bytes
656  &actuallyRead, // Actually read bytes
657  nullptr))
658  THROW_EXCEPTION("Error reading from port!");
659 
660  return actuallyRead;
661 #else
662 
663  // Port must be open!
664  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
665 
666  if (!Count) return 0;
667 
668  // Use the "m_totalTimeout_ms" global timeout
669  // and the "m_interBytesTimeout_ms" for inter-bytes:
670  m_timer.Tic();
671 
672  size_t alreadyRead = 0;
673  int leftTime = m_totalTimeout_ms - (int)(m_timer.Tac() * 1000);
674 
675  while (alreadyRead < Count && leftTime >= 0)
676  {
677  // Bytes waiting in the queue?
678  // Check if we are still connected or there is an error...
679  int waiting_bytes = 0;
680  if (ioctl(hCOM, FIONREAD, &waiting_bytes) < 0)
681  {
682  if (errno == EIO)
683  {
684  // The port has been disconnect (for USB ports)
685  this->close();
686  return alreadyRead;
687  }
688  }
689 
690  // Are there any bytes??
691  int nRead = 0;
692 
693  if (waiting_bytes > 0)
694  {
695  int nToRead = min((size_t)waiting_bytes, Count - alreadyRead);
696 
697  if ((nRead = ::read(hCOM, ((char*)Buffer) + alreadyRead, nToRead)) <
698  0)
699  {
700  cerr << "[CSerialPort] Error reading from port..." << endl;
701  }
702 
703  alreadyRead += nRead;
704  }
705  else
706  {
707  // Nope...
708  }
709 
710  if (alreadyRead < Count)
711  {
712  // Wait 1 more ms for new data to arrive.
713  std::this_thread::sleep_for(1ms);
714  }
715 
716  // Reset interbytes timer:
717  leftTime = m_totalTimeout_ms - (int)(m_timer.Tac() * 1000);
718  if (nRead > 0) leftTime = max(leftTime, m_interBytesTimeout_ms);
719  }
720 
721  // cout << "READ DONE: "<< alreadyRead << endl;
722  return alreadyRead;
723 #endif
724  MRPT_END
725 }
726 
727 /** Reads one text line from the serial port in POSIX "canonical mode".
728  * This method reads from the serial port until one of the characters in \a
729  * eol are found.
730  */
732  const int total_timeout_ms, bool* out_timeout, const char* eol_chars)
733 {
735  // Calling ::ReadBuffer() many times would be even worse, so replicate its
736  // code here:
737 
738  ASSERT_(eol_chars != nullptr)
739 
740  // Port must be open!
741  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
742 
743  if (out_timeout) *out_timeout = false; // Will be set to true on timeout
744 
745  m_timer.Tic();
746  string receivedStr; // Rx buffer
747 
748  while (total_timeout_ms < 0 || (m_timer.Tac() * 1e3 < total_timeout_ms))
749  {
750 #ifdef MRPT_OS_WINDOWS
751  // Read just 1 byte:
752  char buf[1];
753 
754  DWORD actuallyRead;
755  if (!ReadFile(
756  hCOM, // Handle,
757  buf, // Buffer
758  1, // Max expected bytes
759  &actuallyRead, // Actually read bytes
760  nullptr))
761  THROW_EXCEPTION("Error reading from port!");
762 
763  if (actuallyRead)
764  { // Append to string, if it's not a control char:
765  if (!strchr(eol_chars, buf[0]))
766  receivedStr.push_back(buf[0]);
767  else
768  { // end of string!
769  return receivedStr;
770  }
771  }
772  // If we are still here, string is not finished:
773  std::this_thread::sleep_for(
774  1ms); // Wait 1 more ms for new data to arrive.
775 #else
776  // Bytes waiting in the queue?
777  // Check if we are still connected or there is an error...
778  int waiting_bytes = 0;
779  if (ioctl(hCOM, FIONREAD, &waiting_bytes) < 0)
780  {
781  if (errno == EIO)
782  { // The port has been disconnect (for USB ports)
783  this->close();
784  THROW_EXCEPTION("Error reading port before end of line")
785  }
786  }
787 
788  // Are there any bytes??
789  int nRead = 0;
790  if (waiting_bytes > 0)
791  {
792  // Read just 1 byte:
793  char buf[1];
794  if ((nRead = ::read(hCOM, buf, 1)) < 0)
795  {
796  cerr << "[CSerialPort] Error reading from port..." << endl;
797  }
798  if (nRead)
799  { // Append to string, if it's not a control char:
800  if (!strchr(eol_chars, buf[0]))
801  receivedStr.push_back(buf[0]);
802  else
803  { // end of string!
804  return receivedStr;
805  }
806  }
807  }
808  else
809  {
810  // we decide to move the sleep here to satisfy realtime requirement
811  // in the case where we are waiting a n-length string at a frequency
812  // greater than 1/n...
813  std::this_thread::sleep_for(
814  1ms); // Wait 1 more ms for new data to arrive.
815  }
816 // If we are still here, string is not finished:
817 #endif
818  }
819 
820  // Timeout:
821  if (out_timeout) *out_timeout = true;
822  return receivedStr;
824 }
825 
826 size_t CSerialPort::Write(const void* Buffer, size_t Count)
827 {
828  MRPT_START
829  // Port must be open!
830  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
831 
832 #ifdef MRPT_OS_WINDOWS
833  DWORD actuallyWritten;
834  if (!WriteFile(hCOM, Buffer, (DWORD)Count, &actuallyWritten, nullptr))
835  THROW_EXCEPTION("Error writing to port!");
836  return actuallyWritten;
837 #else
838  // Write the data to the serial port. Keep retrying if EAGAIN
839  // error is received.
840 
841  /** \todo Add support for write timeout here */
842  struct timeval start, end;
843  int num_of_bytes_written = -1;
844  size_t total_bytes_written = 0;
845  do
846  {
847  gettimeofday(&start, nullptr);
848  num_of_bytes_written = write(
849  hCOM, reinterpret_cast<const char*>(Buffer) + total_bytes_written,
850  Count - total_bytes_written);
851  // cout << "wr: " << num_of_bytes_written << " tot: " <<
852  // total_bytes_written << " of " << Count << " err: " << errno << endl;
853  if (num_of_bytes_written > 0)
854  total_bytes_written += num_of_bytes_written;
855 
856  if (num_of_bytes_written < (int)Count)
857  {
858  // JL: These few lines are from the Player/Stage project:
859 
860  // need to do this sort of busy wait to ensure the right timing
861  // although I've noticed you will get some anamolies that are
862  // in the ms range; this could be a problem...
863  int usecs;
864  do
865  {
866  gettimeofday(&end, nullptr);
867  usecs = (end.tv_sec - start.tv_sec) * 1000000 +
868  (end.tv_usec - start.tv_usec);
869  } while (usecs < 60);
870  // std::this_thread::sleep_for(1ms); // we'll continue writting is a
871  // ms.
872  }
873  } while ((total_bytes_written < Count) && (!errno || EAGAIN == errno));
874  //
875  if (num_of_bytes_written <
876  0) // This means we exit the loop due to a bad "errno".
878  "Error writing data to the serial port: %s", strerror(errno));
879 
880  // Make sure the queue is drained
881  // Synchronous IO doesnt always work
882  ::tcdrain(hCOM);
883 
884  // OK:
885  return total_bytes_written;
886 #endif
887 
888  MRPT_END
889 }
890 
892 {
893  MRPT_START
894 
895  // Port must be open!
896  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
897 
898 #ifdef MRPT_OS_WINDOWS
899  if (!PurgeComm(
900  hCOM,
901  PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
902  THROW_EXCEPTION("Error during COM port purge");
903 #else
904  /* Flush the input buffer associated with the port. */
905  if (tcflush(hCOM, TCIFLUSH) < 0)
906  THROW_EXCEPTION_FMT("Cannot flush serial port: %s", strerror(errno));
907 #endif
908 
909  MRPT_END
910 }
size_t Write(const void *Buffer, size_t Count)
Implements the virtual method responsible for writing to the stream.
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
#define min(a, b)
std::string ReadString(const int total_timeout_ms=-1, bool *out_timeout=nullptr, const char *eol_chars="\")
Reads one text line from the serial port in POSIX "canonical mode".
#define THROW_EXCEPTION(msg)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
void open()
Open the port.
Definition: CSerialPort.cpp:88
STL namespace.
void setConfig(int baudRate, int parity=0, int bits=8, int nStopBits=1, bool enableFlowControl=false)
Changes the configuration of the port.
void Tic()
Starts the stopwatch.
Definition: CTicTac.cpp:82
#define MRPT_TRY_END
mrpt::utils::CTicTac m_timer
Used only in ReadString.
Definition: CSerialPort.h:219
#define MRPT_END
GLuint GLuint end
Definition: glext.h:3528
bool isOpen() const
Returns if port has been correctly open.
void purgeBuffers()
Purge tx and rx buffers.
GLsizei const GLchar ** string
Definition: glext.h:4101
void close()
Close the port.
void setTimeouts(int ReadIntervalTimeout, int ReadTotalTimeoutMultiplier, int ReadTotalTimeoutConstant, int WriteTotalTimeoutMultiplier, int WriteTotalTimeoutConstant)
Changes the timeouts of the port, in milliseconds.
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::string m_serialName
The complete name of the serial port device (i.e.
Definition: CSerialPort.h:214
double Tac()
Stops the stopwatch.
Definition: CTicTac.cpp:97
#define MRPT_TRY_START
#define ASSERT_(f)
Serial and networking devices and utilities.
GLuint start
Definition: glext.h:3528
CSerialPort()
Default constructor: it does not open any port - later you must call "setSerialPortName" and then "op...
Definition: CSerialPort.cpp:67
int hCOM
The file handle (-1: Not open)
Definition: CSerialPort.h:228
size_t Read(void *Buffer, size_t Count)
Implements the virtual method responsible for reading from the stream - Unlike CStream::ReadBuffer, this method will not raise an exception on zero bytes read, as long as there is not any fatal error in the communications.
virtual ~CSerialPort()
Destructor.
Definition: CSerialPort.cpp:80



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019