JeVois  1.5
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Serial.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #include <jevois/Core/Serial.H>
19 
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 
25 // ######################################################################
26 jevois::Serial::Serial(std::string const & instance, jevois::UserInterface::Type type) :
27  jevois::UserInterface(instance), itsDev(-1), itsWriteOverflowCounter(0), itsType(type)
28 { }
29 
30 // ######################################################################
32 {
33  // Open the port, non-blocking mode by default:
34  if (itsDev != -1) ::close(itsDev);
35  itsDev = ::open(jevois::serial::devname::get().c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
36  if (itsDev == -1) LFATAL("Could not open serial port [" << jevois::serial::devname::get() << ']');
37 
38  // Save current state
39  if (tcgetattr(itsDev, &itsSavedState) == -1) LFATAL("Failed to save current state");
40 
41  // reset all the flags
42  ////if (fcntl(itsDev, F_SETFL, 0) == -1) LFATAL("Failed to reset flags");
43 
44  // Get the current option set:
45  termios options = { };
46  if (tcgetattr(itsDev, &options) == -1) LFATAL("Failed to get options");
47 
48  // get raw input from the port
49  options.c_cflag |= ( CLOCAL // ignore modem control lines
50  | CREAD ); // enable the receiver
51 
52  options.c_iflag &= ~( IGNBRK // ignore BREAK condition on input
53  | BRKINT // If IGNBRK is not set, generate SIGINT on BREAK condition, else read BREAK as \0
54  | PARMRK
55  | ISTRIP // strip off eighth bit
56  | INLCR // donot translate NL to CR on input
57  | IGNCR // ignore CR
58  | ICRNL // translate CR to newline on input
59  | IXON // disable XON/XOFF flow control on output
60  );
61 
62  // disable implementation-defined output processing
63  options.c_oflag &= ~OPOST;
64  options.c_lflag &= ~(ECHO // dont echo i/p chars
65  | ECHONL // do not echo NL under any circumstance
66  | ICANON // disable cannonical mode
67  | ISIG // do not signal for INTR, QUIT, SUSP etc
68  | IEXTEN // disable platform dependent i/p processing
69  );
70 
71  // Set the baudrate:
72  unsigned int rate;
73  switch (jevois::serial::baudrate::get())
74  {
75  case 4000000: rate = B4000000; break;
76  case 3500000: rate = B3500000; break;
77  case 3000000: rate = B3000000; break;
78  case 2500000: rate = B2500000; break;
79  case 2000000: rate = B2000000; break;
80  case 1500000: rate = B1500000; break;
81  case 1152000: rate = B1152000; break;
82  case 1000000: rate = B1000000; break;
83  case 921600: rate = B921600; break;
84  case 576000: rate = B576000; break;
85  case 500000: rate = B500000; break;
86  case 460800: rate = B460800; break;
87  case 230400: rate = B230400; break;
88  case 115200: rate = B115200; break;
89  case 57600: rate = B57600; break;
90  case 38400: rate = B38400; break;
91  case 19200: rate = B19200; break;
92  case 9600: rate = B9600; break;
93  case 4800: rate = B4800; break;
94  case 2400: rate = B2400; break;
95  case 1200: rate = B1200; break;
96  case 600: rate = B600; break;
97  case 300: rate = B300; break;
98  case 110: rate = B110; break;
99  case 0: rate = B0; break;
100  default: LFATAL("Invalid baud rate " <<jevois::serial::baudrate::get());
101  }
102 
103  cfsetispeed(&options, rate);
104  cfsetospeed(&options, rate);
105 
106  // Parse the serial format string:
107  std::string const format = jevois::serial::format::get();
108  if (format.length() != 3) LFATAL("Incorrect format string: " << format);
109 
110  // Set the number of bits:
111  options.c_cflag &= ~CSIZE; // mask off the 'size' bits
112 
113  switch (format[0])
114  {
115  case '5': options.c_cflag |= CS5; break;
116  case '6': options.c_cflag |= CS6; break;
117  case '7': options.c_cflag |= CS7; break;
118  case '8': options.c_cflag |= CS8; break;
119  default: LFATAL("Invalid charbits: " << format[0] << " (should be 5..8)");
120  }
121 
122  // Set parity option:
123  options.c_cflag &= ~(PARENB | PARODD);
124 
125  switch(format[1])
126  {
127  case 'N': break;
128  case 'E': options.c_cflag |= PARENB; break;
129  case 'O': options.c_cflag |= (PARENB | PARODD); break;
130  default: LFATAL("Invalid parity: " << format[1] << " (should be N,E,O)");
131  }
132 
133  // Set the stop bits option:
134  options.c_cflag &= ~CSTOPB;
135  switch(format[2])
136  {
137  case '1': break;
138  case '2': options.c_cflag |= CSTOPB; break;
139  default: LFATAL("Invalid stopbits: " << format[2] << " (should be 1..2)");
140  }
141 
142  // Set the flow control:
143  options.c_cflag &= ~CRTSCTS;
144  options.c_iflag &= ~(IXON | IXANY | IXOFF);
145 
146  if (jevois::serial::flowsoft::get()) options.c_iflag |= (IXON | IXANY | IXOFF);
147  if (jevois::serial::flowhard::get()) options.c_cflag |= CRTSCTS;
148 
149  // Set all the options now:
150  if (tcsetattr(itsDev, TCSANOW, &options) == -1) LFATAL("Failed to set port options");
151  LINFO("Serial driver ready on " << jevois::serial::devname::get());
152 }
153 
154 // ######################################################################
156 {
157  if (itsDev != -1)
158  {
159  sendBreak();
160  if (tcsetattr(itsDev, TCSANOW, &itsSavedState) == -1) LERROR("Failed to restore serial port state -- IGNORED");
161  ::close(itsDev);
162  itsDev = -1;
163  }
164 }
165 
166 // ######################################################################
167 void jevois::Serial::setBlocking(bool blocking, std::chrono::milliseconds const & timeout)
168 {
169  std::lock_guard<std::mutex> _(itsMtx);
170 
171  int flags = fcntl(itsDev, F_GETFL, 0);
172  if (flags == -1) LFATAL("Cannot get flags");
173  if (blocking) flags &= (~O_NONBLOCK); else flags |= O_NONBLOCK;
174  if (fcntl(itsDev, F_SETFL, flags) == -1) LFATAL("Cannot set flags");
175 
176  // If blocking, set a timeout on the descriptor:
177  if (blocking)
178  {
179  termios options;
180  if (tcgetattr(itsDev, &options) == -1) LFATAL("Failed to get options");
181  options.c_cc[VMIN] = 0;
182  options.c_cc[VTIME] = timeout.count() / 100; // vtime is in tenths of second
183  if (tcsetattr(itsDev, TCSANOW, &options) == -1) LFATAL("Failed to set port options");
184  }
185 }
186 
187 // ######################################################################
188 void jevois::Serial::toggleDTR(std::chrono::milliseconds const & dur)
189 {
190  std::lock_guard<std::mutex> _(itsMtx);
191 
192  struct termios tty, old;
193 
194  if (tcgetattr(itsDev, &tty) == -1 || tcgetattr(itsDev, &old) == -1) LFATAL("Failed to get attributes");
195 
196  cfsetospeed(&tty, B0);
197  cfsetispeed(&tty, B0);
198 
199  if (tcsetattr(itsDev, TCSANOW, &tty) == -1) LFATAL("Failed to set attributes");
200 
201  std::this_thread::sleep_for(dur);
202 
203  if (tcsetattr(itsDev, TCSANOW, &old) == -1) LFATAL("Failed to restore attributes");
204 }
205 
206 // ######################################################################
208 {
209  std::lock_guard<std::mutex> _(itsMtx);
210 
211  // Send a Hangup to the port
212  tcsendbreak(itsDev, 0);
213 }
214 
215 // ######################################################################
216 int jevois::Serial::read(void * buffer, const int nbytes)
217 {
218  std::lock_guard<std::mutex> _(itsMtx);
219 
220  int n = ::read(itsDev, buffer, nbytes);
221 
222  if (n == -1) throw std::runtime_error("Serial: Read error");
223  if (n == 0) throw std::runtime_error("Serial: Read timeout");
224 
225  return n;
226 }
227 
228 // ######################################################################
229 int jevois::Serial::read2(void * buffer, const int nbytes)
230 {
231  std::lock_guard<std::mutex> _(itsMtx);
232 
233  int n = ::read(itsDev, buffer, nbytes);
234 
235  if (n == -1)
236  {
237  if (errno == EAGAIN) return false; // no new char available
238  else throw std::runtime_error("Serial: Read error");
239  }
240 
241  if (n == 0) return false; // no new char available
242 
243  return n;
244 }
245 
246 // ######################################################################
247 bool jevois::Serial::readSome(std::string & str)
248 {
249  std::lock_guard<std::mutex> _(itsMtx);
250 
251  unsigned char c;
252 
253  while (true)
254  {
255  int n = ::read(itsDev, reinterpret_cast<char *>(&c), 1);
256 
257  if (n == -1)
258  {
259  if (errno == EAGAIN) return false; // no new char available
260  else throw std::runtime_error("Serial: Read error");
261  }
262 
263  if (n == 0) return false; // no new char available
264 
265  switch (jevois::serial::linestyle::get())
266  {
267  case jevois::serial::LineStyle::LF:
268  if (c == '\n') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
269  else itsPartialString += c;
270  break;
271 
272  case jevois::serial::LineStyle::CR:
273  if (c == '\r') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
274  else itsPartialString += c;
275  break;
276 
277  case jevois::serial::LineStyle::CRLF:
278  if (c == '\n') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
279  else if (c != '\r') itsPartialString += c;
280  break;
281 
282  case jevois::serial::LineStyle::Zero:
283  if (c == 0x00) { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
284  else itsPartialString += c;
285  break;
286 
287  case jevois::serial::LineStyle::Sloppy: // Return when we receive first separator, ignore others
288  if (c == '\r' || c == '\n' || c == 0x00 || c == 0xd0)
289  {
290  if (itsPartialString.empty() == false)
291  { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
292  }
293  else itsPartialString += c;
294  break;
295  }
296  }
297 }
298 
299 // ######################################################################
301 {
302  std::lock_guard<std::mutex> _(itsMtx);
303 
304  std::string str; unsigned char c;
305 
306  while (true)
307  {
308  int n = ::read(itsDev, reinterpret_cast<char *>(&c), 1);
309 
310  if (n == -1)
311  {
312  if (errno == EAGAIN) std::this_thread::sleep_for(std::chrono::milliseconds(2));
313  else throw std::runtime_error("Serial: Read error");
314  }
315  else if (n == 0)
316  std::this_thread::sleep_for(std::chrono::milliseconds(2)); // no new char available
317  else
318  {
319  switch (jevois::serial::linestyle::get())
320  {
321  case jevois::serial::LineStyle::LF: if (c == '\n') return str; else str += c; break;
322 
323  case jevois::serial::LineStyle::CR: if (c == '\r') return str; else str += c; break;
324 
325  case jevois::serial::LineStyle::CRLF: if (c == '\n') return str; else if (c != '\r') str += c; break;
326 
327  case jevois::serial::LineStyle::Zero: if (c == 0x00) return str; else str += c; break;
328 
329  case jevois::serial::LineStyle::Sloppy: // Return when we receive first separator, ignore others
330  if (c == '\r' || c == '\n' || c == 0x00 || c == 0xd0) { if (str.empty() == false) return str; }
331  else str += c;
332  break;
333  }
334  }
335  }
336 }
337 
338 // ######################################################################
339 void jevois::Serial::writeString(std::string const & str)
340 {
341  std::string fullstr(str);
342 
343  switch (jevois::serial::linestyle::get())
344  {
345  case jevois::serial::LineStyle::CR: fullstr += '\r'; break;
346  case jevois::serial::LineStyle::LF: fullstr += '\n'; break;
347  case jevois::serial::LineStyle::CRLF: fullstr += "\r\n"; break;
348  case jevois::serial::LineStyle::Zero: fullstr += '\0'; break;
349  case jevois::serial::LineStyle::Sloppy: fullstr += "\r\n"; break;
350  }
351 
352  this->write(fullstr.c_str(), fullstr.length());
353 }
354 
355 // ######################################################################
356 void jevois::Serial::write(void const * buffer, const int nbytes)
357 {
358  std::lock_guard<std::mutex> _(itsMtx);
359 
360  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer); int iter = 0;
361  while (ndone < nbytes && iter++ < 10)
362  {
363  int n = ::write(itsDev, b + ndone, nbytes - ndone);
364  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
365 
366  // If we did not write the whole thing, the serial port is saturated, we need to wait a bit:
367  ndone += n;
368  if (ndone < nbytes) tcdrain(itsDev);
369  }
370 
371  if (ndone < nbytes)
372  {
373  // If we had a serial overflow, we need to let the user know, but how, since the serial is overflowed already? Let's
374  // first throttle down big time, and then we throw once in a while:
375  std::this_thread::sleep_for(std::chrono::milliseconds(100));
376 
377  tcdrain(itsDev);
378 
379  // Report the overflow once in a while:
380  ++itsWriteOverflowCounter; if (itsWriteOverflowCounter > 100) itsWriteOverflowCounter = 0;
381  if (itsWriteOverflowCounter == 1)
382  throw std::overflow_error("Serial write overflow: need to reduce amount ot serial writing");
383 
384  // Note how we are otherwise just ignoring the overflow and hence dropping data.
385  }
386  else itsWriteOverflowCounter = 0;
387 }
388 
389 // ######################################################################
390 void jevois::Serial::writeNoCheck(void const * buffer, const int nbytes)
391 {
392  std::lock_guard<std::mutex> _(itsMtx);
393 
394  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer); int iter = 0;
395  while (ndone < nbytes && iter++ < 50)
396  {
397  int n = ::write(itsDev, b + ndone, nbytes - ndone);
398  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
399  ndone += n;
400  }
401 
402  if (ndone < nbytes)
403  {
404  // If after a number of iterations there are still unbuffered bytes, flush the output buffer
405  if (tcflush(itsDev, TCOFLUSH) != 0) LDEBUG("Serial flushOut error -- IGNORED");
406 
407  }
408 }
409 
410 // ######################################################################
412 {
413  std::lock_guard<std::mutex> _(itsMtx);
414 
415  // Flush the input
416  if (tcflush(itsDev, TCIFLUSH) != 0) LDEBUG("Serial flush error -- IGNORED");
417 }
418 
419 
420 // ######################################################################
422 { }
423 
424 // ####################################################################################################
426 { return itsType; }
427 
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level. ...
Definition: Log.H:155
void write(void const *buffer, const int nbytes)
Write bytes to the port.
Definition: Serial.C:356
void writeNoCheck(void const *buffer, const int nbytes)
Write bytes to the port - does not wait for buffer to be emptied.
Definition: Serial.C:390
Type
Enum for the interface type.
Definition: UserInterface.H:53
std::string readString()
Read a string, using the line termination convention of serial::linestyle.
Definition: Serial.C:300
void postInit() override
Called after all sub-Components are init()ed.
Definition: Serial.C:31
bool readSome(std::string &str) override
Read some bytes if available, and return true and a string when one is complete.
Definition: Serial.C:247
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:193
void toggleDTR(std::chrono::milliseconds const &dur)
Set the DTR mode off momentarily.
Definition: Serial.C:188
void flush(void)
Flush all inputs.
Definition: Serial.C:411
int read2(void *buffer, const int nbytes)
Attempt to read up to nbytes from serial port into the buffer.
Definition: Serial.C:229
Abstract base class for a string-based user interface.
Definition: UserInterface.H:32
void writeString(std::string const &str) override
Write a string, using the line termination convention of serial::linestyle.
Definition: Serial.C:339
UserInterface::Type type() const override
Return our port type, here Hard or USB.
Definition: Serial.C:425
void postUninit() override
Called after all sub-Components are uninit()ed.
Definition: Serial.C:155
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:212
virtual ~Serial()
destructor
Definition: Serial.C:421
Serial(std::string const &instance, UserInterface::Type type)
Constructor.
Definition: Serial.C:26
void sendBreak(void)
transmit continuous stream of zero-valued bits for specific duration.
Definition: Serial.C:207
void setBlocking(bool blocking, std::chrono::milliseconds const &timeout)
Set the access to blocking or not.
Definition: Serial.C:167
int read(void *buffer, const int nbytes)
Attempt to read up to nbytes from serial port into the buffer.
Definition: Serial.C:216
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:176