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



Page generated by Doxygen 1.9.1 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at mar 26 may 2026 13:12:03 CEST