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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019