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