JeVois  1.9
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 <fstream>
21 
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 
27 // ######################################################################
28 jevois::Serial::Serial(std::string const & instance, jevois::UserInterface::Type type) :
29  jevois::UserInterface(instance), itsDev(-1), itsWriteOverflowCounter(0), itsType(type)
30 { }
31 
32 // ######################################################################
34 {
35  // Open the port, non-blocking mode by default:
36  if (itsDev != -1) ::close(itsDev);
37  itsDev = ::open(jevois::serial::devname::get().c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
38  if (itsDev == -1) LFATAL("Could not open serial port [" << jevois::serial::devname::get() << ']');
39 
40  // Save current state
41  if (tcgetattr(itsDev, &itsSavedState) == -1) LFATAL("Failed to save current state");
42 
43  // reset all the flags
44  ////if (fcntl(itsDev, F_SETFL, 0) == -1) LFATAL("Failed to reset flags");
45 
46  // Get the current option set:
47  termios options = { };
48  if (tcgetattr(itsDev, &options) == -1) LFATAL("Failed to get options");
49 
50  // get raw input from the port
51  options.c_cflag |= ( CLOCAL // ignore modem control lines
52  | CREAD ); // enable the receiver
53 
54  options.c_iflag &= ~( IGNBRK // ignore BREAK condition on input
55  | BRKINT // If IGNBRK is not set, generate SIGINT on BREAK condition, else read BREAK as \0
56  | PARMRK
57  | ISTRIP // strip off eighth bit
58  | INLCR // donot translate NL to CR on input
59  | IGNCR // ignore CR
60  | ICRNL // translate CR to newline on input
61  | IXON // disable XON/XOFF flow control on output
62  );
63 
64  // disable implementation-defined output processing
65  options.c_oflag &= ~OPOST;
66  options.c_lflag &= ~(ECHO // dont echo i/p chars
67  | ECHONL // do not echo NL under any circumstance
68  | ICANON // disable cannonical mode
69  | ISIG // do not signal for INTR, QUIT, SUSP etc
70  | IEXTEN // disable platform dependent i/p processing
71  );
72 
73  // Set the baudrate:
74  unsigned int rate;
75  switch (jevois::serial::baudrate::get())
76  {
77  case 4000000: rate = B4000000; break;
78  case 3500000: rate = B3500000; break;
79  case 3000000: rate = B3000000; break;
80  case 2500000: rate = B2500000; break;
81  case 2000000: rate = B2000000; break;
82  case 1500000: rate = B1500000; break;
83  case 1152000: rate = B1152000; break;
84  case 1000000: rate = B1000000; break;
85  case 921600: rate = B921600; break;
86  case 576000: rate = B576000; break;
87  case 500000: rate = B500000; break;
88  case 460800: rate = B460800; break;
89  case 230400: rate = B230400; break;
90  case 115200: rate = B115200; break;
91  case 57600: rate = B57600; break;
92  case 38400: rate = B38400; break;
93  case 19200: rate = B19200; break;
94  case 9600: rate = B9600; break;
95  case 4800: rate = B4800; break;
96  case 2400: rate = B2400; break;
97  case 1200: rate = B1200; break;
98  case 600: rate = B600; break;
99  case 300: rate = B300; break;
100  case 110: rate = B110; break;
101  case 0: rate = B0; break;
102  default: LFATAL("Invalid baud rate " <<jevois::serial::baudrate::get());
103  }
104 
105  cfsetispeed(&options, rate);
106  cfsetospeed(&options, rate);
107 
108  // Parse the serial format string:
109  std::string const format = jevois::serial::format::get();
110  if (format.length() != 3) LFATAL("Incorrect format string: " << format);
111 
112  // Set the number of bits:
113  options.c_cflag &= ~CSIZE; // mask off the 'size' bits
114 
115  switch (format[0])
116  {
117  case '5': options.c_cflag |= CS5; break;
118  case '6': options.c_cflag |= CS6; break;
119  case '7': options.c_cflag |= CS7; break;
120  case '8': options.c_cflag |= CS8; break;
121  default: LFATAL("Invalid charbits: " << format[0] << " (should be 5..8)");
122  }
123 
124  // Set parity option:
125  options.c_cflag &= ~(PARENB | PARODD);
126 
127  switch(format[1])
128  {
129  case 'N': break;
130  case 'E': options.c_cflag |= PARENB; break;
131  case 'O': options.c_cflag |= (PARENB | PARODD); break;
132  default: LFATAL("Invalid parity: " << format[1] << " (should be N,E,O)");
133  }
134 
135  // Set the stop bits option:
136  options.c_cflag &= ~CSTOPB;
137  switch(format[2])
138  {
139  case '1': break;
140  case '2': options.c_cflag |= CSTOPB; break;
141  default: LFATAL("Invalid stopbits: " << format[2] << " (should be 1..2)");
142  }
143 
144  // Set the flow control:
145  options.c_cflag &= ~CRTSCTS;
146  options.c_iflag &= ~(IXON | IXANY | IXOFF);
147 
148  if (jevois::serial::flowsoft::get()) options.c_iflag |= (IXON | IXANY | IXOFF);
149  if (jevois::serial::flowhard::get()) options.c_cflag |= CRTSCTS;
150 
151  // Set all the options now:
152  if (tcsetattr(itsDev, TCSANOW, &options) == -1) LFATAL("Failed to set port options");
153  LINFO("Serial driver ready on " << jevois::serial::devname::get());
154 }
155 
156 // ######################################################################
158 {
159  if (itsDev != -1)
160  {
161  sendBreak();
162  if (tcsetattr(itsDev, TCSANOW, &itsSavedState) == -1) LERROR("Failed to restore serial port state -- IGNORED");
163  ::close(itsDev);
164  itsDev = -1;
165  }
166 }
167 
168 // ######################################################################
169 void jevois::Serial::setBlocking(bool blocking, std::chrono::milliseconds const & timeout)
170 {
171  std::lock_guard<std::mutex> _(itsMtx);
172 
173  int flags = fcntl(itsDev, F_GETFL, 0);
174  if (flags == -1) LFATAL("Cannot get flags");
175  if (blocking) flags &= (~O_NONBLOCK); else flags |= O_NONBLOCK;
176  if (fcntl(itsDev, F_SETFL, flags) == -1) LFATAL("Cannot set flags");
177 
178  // If blocking, set a timeout on the descriptor:
179  if (blocking)
180  {
181  termios options;
182  if (tcgetattr(itsDev, &options) == -1) LFATAL("Failed to get options");
183  options.c_cc[VMIN] = 0;
184  options.c_cc[VTIME] = timeout.count() / 100; // vtime is in tenths of second
185  if (tcsetattr(itsDev, TCSANOW, &options) == -1) LFATAL("Failed to set port options");
186  }
187 }
188 
189 // ######################################################################
190 void jevois::Serial::toggleDTR(std::chrono::milliseconds const & dur)
191 {
192  std::lock_guard<std::mutex> _(itsMtx);
193 
194  struct termios tty, old;
195 
196  if (tcgetattr(itsDev, &tty) == -1 || tcgetattr(itsDev, &old) == -1) LFATAL("Failed to get attributes");
197 
198  cfsetospeed(&tty, B0);
199  cfsetispeed(&tty, B0);
200 
201  if (tcsetattr(itsDev, TCSANOW, &tty) == -1) LFATAL("Failed to set attributes");
202 
203  std::this_thread::sleep_for(dur);
204 
205  if (tcsetattr(itsDev, TCSANOW, &old) == -1) LFATAL("Failed to restore attributes");
206 }
207 
208 // ######################################################################
210 {
211  std::lock_guard<std::mutex> _(itsMtx);
212 
213  // Send a Hangup to the port
214  tcsendbreak(itsDev, 0);
215 }
216 
217 // ######################################################################
218 int jevois::Serial::read(void * buffer, const int nbytes)
219 {
220  std::lock_guard<std::mutex> _(itsMtx);
221 
222  int n = ::read(itsDev, buffer, nbytes);
223 
224  if (n == -1) throw std::runtime_error("Serial: Read error");
225  if (n == 0) throw std::runtime_error("Serial: Read timeout");
226 
227  return n;
228 }
229 
230 // ######################################################################
231 int jevois::Serial::read2(void * buffer, const int nbytes)
232 {
233  std::lock_guard<std::mutex> _(itsMtx);
234 
235  int n = ::read(itsDev, buffer, nbytes);
236 
237  if (n == -1)
238  {
239  if (errno == EAGAIN) return false; // no new char available
240  else throw std::runtime_error("Serial: Read error");
241  }
242 
243  if (n == 0) return false; // no new char available
244 
245  return n;
246 }
247 
248 // ######################################################################
249 bool jevois::Serial::readSome(std::string & str)
250 {
251  std::lock_guard<std::mutex> _(itsMtx);
252 
253  unsigned char c;
254 
255  while (true)
256  {
257  int n = ::read(itsDev, reinterpret_cast<char *>(&c), 1);
258 
259  if (n == -1)
260  {
261  if (errno == EAGAIN) return false; // no new char available
262  else throw std::runtime_error("Serial: Read error");
263  }
264 
265  if (n == 0) return false; // no new char available
266 
267  switch (jevois::serial::linestyle::get())
268  {
269  case jevois::serial::LineStyle::LF:
270  if (c == '\n') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
271  else itsPartialString += c;
272  break;
273 
274  case jevois::serial::LineStyle::CR:
275  if (c == '\r') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
276  else itsPartialString += c;
277  break;
278 
279  case jevois::serial::LineStyle::CRLF:
280  if (c == '\n') { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
281  else if (c != '\r') itsPartialString += c;
282  break;
283 
284  case jevois::serial::LineStyle::Zero:
285  if (c == 0x00) { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
286  else itsPartialString += c;
287  break;
288 
289  case jevois::serial::LineStyle::Sloppy: // Return when we receive first separator, ignore others
290  if (c == '\r' || c == '\n' || c == 0x00 || c == 0xd0)
291  {
292  if (itsPartialString.empty() == false)
293  { str = std::move(itsPartialString); itsPartialString.clear(); return true; }
294  }
295  else itsPartialString += c;
296  break;
297  }
298  }
299 }
300 
301 // ######################################################################
303 {
304  std::lock_guard<std::mutex> _(itsMtx);
305  return readStringInternal();
306 }
307 
308 // ######################################################################
309 std::string jevois::Serial::readStringInternal()
310 {
311  std::string str; unsigned char c;
312 
313  while (true)
314  {
315  int n = ::read(itsDev, reinterpret_cast<char *>(&c), 1);
316 
317  if (n == -1)
318  {
319  if (errno == EAGAIN) std::this_thread::sleep_for(std::chrono::milliseconds(2));
320  else throw std::runtime_error("Serial: Read error");
321  }
322  else if (n == 0)
323  std::this_thread::sleep_for(std::chrono::milliseconds(2)); // no new char available
324  else
325  {
326  switch (jevois::serial::linestyle::get())
327  {
328  case jevois::serial::LineStyle::LF: if (c == '\n') return str; else str += c; break;
329 
330  case jevois::serial::LineStyle::CR: if (c == '\r') return str; else str += c; break;
331 
332  case jevois::serial::LineStyle::CRLF: if (c == '\n') return str; else if (c != '\r') str += c; break;
333 
334  case jevois::serial::LineStyle::Zero: if (c == 0x00) return str; else str += c; break;
335 
336  case jevois::serial::LineStyle::Sloppy: // Return when we receive first separator, ignore others
337  if (c == '\r' || c == '\n' || c == 0x00 || c == 0xd0) { if (str.empty() == false) return str; }
338  else str += c;
339  break;
340  }
341  }
342  }
343 }
344 
345 // ######################################################################
346 void jevois::Serial::writeString(std::string const & str)
347 {
348  std::string fullstr(str);
349 
350  switch (jevois::serial::linestyle::get())
351  {
352  case jevois::serial::LineStyle::CR: fullstr += '\r'; break;
353  case jevois::serial::LineStyle::LF: fullstr += '\n'; break;
354  case jevois::serial::LineStyle::CRLF: fullstr += "\r\n"; break;
355  case jevois::serial::LineStyle::Zero: fullstr += '\0'; break;
356  case jevois::serial::LineStyle::Sloppy: fullstr += "\r\n"; break;
357  }
358 
359  this->write(fullstr.c_str(), fullstr.length());
360 }
361 
362 // ######################################################################
363 void jevois::Serial::write(void const * buffer, const int nbytes)
364 {
365  std::lock_guard<std::mutex> _(itsMtx);
366  writeInternal(buffer, nbytes);
367 }
368 
369 // ######################################################################
370 void jevois::Serial::writeInternal(void const * buffer, const int nbytes, bool nodrop)
371 {
372  // Nodrop is used to prevent dropping even if the user wants it, e.g., during fileGet().
373  if (nodrop)
374  {
375  // Just write it all, never quit, never drop:
376  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer);
377  while (ndone < nbytes)
378  {
379  int n = ::write(itsDev, b + ndone, nbytes - ndone);
380  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
381 
382  // If we did not write the whole thing, the serial port is saturated, we need to wait a bit:
383  if (n > 0) ndone += n;
384  if (ndone < nbytes) tcdrain(itsDev);
385  }
386  }
387  else if (drop::get())
388  {
389  // Just write and silently drop (after a few attempts) if we could not write everything:
390  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer); int iter = 0;
391  while (ndone < nbytes && iter++ < 50)
392  {
393  int n = ::write(itsDev, b + ndone, nbytes - ndone);
394  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
395 
396  // If we did not write the whole thing, the serial port is saturated, we need to wait a bit:
397  if (n > 0) ndone += n;
398  if (ndone < nbytes) { tcdrain(itsDev); std::this_thread::sleep_for(std::chrono::milliseconds(5)); }
399  }
400  }
401  else
402  {
403  // Try to write a few times, then, if not done, report overflow and drop what remains:
404  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer); int iter = 0;
405  while (ndone < nbytes && iter++ < 50)
406  {
407  int n = ::write(itsDev, b + ndone, nbytes - ndone);
408  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
409 
410  // If we did not write the whole thing, the serial port is saturated, we need to wait a bit:
411  if (n > 0) ndone += n;
412  if (ndone < nbytes) { tcdrain(itsDev); std::this_thread::sleep_for(std::chrono::milliseconds(2)); }
413  }
414 
415  if (ndone < nbytes)
416  {
417  // If we had a serial overflow, we need to let the user know, but how, since the serial is overflowed already?
418  // Let's first throttle down big time, and then we throw:
419  std::this_thread::sleep_for(std::chrono::milliseconds(100));
420 
421  tcdrain(itsDev);
422 
423  // Report the overflow once in a while:
424  ++itsWriteOverflowCounter; if (itsWriteOverflowCounter > 100) itsWriteOverflowCounter = 0;
425  if (itsWriteOverflowCounter == 1)
426  throw std::overflow_error("Serial write overflow: need to reduce amount ot serial writing");
427 
428  // Note how we are otherwise just ignoring the overflow and hence dropping data.
429  }
430  else itsWriteOverflowCounter = 0;
431  }
432 }
433 
434 // ######################################################################
435 void jevois::Serial::writeNoCheck(void const * buffer, const int nbytes)
436 {
437  std::lock_guard<std::mutex> _(itsMtx);
438 
439  int ndone = 0; char const * b = reinterpret_cast<char const *>(buffer); int iter = 0;
440  while (ndone < nbytes && iter++ < 50)
441  {
442  int n = ::write(itsDev, b + ndone, nbytes - ndone);
443  if (n == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Write error");
444  if (n > 0) ndone += n;
445  }
446 
447  if (ndone < nbytes)
448  {
449  // If after a number of iterations there are still unbuffered bytes, flush the output buffer
450  if (tcflush(itsDev, TCOFLUSH) != 0) LDEBUG("Serial flushOut error -- IGNORED");
451  }
452 }
453 
454 // ######################################################################
456 {
457  std::lock_guard<std::mutex> _(itsMtx);
458 
459  // Flush the input
460  if (tcflush(itsDev, TCIFLUSH) != 0) LDEBUG("Serial flush error -- IGNORED");
461 }
462 
463 
464 // ######################################################################
466 { }
467 
468 // ####################################################################################################
470 { return itsType; }
471 
472 // ####################################################################################################
473 void jevois::Serial::fileGet(std::string const & abspath)
474 {
475  std::lock_guard<std::mutex> _(itsMtx);
476 
477  std::ifstream fil(abspath, std::ios::in | std::ios::binary);
478  if (fil.is_open() == false) throw std::runtime_error("Could not read file " + abspath);
479 
480  // Get file length and send it out in ASCII:
481  fil.seekg(0, fil.end); size_t num = fil.tellg(); fil.seekg(0, fil.beg);
482 
483  std::string startstr = "JEVOIS_FILEGET " + std::to_string(num) + '\n';
484  writeInternal(startstr.c_str(), startstr.length(), true);
485 
486  // Read blocks and send them to serial:
487  size_t const bufsiz = std::min(num, size_t(1024 * 1024)); char buffer[1024 * 1024];
488  while (num)
489  {
490  size_t got = std::min(bufsiz, num); fil.read(buffer, got); if (!fil) got = fil.gcount();
491  writeInternal(buffer, got, true);
492  num -= got;
493  }
494 }
495 
496 // ####################################################################################################
497 void jevois::Serial::filePut(std::string const & abspath)
498 {
499  std::lock_guard<std::mutex> _(itsMtx);
500 
501  std::ofstream fil(abspath, std::ios::out | std::ios::binary);
502  if (fil.is_open() == false) throw std::runtime_error("Could not write file " + abspath);
503 
504  // Get file length as ASCII:
505  std::string const lenstr = readStringInternal();
506  if (jevois::stringStartsWith(lenstr, "JEVOIS_FILEPUT ") == false)
507  throw std::runtime_error("Incorrect header while receiving file " + abspath);
508 
509  auto vec = jevois::split(lenstr);
510  if (vec.size() != 2) throw std::runtime_error("Incorrect header fields while receiving file " + abspath);
511 
512  size_t num = std::stoul(vec[1]);
513 
514  // Read blocks from serial and write them to file:
515  size_t const bufsiz = std::min(num, size_t(1024 * 1024)); char buffer[1024 * 1024];
516  while (num)
517  {
518  int got = ::read(itsDev, buffer, bufsiz);
519  if (got == -1 && errno != EAGAIN) throw std::runtime_error("Serial: Read error");
520 
521  if (got > 0)
522  {
523  fil.write(buffer, got);
524  num -= got;
525  }
526  else std::this_thread::sleep_for(std::chrono::milliseconds(2));
527  }
528  fil.close();
529 }
#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:363
void writeNoCheck(void const *buffer, const int nbytes)
Write bytes to the port - does not wait for buffer to be emptied.
Definition: Serial.C:435
Type
Enum for the interface type.
Definition: UserInterface.H:59
void filePut(std::string const &abspath)
Receive a file from the host and write it to the local microSD.
Definition: Serial.C:497
void fileGet(std::string const &abspath)
Send a file from the local microSD to the host computer.
Definition: Serial.C:473
std::string readString()
Read a string, using the line termination convention of serial::linestyle.
Definition: Serial.C:302
void postInit() override
Called after all sub-Components are init()ed.
Definition: Serial.C:33
bool readSome(std::string &str) override
Read some bytes if available, and return true and a string when one is complete.
Definition: Serial.C:249
#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:190
void flush(void)
Flush all inputs.
Definition: Serial.C:455
int read2(void *buffer, const int nbytes)
Attempt to read up to nbytes from serial port into the buffer.
Definition: Serial.C:231
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:346
bool stringStartsWith(std::string const &str, std::string const &prefix)
Return true if str starts with prefix (including if both strings are equal)
Definition: Utils.C:166
UserInterface::Type type() const override
Return our port type, here Hard or USB.
Definition: Serial.C:469
void postUninit() override
Called after all sub-Components are uninit()ed.
Definition: Serial.C:157
#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:465
Serial(std::string const &instance, UserInterface::Type type)
Constructor.
Definition: Serial.C:28
std::string to_string(T const &val)
Convert from type to string.
Definition: UtilsImpl.H:58
void sendBreak(void)
transmit continuous stream of zero-valued bits for specific duration.
Definition: Serial.C:209
void setBlocking(bool blocking, std::chrono::milliseconds const &timeout)
Set the access to blocking or not.
Definition: Serial.C:169
int read(void *buffer, const int nbytes)
Attempt to read up to nbytes from serial port into the buffer.
Definition: Serial.C:218
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:176
std::vector< std::string > split(std::string const &input, std::string const &regex="\+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:142