RokubiminiSerialImpl.cpp
Go to the documentation of this file.
1 /*
2  * RokubiminiSerialImpl.hpp
3  * Created on: April, 2020
4  * Author(s): Mike Karamousadakis, Ilias Patsiaouras
5  */
6 
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <termios.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <cerrno>
14 #include <functional>
15 #include <thread>
16 #include <sys/wait.h>
17 #include <asm/ioctls.h>
18 #include <linux/serial.h>
19 #include <sys/ioctl.h>
20 #include <iostream>
21 #include <fstream>
22 #include <string>
23 
24 #include "ros/ros.h"
25 
28 
29 namespace rokubimini
30 {
31 namespace serial
32 {
33 RokubiminiSerialImpl::RokubiminiSerialImpl(const std::string& name, const std::string& port,
34  const std::uint32_t& baudRate)
35  : name_(name)
36  , port_(port)
37  , baudRate_(baudRate)
38  , usbFileDescriptor_{ -1 }
39  , frameSync_{ false }
40  , usbStreamIn_{ nullptr }
41  , usbStreamOut_{ nullptr }
42  , timeStampSecs_(0.0)
43  , runInThreadedMode_(true)
44  , useDeviceTimeStamps_(false)
46  , pollingThread_()
50  , isRunning_{ true }
57 {
58  ROS_DEBUG("[%s] Device connected to serial port: %s with baudrate: %u \n", name_.c_str(), port_.c_str(), baudRate_);
60 }
61 
63 {
64  ROS_DEBUG("[%s] Attempting to start-up device.", name_.c_str());
65 
66  // Connect to serial port
67  if (connect())
68  {
69  // Sleep for 3.0 seconds for the sensor to boot and start sending data.
70  std::this_thread::sleep_for(std::chrono::microseconds(3000000));
71  ROS_INFO("[%s] Start-up successful.", name_.c_str());
72  }
73  else
74  {
75  ROS_ERROR("[%s] Could not establish connection with device. Start-up failed.", name_.c_str());
76  return false;
77  }
78  if (!setInitMode())
79  {
80  ROS_ERROR("[%s] Could not bring device to INIT mode.", name_.c_str());
81  return false;
82  }
83  return true;
84 }
85 
87 {
88  // Start the polling thread if driver is to run in threaded mode and
89  // if the polling thread is not already running
90  if (runInThreadedMode_ && !pollingThread_.joinable())
91  {
92  ROS_INFO("[%s] Launching polling thread.", name_.c_str());
93  pollingThread_ = boost::thread{ &RokubiminiSerialImpl::pollingWorker, this };
94  }
95  return true;
96 }
97 
99 {
100  return true;
101 }
102 
104 {
105  ROS_INFO("[%s] Driver will attempt to shut-down", name_.c_str());
106 
107  // Signal for termination (all background thread)
108  isRunning_ = false;
109 
110  // Shutdown the connection thread if running
111  if (runInThreadedMode_ && connectionThread_.joinable())
112  {
113  connectionThread_.join();
114  }
115 
116  // Shutdown the polling thread if running
117  if (runInThreadedMode_ && pollingThread_.joinable())
118  {
119  pollingThread_.join();
120  }
121 
122  // Close the serial-port file descriptior
123  if (usbFileDescriptor_ != -1)
124  {
125  ROS_INFO("[%s] Closing Serial Communication", name_.c_str());
126  close(usbFileDescriptor_);
127  }
128  usbStreamIn_.close();
129  usbStreamOut_.close();
130  if (((usbStreamIn_.fail() | usbStreamOut_.fail()) != 0))
131  {
132  ROS_ERROR("[%s] Failed to close file streams.", name_.c_str());
133  }
134 
135  ROS_INFO("[%s] Shut-down successful", name_.c_str());
136 }
137 
139 {
140  // Abort attempt if the driver is already in the process of connecting
142  {
143  return false;
144  }
145  else
146  {
148  }
149 
150  // Close the file descriptor if it was already open
151  if (usbFileDescriptor_ != -1)
152  {
153  close(usbFileDescriptor_);
154  usbFileDescriptor_ = -1;
155  }
156 
157  // Reset error counters and error state
163 
164  // Check if the driver is set to run in threaded mode
165  if (!runInThreadedMode_)
166  {
167  return initSensorCommunication(false);
168  }
169  else
170  {
171  // Start the connection thread
173  }
174 
175  return true;
176 }
177 
178 bool RokubiminiSerialImpl::connect(const std::string& port)
179 {
180  port_ = port;
181  return connect();
182 }
183 
185 {
187  {
188  /* read the next available byte and check if is the header
189  * make sure to unget it after. emulating peek. Using static
190  * variable for the sync flag will preserve status of sync
191  * between calls */
192  while (!frameSync_)
193  {
194  uint8_t possible_header;
195  /* read bytes 1 by 1 to find the header */
196  usbStreamIn_.read((char*)&possible_header, sizeof(possible_header));
197  if (possible_header == frameHeader)
198  {
199  /* read the remaining frame to make check also CRC */
200  usbStreamIn_.read((char*)&frame.bytes[sizeof(frame.header)], sizeof(frame) - sizeof(frame.header));
201  if (frame.crc16_ccitt == calcCrc16X25(frame.data.bytes, sizeof(frame.data)))
202  {
203  ROS_INFO("[%s] Frame synced with 0x%X header", name_.c_str(), frameHeader);
204  frameSync_ = true;
205  }
206  else
207  {
208  /* if there is a frame that included the header 0xAA in
209  * a fixed position. Could be the above checking mechanism
210  * will get stuck because will find the wrong value as header
211  * then will remove from the buffer n bytes where n the size
212  * of the frame and then will find again exactly the same
213  * situation the wrong header. So we read on extra byte to make
214  * sure next time will start from the position that is size of frame
215  * plus 1. It works */
216  char dummy;
217  // usbStreamIn_.read((char*)&dummy, sizeof(dummy));
218  usbStreamIn_.read((char*)&dummy, sizeof(dummy));
219  ROS_WARN("[%s] Skipping incomplete frame", name_.c_str());
220  }
221  }
222  }
223  /* Read the sensor measurements frame assuming that is alligned with the RX buffer */
224  try
225  {
226  usbStreamIn_.read((char*)frame.bytes, sizeof(frame));
227  }
228  catch (std::exception& e)
229  {
230  ROS_ERROR("[%s] Error while reading a packet, %s", name_.c_str(), e.what());
232  return false;
233  }
234 
235  /* Check if the frame is still aligned, otherwise exit */
236  if (frame.header != frameHeader)
237  {
238  frameSync_ = false;
239  /* keep some statistics */
241  {
242  ROS_WARN("[%s] Reached max syncing errors. Disconnecting sensor.", name_.c_str());
245  }
246  return false;
247  }
248  // Read and check CRC 16-bit
249  try
250  {
251  uint16_t crc_received = frame.crc16_ccitt;
252  uint16_t crc_computed = calcCrc16X25(frame.data.bytes, sizeof(frame.data));
253  if (crc_received != crc_computed)
254  {
256  ROS_WARN("[%s] CRC missmatch received: 0x%04x calculated: 0x%04x", name_.c_str(), crc_received, crc_computed);
257  return false;
258  }
259  }
260  catch (std::exception& e)
261  {
262  ROS_ERROR("[%s] Error while reading a packet, %s", name_.c_str(), e.what());
264  return false;
265  }
266 
267  if (frame.data.status.app_took_too_long)
268  {
269  ROS_WARN("[%s] Warning force sensor is skipping measurements, Increase sinc length", name_.c_str());
270  }
271  if (frame.data.status.overrange)
272  {
273  ROS_WARN("[%s] Warning measuring range exceeded", name_.c_str());
274  }
275  if (frame.data.status.invalid_measurements)
276  {
277  ROS_ERROR("[%s] Warning force torque measurements are invalid, Permanent damage may occur", name_.c_str());
278  }
279  if (frame.data.status.raw_measurements)
280  {
281  ROS_WARN_THROTTLE(1.0, "[%s] Warning raw force torque measurements from gages", name_.c_str());
282  }
284  return true;
285  }
286  // This point should not be reached
287  return false;
288 }
289 
291 {
293 }
294 
296 {
298 }
299 
301 {
302  return (errorState_ != ErrorState::NO_ERROR);
303 }
304 
306 {
308 }
309 
311 {
312  return connectionState_;
313 }
314 
316 {
317  return errorState_;
318 }
319 
321 {
322  switch (errorState_)
323  {
325  return std::string{ "No Error" };
326 
328  return std::string{ "Offset Error" };
329 
331  return std::string{ "Calibration Error" };
332 
334  return std::string{ "Packet Reading Error" };
335 
337  return std::string{ "Sync Error" };
338 
339  default:
340  return std::string{ "Undefined" };
341  }
342 
343  return std::string{ "No Error" };
344 }
345 
347 {
348  ROS_INFO("[%s] Initializing serial-port at: %s", name_.c_str(), port_.c_str());
349 
350  // Block, continuously trying to connect to the serial-port, until
351  // signaled to stop or connection is established
352  bool connected;
353  do
354  {
355  connected = initSerialPort(port_);
356  std::this_thread::sleep_for(std::chrono::microseconds(100000));
357  } while (isRunning_ && keepConnecting && !connected);
358 
359  if (!isRunning_)
360  {
361  // connection process was cancelled from somewhere else
362  ROS_ERROR("[%s] Sensor is disconnected.", name_.c_str());
364  return false;
365  }
366 
368  return true;
369 }
370 
371 uint32_t RokubiminiSerialImpl::getBaudRateDefinition(const uint32_t& baudRate)
372 {
373  switch (baudRate)
374  {
375  case 50:
376  return B50;
377  case 75:
378  return B75;
379  case 110:
380  return B110;
381  case 134:
382  return B134;
383  case 150:
384  return B150;
385  case 200:
386  return B200;
387  case 300:
388  return B300;
389  case 600:
390  return B600;
391  case 1200:
392  return B1200;
393  case 1800:
394  return B1800;
395  case 2400:
396  return B2400;
397  case 4800:
398  return B4800;
399  case 9600:
400  return B9600;
401  case 19200:
402  return B19200;
403  case 38400:
404  return B38400;
405  case 57600:
406  return B57600;
407  case 115200:
408  return B115200;
409  case 230400:
410  return B230400;
411  case 460800:
412  return B460800;
413  case 500000:
414  return B500000;
415  case 576000:
416  return B576000;
417  case 921600:
418  return B921600;
419  case 1000000:
420  return B1000000;
421  case 1152000:
422  return B1152000;
423  case 1500000:
424  return B1500000;
425  case 2000000:
426  return B2000000;
427  case 2500000:
428  return B2500000;
429  case 3000000:
430  return B3000000;
431  case 3500000:
432  return B3500000;
433  case 4000000:
434  return B4000000;
435  default:
436  ROS_ERROR("[%s] Baud rate %u is not supported\n", name_.c_str(), baudRate);
437  return B0;
438  }
439 }
440 bool RokubiminiSerialImpl::initSerialPort(const std::string& port)
441 {
442  // int flags;
443  termios newtio;
444  struct serial_struct ser_info;
445 
446  // make the fstreams unbuffered
447  usbStreamIn_.rdbuf()->pubsetbuf(nullptr, 0);
448  usbStreamOut_.rdbuf()->pubsetbuf(nullptr, 0);
449 
450  // Open the Serial Port
451  usbFileDescriptor_ = open(port.c_str(), O_RDWR | O_NOCTTY);
452  if (usbFileDescriptor_ < 0)
453  {
454  ROS_ERROR("[%s] Failed to open serial-port: '%s'", name_.c_str(), port.c_str());
455  return false;
456  }
457 
458  // Get the Serial Descriptor Flags
459  if (tcgetattr(usbFileDescriptor_, &newtio) < 0)
460  {
461  ROS_ERROR("[%s] Failed to get connection attributes.", name_.c_str());
462  return false;
463  }
464 
465  // Set the Serial Speed
466  uint32_t baud_rate_definition = getBaudRateDefinition(baudRate_);
467  if (baud_rate_definition != 0)
468  {
469  cfsetispeed(&newtio, baud_rate_definition);
470  cfsetospeed(&newtio, baud_rate_definition);
471  }
472  else
473  {
474  return false;
475  }
476  cfmakeraw(&newtio);
477 
478  // Set the Serial Connection Attributes (SCA)
479  if (tcsetattr(usbFileDescriptor_, TCSAFLUSH, &newtio) < 0)
480  {
481  ROS_ERROR("[%s] Failed to set connection attributes.", name_.c_str());
482  return false;
483  }
484 
485  // Flush the input and output streams
486  if (tcflush(usbFileDescriptor_, TCIOFLUSH) < 0)
487  {
488  ROS_ERROR("[%s] Failed to flush the input and output streams.", name_.c_str());
489  return false;
490  }
491 
492  // Enable linux FTDI low latency mode
493  ioctl(usbFileDescriptor_, TIOCGSERIAL, &ser_info);
494  ser_info.flags |= ASYNC_LOW_LATENCY;
495  ioctl(usbFileDescriptor_, TIOCSSERIAL, &ser_info);
496 
497  // Get the Serial Descriptor Flags
498  if (fcntl(usbFileDescriptor_, F_GETFL) < 0)
499  {
500  ROS_ERROR("[%s] Failed to set the descriptor flags.", name_.c_str());
501  return false;
502  }
503 
504  // create a std stream to read and write from/to device
505  usbStreamIn_.open(port.c_str(), std::ifstream::in);
506  usbStreamOut_.open(port.c_str(), std::ofstream::out);
507  if (((usbStreamIn_.fail() | usbStreamOut_.fail()) != 0))
508  {
509  ROS_ERROR("[%s] Failed to open file streams.", name_.c_str());
510  return false;
511  }
512 
513  // wait another second to get measurements
514  std::this_thread::sleep_for(std::chrono::seconds(1));
515  return true;
516 }
517 
518 inline uint16_t RokubiminiSerialImpl::calcCrc16X25(uint8_t* data, int len)
519 {
520  uint16_t crc = 0xFFFF;
521  while (len--)
522  crc = crcCcittUpdate(crc, *data++);
523  return ~crc;
524 }
525 
526 uint16_t RokubiminiSerialImpl::crcCcittUpdate(uint16_t crc, uint8_t data)
527 {
528 #define lo8(x) ((x)&0xFF)
529 #define hi8(x) (((x) >> 8) & 0xFF)
530  data ^= lo8(crc);
531  data ^= data << 4;
532 
533  return ((((uint16_t)data << 8) | hi8(crc)) ^ (uint8_t)(data >> 4) ^ ((uint16_t)data << 3));
534 }
535 
537 {
539 }
540 
542 {
543  // Configure the scheduling policy for the pollin gthread
544  int priority = 99;
545  sched_param params;
546  params.sched_priority = priority;
547  if (sched_setscheduler(getpid(), SCHED_FIFO, &params) != 0)
548  {
549  ROS_WARN("[%s] Failed to set thread priority to %d: %s.", name_.c_str(), priority, std::strerror(errno));
550  }
551 
552  // Wait for connection to be established before starting to poll
553  usleep(100000);
554  if (!isConnected())
555  {
556  while (!isConnected() && isRunning_)
557  {
558  usleep(1000);
559  ROS_WARN_THROTTLE(4.0, "[%s] Polling thread waiting for connection. ", name_.c_str());
560  }
561  }
562  double time_stamp = 0.0;
563 
564  // Variables for timing
565  timespec t_current, t_previous, t_cycle, t_loop;
566  double time_delta;
567  bool run_processing = true;
568  unsigned long loop_counter = 0;
570 
571  // create timespec for the cycle time by breaking the time step into integral and fractional part with modf()
572  double int_part;
573  t_cycle.tv_nsec = NSEC_PER_SEC * modf(pollingThreadTimeStep_, &int_part);
574  t_cycle.tv_sec = static_cast<uint64_t>(int_part);
575  clock_gettime(CLOCK_MONOTONIC_RAW, &t_current);
576 
577  // Start sensor polling
579  {
580  t_previous = t_current;
581  clock_gettime(CLOCK_MONOTONIC_RAW, &t_current);
582  time_delta = diffSec(t_previous, t_current);
583  if (time_delta > pollingThreadTimeStep_)
584  {
586  ROS_WARN("[%s] Polling is out of sync! TimeDelta=%f", name_.c_str(), time_delta);
587  }
588  // Poll the serial device, skipping processing in current iteration if no valid data has been received
589  RxFrame frame;
590 
591  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
592  (this->readDevice(frame)) ? (run_processing = true) : (run_processing = false);
593  lock.unlock();
594  // TODO: check if PID of sent data and PID configured match
595  // We have to check the connection again because readDevice() may have signalled to disconnect
596  // if too many communication errors occurred
597  if (connectionState_ == ConnectionState::CONNECTED && run_processing)
598  {
599  /* Set the time-stamp */
601  {
602  time_stamp = (double)frame.data.timestamp * 1e-6;
603  }
604  else
605  {
606  time_stamp = timespecToSec(t_current);
607  }
608  // Store the final measurement into the primary buffer
609  {
610  std::lock_guard<std::recursive_mutex> lock(readingMutex_);
611  frame_ = frame;
612  timeStampSecs_ = time_stamp;
613  }
614  }
615  t_loop = timespecAdd(t_current, t_cycle);
616  // Sleep to implement cyclic polling
617  clock_nanosleep(CLOCK_MONOTONIC_RAW, TIMER_ABSTIME, &t_loop, nullptr);
618  loop_counter++;
619  }
620 
621  // Show error statistics if errors have been detected
623  {
624  ROS_WARN("[%s] Sensor polling thread error report:", name_.c_str());
625  ROS_WARN("[%s] Total iterations: %lu", name_.c_str(), loop_counter);
626  ROS_WARN("[%s] Frames received: %lu, (%f%%)", name_.c_str(), frameReceivedCounter_,
627  100.0 * ((double)frameReceivedCounter_ / (double)loop_counter));
628  ROS_WARN("[%s] Correct frames: %lu, (%f%%)", name_.c_str(), frameSuccessCounter_,
629  100.0 * ((double)frameSuccessCounter_ / (double)frameReceivedCounter_));
630  ROS_WARN("[%s] Polling sync errors: %lu, (%f%%)", name_.c_str(), pollingSyncErrorCounter_,
631  100.0 * ((double)pollingSyncErrorCounter_ / (double)loop_counter));
632  ROS_WARN("[%s] Frame CRC errors: %lu, (%f%%)", name_.c_str(), frameCrcErrorCounter_,
633  100.0 * ((double)frameCrcErrorCounter_ / (double)frameReceivedCounter_));
634  ROS_WARN("[%s] Frame sync errors: %d", name_.c_str(), frameSyncErrorCounter_);
635  }
636 }
637 
639 {
640  if (isConnected())
641  {
642  {
643  std::lock_guard<std::recursive_mutex> lock(readingMutex_);
644 
645  const auto& stamp = ros::Time::now();
646 
647  serialImplReading_.getWrench().header.stamp = stamp;
648  serialImplReading_.getWrench().header.frame_id = name_ + "_wrench";
649  serialImplReading_.getWrench().wrench.force.x = frame_.data.forces[0];
650  serialImplReading_.getWrench().wrench.force.y = frame_.data.forces[1];
651  serialImplReading_.getWrench().wrench.force.z = frame_.data.forces[2];
652  serialImplReading_.getWrench().wrench.torque.x = frame_.data.forces[3];
653  serialImplReading_.getWrench().wrench.torque.y = frame_.data.forces[4];
654  serialImplReading_.getWrench().wrench.torque.z = frame_.data.forces[5];
655 
656  serialImplReading_.getTemperature().header.stamp = stamp;
657  serialImplReading_.getTemperature().header.frame_id = name_ + "_temp";
658  serialImplReading_.getTemperature().temperature = frame_.data.temperature;
659  serialImplReading_.getTemperature().variance = 0; // unknown variance
660  reading = serialImplReading_;
661  }
662  }
663 }
664 
666 {
667  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
668  auto config_command = RokubiminiSerialCommandConfig();
669  std::string str;
670  if (!config_command.formatCommand(str))
671  {
672  ROS_ERROR("[%s] Could not format the config command", name_.c_str());
673  return false;
674  }
675  bool success = writeSerial(str);
676  if (!success)
677  {
678  lock.unlock();
679  return false;
680  }
681  else
682  {
684  lock.unlock();
685  // required delay for the sensor to load all the defaults
686  std::this_thread::sleep_for(std::chrono::microseconds(1500000));
687  if (runInThreadedMode_ && pollingThread_.joinable())
688  {
689  pollingThread_.join();
690  }
691  // Reset error counters and error state
697  return true;
698  }
699 }
700 
702 {
703  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
704  auto run_command = RokubiminiSerialCommandRun();
705  std::string str;
706  if (!run_command.formatCommand(str))
707  {
708  ROS_ERROR("[%s] Could not format the run command", name_.c_str());
709  return false;
710  }
711  bool success = writeSerial(str);
712  lock.unlock();
713  if (!success)
714  {
715  return false;
716  }
717 
719  // Start a new polling thread.
720  return startPollingThread();
721 }
722 
724 {
725  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
726  auto hard_reset_command = RokubiminiSerialCommandHardReset();
727  std::string str;
728  if (!hard_reset_command.formatCommand(str))
729  {
730  ROS_ERROR("[%s] Could not format the hardware reset command", name_.c_str());
731  return false;
732  }
733  bool success = writeSerial(str);
734  lock.unlock();
735  return success;
736 }
737 
739 {
740  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
741  auto soft_reset_command = RokubiminiSerialCommandSoftReset();
742  std::string str;
743  if (!soft_reset_command.formatCommand(str))
744  {
745  ROS_ERROR("[%s] Could not format the software reset command", name_.c_str());
746  return false;
747  }
748  bool success = writeSerial(str);
749  if (!success)
750  {
751  lock.unlock();
752  return false;
753  }
754  else
755  {
757  lock.unlock();
758 
759  std::this_thread::sleep_for(std::chrono::microseconds(500000));
760  if (runInThreadedMode_ && pollingThread_.joinable())
761  {
762  pollingThread_.join();
763  }
764  // Reset error counters and error state
770  return true;
771  }
772 }
773 
775 {
776  if (!isInConfigMode())
777  {
778  return false;
779  }
780 
781  ROS_DEBUG("[%s] Setting force/torque filter", name_.c_str());
782  ROS_DEBUG("[%s] \tsize: %u", name_.c_str(), filter.getSincFilterSize());
783  ROS_DEBUG("[%s] \tchop: %u", name_.c_str(), filter.getChopEnable());
784  ROS_DEBUG("[%s] \tfast: %u", name_.c_str(), filter.getFastEnable());
785  ROS_DEBUG("[%s] \tskip: %u", name_.c_str(), filter.getSkipEnable());
786 
787  auto filter_command = RokubiminiSerialCommandFilter(filter);
788  std::string str;
789  if (!filter_command.formatCommand(str))
790  {
791  ROS_ERROR("[%s] Could not format the filter command", name_.c_str());
792  return false;
793  }
794  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
795  bool success = writeSerial(str);
796  lock.unlock();
797  return success;
798 }
799 
800 bool RokubiminiSerialImpl::setForceTorqueOffset(const Eigen::Matrix<double, 6, 1>& forceTorqueOffset)
801 {
802  if (!isInConfigMode())
803  {
804  return false;
805  }
806 
807  ROS_DEBUG_STREAM("[" << name_.c_str() << "] Setting Force/Torque offset: " << forceTorqueOffset << std::endl);
808  auto offset_command = RokubiminiSerialCommandOffset(forceTorqueOffset);
809  std::string str;
810  if (!offset_command.formatCommand(str))
811  {
812  ROS_ERROR("[%s] Could not format the force torque offset command", name_.c_str());
813  return false;
814  }
815  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
816  bool success = writeSerial(str);
817  lock.unlock();
818  return success;
819 }
820 
822 {
823  if (!isInConfigMode())
824  {
825  return false;
826  }
827  char buffer[100];
828  bool success = true;
829  for (uint32_t row = 0; row < 6; row++)
830  {
831  auto sensor_calibration_row_command = RokubiminiSerialCommandSensorCalibrationRow(
832  sensorCalibration.getCalibrationMatrix()(row, 0), sensorCalibration.getCalibrationMatrix()(row, 1),
833  sensorCalibration.getCalibrationMatrix()(row, 2), sensorCalibration.getCalibrationMatrix()(row, 3),
834  sensorCalibration.getCalibrationMatrix()(row, 4), sensorCalibration.getCalibrationMatrix()(row, 5), row);
835  std::string str;
836  if (!sensor_calibration_row_command.formatCommand(str))
837  {
838  ROS_ERROR("[%s] Could not format the calibration matrix command", name_.c_str());
839  return false;
840  }
841  ROS_DEBUG_STREAM("[" << name_.c_str() << "] Calibration matrix line is: " << str << ". Size is " << str.size());
842  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
843  success &= writeSerial(str);
844  lock.unlock();
845  memset(buffer, 0, sizeof(buffer));
846  // Sleep for 10.0 milliseconds before sending the next chunk of data.
847  std::this_thread::sleep_for(std::chrono::microseconds(10000));
848  }
849  return success;
850 }
851 
853 {
854  if (!isInConfigMode())
855  {
856  return false;
857  }
858  ROS_DEBUG("[%s] Setting sensor configuration", name_.c_str());
859 
860  uint32_t baud_rate = baudRate_;
861  uint8_t data_format = 0;
862  return setCommunicationSetup(sensorConfiguration, data_format, baud_rate);
863 }
865  const uint8_t& dataFormat, const uint32_t& baudRate)
866 {
867  ROS_DEBUG("[%s] Setting communication setup", name_.c_str());
868  uint8_t baud_rate;
869  switch (baudRate)
870  {
871  case 9600:
872  baud_rate = 0;
873  break;
874  case 57600:
875  baud_rate = 1;
876  break;
877  case 115200:
878  baud_rate = 2;
879  break;
880  case 230400:
881  baud_rate = 3;
882  break;
883  case 460800:
884  baud_rate = 4;
885  break;
886  default:
887  ROS_ERROR("[%s] The baud rate %u is not supported\n", name_.c_str(), baudRate);
888  return false;
889  }
890  auto comm_setup = RokubiminiSerialCommandCommSetup(sensorConfiguration, dataFormat, baud_rate);
891  std::string str;
892  if (!comm_setup.formatCommand(str))
893  {
894  ROS_ERROR("[%s] Could not format the communication setup command", name_.c_str());
895  return false;
896  }
897  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
898  bool success = writeSerial(str);
899  lock.unlock();
900  return success;
901 }
902 
904 {
905  if (!isInConfigMode())
906  {
907  return false;
908  }
909  auto save_config = RokubiminiSerialCommandSave();
910  std::string str;
911  if (!save_config.formatCommand(str))
912  {
913  ROS_ERROR("[%s] Could not format the save config command", name_.c_str());
914  return false;
915  }
916  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
917  bool success = writeSerial(str);
918  lock.unlock();
919  return success;
920 }
922 {
923  if (!isInConfigMode())
924  {
925  return false;
926  }
927  auto load_config = RokubiminiSerialCommandLoad();
928  std::string str;
929  if (!load_config.formatCommand(str))
930  {
931  ROS_ERROR("[%s] Could not format the load config command", name_.c_str());
932  return false;
933  }
934  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
935  bool success = writeSerial(str);
936  lock.unlock();
937  return success;
938 }
940 {
941  if (!isInConfigMode())
942  {
943  return false;
944  }
945  auto print_config = RokubiminiSerialCommandPrint();
946  std::string str;
947  if (!print_config.formatCommand(str))
948  {
949  ROS_ERROR("[%s] Could not format the print config command", name_.c_str());
950  return false;
951  }
952  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
953  bool success = writeSerial(str);
954  lock.unlock();
955  if (!success)
956  {
957  return false;
958  }
959  uint8_t c;
960  uint64_t timespec_diff_ns;
961  timespec tnow, t_loop;
962  clock_gettime(CLOCK_MONOTONIC_RAW, &tnow);
963  t_loop = tnow;
964  lock.lock();
965  ROS_INFO("[%s] Printing user configuration:", name_.c_str());
966  do
967  {
968  /* run the next readsome() only if there are available bytes in the input buffer.*/
969  if (usbStreamIn_.rdbuf()->in_avail() > 0)
970  {
971  /* read bytes 1 by 1 and print them.*/
972  usbStreamIn_.readsome((char*)&c, 1);
973  printf("%c", c);
974  }
975  clock_gettime(CLOCK_MONOTONIC_RAW, &t_loop);
976  timespec_diff_ns = (t_loop.tv_sec - tnow.tv_sec) * 1e9 + (t_loop.tv_nsec - tnow.tv_nsec);
977  } while (timespec_diff_ns < 1e9); // run the loop for 1sec.
978  lock.unlock();
979  return true;
980 }
981 
982 bool RokubiminiSerialImpl::writeSerial(const std::string& str)
983 {
984  try
985  {
986  // check that the string size doesn't exceed the serial input buffer's size (64) of the device.
987  if (str.size() > 64)
988  {
989  ROS_WARN("[%s] String's length exceeds permittable limit (64)", name_.c_str());
990  return false;
991  }
992  ROS_DEBUG("[%s] Number of chars: %zu", name_.c_str(), str.size());
993  ROS_DEBUG("[%s] String chars: %s", name_.c_str(), str.c_str());
994  if (usbStreamIn_.is_open() && usbStreamOut_.is_open())
995  {
996  usbStreamIn_.sync();
997  char cstr[str.size() + 1];
998  strcpy(cstr, str.c_str());
999  for (uint8_t i = 0; i < str.size(); i++)
1000  {
1001  usbStreamOut_.put(cstr[i]);
1002  std::this_thread::sleep_for(std::chrono::microseconds(5000));
1003  usbStreamOut_.flush();
1004  }
1005  if ((usbStreamOut_.fail() | usbStreamIn_.fail()) != 0)
1006  {
1007  ROS_WARN("[%s] Serial Write or Read failed", name_.c_str());
1008  return false;
1009  }
1010  }
1011  else
1012  {
1013  ROS_WARN("[%s] Streams are not yet open.", name_.c_str());
1014  return false;
1015  }
1016  }
1017  catch (const std::exception& e)
1018  {
1019  ROS_ERROR("[%s] %s", name_.c_str(), e.what());
1020  return false;
1021  }
1022  return true;
1023 }
1024 
1025 bool RokubiminiSerialImpl::firmwareUpdate(const std::string& filePath)
1026 {
1027  pid_t pid;
1028  int status;
1029  ROS_INFO("Flashing firmware...");
1030  std::unique_lock<std::recursive_mutex> lock(serialMutex_);
1031  setHardwareReset();
1032  pid = fork();
1033 
1034  if (pid == -1)
1035  {
1036  // pid == -1 means error occured
1037  ROS_ERROR("Could not fork, error occured");
1038  lock.unlock();
1039  return false;
1040  }
1041  else if (pid == 0)
1042  {
1043  ROS_DEBUG("Child process, pid = %u\n", getpid());
1044  // pid == 0 means child process created
1045  char path_argument[filePath.size() + 15];
1046  int ret = sprintf(path_argument, "-Uflash:w:%s:i", filePath.c_str());
1047  if (ret < 0)
1048  {
1049  ROS_ERROR("[%s] sprintf failed to write data", name_.c_str());
1050  lock.unlock();
1051  return false;
1052  }
1053  char port_argument[port_.size() + 2];
1054  ret = sprintf(port_argument, "-P%s", port_.c_str());
1055  if (ret < 0)
1056  {
1057  ROS_ERROR("[%s] sprintf failed to write data", name_.c_str());
1058  lock.unlock();
1059  return false;
1060  }
1061  // the argv list first argument should point to
1062  // filename associated with file being executed
1063  // the array pointer must be terminated by a NULL
1064  // pointer
1065  const char* argv_list[9] = { "avrdude", "-v", "-patmega328p", "-carduino", port_argument,
1066  "-b230400", "-D", path_argument, nullptr };
1067  ROS_DEBUG("Complete command for avrdude:");
1068  for (std::size_t i = 0; i < sizeof(argv_list); i++)
1069  {
1070  ROS_DEBUG("%s", argv_list[i]);
1071  }
1072  ROS_DEBUG("\n");
1073  // the execv() only returns if error occurred (then the return value is -1)
1074  ret = execvp(argv_list[0], const_cast<char* const*>(argv_list));
1075  if (ret < 0)
1076  {
1077  ROS_ERROR("[%s] Execution of avrdude failed: %s", name_.c_str(), strerror(errno));
1078  kill(getpid(), SIGKILL);
1079  }
1080  exit(0);
1081  }
1082  else
1083  {
1084  // a positive number is returned which is the pid of child process
1085  // the parent process calls waitpid() on the child.
1086  // waitpid() system call suspends execution of
1087  // calling process until a child specified by pid
1088  // argument has changed state
1089  // see wait() man page for all the flags or options
1090  // used here
1091  if (waitpid(pid, &status, WUNTRACED) > 0)
1092  {
1093  if (WIFEXITED(status) && !WEXITSTATUS(status))
1094  {
1095  ROS_INFO("[%s] avrdude returned successfully", name_.c_str());
1096  // stop polling and prepare to shutdown
1097  isRunning_ = false;
1098  }
1099  else if (WIFEXITED(status) && WEXITSTATUS(status))
1100  {
1101  if (WEXITSTATUS(status) == 127)
1102  {
1103  // execvp failed
1104  ROS_ERROR("[%s] execvp failed", name_.c_str());
1105  lock.unlock();
1106  return false;
1107  }
1108  else
1109  {
1110  ROS_ERROR("[%s] program terminated normally,"
1111  " but returned a non-zero status",
1112  name_.c_str());
1113  // stop polling and prepare to shutdown
1114  isRunning_ = false;
1115  lock.unlock();
1116  return false;
1117  }
1118  }
1119  else
1120  {
1121  ROS_ERROR("[%s] program didn't terminate normally", name_.c_str());
1122  lock.unlock();
1123  return false;
1124  }
1125  }
1126  else
1127  {
1128  // waitpid() failed
1129  ROS_ERROR("[%s] waitpid() failed", name_.c_str());
1130  lock.unlock();
1131  return false;
1132  }
1133  }
1134  lock.unlock();
1135  return true;
1136 }
1137 } // namespace serial
1138 } // namespace rokubimini
boost::atomic< bool > frameSync_
Flag that indicates if the frame is synced.
void connectionWorker()
Worker threads for managing sensor connections.
ConnectionState getConnectionState() const
Retreive sensor state/status.
bool readDevice(RxFrame &frame)
Reads a raw measurement frame from the serial-port.
bool startup()
This method starts up communication with the device.
bool initSerialPort(const std::string &port)
Sets up and initializes the serial port for communication.
std::ifstream usbStreamIn_
Input stream for the USB file descriptor.
ErrorState getErrorState() const
Gets the current error status.
#define ROS_WARN_THROTTLE(rate,...)
const WrenchType & getWrench() const
bool isConnecting() const
Checks if device is already in the process of connecting.
bool init()
This method initializes internal variables and the device for communication. It connects to the seria...
boost::atomic< ModeState > modeState_
Mode state of the sensor.
bool setSensorConfiguration(const configuration::SensorConfiguration &sensorConfiguration)
Sets a sensor configuration to the device.
double timespecToSec(timespec t)
Converts a timespec to seconds.
bool writeSerial(const std::string &str)
Writes an Ascii string to the serial device.
unsigned long frameCrcErrorCounter_
Counter for frames with CRC errors.
bool setSensorCalibration(const calibration::SensorCalibration &sensorCalibration)
Sets a sensor calibration to the device.
bool printUserConfig()
Prints all the user configurable parameters.
unsigned long frameReceivedCounter_
Received frame counter.
bool setConfigMode()
Sets the device in config mode.
double pollingThreadTimeStep_
If setup, the sensor polling thread will poll at this rate.
bool setInitMode()
Triggers a software reset of the sensor bringing it to a known state.
bool isConnected() const
Quick sensor/state testers.
#define ROS_WARN(...)
boost::atomic< bool > isRunning_
Internal flags/indicators.
uint16_t calcCrc16X25(uint8_t *data, int len)
uint16_t crcCcittUpdate(uint16_t crc, uint8_t data)
Implementation function of the CRC16 X25 checksum for the input data.
std::recursive_mutex readingMutex_
Mutex prohibiting simultaneous access the internal Reading variable.
unsigned long frameSuccessCounter_
Correct frames counter.
Reading serialImplReading_
The internal reading variable. It&#39;s used for providing to client code the sensor readings, through the getReading () function.
std::uint32_t getBaudRateDefinition(const uint32_t &baudRate)
Returns the system definition for the given baudRate.
bool loadConfig()
Loads the configuration of the device.
bool initSensorCommunication(bool keepConnecting)
Initializatin-time helper functions.
std::string getErrorString() const
Retrieves detailed error indication.
bool useDeviceTimeStamps_
Flag to indicate whether time-stamps should be generated using sensor or system time.
std::string port_
The serial port to connect to.
bool firmwareUpdate(const std::string &filePath)
Updates the firmware of the device.
bool isInConfigMode() const
Checks if the ModeState is Config Mode.
#define ROS_INFO(...)
static void init()
#define lo8(x)
unsigned int frameSyncErrorCounter_
Frame sync errors.
bool setForceTorqueFilter(const configuration::ForceTorqueFilter &filter)
Sets a force torque filter to the device.
RokubiminiSerialImpl()=delete
Default constructor is deleted.
timespec timespecAdd(timespec a, timespec b)
Calculates the sum of two timespecs.
void shutdown()
Shuts down the device. It automatically shuts-down threads and disconnects from serial-port.
RxFrame frame_
The internal variable for the receiving frame. This variable represents the raw data received from th...
bool setRunMode()
Sets the device in run mode.
std::ofstream usbStreamOut_
Output stream for the USB file descriptor.
#define ROS_DEBUG_STREAM(args)
bool saveConfigParameter()
Saves the current configuration to the device.
bool hasError() const
Checks the connection has errors.
bool runInThreadedMode_
Flag to indicate whether the driver should setup worker threads at startup.
The frame transmitted and received via the serial bus.
void getReading(rokubimini::Reading &reading)
Gets the internal reading variable.
unsigned int maxFrameSyncErrorCounts_
Maximum acceptable frame sync errors.
bool setForceTorqueOffset(const Eigen::Matrix< double, 6, 1 > &forceTorqueOffset)
Sets a force torque offset to the device.
bool setCommunicationSetup(const configuration::SensorConfiguration &sensorConfiguration, const uint8_t &dataFormat, const uint32_t &baudRate)
Sets communication setup for the device. This includes setting the temperature compensation, the matrix calibration, the data format and the baud rate.
void pollingWorker()
Worker threads for polling the sensors.
const TempType & getTemperature() const
static Time now()
boost::atomic< ConnectionState > connectionState_
Internal connection state.
unsigned long pollingSyncErrorCounter_
Internal statistics and error counters.
boost::atomic< ErrorState > errorState_
Internal error state.
std::recursive_mutex serialMutex_
Mutex prohibiting simultaneous access to Serial device.
bool setHardwareReset()
Triggers a hardware reset of the sensor.
double diffSec(timespec a, timespec b)
Calculates the difference (b-a) of two timespecs in seconds.
#define ROS_ERROR(...)
const Eigen::Matrix< double, 6, 6 > & getCalibrationMatrix() const
bool connect()
Connects to the Serial-over-USB port.
boost::thread connectionThread_
Connection thread handle.
uint8_t frameHeader
The frame header value.
boost::atomic< int > usbFileDescriptor_
The USB file descriptor.
#define hi8(x)
std::uint32_t baudRate_
The baud rate of the serial communication.
System dependencies.
#define ROS_DEBUG(...)
bool startPollingThread()
This method starts up the polling thread.
boost::thread pollingThread_
Polling thread handle.


rokubimini_serial
Author(s):
autogenerated on Wed Mar 3 2021 03:09:18