Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
diagBatt.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Program: laelaps_diag
6 //
7 // File: diagBatt.cxx
8 //
9 /*! \file
10  *
11  * \brief Perform Laelaps battery diagnostics.
12  *
13  * \author Robin Knight (robin.knight@roadnarrows.com)
14  *
15  * \par Copyright
16  * \h_copy 2017. RoadNarrows LLC.\n
17  * http://www.roadnarrows.com\n
18  * All Rights Reserved
19  */
20 /*
21  * @EulaBegin@
22  *
23  * Unless otherwise stated explicitly, all materials contained are copyrighted
24  * and may not be used without RoadNarrows LLC's written consent,
25  * except as provided in these terms and conditions or in the copyright
26  * notice (documents and software) or other proprietary notice provided with
27  * the relevant materials.
28  *
29  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
30  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
31  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
32  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
33  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
34  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
37  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
38  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
39  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
40  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41  *
42  * @EulaEnd@
43  */
44 ////////////////////////////////////////////////////////////////////////////////
45 
46 #include <unistd.h>
47 #include <termios.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <errno.h>
53 
54 #include <iostream>
55 #include <fstream>
56 #include <string>
57 #include <vector>
58 
59 #include "rnr/rnrconfig.h"
60 #include "rnr/log.h"
61 #include "rnr/opts.h"
62 #include "rnr/pkg.h"
63 
64 // common
65 #include "Laelaps/laelaps.h"
66 #include "Laelaps/laeUtils.h"
67 
68 // hardware
69 #include "Laelaps/laeSysDev.h"
70 #include "Laelaps/RoboClaw.h"
71 #include "Laelaps/laeGpio.h"
72 #include "Laelaps/laeWd.h"
73 #include "Laelaps/laeMotor.h"
74 
75 #include "laelaps_diag.h"
76 
77 using namespace std;
78 using namespace laelaps;
79 using namespace motor::roboclaw;
80 
81 static const char *SubSysName = "Batteries";
82 
83 /*!
84  * \brief Laelaps motors subsystem information structure.
85  */
87 {
88  const char *m_sCtlrKey; ///< motor controller key
89  uint_t m_addr; ///< motor controller address.
90  int m_idx; ///< motor controller index
91  bool m_bIsStd; ///< motor controller is [not] standard option
92  bool m_bBlackListed; ///< motor controller is black listed (bad)
93  struct ///< motors info sub-structure
94  {
95  const char *m_sMotorKey; ///< motor key
96  int m_idx; ///< motor index in controller
97  } m_motors[LaeNumMotorsPerCtlr]; ///< motors info
98 };
99 
100 /*!
101  * \brief Laelaps motors subsystem information.
102  */
103 static LaelapsMotorsInfo MotorsInfo[LaeNumMotorCtlrs] =
104 {
105  {
106  LaeKeyFront, LaeMotorCtlrAddrFront, LaeMotorCtlrIdFront, true, false,
107  { {LaeKeyLeftFront, LaeMotorLeft}, {LaeKeyRightFront, LaeMotorRight} }
108  },
109 
110  {
111  LaeKeyRear, LaeMotorCtlrAddrRear, LaeMotorCtlrIdRear, true, false,
112  { {LaeKeyLeftRear, LaeMotorLeft}, {LaeKeyRightRear, LaeMotorRight} }
113  },
114 };
115 
116 //
117 // The interface.
118 //
119 #if 0 // Deprecated
120 static LaeMotorCtlrEnable MotorCtlrEnable; ///< motor controllers power enable
121 static LaeMotorCtlrChipSelect MotorCtlrCs; ///< motor controllers chip select
122 #endif // Deprecated
123 
124 static RoboClawComm MotorCtlrComm; ///< motor ctrl serial communication bus
125 static RoboClaw *MotorCtlr[LaeNumMotorCtlrs];
126  ///< RoboClaw motor controllers
127 
128 static DiagStats initMotorInterfaces()
129 {
130  string strDevSymName(LaeDevMotorCtlrs); // symbolic name
131  string strDevName; // real device names
132  int nBaudRate = LaeBaudRateMotorCtlrs; // serial baudrate
133  int nCtlr; // motor controller id
134  DiagStats stats; // test stats
135 
136  printSubHdr("Initialize Motor Controller Interfaces");
137 
138  //
139  // Enable power to the motor controllers via watchdog subprocessor gpio.
140  //
141  ++stats.testCnt;
142 
143  if( WatchDog.cmdEnableMotorCtlrs(true) < 0 )
144  {
145  printTestResult(FatalTag, "%s: Motor controllers power enable.",
146  SubSysName);
147  stats.fatal = true;
148  return stats;
149  }
150  else
151  {
152  printTestResult(PassTag, "%s: Enabled power to motor controllers.",
153  SubSysName);
154  ++stats.passCnt;
155  }
156 
157 #if 0 // Deprecated
158  //
159  // Enable power to the motor controllers via odroid gpio.
160  //
161  MotorCtlrEnable.sync();
162  if( MotorCtlrEnable.isConfigured() )
163  {
164  ++stats.testCnt;
165  if( MotorCtlrEnable.isEnabled() )
166  {
167  printTestResult(WarnTag,
168  "Motor controllers are already enabled - may be in usse by another "
169  "application.");
170  ++stats.passCnt;
171  }
172  else if( MotorCtlrEnable.enable() )
173  {
174  printTestResult(PassTag, "Motor controllers enabled.");
175  ++stats.passCnt;
176  }
177  else
178  {
179  printTestResult(FatalTag, "Motor controllers failed to be enabled.");
180  stats.fatal = true;
181  return stats;
182  }
183  }
184 #endif // Deprecated
185 
186 #if 0 // Deprecated
187  //
188  // Open the motor controller chip select via gpio
189  //
190  ++stats.testCnt;
191 
192  if( MotorCtlrCs.open(LaeGpioMotorCtlrCs) < 0 )
193  {
194  printTestResult(FatalTag,
195  "Failed to open motor controllers chip select on GPIO pin %d: %s(%d).",
196  LaeGpioMotorCtlrCs, strerror(errno), errno);
197  stats.fatal = true;
198  }
199  else
200  {
201  printTestResult(PassTag,
202  "Open motor controllers chip select on GPIO pin %d.",
203  LaeGpioMotorCtlrCs);
204  ++stats.passCnt;
205  }
206 #endif // Deprecated
207 
208  //
209  // Open the motor controller.
210  //
211  ++stats.testCnt;
212 
213  // get real device name, not any symbolic links
214  strDevName = getRealDeviceName(strDevSymName);
215 
216  if( MotorCtlrComm.open(strDevName, nBaudRate) < 0 )
217  {
218  printTestResult(FatalTag,
219  "%s: %s: Failed to open motor controllers comm at %d baud: %s(%d)",
220  SubSysName, strDevName.c_str(), nBaudRate, strerror(errno), errno);
221  stats.fatal = true;
222  }
223  else
224  {
225  printTestResult(PassTag,
226  "%s: Open motor controllers serial communication on %s@%d.",
227  SubSysName, strDevName.c_str(), nBaudRate);
228  ++stats.passCnt;
229  }
230 
231  if( stats.fatal )
232  {
233  return stats;
234  }
235 
236  //
237  // Create front motor controller interface.
238  //
239  ++stats.testCnt;
240 
241  nCtlr = LaeMotorCtlrIdFront;
242  MotorCtlr[nCtlr] =
243  new RoboClaw(MotorCtlrComm, LaeMotorCtlrAddrFront, LaeKeyFront);
244  MotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
245  MotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
246  printTestResult(PassTag,
247  "%s: Created %s motor controller interface.\n address=0x%02x.",
248  SubSysName, LaeKeyFront, LaeMotorCtlrAddrFront);
249  ++stats.passCnt;
250 
251  //
252  // Create rear motor controller interface.
253  //
254  ++stats.testCnt;
255 
256  nCtlr = LaeMotorCtlrIdRear;
257  MotorCtlr[nCtlr] =
258  new RoboClaw(MotorCtlrComm, LaeMotorCtlrAddrRear, LaeKeyRear);
259  MotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
260  MotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
261  printTestResult(PassTag,
262  "%s: Created %s motor controller interface, address=0x%02x.",
263  SubSysName, LaeKeyRear, LaeMotorCtlrAddrRear);
264  ++stats.passCnt;
265 
266  return stats;
267 }
268 
269 static DiagStats testBatteryState(int cnt)
270 {
271  int nCtlr;
272  double voltMotBatt[LaeNumMotorCtlrs];
273  double voltWdJack;
274  double voltWdBatt;
275  bool isCharging;
276  bool showLabel;
277  int rc;
278 
279  DiagStats stats;
280 
281  showLabel = cnt == 0;
282 
283  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
284  {
285  ++stats.testCnt;
286 
287  rc = MotorCtlr[nCtlr]->cmdReadMainBattVoltage(voltMotBatt[nCtlr]);
288 
289  if( rc == OK )
290  {
291  ++stats.passCnt;
292  }
293  else
294  {
295  printTestResult(FailTag,
296  "%s: Motor Controller %s(0x%02x): Read main battery voltage.",
297  SubSysName, MotorsInfo[nCtlr].m_sCtlrKey, MotorsInfo[nCtlr].m_addr);
298 
299  voltMotBatt[nCtlr] = 0.0;
300  showLabel = true;
301  }
302  }
303 
304  ++stats.testCnt;
305 
306  // petting the dog checks the charging state
307  if( (rc = WatchDog.cmdPetTheDog()) == LAE_OK )
308  {
309  ++stats.passCnt;
310  isCharging = WatchDog.isCharging();
311  }
312  else
313  {
314  printTestResult(FailTag, "%s: Failed to pet the watchdog.", SubSysName);
315  isCharging = false;
316  showLabel = true;
317  }
318 
319  ++stats.testCnt;
320 
321  if( (rc = WatchDog.cmdReadVoltages(voltWdJack, voltWdBatt)) == LAE_OK )
322  {
323  ++stats.passCnt;
324  }
325  else
326  {
327  printTestResult(FailTag, "%s: Failed to read the watchdog voltages.",
328  SubSysName);
329  voltWdJack = 0.0;
330  voltWdBatt = 0.0;
331  showLabel = true;
332  }
333 
334  if( showLabel )
335  {
336  printf("%8s%13s%17s\n", "", "motor-ctlrs", "watchdog ");
337  printf("%8s%6s %6s %6s chg %6s\n", "",
338  LaeKeyFront, LaeKeyRear, "batt", "jack");
339  }
340 
341  printf("%6d. %5.1lfV %5.1lfV %5.1lfV %s %5.1lfV\r",
342  cnt,
343  voltMotBatt[0], voltMotBatt[1], voltWdBatt,
344  (isCharging? "yes": " no"), voltWdJack);
345  fflush(stdout);
346 
347  return stats;
348 }
349 
350 static DiagStats deinitMotorInterfaces()
351 {
352  DiagStats stats; // test stats
353 
354  printSubHdr("De-Initialize Motor Controller Interfaces");
355 
356  //
357  // Enable power to the motor controllers via watchdog subprocessor gpio.
358  //
359  ++stats.testCnt;
360 
361  if( WatchDog.cmdEnableMotorCtlrs(false) < 0 )
362  {
363  printTestResult(FailTag, "%s: Motor controllers power disable.",
364  SubSysName);
365  }
366  else
367  {
368  printTestResult(PassTag, "%s: Disabled power to motor controllers.",
369  SubSysName);
370  ++stats.passCnt;
371  }
372 
373  //
374  // Close serial connection with motor controllers.
375  //
376  ++stats.testCnt;
377 
378  if( MotorCtlrComm.close() < 0 )
379  {
380  printTestResult(FailTag, "%s: Close motor controllers serial connection.",
381  SubSysName);
382  }
383  else
384  {
385  printTestResult(PassTag,
386  "%s: Close serial connection with motor controllers.",
387  SubSysName);
388  ++stats.passCnt;
389  }
390 
391  return stats;
392 }
393 
394 DiagStats runBatteryDiagnostics(bool bAnyKey)
395 {
396  DiagStats statsTest;
397  DiagStats statsTotal;
398  int cnt;
399  bool bQuit;
400 
401  printHdr("Battery Diagnostics");
402 
403  //
404  // Init Tests
405  //
406  statsTest = initMotorInterfaces();
407 
408  printSubTotals(statsTest);
409 
410  statsTotal += statsTest;
411 
412  statsTest.zero();
413  bQuit = statsTotal.fatal;
414  cnt = 0;
415 
416  printSubHdr("Battery Voltages");
417 
418  //
419  // Read battery voltages and charging state.
420  //
421  while( !bQuit )
422  {
423  statsTest += testBatteryState(cnt++);
424 
425  if( !bAnyKey || kbhit() || statsTest.fatal )
426  {
427  printf("\n");
428  printSubTotals(statsTest);
429  bQuit = true;
430  }
431 
432  else
433  {
434  usleep(500000);
435  }
436  }
437 
438  statsTotal += statsTest;
439 
440  //
441  // Deinit Tests
442  //
443  statsTest = deinitMotorInterfaces();
444 
445  printSubTotals(statsTest);
446 
447  statsTotal += statsTest;
448 
449  printSubTotals(statsTest);
450 
451  statsTotal += statsTest;
452 
453  //
454  // Summary
455  //
456  printTotals(statsTotal);
457 
458  return statsTotal;
459 }
Diagnotics header file.
virtual int cmdEnableMotorCtlrs(bool bEnable)
Enable/disable power in to motor controllers.
Definition: laeWd.cxx:668
RoboClaw 2 motor controller class.
Definition: RoboClaw.h:808
virtual void setMotorDir(int motor, int motorDir)
set the direction of motor rotation.
Definition: RoboClaw.h:943
virtual int open(std::string &strDevName, int nBaudRate, RoboClawChipSelect *pChipSelect=NULL)
Open connection to motor controller(s).
Definition: RoboClaw.cxx:234
bool m_bBlackListed
motor controller is black listed (bad)
Definition: diagBatt.cxx:92
uint_t m_addr
motor controller address.
Definition: diagBatt.cxx:89
bool isEnabled()
Test if power to motor controllers is enabled.
Definition: laeGpio.cxx:240
Laelaps motor controller power enable class.
Definition: laeGpio.h:200
Laelaps motors subsystem information structure.
Definition: diagBatt.cxx:86
RoboClaw motor controller chip select class.
Definition: laeMotor.h:190
RoboClaw motor controller class interface.
Laelaps WatchDog software class interface.
virtual int cmdPetTheDog()
Pet the watchdog command.
Definition: laeWd.cxx:215
bool enable()
Enable power to motor controllers.
Definition: laeGpio.cxx:179
static LaelapsMotorsInfo MotorsInfo[LaeNumMotorCtlrs]
Laelaps motors subsystem information.
Definition: diagBatt.cxx:103
bool m_bIsStd
motor controller is [not] standard option
Definition: diagBatt.cxx:91
Laelaps Odroid General Purpose I/O class interfaces.
void zero()
Zero statistics.
Definition: laelaps_diag.h:131
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
virtual int cmdReadVoltages(double &fJackV, double &fBattV)
Read sensed voltages.
Definition: laeWd.cxx:933
Simple diagnostics statistics class.
Definition: laelaps_diag.h:106
Laelaps common utilities.
const char * m_sMotorKey
motor key
Definition: diagBatt.cxx:95
bool isConfigured() const
Is the exported GPIO number configured to match this ojbect?
Definition: laeGpio.h:171
Laelaps system devices.
static RoboClaw * MotorCtlr[LaeNumMotorCtlrs]
RoboClaw motor controllers.
Definition: diagBatt.cxx:125
Laelaps motors, encoder, and controllers hardware abstraction interfaces.
int m_idx
motor controller index
Definition: diagBatt.cxx:90
virtual int cmdReadMainBattVoltage(double &volts)
Read the RoboClaw&#39;s main battery input voltage.
Definition: RoboClaw.cxx:667
virtual int close()
Close connection to motor controller.
Definition: RoboClaw.cxx:258
const char * m_sCtlrKey
motor controller key
Definition: diagBatt.cxx:88
RoboClaw communication class.
Definition: RoboClaw.h:487
virtual int open(int pinGpio)
Open GPIO pin interface.
Definition: laeMotor.cxx:83
bool isCharging()
Test if battery is charging.
Definition: laeWd.h:381
static RoboClawComm MotorCtlrComm
motor ctrl serial communication bus
Definition: diagBatt.cxx:124
virtual void sync()
Synchronized this with GPIO hardware state.
Definition: laeGpio.cxx:168
std::string getRealDeviceName(const std::string &strDevName)
Get real device name.
Top-level package include file.