Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
RoboClaw.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelasp
4 //
5 // Library: liblaelaps
6 //
7 // File: RoboClaw.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2016-04-13 11:47:47 -0600 (Wed, 13 Apr 2016) $
12  * $Rev: 4386 $
13  *
14  * \brief RoboClaw motor controller class implementation.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \par Copyright
19  * \h_copy 2015-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 /*
24  * @EulaBegin@
25  *
26  * Unless otherwise stated explicitly, all materials contained are copyrighted
27  * and may not be used without RoadNarrows LLC's written consent,
28  * except as provided in these terms and conditions or in the copyright
29  * notice (documents and software) or other proprietary notice provided with
30  * the relevant materials.
31  *
32  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
33  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
34  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
35  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
36  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
37  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
40  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
42  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
43  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
44  *
45  * @EulaEnd@
46  */
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 
53 #include <string>
54 
55 #include "rnr/rnrconfig.h"
56 #include "rnr/log.h"
57 #include "rnr/serdev.h"
58 
59 #include "Laelaps/RoboClaw.h"
60 
61 using namespace std;
62 using namespace motor::roboclaw;
63 
64 //-----------------------------------------------------------------------------
65 // Private
66 //-----------------------------------------------------------------------------
67 
68 /*!
69  * \brief Test for valid motor index.
70  *
71  * This macro only within RoboClaw methods.
72  *
73  * \param motor Motor index.
74  *
75  * \return On failure, forces return from calling function with the appropriate
76  * error code.
77  */
78 #define ROBOCLAW_TRY_MOTOR(motor) \
79  do \
80  { \
81  if( !isValidMotor(motor) ) \
82  { \
83  LOGERROR("Motor controller 0x%02x: Motor index %d invalid.", \
84  m_address, motor); \
85  return RC_ERROR; \
86  } \
87  } while(0)
88 
89 #undef ROBOCLAW_DBG_ENABLE ///< debug switch
90 #ifdef ROBOCLAW_DBG_ENABLE
91 
92 /*!
93  * \brief Debug print.
94  *
95  * \param addr Motor controller address
96  * \param cmdNum Command number.
97  * \param fmt Format string.
98  * \param ... Optional variable arguments for fmt.
99  */
100 #define ROBOCLAW_DBG(addr, cmdNum, fmt, ...) \
101  fprintf(stderr, "DBG: %s[%d]: Motor controller=0x%02x: Cmd=%d: " fmt, \
102  __FILE__, __LINE__, addr, cmdNum, ##__VA_ARGS__)
103 
104 /*!
105  * \brief Debug print message.
106  *
107  * \param addr Motor controller address
108  * \param cmdNum Command number.
109  * \param buf Buffer to print.
110  * \param len Buffer length.
111  * \param fmt Format string.
112  * \param ... Optional variable arguments for fmt.
113  */
114 #define ROBOCLAW_DBG_MSG(addr, cmdNum, buf, len, fmt, ...) \
115  do \
116  { \
117  fprintf(stderr, "DBG: %s[%d]: Motor controller=0x%02x: Cmd=%d " fmt, \
118  __FILE__, __LINE__, addr, cmdNum, ##__VA_ARGS__); \
119  for(int i=0;i<len; ++i) { fprintf(stderr, "0x%02x ", buf[i]); } \
120  fprintf(stderr, "\n"); \
121  } while(0)
122 
123 /*!
124  * \brief Debug print buffer.
125  *
126  * \param buf Buffer to print.
127  * \param len Buffer length.
128  * \param fmt Format string.
129  * \param ... Optional variable arguments for fmt.
130  */
131 #define ROBOCLAW_DBG_BUF(buf, len, fmt, ...) \
132  do \
133  { \
134  fprintf(stderr, "DBG: %s[%d]: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
135  for(int i=0;i<len; ++i) { fprintf(stderr, "0x%02x ", buf[i]); } \
136  fprintf(stderr, "\n"); \
137  } while(0)
138 
139 #else // Disabled
140 
141 /*!
142  * \brief Debug print disabled.
143  */
144 #define ROBOCLAW_DBG(addr, cmdNum, fmt, ...)
145 
146 /*!
147  * \brief Debug print message disabled.
148  */
149 #define ROBOCLAW_DBG_MSG(addr, cmdNum, buf, len, fmt, ...)
150 
151 /*!
152  * \brief Debug print buffer disabled.
153  */
154 #define ROBOCLAW_DBG_BUF(buf, len, fmt, ...) \
156 #endif // ROBOCLAW_DBG_ENABLE
157 
158 /*!
159  * \brief Integer absolute value.
160  *
161  * \param a Integer value.
162  *
163  * \return |a|
164  */
165 inline s64_t iabs(s64_t a)
166 {
167  return a>=0? a: -a;
168 }
169 
170 
171 //-----------------------------------------------------------------------------
172 // Class RoboClawChipSelect
173 //-----------------------------------------------------------------------------
174 
175 RoboClawChipSelect::RoboClawChipSelect()
176 {
177  m_addrLast = 0;
178 }
179 
180 RoboClawChipSelect::~RoboClawChipSelect()
181 {
182  m_addrLast = 0;
183 }
184 
185 void RoboClawChipSelect::select(int fd, byte_t addrSel)
186 {
187  m_addrLast = addrSel;
188 }
189 
190 /*! Fixed no-op selection object. */
192 
193 
194 //-----------------------------------------------------------------------------
195 // Class RoboClawComm
196 //-----------------------------------------------------------------------------
197 
198 RoboClawComm::RoboClawComm()
199 {
200  m_nBaudRate = 0;
201  m_fd = -1;
202  m_pChipSelect = &csNoOp;
203  m_bDbgEnable = false;
204 }
205 
206 RoboClawComm::RoboClawComm(std::string &strDevName,
207  int nBaudRate,
208  RoboClawChipSelect *pChipSelect)
209 {
210  m_nBaudRate = 0;
211  m_fd = -1;
212  m_pChipSelect = &csNoOp;
213  m_bDbgEnable = false;
214 
215  open(strDevName, nBaudRate, pChipSelect);
216 }
218 RoboClawComm::~RoboClawComm()
219 {
220  close();
221 }
222 
223 int RoboClawComm::open(std::string &strDevName,
224  int nBaudRate,
225  RoboClawChipSelect *pChipSelect)
226 {
227  m_fd = SerDevOpen(strDevName.c_str(), nBaudRate, 8, 'N', 1, false, false);
228 
229  if( m_fd < 0 )
230  {
231  LOGERROR("Failed to open %s@%d.", strDevName.c_str(), nBaudRate);
232  return RC_ERROR;
233  }
235  flushInput();
236 
237  m_strDevName = strDevName;
238  m_nBaudRate = nBaudRate;
239  m_pChipSelect = pChipSelect == NULL? &csNoOp: pChipSelect;
240 
241  LOGDIAG3("Opened interface to RoboClaw motor controller %s@%d.",
242  strDevName.c_str(), nBaudRate);
243 
244  return OK;
245 }
246 
247 int RoboClawComm::close()
248 {
249  if( m_fd >= 0 )
250  {
251  SerDevClose(m_fd);
252  LOGDIAG3("Closed %s interface to RoboClaw motor controller.",
253  m_strDevName.c_str());
254  }
255 
256  m_strDevName.clear();
257  m_nBaudRate = 0;
258  m_fd = -1;
259  m_pChipSelect = &csNoOp;
260  m_bDbgEnable = false;
261 
262  return OK;
263 }
264 
265 int RoboClawComm::flushInput()
266 {
267  SerDevFIFOInputFlush(m_fd);
268 }
269 
270 int RoboClawComm::execCmd(byte_t cmd[], size_t lenCmd)
271 {
272  int rc;
273 
274  LOGWARN("Motor controller 0x%02x: execCmd() function is deprecated.");
275 
276  if( (rc = sendCmd(cmd, lenCmd)) != OK )
277  {
278  LOGERROR("Motor controller 0x%02x: Failed to send command %u.",
279  cmd[FieldPosAddr], cmd[FieldPosCmd]);
280  flushInput();
281  }
282 
283  return rc;
284 }
285 
286 int RoboClawComm::execCmdWithAckRsp(byte_t cmd[], size_t lenCmd, MsgFmt fmtCmd)
287 {
288  int rc;
289 
290  if( (rc = sendCmd(cmd, lenCmd, fmtCmd)) == OK )
291  {
292  if( (rc = recvAck()) != OK )
293  {
294  LOGERROR("Motor controller 0x%02x: Failed to receive ack for command %u.",
295  cmd[FieldPosAddr], cmd[FieldPosCmd]);
296  rc = RC_ERROR;
297  }
298  }
299  else
300  {
301  LOGERROR("Motor controller 0x%02x: Failed to send command %u.",
302  cmd[FieldPosAddr], cmd[FieldPosCmd]);
303  rc = RC_ERROR;
304  }
305 
306  if( rc == RC_ERROR )
307  {
308  flushInput();
309  }
310 
311  return rc;
312 }
313 
314 int RoboClawComm::execCmdWithDataRsp(byte_t cmd[], size_t lenCmd,
315  byte_t rsp[], size_t lenRsp,
316  MsgFmt fmtCmd, MsgFmt fmtRsp)
317 {
318  uint_t uCrcCalc;
319  int n;
320  int rc;
321 
322  if( (rc = sendCmd(cmd, lenCmd, fmtCmd)) == OK )
323  {
324  if( (n = recvDataRsp(rsp, lenRsp)) == (int)lenRsp )
325  {
326  if( fmtRsp == MsgWithCrc )
327  {
328  uCrcCalc = crc16(m_uCrcCmd, rsp, lenRsp-2);
329  if( m_uCrcRsp != uCrcCalc )
330  {
331  LOGERROR("Motor controller 0x%02x: "
332  "Command %u response CRC-16 mismatch: "
333  "Expected 0x%04x, received 0x%04x.",
334  cmd[FieldPosAddr], cmd[FieldPosCmd],
335  uCrcCalc, m_uCrcRsp);
336  rc = RC_ERROR;
337  }
338  }
339  }
340  else if( n > 0 )
341  {
342  LOGWARN("Motor controller 0x%02x: "
343  "Command %u received partial response of %d/%zu bytes - ignoring.",
344  cmd[FieldPosAddr], cmd[FieldPosCmd], n, lenRsp);
345  rc = RC_ERROR;
346  }
347  else
348  {
349  LOGERROR("Motor controller 0x%02x: Command %u received no response.",
350  cmd[FieldPosAddr], cmd[FieldPosCmd]);
351  rc = RC_ERROR;
352  }
353  }
354  else
355  {
356  LOGERROR("Motor controller 0x%02x: Failed to send command %u.",
357  cmd[FieldPosAddr], cmd[FieldPosCmd]);
358  rc = RC_ERROR;
359  }
360 
361  if( rc == RC_ERROR )
362  {
363  flushInput();
364  }
365 
366  return rc;
367 }
368 
369 int RoboClawComm::sendCmd(byte_t cmd[], size_t lenCmd, MsgFmt fmtCmd)
370 {
371  m_pChipSelect->select(m_fd, cmd[FieldPosAddr]);
372 
373  // command's CRC
374  m_uCrcCmd = crc16(0, cmd, lenCmd);
375 
376  // append CRC
377  if( fmtCmd == MsgWithCrc )
378  {
379  pack16(m_uCrcCmd, &cmd[lenCmd]);
380  lenCmd += 2;
381  }
382 
383  if( m_bDbgEnable )
384  {
385  ROBOCLAW_DBG_BUF(cmd, lenCmd,
386  "Motor controller=0x%02x: Cmd=%d: CRC=0x%04x: sendCmd(): ",
387  cmd[FieldPosAddr], cmd[FieldPosCmd], m_uCrcCmd);
388  }
389 
390  if( SerDevWrite(m_fd, cmd, lenCmd, CmdTimeout) == lenCmd )
391  {
392  return OK;
393  }
394  else
395  {
396  return RC_ERROR;
397  }
398 }
399 
400 int RoboClawComm::recvDataRsp(byte_t rsp[], size_t lenRsp)
401 {
402  ssize_t n;
403 
404  if( (n = SerDevRead(m_fd, rsp, lenRsp, RspTimeout)) >= 0 )
405  {
406  // last two bytes are the 16-bit crc
407  if( n >= 2 )
408  {
409  unpack16(&rsp[n-2], m_uCrcRsp);
410  }
412  if( m_bDbgEnable )
413  {
414  ROBOCLAW_DBG_BUF(rsp, n, "recvDataRsp(): ");
415  }
416 
417  return (int)n;
418  }
419  else
420  {
421  return RC_ERROR;
422  }
423 }
424 
425 int RoboClawComm::recvAck()
426 {
427  ssize_t n;
428  byte_t ack;
429 
430  n = SerDevRead(m_fd, &ack, 1, RspTimeout);
431 
432  if( n != 1 )
433  {
434  return RC_ERROR;
435  }
437  if( m_bDbgEnable )
438  {
439  ROBOCLAW_DBG_BUF((&ack), n, "recvAck(): ");
440  }
441 
442  if( ack != RspAck )
443  {
444  return RC_ERROR;
445  }
446  else
447  {
448  return OK;
449  }
450 }
451 
452 int RoboClawComm::pack16(uint_t val, byte_t buf[])
453 {
454  buf[0] = (byte_t)((val >> 8) & 0xff);
455  buf[1] = (byte_t)(val & 0xff);
456  return 2;
457 }
458 
459 int RoboClawComm::unpack16(byte_t buf[], uint_t &val)
460 {
461  val = ((uint_t)(buf[0]) << 8) | (uint_t)buf[1];
462  return 2;
463 }
464 
465 int RoboClawComm::unpack16(byte_t buf[], int &val)
466 {
467  s16_t v;
468 
469  v = ((s16_t)(buf[0]) << 8) | (s16_t)buf[1];
470  val = (int)v;
471 
472  return 2;
473 }
474 
475 int RoboClawComm::pack32(uint_t val, byte_t buf[])
476 {
477  buf[0] = (byte_t)((val >> 24) & 0xff);
478  buf[1] = (byte_t)((val >> 16) & 0xff);
479  buf[2] = (byte_t)((val >> 8) & 0xff);
480  buf[3] = (byte_t)(val & 0xff);
481  return 4;
482 }
483 
484 int RoboClawComm::unpack32(byte_t buf[], uint_t &val)
485 {
486  val = ((uint_t)(buf[0]) << 24) |
487  ((uint_t)(buf[1]) << 16) |
488  ((uint_t)(buf[2]) << 8) |
489  (uint_t)buf[3];
490  return 4;
491 }
492 
493 int RoboClawComm::unpack32(byte_t buf[], int &val)
494 {
495  s32_t v;
496 
497  v = ((s32_t)(buf[0]) << 24) |
498  ((s32_t)(buf[1]) << 16) |
499  ((s32_t)(buf[2]) << 8) |
500  (s32_t)buf[3];
501  val = (int)v;
502 
503  return 4;
504 }
505 
506 byte_t RoboClawComm::checksum(byte_t buf[], size_t lenBuf, bool bAck)
507 {
508  uint_t sum;
509  byte_t chksum;
510  size_t i;
511 
512  for(i=0, sum=0; i<lenBuf; ++i)
513  {
514  sum += (uint_t)buf[i];
515  }
516 
517  chksum = (byte_t)(sum) & CheckSumMask;
518 
519  if( bAck )
520  {
521  chksum |= AckReqBit;
522  }
523 
524  return chksum;
525 }
526 
527 uint_t RoboClawComm::crc16(uint_t crc, byte_t buf[], size_t lenBuf)
528 {
529  size_t i;
530  size_t bit;
531 
532  for(i = 0; i < lenBuf; ++i)
533  {
534  crc = crc ^ ((uint_t)buf[i] << 8);
535 
536  for(bit = 0; bit < 8; ++bit)
537  {
538  if( crc & 0x8000 )
539  {
540  crc = (crc << 1) ^ 0x1021;
541  }
542  else
543  {
544  crc = crc << 1;
545  }
546  }
547  }
548  return crc & 0xffff;
549 }
550 
551 
552 //-----------------------------------------------------------------------------
553 // Class RoboClaw
554 //-----------------------------------------------------------------------------
555 
556 RoboClaw::RoboClaw(RoboClawComm &comm,
557  const byte_t address,
558  const string &strNameId) :
559  m_comm(comm), m_address(address), m_strNameId(strNameId)
560 {
561  for(int i=0; i<NumMotors; ++i)
562  {
563  m_nMotorDir[i] = MotorDirNormal;
564  m_nEncPrev[i] = 0;
565  m_nEncoder[i] = 0;
566  }
567 }
568 
569 RoboClaw::~RoboClaw()
570 {
571  if( m_comm.isOpen() )
572  {
573  cmdStop();
574  }
575 }
576 
577 bool RoboClaw::probe(byte_t address)
578 {
579  byte_t cmd[CmdMaxBufLen];
580  byte_t rsp[RspMaxBufLen];
581  size_t n = 0;
582  int rc;
583 
584  cmd[n++] = address;
585  cmd[n++] = CmdReadMainBattVolt; // probe with a safe read command
586 
587  rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4);
589  return rc == OK;
590 }
591 
592 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
593 // Identity Command
594 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
595 
596 // Command 21
597 int RoboClaw::cmdReadFwVersion(string &strFwVer)
598 {
599  byte_t cmd[CmdMaxBufLen];
600  byte_t rsp[RspMaxBufLen];
601  uint_t uCrcCmd;
602  uint_t uCrcRsp;
603  uint_t uCrcCalc;
604  size_t n = 0;
605 
606  cmd[n++] = m_address;
607  cmd[n++] = CmdReadFwVersion;
609  if( m_comm.sendCmd(cmd, n) != OK )
610  {
611  return RC_ERROR;
612  }
613 
614  n = m_comm.recvDataRsp(rsp, ParamVerLen);
615 
616  if( n > ParamVerLenMin )
617  {
618  //
619  // Validate CRC
620  //
621  uCrcCmd = m_comm.getLastCmdCrc();
622  uCrcRsp = m_comm.getLastRspCrc();
623  uCrcCalc = m_comm.crc16(uCrcCmd, rsp, n-2);
624 
625  if( uCrcRsp != uCrcCalc )
626  {
627  LOGERROR("Motor controller 0x%02x: "
628  "Command %u response CRC-16 mismatch: "
629  "Expected 0x%04x, received 0x%04x.",
630  cmd[FieldPosAddr], cmd[FieldPosCmd],
631  uCrcCalc, uCrcRsp);
632  return RC_ERROR;
633  }
634 
635  rsp[n-ParamVerLenMin] = 0; // take off trailing junk
636  strFwVer = (char *)rsp; // convert to string
637 
638  LOGDIAG3("%s.", strFwVer.c_str());
639 
640  return OK;
641  }
642  else
643  {
644  LOGERROR("Motor controller 0x%02x: "
645  "Failed to receive firmware version.",
646  m_address);
647  return RC_ERROR;
648  }
649 }
650 
651 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
652 // Main Battery Voltage Commands
653 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
654 
655 // Command 24
656 int RoboClaw::cmdReadMainBattVoltage(double &volts)
657 {
658  byte_t cmd[CmdMaxBufLen];
659  byte_t rsp[RspMaxBufLen];
660  size_t n = 0;
661  uint_t val;
662  int rc;
663 
664  cmd[n++] = m_address;
665  cmd[n++] = CmdReadMainBattVolt;
666 
667  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
668  {
669  m_comm.unpack16(rsp, val);
670  volts = (double)val * ParamVoltScale;
671  }
672 
673  return rc;
674 }
675 
676 void RoboClaw::getMainBattCutoffRange(double &minmin, double &maxmax) const
677 {
678  minmin = ParamVoltMainMin;
679  maxmax = ParamVoltMax;
680 }
681 
682 // Command 59
683 int RoboClaw::cmdReadMainBattCutoffs(double &min, double &max)
684 {
685  byte_t cmd[CmdMaxBufLen];
686  byte_t rsp[RspMaxBufLen];
687  size_t n = 0;
688  uint_t valMin, valMax;
689  int rc;
690 
691  cmd[n++] = m_address;
692  cmd[n++] = CmdReadMainBattCutoffs;
693 
694  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 6)) == OK )
695  {
696  n = m_comm.unpack16(rsp, valMin);
697  n += m_comm.unpack16(rsp+n, valMax);
698 
699  ROBOCLAW_DBG(cmd[FieldPosAddr], cmd[FieldPosCmd],
700  "MainBatt Cutoffs [%u, %u]\n", valMin, valMax);
701 
702  min = (double)valMin * ParamVoltScale;
703  max = (double)valMax * ParamVoltScale;
704  }
705 
706  return rc;
707 }
708 
709 // Command 57
710 int RoboClaw::cmdSetMainBattCutoffs(const double min, const double max)
711 {
712  byte_t cmd[CmdMaxBufLen];
713  size_t k, n = 0;
714  double fMin, fMax;
715  uint_t valMin, valMax;
716  int rc;
717 
718  fMin = min / ParamVoltScale;
719  valMin = (uint_t)fcap(fMin, ParamVoltMainMin, ParamVoltMax);
720 
721  fMax = max / ParamVoltScale;
722  valMax = (uint_t)fcap(fMax, fMin, ParamVoltMax);
723 
724  //
725  // header
726  //
727  cmd[n++] = m_address;
728  cmd[n++] = CmdSetMainBattCutoffs;
729 
730  //
731  // values
732  //
733  k = m_comm.pack16(valMin, cmd+n);
734  n += k;
735 
736  k = m_comm.pack16(valMax, cmd+n);
737  n += k;
738 
739  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
740 }
741 
742 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
743 // Motor Current Commands
744 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
745 
746 // Command 49
747 //
748 // Note: RoboClaw User Manual v5 error. The data are signed 16-bit integers
749 //
750 int RoboClaw::cmdReadMotorCurrentDraw(double &amps1, double &amps2)
751 {
752  byte_t cmd[CmdMaxBufLen];
753  byte_t rsp[RspMaxBufLen];
754  size_t k, n = 0;
755  int val1, val2;
756  int rc;
757 
758  cmd[n++] = m_address;
759  cmd[n++] = CmdReadMotorDraw;
760 
761  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 6)) == OK )
762  {
763  n = 0;
764 
765  k = m_comm.unpack16(rsp, val1);
766  n += k;
767 
768  k = m_comm.unpack16(rsp+n, val2);
769  n += k;
770 
771  amps1 = (double)val1 * ParamAmpScale;
772  amps2 = (double)val2 * ParamAmpScale;
773  }
774 
775  return rc;
776 }
777 
778 // Command 135, 136
779 //
780 // Note: RoboClaw User Manual v5 error. Each value is 32 bits.
781 //
782 int RoboClaw::cmdReadMotorMaxCurrentLimit(int motor, double &maxAmps)
783 {
784  byte_t cmd[CmdMaxBufLen];
785  byte_t rsp[RspMaxBufLen];
786  size_t k, n = 0;
787  uint_t val1, val2;
788  double minAmps; // always zero - so ignore
789  int rc;
790 
791  cmd[n++] = m_address;
792  cmd[n++] = motor == Motor1? CmdReadMaxCurrentMot1: CmdReadMaxCurrentMot2;
794  m_comm.enableDbg(true);
795 
796  rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 10);
797 
798  if( rc == OK )
799  {
800  n = 0;
801 
802  k = m_comm.unpack32(rsp, val1);
803  n += k;
804 
805  k = m_comm.unpack32(rsp+n, val2);
806  n += k;
807 
808  maxAmps = (double)val1 * ParamAmpScale;
809  minAmps = (double)val2 * ParamAmpScale;
810  }
811 
812  m_comm.enableDbg(false);
813 
814  return rc;
815 }
816 
817 // Command 133, 134
818 //
819 // Note: RoboClaw User Manual v5 error. Each value is 32 bits.
820 //
821 int RoboClaw::cmdSetMotorMaxCurrentLimit(int motor, const double maxAmps)
822 {
823  byte_t cmd[CmdMaxBufLen];
824  byte_t rsp[RspMaxBufLen];
825  size_t k, n = 0;
826  double fMax;
827  uint_t valMin; // always zero
828  uint_t valMax;
829  int rc;
830 
831  // convert interface values
832  // RDK fMax = fcap(maxAmps, ParamAmpMinSane, ParamAmpMax);
833  fMax = fcap(maxAmps, ParamAmpMinSane, 100.0); // RDK
834  valMax = (uint_t)(fMax / ParamAmpScale);
835 
836  valMin = 0;
837 
838  //
839  // pack header
840  //
841  cmd[n++] = m_address;
842  cmd[n++] = motor == Motor1? CmdSetMaxCurrentMot1: CmdSetMaxCurrentMot2;
843 
844  //
845  // pack values
846  //
847  k = m_comm.pack32(valMax, cmd+n);
848  n += k;
849 
850  k = m_comm.pack32(valMin, cmd+n);
851  n += k;
852 
853  // execute
854  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
855 }
856 
857 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
858 // Logic Battery Voltage Commands
859 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
860 
861 // Command 25
862 int RoboClaw::cmdReadLogicVoltage(double &volts)
863 {
864  byte_t cmd[CmdMaxBufLen];
865  byte_t rsp[RspMaxBufLen];
866  size_t n = 0;
867  uint_t val;
868  int rc;
869 
870  cmd[n++] = m_address;
871  cmd[n++] = CmdReadLogicVolt;
872 
873  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
874  {
875  m_comm.unpack16(rsp, val);
876  volts = (double)val * ParamVoltScale;
877  }
878 
879  return rc;
880 }
881 
882 void RoboClaw::getLogicCutoffRange(double &minmin, double &maxmax) const
883 {
884  minmin = ParamVoltLogicMin;
885  maxmax = ParamVoltMax;
886 }
887 
888 // Command 60
889 int RoboClaw::cmdReadLogicCutoffs(double &min, double &max)
890 {
891  byte_t cmd[CmdMaxBufLen];
892  byte_t rsp[RspMaxBufLen];
893  size_t n = 0;
894  uint_t valMin, valMax;
895  int rc;
896 
897  cmd[n++] = m_address;
898  cmd[n++] = CmdReadLogicCutoffs;
899 
900  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 6)) == OK )
901  {
902  n = m_comm.unpack16(rsp, valMin);
903  n += m_comm.unpack16(rsp+n, valMax);
904 
905  ROBOCLAW_DBG(cmd[FieldPosAddr], cmd[FieldPosCmd],
906  "Logic Cutoffs [%u, %u]\n", valMin, valMax);
907 
908  min = (double)valMin * ParamVoltScale;
909  max = (double)valMax * ParamVoltScale;
910  }
911 
912  return rc;
913 }
914 
915 // Command 58
916 int RoboClaw::cmdSetLogicCutoffs(const double min, const double max)
917 {
918  byte_t cmd[CmdMaxBufLen];
919  size_t k, n = 0;
920  double fMin, fMax;
921  uint_t valMin, valMax;
922  int rc;
923 
924  fMin = min / ParamVoltScale;
925  valMin = (uint_t)fcap(fMin, ParamVoltLogicMin, ParamVoltMax);
926 
927  fMax = max / ParamVoltScale;
928  valMax = (uint_t)fcap(fMax, fMin, ParamVoltMax);
929 
930  cmd[n++] = m_address;
931  cmd[n++] = CmdSetLogicCutoffs;
932 
933  k = m_comm.pack16(valMin, cmd+n);
934  n += k;
935 
936  k = m_comm.pack16(valMax, cmd+n);
937  n += k;
938 
939  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
940 }
941 
942 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
943 // Motor PID Commands
944 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
945 
946 // Command 55, 56
947 int RoboClaw::cmdReadVelocityPidConst(int motor,
948  u32_t &Kp,
949  u32_t &Ki,
950  u32_t &Kd,
951  u32_t &qpps)
952 {
953  byte_t cmd[CmdMaxBufLen];
954  byte_t rsp[RspMaxBufLen];
955  size_t k, n = 0;
956  int rc;
957 
958  cmd[n++] = m_address;
959  cmd[n++] = motor == Motor1? CmdReadVelPidMot1: CmdReadVelPidMot2;
960 
961  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 18)) == OK )
962  {
963  n = 0;
964 
965  k = m_comm.unpack32(rsp, Kp);
966  n += k;
967 
968  k = m_comm.unpack32(rsp+n, Ki);
969  n += k;
970 
971  k = m_comm.unpack32(rsp+n, Kd);
972  n += k;
973 
974  k = m_comm.unpack32(rsp+n, qpps);
975  n += k;
976 
977  ROBOCLAW_DBG(cmd[FieldPosAddr], cmd[FieldPosCmd],
978  "VelPid: motor=%d, P=0x%08x, I=0x%08x, D=0x%08x, Q=%u\n",
979  motor, Kp, Ki, Kd, qpps);
980  }
981 
982  return rc;
983 }
984 
985 // Command 28,29
986 int RoboClaw::cmdSetVelocityPidConst(int motor,
987  const u32_t Kp,
988  const u32_t Ki,
989  const u32_t Kd,
990  const u32_t qpps)
991 {
992  byte_t cmd[CmdMaxBufLen];
993  byte_t rsp[RspMaxBufLen];
994  size_t k, n = 0;
995  int rc;
996 
998 
999  cmd[n++] = m_address;
1000  cmd[n++] = motor == Motor1? CmdSetVelPidMot1: CmdSetVelPidMot2;
1001 
1002  // order is strangly D,P,I,QPPS
1003  k = m_comm.pack32(Kd, cmd+n);
1004  n += k;
1005 
1006  k = m_comm.pack32(Kp, cmd+n);
1007  n += k;
1008 
1009  k = m_comm.pack32(Ki, cmd+n);
1010  n += k;
1011 
1012  k = m_comm.pack32(qpps, cmd+n);
1013  n += k;
1014 
1015  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1016 }
1017 
1018 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1019 // Health and Status Commands
1020 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1021 
1022 // Command 82
1023 int RoboClaw::cmdReadBoardTemperature(double &temp)
1024 {
1025  byte_t cmd[CmdMaxBufLen];
1026  byte_t rsp[RspMaxBufLen];
1027  size_t n = 0;
1028  uint_t val;
1029  int rc;
1030 
1031  cmd[n++] = m_address;
1032  cmd[n++] = CmdReadTemp;
1033 
1034  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
1035  {
1036  m_comm.unpack16(rsp, val);
1037  temp = (double)val * ParamTempScale;
1038  }
1039 
1040  return rc;
1041 }
1042 
1043 // Command 90
1044 int RoboClaw::cmdReadStatus(uint_t &status)
1045 {
1046  byte_t cmd[CmdMaxBufLen];
1047  byte_t rsp[RspMaxBufLen];
1048  size_t n = 0;
1049  uint_t val;
1050  int rc;
1051 
1052  cmd[n++] = m_address;
1053  cmd[n++] = CmdReadStatus;
1054 
1055  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
1056  {
1057  m_comm.unpack16(rsp, status);
1058  }
1059 
1060  return rc;
1061 }
1062 
1063 // Command 47
1064 int RoboClaw::cmdReadCmdBufLengths(uint_t &len1, uint_t &len2)
1065 {
1066  byte_t cmd[CmdMaxBufLen];
1067  byte_t rsp[RspMaxBufLen];
1068  size_t n = 0;
1069  uint_t val;
1070  int rc;
1071 
1072  cmd[n++] = m_address;
1073  cmd[n++] = CmdReadBufLen;
1074 
1075  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
1076  {
1077  len1 = (uint_t)rsp[0];
1078  len2 = (uint_t)rsp[1];
1079  }
1080 
1081  return rc;
1082 }
1083 
1084 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1085 // Encoder Commands
1086 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1087 
1088 // Command 91
1089 int RoboClaw::cmdReadEncoderMode(byte_t &mode1, byte_t &mode2)
1090 {
1091  byte_t cmd[CmdMaxBufLen];
1092  byte_t rsp[RspMaxBufLen];
1093  size_t n = 0;
1094  uint_t val;
1095  int rc;
1096 
1097  cmd[n++] = m_address;
1098  cmd[n++] = CmdReadEncoderMode;
1099 
1100  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 4)) == OK )
1101  {
1102  mode1 = rsp[0];
1103  mode2 = rsp[1];
1104  //LOGDIAG3("Motor controller 0x%02x: mode1=0x%02x, mode2=0x%02x.",
1105  // m_address, mode1, mode2);
1106  }
1107 
1108  return rc;
1109 }
1110 
1111 // Command 92, 93
1112 int RoboClaw::cmdSetEncoderMode(int motor, byte_t mode)
1113 {
1114  byte_t cmd[CmdMaxBufLen];
1115  byte_t mask = ParamEncModeRCAnalogBit | ParamEncModeQuadAbsBit;
1116  size_t n = 0;
1117 
1118  ROBOCLAW_TRY_MOTOR(motor);
1119 
1120  cmd[n++] = m_address;
1121  cmd[n++] = motor == Motor1? CmdSetEncoderModeMot1: CmdSetEncoderModeMot2;
1122  cmd[n++] = (byte_t)(mask & mode);
1124  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1125 }
1126 
1127 // Command 92, 93
1128 int RoboClaw::cmdSetEncoderMode2(byte_t mode1, byte_t mode2)
1129 {
1130  int rc;
1131 
1132  if( (rc = cmdSetEncoderMode(Motor1, mode1)) == OK )
1133  {
1134  rc = cmdSetEncoderMode(Motor2, mode2);
1135  }
1136 
1137  return rc;
1138 }
1140 // Command 20
1141 int RoboClaw::cmdResetQEncoders()
1142 {
1143  byte_t cmd[CmdMaxBufLen];
1144  size_t n = 0;
1145  int rc;
1146 
1147  cmd[n++] = m_address;
1148  cmd[n++] = CmdResetEncoderCntrs;
1149 
1150  if( (rc = m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc)) == OK )
1151  {
1152  for(int i=0; i<NumMotors; ++i)
1153  {
1154  m_nEncPrev[i] = 0;
1155  m_nEncoder[i] = 0;
1156  }
1157  }
1158 
1159  return rc;
1160 }
1161 
1162 // Command 16, 17
1163 int RoboClaw::cmdReadQEncoder(int motor, s64_t &encoder)
1164 {
1165  static s64_t MaxEncDelta = 2048; // maximum encoder delta on wrap
1166 
1167  byte_t cmd[CmdMaxBufLen];
1168  byte_t rsp[RspMaxBufLen];
1169  size_t n = 0;
1170  uint_t enc32; // 32-bit encoder value (quad pulses).
1171  byte_t status; // status bits indicating sign, and over/under flow
1172 
1173  s64_t enc64; // 64-bit signed encoder value (quad pulses).
1174  s64_t delta; // encoder delta from previous value
1175  int rc;
1176 
1177  ROBOCLAW_TRY_MOTOR(motor);
1178 
1179  cmd[n++] = m_address;
1180  cmd[n++] = motor == Motor1? CmdReadEncoderMot1: CmdReadEncoderMot2;
1181 
1182  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 7)) == OK )
1183  {
1184  ROBOCLAW_DBG_MSG(cmd[FieldPosAddr], cmd[FieldPosCmd], rsp, 7,
1185  "cmdReadQEncoder(motor=%d): rsp = ", motor);
1186 
1187  //
1188  // Get 32-bit unsigned encoder value plus its associated status.
1189  //
1190  // Note: This 2-tuple should be atomically kept insync by the firmware but
1191  // this is not always the case. During a sharp velocity change
1192  // (e.g. stopping) the velocity pid will rightly try to back drive.
1193  // The status byte will indicate the _desired_ direction, not the
1194  // actual.
1195  //
1196  n = m_comm.unpack32(rsp, enc32);
1197  status = rsp[n];
1198 
1199  enc64 = (s64_t)enc32;
1200 
1201  //
1202  // Debug left_front encoder to reverse engineer and test encoder algorithm.
1203  //
1204  //if( m_address == 0x80 && motor == Motor1 && m_nEncPrev[motor] != enc64 )
1205  //{
1206  // fprintf(stderr, "DBG %lld 0x%02x\n", enc64, status);
1207  //}
1208 
1209  //
1210  // Track underflow/overflow for now. Not too sure if any action or special
1211  // calculations are needed.
1212  //
1213  // Note: Condition is cleared after reading.
1214  //
1215  if( status & ParamEncStatusUnderFlow )
1216  {
1217  LOGDIAG2("Motor controller 0x%02x: Motor %d: Encoder underflow: "
1218  "Encoder32=%u.",
1219  m_address, motor, enc32);
1220  }
1221  if( status & ParamEncStatusOverFlow )
1222  {
1223  LOGDIAG2("Motor controller 0x%02x: Motor %d: Encoder overflow: "
1224  "Encoder32=%u",
1225  m_address, motor, enc32);
1226  }
1227 
1228  //
1229  // Moving backwards. There are two cases:
1230  // Case 1: no wrap: example: previous 1500, current: 1000
1231  // Case 2: wrap: example: previous 15, current: 4294967200
1232  //
1233  if( status & ParamEncStatusDirBackward )
1234  {
1235  if( enc64 <= m_nEncPrev[motor] ) // no wrap
1236  {
1237  delta = enc64 - m_nEncPrev[motor];
1238  }
1239  else // wrap
1240  {
1241  delta = enc64 - ParamEncQuadMax - m_nEncPrev[motor];
1242  if( iabs(delta) > MaxEncDelta ) // not really backwards
1243  {
1244  delta = enc64 - m_nEncPrev[motor];
1245  }
1246  }
1247  }
1248 
1249  //
1250  // Moving forwards. There are two cases:
1251  // Case 1: no wrap: example: previous 1000, current: 1500
1252  // Case 2: wrap: example: previous 4294967200, current: 15
1253  //
1254  else
1255  {
1256  if( enc64 >= m_nEncPrev[motor] ) // no wrap
1257  {
1258  delta = enc64 - m_nEncPrev[motor];
1259  }
1260  else // wrap
1261  {
1262  delta = enc64 + ParamEncQuadMax - m_nEncPrev[motor];
1263  if( iabs(delta) > MaxEncDelta ) // not really forwards
1264  {
1265  delta = enc64 - m_nEncPrev[motor];
1266  }
1267  }
1268  }
1269 
1270  //
1271  // Update accumulated value and save current as new previous.
1272  //
1273  m_nEncoder[motor] += (m_nMotorDir[motor] * delta);
1274  m_nEncPrev[motor] = enc64;
1275  encoder = m_nEncoder[motor];
1276  }
1277 
1278  return rc;
1279 }
1280 
1281 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1282 // Duty Cycle Commands
1283 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1284 
1285 // Command 34
1286 int RoboClaw::cmdDutyDrive2(double duty1, double duty2)
1287 {
1288  byte_t cmd[CmdMaxBufLen];
1289  int speed1, speed2;
1290  size_t k, n = 0;
1291 
1292  speed1 = (int)((double)duty1 * ParamDutyCycleMax);
1293  speed2 = (int)((double)duty2 * ParamDutyCycleMax);
1294 
1295  cmd[n++] = m_address;
1296  cmd[n++] = CmdDriveDuty;
1298  k = m_comm.pack16((int)speed1, cmd+n);
1299  n += k;
1300 
1301  k = m_comm.pack16((int)speed2, cmd+n);
1302  n += k;
1303 
1304  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1305 }
1306 
1307 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1308 // Speed and Position Commands
1309 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1310 
1311 // Command 18, 19
1312 int RoboClaw::cmdReadQSpeed(int motor, s32_t &speed)
1313 {
1314  byte_t cmd[CmdMaxBufLen];
1315  byte_t rsp[RspMaxBufLen];
1316  size_t n = 0;
1317  uint_t val;
1318  byte_t status;
1319  int rc;
1320 
1321  ROBOCLAW_TRY_MOTOR(motor);
1322 
1323  cmd[n++] = m_address;
1324  cmd[n++] = motor == Motor1? CmdReadSpeedMot1: CmdReadSpeedMot2;
1325 
1326  if( (rc = m_comm.execCmdWithDataRsp(cmd, n, rsp, 7)) == OK )
1327  {
1328  ROBOCLAW_DBG_MSG(cmd[FieldPosAddr], cmd[FieldPosCmd], rsp, 7,
1329  "cmdReadQSpeed(motor=%d): rsp = ", motor);
1330 
1331  n = m_comm.unpack32(rsp, val);
1332  status = rsp[n];
1333  speed = (s32_t)val;
1334 
1335  if( status & ParamEncStatusDirBackward )
1336  {
1337  speed = -speed;
1338  }
1339  }
1340 
1341  return rc;
1342 }
1343 
1344 // Command 35, 36
1345 int RoboClaw::cmdQDrive(int motor, s32_t speed)
1346 {
1347  byte_t cmd[CmdMaxBufLen];
1348  size_t k, n = 0;
1349 
1350  ROBOCLAW_TRY_MOTOR(motor);
1351 
1352  speed *= m_nMotorDir[motor];
1353 
1354  cmd[n++] = m_address;
1355  cmd[n++] = motor == Motor1? CmdDriveQMot1: CmdDriveQMot2;
1357  k = m_comm.pack32((int)speed, cmd+n);
1358  n += k;
1359 
1360  ROBOCLAW_DBG_MSG(cmd[FieldPosAddr], cmd[FieldPosCmd], cmd, n,
1361  "cmdQDrive(motor=%d, speed=%d): cmd = ", motor, speed);
1362 
1363  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1364 }
1365 
1366 // Command 37
1367 int RoboClaw::cmdQDrive2(s32_t speed1, s32_t speed2)
1368 {
1369  byte_t cmd[CmdMaxBufLen];
1370  size_t k, n = 0;
1371 
1372  speed1 *= m_nMotorDir[Motor1];
1373  speed2 *= m_nMotorDir[Motor2];
1374 
1375  cmd[n++] = m_address;
1376  cmd[n++] = CmdDriveQ;
1377 
1378  k = m_comm.pack32((int)speed1, cmd+n);
1379  n += k;
1380 
1381  k = m_comm.pack32((int)speed2, cmd+n);
1382  n += k;
1383 
1384  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1385 }
1386 
1387 // Command 38, 39
1388 int RoboClaw::cmdQDriveWithAccel(int motor, s32_t speed, u32_t accel)
1389 {
1390  byte_t cmd[CmdMaxBufLen];
1391  size_t k, n = 0;
1392 
1393  ROBOCLAW_TRY_MOTOR(motor);
1394 
1395  speed *= m_nMotorDir[motor];
1396 
1397  cmd[n++] = m_address;
1398  cmd[n++] = motor == Motor1? CmdDriveQAccelMot1: CmdDriveQAccelMot2;
1400  k = m_comm.pack32((uint_t)accel, cmd+n);
1401  n += k;
1402 
1403  k = m_comm.pack32((int)speed, cmd+n);
1404  n += k;
1405 
1406  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1407 }
1408 
1409 // Command 50
1410 int RoboClaw::cmdQDriveWithAccel(s32_t speed1, u32_t accel1,
1411  s32_t speed2, u32_t accel2)
1412 {
1413  byte_t cmd[CmdMaxBufLen];
1414  size_t k, n = 0;
1415 
1416  speed1 *= m_nMotorDir[Motor1];
1417  speed2 *= m_nMotorDir[Motor2];
1418 
1419  cmd[n++] = m_address;
1420  cmd[n++] = CmdDriveQAccel2;
1422  k = m_comm.pack32((uint_t)accel1, cmd+n);
1423  n += k;
1424 
1425  k = m_comm.pack32((int)speed1, cmd+n);
1426  n += k;
1427 
1428  k = m_comm.pack32((uint_t)accel2, cmd+n);
1429  n += k;
1430 
1431  k = m_comm.pack32((int)speed2, cmd+n);
1432  n += k;
1433 
1434  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1435 }
1436 
1437 // Command 41, 42
1438 int RoboClaw::cmdQDriveForDist(int motor, s32_t speed, u32_t dist, bool bQueue)
1439 {
1440  byte_t cmd[CmdMaxBufLen];
1441  size_t k, n = 0;
1442 
1443  ROBOCLAW_TRY_MOTOR(motor);
1444 
1445  speed *= m_nMotorDir[motor];
1446 
1447  cmd[n++] = m_address;
1448  cmd[n++] = motor == Motor1? CmdBufDriveQDistMot1: CmdBufDriveQDistMot2;
1450  k = m_comm.pack32((int)speed, cmd+n);
1451  n += k;
1452 
1453  k = m_comm.pack32((uint_t)dist, cmd+n);
1454  n += k;
1455 
1456  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1457 
1458  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1459 }
1460 
1461 // Command 43
1462 int RoboClaw::cmdQDriveForDist(s32_t speed1, u32_t dist1,
1463  s32_t speed2, u32_t dist2,
1464  bool bQueue)
1465 {
1466  byte_t cmd[CmdMaxBufLen];
1467  size_t k, n = 0;
1468 
1469  speed1 *= m_nMotorDir[Motor1];
1470  speed2 *= m_nMotorDir[Motor2];
1471 
1472  cmd[n++] = m_address;
1473  cmd[n++] = CmdBufDriveQDist;
1474 
1475  k = m_comm.pack32((int)speed1, cmd+n);
1476  n += k;
1477 
1478  k = m_comm.pack32((uint_t)dist1, cmd+n);
1479  n += k;
1480 
1481  k = m_comm.pack32((int)speed2, cmd+n);
1482  n += k;
1483 
1484  k = m_comm.pack32((uint_t)dist2, cmd+n);
1485  n += k;
1486 
1487  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1488 
1489  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1490 }
1491 
1492 // Command 44, 45
1493 int RoboClaw::cmdQDriveWithAccelForDist(int motor,
1494  s32_t speed,
1495  u32_t accel,
1496  u32_t dist,
1497  bool bQueue)
1498 {
1499  byte_t cmd[CmdMaxBufLen];
1500  size_t k, n = 0;
1501 
1502  ROBOCLAW_TRY_MOTOR(motor);
1503 
1504  speed *= m_nMotorDir[motor];
1505 
1506  cmd[n++] = m_address;
1507  cmd[n++] = motor == Motor1? CmdBufDriveQAccelDistMot1:
1509 
1510  k = m_comm.pack32((uint_t)accel, cmd+n);
1511  n += k;
1512 
1513  k = m_comm.pack32((int)speed, cmd+n);
1514  n += k;
1515 
1516  k = m_comm.pack32((uint_t)dist, cmd+n);
1517  n += k;
1518 
1519  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1520 
1521  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1522 }
1523 
1524 // Command 51
1525 int RoboClaw::cmdQDriveWithAccelForDist(s32_t speed1,
1526  u32_t accel1,
1527  u32_t dist1,
1528  s32_t speed2,
1529  u32_t accel2,
1530  u32_t dist2,
1531  bool bQueue)
1532 {
1533  byte_t cmd[CmdMaxBufLen];
1534  size_t k, n = 0;
1535 
1536  speed1 *= m_nMotorDir[Motor1];
1537  speed2 *= m_nMotorDir[Motor2];
1538 
1539  cmd[n++] = m_address;
1540  cmd[n++] = CmdBufDriveQAccel2Dist;
1541 
1542  k = m_comm.pack32((uint_t)accel1, cmd+n);
1543  n += k;
1544 
1545  k = m_comm.pack32((int)speed1, cmd+n);
1546  n += k;
1547 
1548  k = m_comm.pack32((uint_t)dist1, cmd+n);
1549  n += k;
1550 
1551  k = m_comm.pack32((uint_t)accel2, cmd+n);
1552  n += k;
1553 
1554  k = m_comm.pack32((int)speed2, cmd+n);
1555  n += k;
1556 
1557  k = m_comm.pack32((uint_t)dist2, cmd+n);
1558  n += k;
1559 
1560  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1561 
1562  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1563 }
1564 
1565 // Command 65, 66
1566 int RoboClaw::cmdQDriveWithProfileToPos(int motor,
1567  s32_t speed,
1568  u32_t accel,
1569  u32_t deccel,
1570  s32_t pos,
1571  bool bQueue)
1572 {
1573  byte_t cmd[CmdMaxBufLen];
1574  size_t k, n = 0;
1575 
1576  ROBOCLAW_TRY_MOTOR(motor);
1578  speed *= m_nMotorDir[motor];
1579 
1580  cmd[n++] = m_address;
1581  cmd[n++] = motor == Motor1? CmdBufDriveQProfPosMot1:
1583 
1584  k = m_comm.pack32((uint_t)accel, cmd+n);
1585  n += k;
1586 
1587  k = m_comm.pack32((int)speed, cmd+n);
1588  n += k;
1589 
1590  k = m_comm.pack32((uint_t)deccel, cmd+n);
1591  n += k;
1592 
1593  k = m_comm.pack32((int)pos, cmd+n);
1594  n += k;
1595 
1596  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1597 
1598  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1599 }
1600 
1601 // Command 67
1602 int RoboClaw::cmdQDriveWithProfileToPos(s32_t speed1,
1603  u32_t accel1,
1604  u32_t deccel1,
1605  s32_t pos1,
1606  s32_t speed2,
1607  u32_t accel2,
1608  u32_t deccel2,
1609  s32_t pos2,
1610  bool bQueue)
1611 {
1612  byte_t cmd[CmdMaxBufLen];
1613  size_t k, n = 0;
1614 
1615  speed1 *= m_nMotorDir[Motor1];
1616  speed2 *= m_nMotorDir[Motor2];
1617 
1618  cmd[n++] = m_address;
1619  cmd[n++] = CmdBufDriveQProfPos;
1620 
1621  k = m_comm.pack32((uint_t)accel1, cmd+n);
1622  n += k;
1623 
1624  k = m_comm.pack32((int)speed1, cmd+n);
1625  n += k;
1626 
1627  k = m_comm.pack32((uint_t)deccel1, cmd+n);
1628  n += k;
1629 
1630  k = m_comm.pack32((int)pos1, cmd+n);
1631  n += k;
1632 
1633  k = m_comm.pack32((uint_t)accel2, cmd+n);
1634  n += k;
1635 
1636  k = m_comm.pack32((int)speed2, cmd+n);
1637  n += k;
1638 
1639  k = m_comm.pack32((uint_t)deccel2, cmd+n);
1640  n += k;
1641 
1642  k = m_comm.pack32((int)pos2, cmd+n);
1643  n += k;
1644 
1645  cmd[n++] = bQueue? ParamCmdBufQueue: ParamCmdBufPreempt;
1646 
1647  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1648 }
1649 
1650 // Command 0, 1, 4, 5
1651 int RoboClaw::cmdSDrive(int motor, int speed)
1652 {
1653  byte_t cmd[CmdMaxBufLen];
1654  size_t n = 0;
1655 
1656  ROBOCLAW_TRY_MOTOR(motor);
1657 
1658  speed = RoboClaw::cap(speed, MotorSpeedMaxBackward, MotorSpeedMaxForward);
1659  speed *= m_nMotorDir[motor];
1660 
1661  if( speed >= 0 )
1662  {
1663  cmd[n++] = m_address;
1664  cmd[n++] = motor == Motor1? CmdDriveForwardMot1: CmdDriveForwardMot2;
1665  cmd[n++] = (byte_t)speed;
1666  }
1667  else
1668  {
1669  cmd[n++] = m_address;
1670  cmd[n++] = motor == Motor1? CmdDriveBackwardMot1: CmdDriveBackwardMot2;
1671  cmd[n++] = (byte_t)(-speed);
1672  }
1673 
1674  return m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1675 }
1676 
1677 // Command 0, 1, 4, 5
1678 int RoboClaw::cmdSDrive2(int speed1, int speed2)
1679 {
1680  int rc;
1681 
1682  if( (rc = cmdSDrive(Motor1, speed1)) == OK )
1683  {
1684  rc = cmdSDrive(Motor2, speed2);
1685  }
1686  return rc;
1687 }
1688 
1689 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1690 // Stop Commands
1691 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1692 
1693 // Command 35, 36
1694 int RoboClaw::cmdStop(int motor)
1695 {
1696  return cmdQDrive(motor, 0);
1697 }
1698 
1699 // Command 38, 39
1700 int RoboClaw::cmdStopWithDecel(int motor, u32_t decel)
1701 {
1702  return cmdQDriveWithAccel(motor, 0, decel);
1703 }
1704 
1705 // Command 37
1706 int RoboClaw::cmdStop()
1707 {
1708  return cmdQDrive2(0, 0);
1709 }
1710 
1711 // Command 50
1712 int RoboClaw::cmdStopWithDecel(u32_t decel)
1713 {
1714  return cmdQDriveWithAccel(0, decel, 0, decel);
1715 }
1716 
1717 // Command 37
1718 int RoboClaw::cmdEStop()
1719 {
1720  return cmdStop();
1721 }
1722 
1723 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1724 // Misc Commands
1725 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1726 
1727 //
1728 // Command 94
1729 //
1730 // Note: RoboClaw User Manual v5 errors.
1731 // The command appends a 4-btye magic number
1732 // The command includes a CRC checksum.
1733 //
1734 int RoboClaw::cmdWriteSettingsToEEPROM()
1735 {
1736  byte_t cmd[CmdMaxBufLen];
1737  size_t n = 0;
1738  int rc;
1739 
1740  cmd[n++] = m_address;
1741  cmd[n++] = CmdWriteEEPROM;
1742 
1743  // magic number
1744  cmd[n++] = 0xe2;
1745  cmd[n++] = 0x2e;
1746  cmd[n++] = 0xab;
1747  cmd[n++] = 0x7a;
1748 
1749  rc = m_comm.execCmdWithAckRsp(cmd, n, MsgWithCrc);
1750 
1751  // give a little time for the controller to write to its NVM
1752  if( rc == OK )
1753  {
1754  usleep(100000); // 0.1 second to give controller time to write to nvm
1755  }
1756 
1757  return rc;
1758 }
drive motros at qpps with 2 accel vals
Definition: RoboClaw.h:180
#define ROBOCLAW_DBG_MSG(addr, cmdNum, buf, len, fmt,...)
Debug print message disabled.
Definition: RoboClaw.cxx:160
buffered drive motor 1 to dist at qpps
Definition: RoboClaw.h:166
drive motor 2 backward
Definition: RoboClaw.h:128
message includes a 16-bit CRC
Definition: RoboClaw.h:108
read motor 2 velocity PID constants
Definition: RoboClaw.h:187
buffered drive motor 2 to dist at qpps with accel.
Definition: RoboClaw.h:171
static RoboClawChipSelect csNoOp
Definition: RoboClaw.cxx:202
drive motor 1 forward
Definition: RoboClaw.h:127
read current board status
Definition: RoboClaw.h:207
read motor 1 speed (qpps)
Definition: RoboClaw.h:143
drive motor 1 forward
Definition: RoboClaw.h:123
drive motor 1 at quad. pulses/second
Definition: RoboClaw.h:160
reset encoder (quadrature )counters
Definition: RoboClaw.h:145
set motor 2 encoder mode
Definition: RoboClaw.h:210
write settings to EEPROM
Definition: RoboClaw.h:211
double fcap(double a, double min, double max)
Cap value within limits [min, max].
Definition: laeUtils.h:162
drive motor 1 at quad. pps with accel.
Definition: RoboClaw.h:163
drive motors with signed qpps, accel, deccel and position
Definition: RoboClaw.h:200
RoboClaw motor controller class interface.
set main battery voltage cutoffs
Definition: RoboClaw.h:188
buffered drive motor 2 to dist at qpps
Definition: RoboClaw.h:167
buffered drive motors to dist at qpps w/ 2 accel
Definition: RoboClaw.h:186
drive motor 2 with signed qpps, accel, deccel and position
Definition: RoboClaw.h:198
set motor 2 velocity PID constants
Definition: RoboClaw.h:154
drive motor 1 backward
Definition: RoboClaw.h:124
read motor 1 maximum current limit
Definition: RoboClaw.h:219
set motor 1 maximum current limit
Definition: RoboClaw.h:217
read motors amp draw
Definition: RoboClaw.h:179
RoboClaw motor controller chip select class.
Definition: RoboClaw.h:444
read motor 1 encoder
Definition: RoboClaw.h:141
s64_t iabs(s64_t a)
Integer absolute value.
Definition: RoboClaw.cxx:176
set motor 1 velocity PID constants
Definition: RoboClaw.h:153
int cap(int a, int min, int max)
Cap value within limits [min, max].
Definition: laeUtils.h:176
read encoder mode for both motors
Definition: RoboClaw.h:208
#define ROBOCLAW_TRY_MOTOR(motor)
Test for valid motor index.
Definition: RoboClaw.cxx:78
read firmware version
Definition: RoboClaw.h:146
drive motors at quad. pulses/second
Definition: RoboClaw.h:162
read motor 2 speed (qpps)
Definition: RoboClaw.h:144
read motors bufferd command length
Definition: RoboClaw.h:175
read main battery voltage
Definition: RoboClaw.h:149
drive motor 1 with signed qpps, accel, deccel and position
Definition: RoboClaw.h:196
read main battery cutoff settings
Definition: RoboClaw.h:190
#define ROBOCLAW_DBG(addr, cmdNum, fmt,...)
Debug print disabled.
Definition: RoboClaw.cxx:155
read logic voltage cutoff settings
Definition: RoboClaw.h:191
#define ROBOCLAW_DBG_BUF(buf, len, fmt,...)
Debug print buffer disabled.
Definition: RoboClaw.cxx:165
buffered drive motor 1 to dist at qpps with accel.
Definition: RoboClaw.h:169
Definition: RoboClaw.h:64
buffered drive motors to dist at qpps
Definition: RoboClaw.h:168
set motor 1 encoder mode
Definition: RoboClaw.h:209
drive motor 2 at quad. pps with accel.
Definition: RoboClaw.h:164
RoboClaw communication class.
Definition: RoboClaw.h:487
read board temperature
Definition: RoboClaw.h:205
set logic voltage cutoffs
Definition: RoboClaw.h:189
drive motor 2 at quad. pulses/second
Definition: RoboClaw.h:161
MsgFmt
Command and response message formats.
Definition: RoboClaw.h:105
read motor 2 encoder
Definition: RoboClaw.h:142
drive motors at duty cycle (no quad.)
Definition: RoboClaw.h:159
read motor 2 maximum current limit
Definition: RoboClaw.h:220
read logic battery voltage
Definition: RoboClaw.h:150
set motor 2 maximum current limit
Definition: RoboClaw.h:218