JeVois  1.12
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;
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 
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  {
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 
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
Data collection mode RAW means that the latest available raw data is returned each time get() is called
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
Magnetometer sampling or Off to disable or Once to only get one measurement You can repeatedly set this parameter to Once to obtain repeated measurements at your own pace In JeVois you need to alternate between Off and Once In FIFO grate controls the data rate
Definition: ICM20948.H:110
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