blob: 7dcca190318bc3f2aa870468710b6d093a062fcb [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
Brian Silverman1a675112016-02-20 20:42:49 -05002/* Copyright (c) FIRST 2009-2016. All Rights Reserved. */
Brian Silverman26e4e522015-12-17 01:56:40 -05003/* Open Source Software - may be modified and shared by FRC teams. The code */
Brian Silverman1a675112016-02-20 20:42:49 -05004/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
Brian Silverman26e4e522015-12-17 01:56:40 -05006/*----------------------------------------------------------------------------*/
7
8#include "CANJaguar.h"
9#include "Timer.h"
10#define tNIRIO_i32 int
11#include "NetworkCommunication/CANSessionMux.h"
12#include "WPIErrors.h"
13#include <cstdio>
14#include <cassert>
15#include "LiveWindow/LiveWindow.h"
16
17/* we are on ARM-LE now, not Freescale so no need to swap */
18#define swap16(x) (x)
19#define swap32(x) (x)
20
21/* Compare floats for equality as fixed point numbers */
22#define FXP8_EQ(a, b) ((int16_t)((a)*256.0) == (int16_t)((b)*256.0))
23#define FXP16_EQ(a, b) ((int32_t)((a)*65536.0) == (int32_t)((b)*65536.0))
24
25const int32_t CANJaguar::kControllerRate;
26constexpr double CANJaguar::kApproxBusVoltage;
27
28static const int32_t kSendMessagePeriod = 20;
29static const uint32_t kFullMessageIDMask =
30 (CAN_MSGID_API_M | CAN_MSGID_MFR_M | CAN_MSGID_DTYPE_M);
31
32static const int32_t kReceiveStatusAttempts = 50;
33
34static std::unique_ptr<Resource> allocated;
35
36static int32_t sendMessageHelper(uint32_t messageID, const uint8_t *data,
37 uint8_t dataSize, int32_t period) {
38 static const uint32_t kTrustedMessages[] = {
39 LM_API_VOLT_T_EN, LM_API_VOLT_T_SET, LM_API_SPD_T_EN, LM_API_SPD_T_SET,
40 LM_API_VCOMP_T_EN, LM_API_VCOMP_T_SET, LM_API_POS_T_EN, LM_API_POS_T_SET,
41 LM_API_ICTRL_T_EN, LM_API_ICTRL_T_SET};
42
43 int32_t status = 0;
44
45 for (auto& kTrustedMessage : kTrustedMessages) {
46 if ((kFullMessageIDMask & messageID) == kTrustedMessage) {
47 uint8_t dataBuffer[8];
48 dataBuffer[0] = 0;
49 dataBuffer[1] = 0;
50
51 // Make sure the data will still fit after adjusting for the token.
52 assert(dataSize <= 6);
53
54 for (uint8_t j = 0; j < dataSize; j++) {
55 dataBuffer[j + 2] = data[j];
56 }
57
58 FRC_NetworkCommunication_CANSessionMux_sendMessage(
59 messageID, dataBuffer, dataSize + 2, period, &status);
60
61 return status;
62 }
63 }
64
65 FRC_NetworkCommunication_CANSessionMux_sendMessage(messageID, data, dataSize,
66 period, &status);
67
68 return status;
69}
70
71/**
72 * Common initialization code called by all constructors.
73 */
74void CANJaguar::InitCANJaguar() {
75 m_safetyHelper = std::make_unique<MotorSafetyHelper>(this);
76
77 bool receivedFirmwareVersion = false;
78 uint8_t dataBuffer[8];
79 uint8_t dataSize;
80
81 // Request firmware and hardware version only once
82 requestMessage(CAN_IS_FRAME_REMOTE | CAN_MSGID_API_FIRMVER);
83 requestMessage(LM_API_HWVER);
84
85 // Wait until we've gotten all of the status data at least once.
86 for (int i = 0; i < kReceiveStatusAttempts; i++) {
87 Wait(0.001);
88
89 setupPeriodicStatus();
90 updatePeriodicStatus();
91
92 if (!receivedFirmwareVersion &&
93 getMessage(CAN_MSGID_API_FIRMVER, CAN_MSGID_FULL_M, dataBuffer,
94 &dataSize)) {
95 m_firmwareVersion = unpackint32_t(dataBuffer);
96 receivedFirmwareVersion = true;
97 }
98
99 if (m_receivedStatusMessage0 && m_receivedStatusMessage1 &&
100 m_receivedStatusMessage2 && receivedFirmwareVersion) {
101 break;
102 }
103 }
104
105 if (!m_receivedStatusMessage0 || !m_receivedStatusMessage1 ||
106 !m_receivedStatusMessage2 || !receivedFirmwareVersion) {
107 wpi_setWPIErrorWithContext(JaguarMessageNotFound, "Status data not found");
108 }
109
110 if (getMessage(LM_API_HWVER, CAN_MSGID_FULL_M, dataBuffer, &dataSize))
111 m_hardwareVersion = dataBuffer[0];
112
113 if (m_deviceNumber < 1 || m_deviceNumber > 63) {
114 std::stringstream buf;
115 buf << "device number \"" << m_deviceNumber
116 << "\" must be between 1 and 63";
117 wpi_setWPIErrorWithContext(ParameterOutOfRange, buf.str());
118 return;
119 }
120
121 if (StatusIsFatal()) return;
122
123 // 3330 was the first shipping RDK firmware version for the Jaguar
124 if (m_firmwareVersion >= 3330 || m_firmwareVersion < 108) {
125 std::stringstream buf;
126 if (m_firmwareVersion < 3330) {
127 buf << "Jag #" << m_deviceNumber << " firmware (" << m_firmwareVersion
128 << ") is too old (must be at least version 108 "
129 "of the FIRST approved firmware)";
130 } else {
131 buf << "Jag #" << m_deviceNumber << " firmware (" << m_firmwareVersion
132 << ") is not FIRST approved (must be at least "
133 "version 108 of the FIRST approved firmware)";
134 }
135 wpi_setWPIErrorWithContext(JaguarVersionError, buf.str());
136 return;
137 }
138
139 switch (m_controlMode) {
140 case kPercentVbus:
141 case kVoltage:
142 // No additional configuration required... start enabled.
143 EnableControl();
144 break;
145 default:
146 break;
147 }
148 HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber,
149 m_controlMode);
150 LiveWindow::GetInstance()->AddActuator("CANJaguar", m_deviceNumber, this);
151}
152
153/**
154 * Constructor for the CANJaguar device.<br>
155 * By default the device is configured in Percent mode.
156 * The control mode can be changed by calling one of the control modes listed
157 * below.
158 *
159 * @param deviceNumber The address of the Jaguar on the CAN bus.
160 * @see CANJaguar#SetCurrentMode(double, double, double)
161 * @see CANJaguar#SetCurrentMode(PotentiometerTag, double, double, double)
162 * @see CANJaguar#SetCurrentMode(EncoderTag, int, double, double, double)
163 * @see CANJaguar#SetCurrentMode(QuadEncoderTag, int, double, double, double)
164 * @see CANJaguar#SetPercentMode()
165 * @see CANJaguar#SetPercentMode(PotentiometerTag)
166 * @see CANJaguar#SetPercentMode(EncoderTag, int)
167 * @see CANJaguar#SetPercentMode(QuadEncoderTag, int)
168 * @see CANJaguar#SetPositionMode(PotentiometerTag, double, double, double)
169 * @see CANJaguar#SetPositionMode(QuadEncoderTag, int, double, double, double)
170 * @see CANJaguar#SetSpeedMode(EncoderTag, int, double, double, double)
171 * @see CANJaguar#SetSpeedMode(QuadEncoderTag, int, double, double, double)
172 * @see CANJaguar#SetVoltageMode()
173 * @see CANJaguar#SetVoltageMode(PotentiometerTag)
174 * @see CANJaguar#SetVoltageMode(EncoderTag, int)
175 * @see CANJaguar#SetVoltageMode(QuadEncoderTag, int)
176 */
177CANJaguar::CANJaguar(uint8_t deviceNumber)
178 : m_deviceNumber(deviceNumber) {
179 std::stringstream buf;
180 buf << "CANJaguar device number " << m_deviceNumber;
181 Resource::CreateResourceObject(allocated, 63);
182
183 if (allocated->Allocate(m_deviceNumber - 1, buf.str()) ==
184 std::numeric_limits<uint32_t>::max()) {
185 CloneError(*allocated);
186 return;
187 }
188
189 SetPercentMode();
190 InitCANJaguar();
191 ConfigMaxOutputVoltage(kApproxBusVoltage);
192}
193
194CANJaguar::~CANJaguar() {
195 allocated->Free(m_deviceNumber - 1);
196
197 int32_t status;
198
199 // Disable periodic setpoints
200 if (m_controlMode == kPercentVbus)
201 FRC_NetworkCommunication_CANSessionMux_sendMessage(
202 m_deviceNumber | LM_API_VOLT_T_SET, nullptr, 0,
203 CAN_SEND_PERIOD_STOP_REPEATING, &status);
204 else if (m_controlMode == kSpeed)
205 FRC_NetworkCommunication_CANSessionMux_sendMessage(
206 m_deviceNumber | LM_API_SPD_T_SET, nullptr, 0,
207 CAN_SEND_PERIOD_STOP_REPEATING, &status);
208 else if (m_controlMode == kPosition)
209 FRC_NetworkCommunication_CANSessionMux_sendMessage(
210 m_deviceNumber | LM_API_POS_T_SET, nullptr, 0,
211 CAN_SEND_PERIOD_STOP_REPEATING, &status);
212 else if (m_controlMode == kCurrent)
213 FRC_NetworkCommunication_CANSessionMux_sendMessage(
214 m_deviceNumber | LM_API_ICTRL_T_SET, nullptr, 0,
215 CAN_SEND_PERIOD_STOP_REPEATING, &status);
216 else if (m_controlMode == kVoltage)
217 FRC_NetworkCommunication_CANSessionMux_sendMessage(
218 m_deviceNumber | LM_API_VCOMP_T_SET, nullptr, 0,
219 CAN_SEND_PERIOD_STOP_REPEATING, &status);
220
221 if (m_table != nullptr) m_table->RemoveTableListener(this);
222}
223
224/**
225 * @return The CAN ID passed in the constructor
226 */
227uint8_t CANJaguar::getDeviceNumber() const { return m_deviceNumber; }
228
229/**
230 * Sets the output set-point value.
231 *
232 * The scale and the units depend on the mode the Jaguar is in.<br>
233 * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM
234 * Jaguar).<br>
235 * In voltage Mode, the outputValue is in volts. <br>
236 * In current Mode, the outputValue is in amps. <br>
237 * In speed Mode, the outputValue is in rotations/minute.<br>
238 * In position Mode, the outputValue is in rotations.
239 *
240 * @param outputValue The set-point to sent to the motor controller.
241 * @param syncGroup The update group to add this Set() to, pending
242 * UpdateSyncGroup(). If 0, update immediately.
243 */
244void CANJaguar::Set(float outputValue, uint8_t syncGroup) {
245 uint32_t messageID;
246 uint8_t dataBuffer[8];
247 uint8_t dataSize;
248
Brian Silverman1a675112016-02-20 20:42:49 -0500249 if (m_safetyHelper) m_safetyHelper->Feed();
250
251 if (m_stopped) {
Brian Silverman26e4e522015-12-17 01:56:40 -0500252 EnableControl();
Brian Silverman1a675112016-02-20 20:42:49 -0500253 m_stopped = false;
Brian Silverman26e4e522015-12-17 01:56:40 -0500254 }
255
256 if (m_controlEnabled) {
257 switch (m_controlMode) {
258 case kPercentVbus: {
259 messageID = LM_API_VOLT_T_SET;
260 if (outputValue > 1.0) outputValue = 1.0;
261 if (outputValue < -1.0) outputValue = -1.0;
262 dataSize = packPercentage(dataBuffer,
263 (m_isInverted ? -outputValue : outputValue));
264 } break;
265 case kSpeed: {
266 messageID = LM_API_SPD_T_SET;
267 dataSize = packFXP16_16(dataBuffer,
268 (m_isInverted ? -outputValue : outputValue));
269 } break;
270 case kPosition: {
271 messageID = LM_API_POS_T_SET;
272 dataSize = packFXP16_16(dataBuffer, outputValue);
273 } break;
274 case kCurrent: {
275 messageID = LM_API_ICTRL_T_SET;
276 dataSize = packFXP8_8(dataBuffer, outputValue);
277 } break;
278 case kVoltage: {
279 messageID = LM_API_VCOMP_T_SET;
280 dataSize =
281 packFXP8_8(dataBuffer, (m_isInverted ? -outputValue : outputValue));
282 } break;
283 default:
284 wpi_setWPIErrorWithContext(IncompatibleMode,
285 "The Jaguar only supports Current, Voltage, "
286 "Position, Speed, and Percent (Throttle) "
287 "modes.");
288 return;
289 }
290 if (syncGroup != 0) {
291 dataBuffer[dataSize] = syncGroup;
292 dataSize++;
293 }
294
295 sendMessage(messageID, dataBuffer, dataSize, kSendMessagePeriod);
Brian Silverman26e4e522015-12-17 01:56:40 -0500296 }
297
298 m_value = outputValue;
299
300 verify();
301}
302
303/**
304 * Get the recently set outputValue setpoint.
305 *
306 * The scale and the units depend on the mode the Jaguar is in.<br>
307 * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM
308 * Jaguar).<br>
309 * In voltage Mode, the outputValue is in volts.<br>
310 * In current Mode, the outputValue is in amps.<br>
311 * In speed Mode, the outputValue is in rotations/minute.<br>
312 * In position Mode, the outputValue is in rotations.<br>
313 *
314 * @return The most recently set outputValue setpoint.
315 */
316float CANJaguar::Get() const { return m_value; }
317
318/**
319* Common interface for disabling a motor.
320*
321* @deprecated Call {@link #DisableControl()} instead.
322*/
323void CANJaguar::Disable() { DisableControl(); }
324
325/**
326 * Write out the PID value as seen in the PIDOutput base object.
327 *
328 * @deprecated Call Set instead.
329 *
330 * @param output Write out the PercentVbus value as was computed by the
331 * PIDController
332 */
333void CANJaguar::PIDWrite(float output) {
334 if (m_controlMode == kPercentVbus) {
335 Set(output);
336 } else {
337 wpi_setWPIErrorWithContext(IncompatibleMode,
338 "PID only supported in PercentVbus mode");
339 }
340}
341
342uint8_t CANJaguar::packPercentage(uint8_t *buffer, double value) {
343 int16_t intValue = (int16_t)(value * 32767.0);
344 *((int16_t *)buffer) = swap16(intValue);
345 return sizeof(int16_t);
346}
347
348uint8_t CANJaguar::packFXP8_8(uint8_t *buffer, double value) {
349 int16_t intValue = (int16_t)(value * 256.0);
350 *((int16_t *)buffer) = swap16(intValue);
351 return sizeof(int16_t);
352}
353
354uint8_t CANJaguar::packFXP16_16(uint8_t *buffer, double value) {
355 int32_t intValue = (int32_t)(value * 65536.0);
356 *((int32_t *)buffer) = swap32(intValue);
357 return sizeof(int32_t);
358}
359
360uint8_t CANJaguar::packint16_t(uint8_t *buffer, int16_t value) {
361 *((int16_t *)buffer) = swap16(value);
362 return sizeof(int16_t);
363}
364
365uint8_t CANJaguar::packint32_t(uint8_t *buffer, int32_t value) {
366 *((int32_t *)buffer) = swap32(value);
367 return sizeof(int32_t);
368}
369
370double CANJaguar::unpackPercentage(uint8_t *buffer) const {
371 int16_t value = *((int16_t *)buffer);
372 value = swap16(value);
373 return value / 32767.0;
374}
375
376double CANJaguar::unpackFXP8_8(uint8_t *buffer) const {
377 int16_t value = *((int16_t *)buffer);
378 value = swap16(value);
379 return value / 256.0;
380}
381
382double CANJaguar::unpackFXP16_16(uint8_t *buffer) const {
383 int32_t value = *((int32_t *)buffer);
384 value = swap32(value);
385 return value / 65536.0;
386}
387
388int16_t CANJaguar::unpackint16_t(uint8_t *buffer) const {
389 int16_t value = *((int16_t *)buffer);
390 return swap16(value);
391}
392
393int32_t CANJaguar::unpackint32_t(uint8_t *buffer) const {
394 int32_t value = *((int32_t *)buffer);
395 return swap32(value);
396}
397
398/**
399 * Send a message to the Jaguar.
400 *
401 * @param messageID The messageID to be used on the CAN bus (device number is
402 * added internally)
403 * @param data The up to 8 bytes of data to be sent with the message
404 * @param dataSize Specify how much of the data in "data" to send
405 * @param periodic If positive, tell Network Communications to send the message
406 * every "period" milliseconds.
407 */
408void CANJaguar::sendMessage(uint32_t messageID, const uint8_t *data,
409 uint8_t dataSize, int32_t period) {
410 int32_t localStatus =
411 sendMessageHelper(messageID | m_deviceNumber, data, dataSize, period);
412
413 if (localStatus < 0) {
414 wpi_setErrorWithContext(localStatus, "sendMessage");
415 }
416}
417
418/**
419 * Request a message from the Jaguar, but don't wait for it to arrive.
420 *
421 * @param messageID The message to request
422 * @param periodic If positive, tell Network Communications to send the message
423 * every "period" milliseconds.
424 */
425void CANJaguar::requestMessage(uint32_t messageID, int32_t period) {
426 sendMessageHelper(messageID | m_deviceNumber, nullptr, 0, period);
427}
428
429/**
430 * Get a previously requested message.
431 *
432 * Jaguar always generates a message with the same message ID when replying.
433 *
434 * @param messageID The messageID to read from the CAN bus (device number is
435 * added internally)
436 * @param data The up to 8 bytes of data that was received with the message
437 * @param dataSize Indicates how much data was received
438 *
439 * @return true if the message was found. Otherwise, no new message is
440 * available.
441 */
442bool CANJaguar::getMessage(uint32_t messageID, uint32_t messageMask,
443 uint8_t *data, uint8_t *dataSize) const {
444 uint32_t targetedMessageID = messageID | m_deviceNumber;
445 int32_t status = 0;
446 uint32_t timeStamp;
447
448 // Caller may have set bit31 for remote frame transmission so clear invalid
449 // bits[31-29]
450 targetedMessageID &= CAN_MSGID_FULL_M;
451
452 // Get the data.
453 FRC_NetworkCommunication_CANSessionMux_receiveMessage(
454 &targetedMessageID, messageMask, data, dataSize, &timeStamp, &status);
455
456 // Do we already have the most recent value?
457 if (status == ERR_CANSessionMux_MessageNotFound)
458 return false;
459 else
460 wpi_setErrorWithContext(status, "receiveMessage");
461
462 return true;
463}
464
465/**
466 * Enables periodic status updates from the Jaguar.
467 */
468void CANJaguar::setupPeriodicStatus() {
469 uint8_t data[8];
470 uint8_t dataSize;
471
472 // Message 0 returns bus voltage, output voltage, output current, and
473 // temperature.
474 static const uint8_t kMessage0Data[] = {
475 LM_PSTAT_VOLTBUS_B0, LM_PSTAT_VOLTBUS_B1, LM_PSTAT_VOLTOUT_B0,
476 LM_PSTAT_VOLTOUT_B1, LM_PSTAT_CURRENT_B0, LM_PSTAT_CURRENT_B1,
477 LM_PSTAT_TEMP_B0, LM_PSTAT_TEMP_B1};
478
479 // Message 1 returns position and speed
480 static const uint8_t kMessage1Data[] = {
481 LM_PSTAT_POS_B0, LM_PSTAT_POS_B1, LM_PSTAT_POS_B2, LM_PSTAT_POS_B3,
482 LM_PSTAT_SPD_B0, LM_PSTAT_SPD_B1, LM_PSTAT_SPD_B2, LM_PSTAT_SPD_B3};
483
484 // Message 2 returns limits and faults
485 static const uint8_t kMessage2Data[] = {LM_PSTAT_LIMIT_CLR, LM_PSTAT_FAULT,
486 LM_PSTAT_END};
487
488 dataSize = packint16_t(data, kSendMessagePeriod);
489 sendMessage(LM_API_PSTAT_PER_EN_S0, data, dataSize);
490 sendMessage(LM_API_PSTAT_PER_EN_S1, data, dataSize);
491 sendMessage(LM_API_PSTAT_PER_EN_S2, data, dataSize);
492
493 dataSize = 8;
494 sendMessage(LM_API_PSTAT_CFG_S0, kMessage0Data, dataSize);
495 sendMessage(LM_API_PSTAT_CFG_S1, kMessage1Data, dataSize);
496 sendMessage(LM_API_PSTAT_CFG_S2, kMessage2Data, dataSize);
497}
498
499/**
500 * Check for new periodic status updates and unpack them into local variables
501 */
502void CANJaguar::updatePeriodicStatus() const {
503 uint8_t data[8];
504 uint8_t dataSize;
505
506 // Check if a new bus voltage/output voltage/current/temperature message
507 // has arrived and unpack the values into the cached member variables
508 if (getMessage(LM_API_PSTAT_DATA_S0, CAN_MSGID_FULL_M, data, &dataSize)) {
509 m_mutex.lock();
510 m_busVoltage = unpackFXP8_8(data);
511 m_outputVoltage = unpackPercentage(data + 2) * m_busVoltage;
512 m_outputCurrent = unpackFXP8_8(data + 4);
513 m_temperature = unpackFXP8_8(data + 6);
514 m_mutex.unlock();
515
516 m_receivedStatusMessage0 = true;
517 }
518
519 // Check if a new position/speed message has arrived and do the same
520 if (getMessage(LM_API_PSTAT_DATA_S1, CAN_MSGID_FULL_M, data, &dataSize)) {
521 m_mutex.lock();
522 m_position = unpackFXP16_16(data);
523 m_speed = unpackFXP16_16(data + 4);
524 m_mutex.unlock();
525
526 m_receivedStatusMessage1 = true;
527 }
528
529 // Check if a new limits/faults message has arrived and do the same
530 if (getMessage(LM_API_PSTAT_DATA_S2, CAN_MSGID_FULL_M, data, &dataSize)) {
531 m_mutex.lock();
532 m_limits = data[0];
533 m_faults = data[1];
534 m_mutex.unlock();
535
536 m_receivedStatusMessage2 = true;
537 }
538}
539
540/**
541 * Check all unverified params and make sure they're equal to their local
542 * cached versions. If a value isn't available, it gets requested. If a value
543 * doesn't match up, it gets set again.
544 */
545void CANJaguar::verify() {
546 uint8_t dataBuffer[8];
547 uint8_t dataSize;
548
549 // If the Jaguar lost power, everything should be considered unverified.
550 if (getMessage(LM_API_STATUS_POWER, CAN_MSGID_FULL_M, dataBuffer,
551 &dataSize)) {
552 bool powerCycled = (bool)dataBuffer[0];
553
554 if (powerCycled) {
555 // Clear the power cycled bit
556 dataBuffer[0] = 1;
557 sendMessage(LM_API_STATUS_POWER, dataBuffer, sizeof(uint8_t));
558
559 // Mark everything as unverified
560 m_controlModeVerified = false;
561 m_speedRefVerified = false;
562 m_posRefVerified = false;
563 m_neutralModeVerified = false;
564 m_encoderCodesPerRevVerified = false;
565 m_potentiometerTurnsVerified = false;
566 m_forwardLimitVerified = false;
567 m_reverseLimitVerified = false;
568 m_limitModeVerified = false;
569 m_maxOutputVoltageVerified = false;
570 m_faultTimeVerified = false;
571
572 if (m_controlMode == kPercentVbus || m_controlMode == kVoltage) {
573 m_voltageRampRateVerified = false;
574 } else {
575 m_pVerified = false;
576 m_iVerified = false;
577 m_dVerified = false;
578 }
579
580 // Verify periodic status messages again
581 m_receivedStatusMessage0 = false;
582 m_receivedStatusMessage1 = false;
583 m_receivedStatusMessage2 = false;
584
585 // Remove any old values from netcomms. Otherwise, parameters are
586 // incorrectly marked as verified based on stale messages.
587 getMessage(LM_API_SPD_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
588 getMessage(LM_API_POS_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
589 getMessage(LM_API_SPD_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
590 getMessage(LM_API_POS_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
591 getMessage(LM_API_ICTRL_PC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
592 getMessage(LM_API_SPD_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
593 getMessage(LM_API_POS_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
594 getMessage(LM_API_ICTRL_IC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
595 getMessage(LM_API_SPD_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
596 getMessage(LM_API_POS_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
597 getMessage(LM_API_ICTRL_DC, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
598 getMessage(LM_API_CFG_BRAKE_COAST, CAN_MSGID_FULL_M, dataBuffer,
599 &dataSize);
600 getMessage(LM_API_CFG_ENC_LINES, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
601 getMessage(LM_API_CFG_POT_TURNS, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
602 getMessage(LM_API_CFG_LIMIT_MODE, CAN_MSGID_FULL_M, dataBuffer,
603 &dataSize);
604 getMessage(LM_API_CFG_LIMIT_FWD, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
605 getMessage(LM_API_CFG_LIMIT_REV, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
606 getMessage(LM_API_CFG_MAX_VOUT, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
607 getMessage(LM_API_VOLT_SET_RAMP, CAN_MSGID_FULL_M, dataBuffer, &dataSize);
608 getMessage(LM_API_VCOMP_COMP_RAMP, CAN_MSGID_FULL_M, dataBuffer,
609 &dataSize);
610 getMessage(LM_API_CFG_FAULT_TIME, CAN_MSGID_FULL_M, dataBuffer,
611 &dataSize);
612 }
613 } else {
614 requestMessage(LM_API_STATUS_POWER);
615 }
616
617 // Verify that any recently set parameters are correct
618 if (!m_controlModeVerified && m_controlEnabled) {
619 if (getMessage(LM_API_STATUS_CMODE, CAN_MSGID_FULL_M, dataBuffer,
620 &dataSize)) {
621 ControlMode mode = (ControlMode)dataBuffer[0];
622
623 if (m_controlMode == mode)
624 m_controlModeVerified = true;
625 else
626 // Enable control again to resend the control mode
627 EnableControl();
628 } else {
629 // Verification is needed but not available - request it again.
630 requestMessage(LM_API_STATUS_CMODE);
631 }
632 }
633
634 if (!m_speedRefVerified) {
635 if (getMessage(LM_API_SPD_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize)) {
636 uint8_t speedRef = dataBuffer[0];
637
638 if (m_speedReference == speedRef)
639 m_speedRefVerified = true;
640 else
641 // It's wrong - set it again
642 SetSpeedReference(m_speedReference);
643 } else {
644 // Verification is needed but not available - request it again.
645 requestMessage(LM_API_SPD_REF);
646 }
647 }
648
649 if (!m_posRefVerified) {
650 if (getMessage(LM_API_POS_REF, CAN_MSGID_FULL_M, dataBuffer, &dataSize)) {
651 uint8_t posRef = dataBuffer[0];
652
653 if (m_positionReference == posRef)
654 m_posRefVerified = true;
655 else
656 // It's wrong - set it again
657 SetPositionReference(m_positionReference);
658 } else {
659 // Verification is needed but not available - request it again.
660 requestMessage(LM_API_POS_REF);
661 }
662 }
663
664 if (!m_pVerified) {
665 uint32_t message = 0;
666
667 if (m_controlMode == kSpeed)
668 message = LM_API_SPD_PC;
669 else if (m_controlMode == kPosition)
670 message = LM_API_POS_PC;
671 else if (m_controlMode == kCurrent)
672 message = LM_API_ICTRL_PC;
673 else {
674 wpi_setWPIErrorWithContext(
675 IncompatibleMode,
676 "PID constants only apply in Speed, Position, and Current mode");
677 return;
678 }
679
680 if (getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize)) {
681 double p = unpackFXP16_16(dataBuffer);
682
683 if (FXP16_EQ(m_p, p))
684 m_pVerified = true;
685 else
686 // It's wrong - set it again
687 SetP(m_p);
688 } else {
689 // Verification is needed but not available - request it again.
690 requestMessage(message);
691 }
692 }
693
694 if (!m_iVerified) {
695 uint32_t message = 0;
696
697 if (m_controlMode == kSpeed)
698 message = LM_API_SPD_IC;
699 else if (m_controlMode == kPosition)
700 message = LM_API_POS_IC;
701 else if (m_controlMode == kCurrent)
702 message = LM_API_ICTRL_IC;
703 else {
704 wpi_setWPIErrorWithContext(
705 IncompatibleMode,
706 "PID constants only apply in Speed, Position, and Current mode");
707 return;
708 }
709
710 if (getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize)) {
711 double i = unpackFXP16_16(dataBuffer);
712
713 if (FXP16_EQ(m_i, i))
714 m_iVerified = true;
715 else
716 // It's wrong - set it again
717 SetI(m_i);
718 } else {
719 // Verification is needed but not available - request it again.
720 requestMessage(message);
721 }
722 }
723
724 if (!m_dVerified) {
725 uint32_t message = 0;
726
727 if (m_controlMode == kSpeed)
728 message = LM_API_SPD_DC;
729 else if (m_controlMode == kPosition)
730 message = LM_API_POS_DC;
731 else if (m_controlMode == kCurrent)
732 message = LM_API_ICTRL_DC;
733 else {
734 wpi_setWPIErrorWithContext(
735 IncompatibleMode,
736 "PID constants only apply in Speed, Position, and Current mode");
737 return;
738 }
739
740 if (getMessage(message, CAN_MSGID_FULL_M, dataBuffer, &dataSize)) {
741 double d = unpackFXP16_16(dataBuffer);
742
743 if (FXP16_EQ(m_d, d))
744 m_dVerified = true;
745 else
746 // It's wrong - set it again
747 SetD(m_d);
748 } else {
749 // Verification is needed but not available - request it again.
750 requestMessage(message);
751 }
752 }
753
754 if (!m_neutralModeVerified) {
755 if (getMessage(LM_API_CFG_BRAKE_COAST, CAN_MSGID_FULL_M, dataBuffer,
756 &dataSize)) {
757 NeutralMode mode = (NeutralMode)dataBuffer[0];
758
759 if (mode == m_neutralMode)
760 m_neutralModeVerified = true;
761 else
762 // It's wrong - set it again
763 ConfigNeutralMode(m_neutralMode);
764 } else {
765 // Verification is needed but not available - request it again.
766 requestMessage(LM_API_CFG_BRAKE_COAST);
767 }
768 }
769
770 if (!m_encoderCodesPerRevVerified) {
771 if (getMessage(LM_API_CFG_ENC_LINES, CAN_MSGID_FULL_M, dataBuffer,
772 &dataSize)) {
773 uint16_t codes = unpackint16_t(dataBuffer);
774
775 if (codes == m_encoderCodesPerRev)
776 m_encoderCodesPerRevVerified = true;
777 else
778 // It's wrong - set it again
779 ConfigEncoderCodesPerRev(m_encoderCodesPerRev);
780 } else {
781 // Verification is needed but not available - request it again.
782 requestMessage(LM_API_CFG_ENC_LINES);
783 }
784 }
785
786 if (!m_potentiometerTurnsVerified) {
787 if (getMessage(LM_API_CFG_POT_TURNS, CAN_MSGID_FULL_M, dataBuffer,
788 &dataSize)) {
789 uint16_t turns = unpackint16_t(dataBuffer);
790
791 if (turns == m_potentiometerTurns)
792 m_potentiometerTurnsVerified = true;
793 else
794 // It's wrong - set it again
795 ConfigPotentiometerTurns(m_potentiometerTurns);
796 } else {
797 // Verification is needed but not available - request it again.
798 requestMessage(LM_API_CFG_POT_TURNS);
799 }
800 }
801
802 if (!m_limitModeVerified) {
803 if (getMessage(LM_API_CFG_LIMIT_MODE, CAN_MSGID_FULL_M, dataBuffer,
804 &dataSize)) {
805 LimitMode mode = (LimitMode)dataBuffer[0];
806
807 if (mode == m_limitMode)
808 m_limitModeVerified = true;
809 else {
810 // It's wrong - set it again
811 ConfigLimitMode(m_limitMode);
812 }
813 } else {
814 // Verification is needed but not available - request it again.
815 requestMessage(LM_API_CFG_LIMIT_MODE);
816 }
817 }
818
819 if (!m_forwardLimitVerified) {
820 if (getMessage(LM_API_CFG_LIMIT_FWD, CAN_MSGID_FULL_M, dataBuffer,
821 &dataSize)) {
822 double limit = unpackFXP16_16(dataBuffer);
823
824 if (FXP16_EQ(limit, m_forwardLimit))
825 m_forwardLimitVerified = true;
826 else {
827 // It's wrong - set it again
828 ConfigForwardLimit(m_forwardLimit);
829 }
830 } else {
831 // Verification is needed but not available - request it again.
832 requestMessage(LM_API_CFG_LIMIT_FWD);
833 }
834 }
835
836 if (!m_reverseLimitVerified) {
837 if (getMessage(LM_API_CFG_LIMIT_REV, CAN_MSGID_FULL_M, dataBuffer,
838 &dataSize)) {
839 double limit = unpackFXP16_16(dataBuffer);
840
841 if (FXP16_EQ(limit, m_reverseLimit))
842 m_reverseLimitVerified = true;
843 else {
844 // It's wrong - set it again
845 ConfigReverseLimit(m_reverseLimit);
846 }
847 } else {
848 // Verification is needed but not available - request it again.
849 requestMessage(LM_API_CFG_LIMIT_REV);
850 }
851 }
852
853 if (!m_maxOutputVoltageVerified) {
854 if (getMessage(LM_API_CFG_MAX_VOUT, CAN_MSGID_FULL_M, dataBuffer,
855 &dataSize)) {
856 double voltage = unpackFXP8_8(dataBuffer);
857
858 // The returned max output voltage is sometimes slightly higher or
859 // lower than what was sent. This should not trigger resending
860 // the message.
861 if (std::abs(voltage - m_maxOutputVoltage) < 0.1)
862 m_maxOutputVoltageVerified = true;
863 else {
864 // It's wrong - set it again
865 ConfigMaxOutputVoltage(m_maxOutputVoltage);
866 }
867 } else {
868 // Verification is needed but not available - request it again.
869 requestMessage(LM_API_CFG_MAX_VOUT);
870 }
871 }
872
873 if (!m_voltageRampRateVerified) {
874 if (m_controlMode == kPercentVbus) {
875 if (getMessage(LM_API_VOLT_SET_RAMP, CAN_MSGID_FULL_M, dataBuffer,
876 &dataSize)) {
877 double rate = unpackPercentage(dataBuffer);
878
879 if (FXP16_EQ(rate, m_voltageRampRate))
880 m_voltageRampRateVerified = true;
881 else {
882 // It's wrong - set it again
883 SetVoltageRampRate(m_voltageRampRate);
884 }
885 } else {
886 // Verification is needed but not available - request it again.
887 requestMessage(LM_API_VOLT_SET_RAMP);
888 }
889 } else if (m_controlMode == kVoltage) {
890 if (getMessage(LM_API_VCOMP_COMP_RAMP, CAN_MSGID_FULL_M, dataBuffer,
891 &dataSize)) {
892 double rate = unpackFXP8_8(dataBuffer);
893
894 if (FXP8_EQ(rate, m_voltageRampRate))
895 m_voltageRampRateVerified = true;
896 else {
897 // It's wrong - set it again
898 SetVoltageRampRate(m_voltageRampRate);
899 }
900 } else {
901 // Verification is needed but not available - request it again.
902 requestMessage(LM_API_VCOMP_COMP_RAMP);
903 }
904 }
905 }
906
907 if (!m_faultTimeVerified) {
908 if (getMessage(LM_API_CFG_FAULT_TIME, CAN_MSGID_FULL_M, dataBuffer,
909 &dataSize)) {
910 uint16_t faultTime = unpackint16_t(dataBuffer);
911
912 if ((uint16_t)(m_faultTime * 1000.0) == faultTime)
913 m_faultTimeVerified = true;
914 else {
915 // It's wrong - set it again
916 ConfigFaultTime(m_faultTime);
917 }
918 } else {
919 // Verification is needed but not available - request it again.
920 requestMessage(LM_API_CFG_FAULT_TIME);
921 }
922 }
923
924 if (!m_receivedStatusMessage0 || !m_receivedStatusMessage1 ||
925 !m_receivedStatusMessage2) {
926 // If the periodic status messages haven't been verified as received,
927 // request periodic status messages again and attempt to unpack any
928 // available ones.
929 setupPeriodicStatus();
930 GetTemperature();
931 GetPosition();
932 GetFaults();
933 }
934}
935
936/**
937 * Set the reference source device for speed controller mode.
938 *
939 * Choose encoder as the source of speed feedback when in speed control mode.
940 *
941 * @param reference Specify a speed reference.
942 */
943void CANJaguar::SetSpeedReference(uint8_t reference) {
944 uint8_t dataBuffer[8];
945
946 // Send the speed reference parameter
947 dataBuffer[0] = reference;
948 sendMessage(LM_API_SPD_REF, dataBuffer, sizeof(uint8_t));
949
950 m_speedReference = reference;
951 m_speedRefVerified = false;
952}
953
954/**
955 * Get the reference source device for speed controller mode.
956 *
957 * @return A speed reference indicating the currently selected reference device
958 * for speed controller mode.
959 */
960uint8_t CANJaguar::GetSpeedReference() const { return m_speedReference; }
961
962/**
963 * Set the reference source device for position controller mode.
964 *
965 * Choose between using and encoder and using a potentiometer
966 * as the source of position feedback when in position control mode.
967 *
968 * @param reference Specify a PositionReference.
969 */
970void CANJaguar::SetPositionReference(uint8_t reference) {
971 uint8_t dataBuffer[8];
972
973 // Send the position reference parameter
974 dataBuffer[0] = reference;
975 sendMessage(LM_API_POS_REF, dataBuffer, sizeof(uint8_t));
976
977 m_positionReference = reference;
978 m_posRefVerified = false;
979}
980
981/**
982 * Get the reference source device for position controller mode.
983 *
984 * @return A PositionReference indicating the currently selected reference
985 * device for position controller mode.
986 */
987uint8_t CANJaguar::GetPositionReference() const { return m_positionReference; }
988
989/**
990 * Set the P, I, and D constants for the closed loop modes.
991 *
992 * @param p The proportional gain of the Jaguar's PID controller.
993 * @param i The integral gain of the Jaguar's PID controller.
994 * @param d The differential gain of the Jaguar's PID controller.
995 */
996void CANJaguar::SetPID(double p, double i, double d) {
997 SetP(p);
998 SetI(i);
999 SetD(d);
1000}
1001
1002/**
1003 * Set the P constant for the closed loop modes.
1004 *
1005 * @param p The proportional gain of the Jaguar's PID controller.
1006 */
1007void CANJaguar::SetP(double p) {
1008 uint8_t dataBuffer[8];
1009 uint8_t dataSize;
1010
1011 switch (m_controlMode) {
1012 case kPercentVbus:
1013 case kVoltage:
1014 case kFollower:
Brian Silverman1a675112016-02-20 20:42:49 -05001015 case kMotionProfile:
Brian Silverman26e4e522015-12-17 01:56:40 -05001016 wpi_setWPIErrorWithContext(
1017 IncompatibleMode,
1018 "PID constants only apply in Speed, Position, and Current mode");
1019 break;
1020 case kSpeed:
1021 dataSize = packFXP16_16(dataBuffer, p);
1022 sendMessage(LM_API_SPD_PC, dataBuffer, dataSize);
1023 break;
1024 case kPosition:
1025 dataSize = packFXP16_16(dataBuffer, p);
1026 sendMessage(LM_API_POS_PC, dataBuffer, dataSize);
1027 break;
1028 case kCurrent:
1029 dataSize = packFXP16_16(dataBuffer, p);
1030 sendMessage(LM_API_ICTRL_PC, dataBuffer, dataSize);
1031 break;
1032 }
1033
1034 m_p = p;
1035 m_pVerified = false;
1036}
1037
1038/**
1039 * Set the I constant for the closed loop modes.
1040 *
1041 * @param i The integral gain of the Jaguar's PID controller.
1042 */
1043void CANJaguar::SetI(double i) {
1044 uint8_t dataBuffer[8];
1045 uint8_t dataSize;
1046
1047 switch (m_controlMode) {
1048 case kPercentVbus:
1049 case kVoltage:
1050 case kFollower:
Brian Silverman1a675112016-02-20 20:42:49 -05001051 case kMotionProfile:
Brian Silverman26e4e522015-12-17 01:56:40 -05001052 wpi_setWPIErrorWithContext(
1053 IncompatibleMode,
1054 "PID constants only apply in Speed, Position, and Current mode");
1055 break;
1056 case kSpeed:
1057 dataSize = packFXP16_16(dataBuffer, i);
1058 sendMessage(LM_API_SPD_IC, dataBuffer, dataSize);
1059 break;
1060 case kPosition:
1061 dataSize = packFXP16_16(dataBuffer, i);
1062 sendMessage(LM_API_POS_IC, dataBuffer, dataSize);
1063 break;
1064 case kCurrent:
1065 dataSize = packFXP16_16(dataBuffer, i);
1066 sendMessage(LM_API_ICTRL_IC, dataBuffer, dataSize);
1067 break;
1068 }
1069
1070 m_i = i;
1071 m_iVerified = false;
1072}
1073
1074/**
1075 * Set the D constant for the closed loop modes.
1076 *
1077 * @param d The derivative gain of the Jaguar's PID controller.
1078 */
1079void CANJaguar::SetD(double d) {
1080 uint8_t dataBuffer[8];
1081 uint8_t dataSize;
1082
1083 switch (m_controlMode) {
1084 case kPercentVbus:
1085 case kVoltage:
1086 case kFollower:
Brian Silverman1a675112016-02-20 20:42:49 -05001087 case kMotionProfile:
Brian Silverman26e4e522015-12-17 01:56:40 -05001088 wpi_setWPIErrorWithContext(
1089 IncompatibleMode,
1090 "PID constants only apply in Speed, Position, and Current mode");
1091 break;
1092 case kSpeed:
1093 dataSize = packFXP16_16(dataBuffer, d);
1094 sendMessage(LM_API_SPD_DC, dataBuffer, dataSize);
1095 break;
1096 case kPosition:
1097 dataSize = packFXP16_16(dataBuffer, d);
1098 sendMessage(LM_API_POS_DC, dataBuffer, dataSize);
1099 break;
1100 case kCurrent:
1101 dataSize = packFXP16_16(dataBuffer, d);
1102 sendMessage(LM_API_ICTRL_DC, dataBuffer, dataSize);
1103 break;
1104 }
1105
1106 m_d = d;
1107 m_dVerified = false;
1108}
1109
1110/**
1111 * Get the Proportional gain of the controller.
1112 *
1113 * @return The proportional gain.
1114 */
1115double CANJaguar::GetP() const {
1116 if (m_controlMode == kPercentVbus || m_controlMode == kVoltage) {
1117 wpi_setWPIErrorWithContext(
1118 IncompatibleMode,
1119 "PID constants only apply in Speed, Position, and Current mode");
1120 return 0.0;
1121 }
1122
1123 return m_p;
1124}
1125
1126/**
1127 * Get the Intregral gain of the controller.
1128 *
1129 * @return The integral gain.
1130 */
1131double CANJaguar::GetI() const {
1132 if (m_controlMode == kPercentVbus || m_controlMode == kVoltage) {
1133 wpi_setWPIErrorWithContext(
1134 IncompatibleMode,
1135 "PID constants only apply in Speed, Position, and Current mode");
1136 return 0.0;
1137 }
1138
1139 return m_i;
1140}
1141
1142/**
1143 * Get the Differential gain of the controller.
1144 *
1145 * @return The differential gain.
1146 */
1147double CANJaguar::GetD() const {
1148 if (m_controlMode == kPercentVbus || m_controlMode == kVoltage) {
1149 wpi_setWPIErrorWithContext(
1150 IncompatibleMode,
1151 "PID constants only apply in Speed, Position, and Current mode");
1152 return 0.0;
1153 }
1154
1155 return m_d;
1156}
1157
1158/**
1159 * Enable the closed loop controller.
1160 *
1161 * Start actually controlling the output based on the feedback.
1162 * If starting a position controller with an encoder reference,
1163 * use the encoderInitialPosition parameter to initialize the
1164 * encoder state.
1165 *
1166 * @param encoderInitialPosition Encoder position to set if position with
1167 * encoder reference. Ignored otherwise.
1168 */
1169void CANJaguar::EnableControl(double encoderInitialPosition) {
1170 uint8_t dataBuffer[8];
1171 uint8_t dataSize = 0;
1172
1173 switch (m_controlMode) {
1174 case kPercentVbus:
1175 sendMessage(LM_API_VOLT_T_EN, dataBuffer, dataSize);
1176 break;
1177 case kSpeed:
1178 sendMessage(LM_API_SPD_T_EN, dataBuffer, dataSize);
1179 break;
1180 case kPosition:
1181 dataSize = packFXP16_16(dataBuffer, encoderInitialPosition);
1182 sendMessage(LM_API_POS_T_EN, dataBuffer, dataSize);
1183 break;
1184 case kCurrent:
1185 sendMessage(LM_API_ICTRL_T_EN, dataBuffer, dataSize);
1186 break;
1187 case kVoltage:
1188 sendMessage(LM_API_VCOMP_T_EN, dataBuffer, dataSize);
1189 break;
1190 default:
1191 wpi_setWPIErrorWithContext(IncompatibleMode,
1192 "The Jaguar only supports Current, Voltage, "
1193 "Position, Speed, and Percent (Throttle) "
1194 "modes.");
1195 return;
1196 }
1197
1198 m_controlEnabled = true;
1199 m_controlModeVerified = false;
1200}
1201
1202/**
1203 * Disable the closed loop controller.
1204 *
1205 * Stop driving the output based on the feedback.
1206 */
1207void CANJaguar::DisableControl() {
1208 uint8_t dataBuffer[8];
1209 uint8_t dataSize = 0;
1210
1211 // Disable all control
1212 sendMessage(LM_API_VOLT_DIS, dataBuffer, dataSize);
1213 sendMessage(LM_API_SPD_DIS, dataBuffer, dataSize);
1214 sendMessage(LM_API_POS_DIS, dataBuffer, dataSize);
1215 sendMessage(LM_API_ICTRL_DIS, dataBuffer, dataSize);
1216 sendMessage(LM_API_VCOMP_DIS, dataBuffer, dataSize);
1217
1218 // Stop all periodic setpoints
1219 sendMessage(LM_API_VOLT_T_SET, dataBuffer, dataSize,
1220 CAN_SEND_PERIOD_STOP_REPEATING);
1221 sendMessage(LM_API_SPD_T_SET, dataBuffer, dataSize,
1222 CAN_SEND_PERIOD_STOP_REPEATING);
1223 sendMessage(LM_API_POS_T_SET, dataBuffer, dataSize,
1224 CAN_SEND_PERIOD_STOP_REPEATING);
1225 sendMessage(LM_API_ICTRL_T_SET, dataBuffer, dataSize,
1226 CAN_SEND_PERIOD_STOP_REPEATING);
1227 sendMessage(LM_API_VCOMP_T_SET, dataBuffer, dataSize,
1228 CAN_SEND_PERIOD_STOP_REPEATING);
1229
1230 m_controlEnabled = false;
1231}
1232
1233/**
1234 * Enable controlling the motor voltage as a percentage of the bus voltage
1235 * without any position or speed feedback.<br>
1236 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1237 * CANJaguar#EnableControl(double)} to enable the device.
1238 */
1239void CANJaguar::SetPercentMode() {
1240 SetControlMode(kPercentVbus);
1241 SetPositionReference(LM_REF_NONE);
1242 SetSpeedReference(LM_REF_NONE);
1243}
1244
1245/**
1246 * Enable controlling the motor voltage as a percentage of the bus voltage,
1247 * and enable speed sensing from a non-quadrature encoder.<br>
1248 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1249 * CANJaguar#EnableControl(double)} to enable the device.
1250 *
1251 * @param tag The constant CANJaguar::Encoder
1252 * @param codesPerRev The counts per revolution on the encoder
1253 */
1254void CANJaguar::SetPercentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev) {
1255 SetControlMode(kPercentVbus);
1256 SetPositionReference(LM_REF_NONE);
1257 SetSpeedReference(LM_REF_ENCODER);
1258 ConfigEncoderCodesPerRev(codesPerRev);
1259}
1260
1261/**
1262 * Enable controlling the motor voltage as a percentage of the bus voltage,
1263 * and enable speed sensing from a non-quadrature encoder.<br>
1264 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1265 * CANJaguar#EnableControl(double)} to enable the device.
1266 *
1267 * @param tag The constant CANJaguar::QuadEncoder
1268 * @param codesPerRev The counts per revolution on the encoder
1269 */
1270void CANJaguar::SetPercentMode(CANJaguar::QuadEncoderStruct,
1271 uint16_t codesPerRev) {
1272 SetControlMode(kPercentVbus);
1273 SetPositionReference(LM_REF_ENCODER);
1274 SetSpeedReference(LM_REF_QUAD_ENCODER);
1275 ConfigEncoderCodesPerRev(codesPerRev);
1276}
1277
1278/**
1279* Enable controlling the motor voltage as a percentage of the bus voltage,
1280* and enable position sensing from a potentiometer and no speed feedback.<br>
1281* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1282* CANJaguar#EnableControl(double)} to enable the device.
1283*
1284* @param potentiometer The constant CANJaguar::Potentiometer
1285*/
1286void CANJaguar::SetPercentMode(CANJaguar::PotentiometerStruct) {
1287 SetControlMode(kPercentVbus);
1288 SetPositionReference(LM_REF_POT);
1289 SetSpeedReference(LM_REF_NONE);
1290 ConfigPotentiometerTurns(1);
1291}
1292
1293/**
1294 * Enable controlling the motor current with a PID loop.<br>
1295 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1296 * CANJaguar#EnableControl(double)} to enable the device.
1297 *
1298 * @param p The proportional gain of the Jaguar's PID controller.
1299 * @param i The integral gain of the Jaguar's PID controller.
1300 * @param d The differential gain of the Jaguar's PID controller.
1301 */
1302void CANJaguar::SetCurrentMode(double p, double i, double d) {
1303 SetControlMode(kCurrent);
1304 SetPositionReference(LM_REF_NONE);
1305 SetSpeedReference(LM_REF_NONE);
1306 SetPID(p, i, d);
1307}
1308
1309/**
1310* Enable controlling the motor current with a PID loop, and enable speed
1311* sensing from a non-quadrature encoder.<br>
1312* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1313* CANJaguar#EnableControl(double)} to enable the device.
1314*
1315* @param encoder The constant CANJaguar::Encoder
1316* @param p The proportional gain of the Jaguar's PID controller.
1317* @param i The integral gain of the Jaguar's PID controller.
1318* @param d The differential gain of the Jaguar's PID controller.
1319*/
1320void CANJaguar::SetCurrentMode(CANJaguar::EncoderStruct, uint16_t codesPerRev,
1321 double p, double i, double d) {
1322 SetControlMode(kCurrent);
1323 SetPositionReference(LM_REF_NONE);
1324 SetSpeedReference(LM_REF_NONE);
1325 ConfigEncoderCodesPerRev(codesPerRev);
1326 SetPID(p, i, d);
1327}
1328
1329/**
1330* Enable controlling the motor current with a PID loop, and enable speed and
1331* position sensing from a quadrature encoder.<br>
1332* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1333* CANJaguar#EnableControl(double)} to enable the device.
1334*
1335* @param endoer The constant CANJaguar::QuadEncoder
1336* @param p The proportional gain of the Jaguar's PID controller.
1337* @param i The integral gain of the Jaguar's PID controller.
1338* @param d The differential gain of the Jaguar's PID controller.
1339*/
1340void CANJaguar::SetCurrentMode(CANJaguar::QuadEncoderStruct,
1341 uint16_t codesPerRev, double p, double i,
1342 double d) {
1343 SetControlMode(kCurrent);
1344 SetPositionReference(LM_REF_ENCODER);
1345 SetSpeedReference(LM_REF_QUAD_ENCODER);
1346 ConfigEncoderCodesPerRev(codesPerRev);
1347 SetPID(p, i, d);
1348}
1349
1350/**
1351* Enable controlling the motor current with a PID loop, and enable position
1352* sensing from a potentiometer.<br>
1353* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1354* CANJaguar#EnableControl(double)} to enable the device.
1355*
1356* @param potentiometer The constant CANJaguar::Potentiometer
1357* @param p The proportional gain of the Jaguar's PID controller.
1358* @param i The integral gain of the Jaguar's PID controller.
1359* @param d The differential gain of the Jaguar's PID controller.
1360*/
1361void CANJaguar::SetCurrentMode(CANJaguar::PotentiometerStruct, double p,
1362 double i, double d) {
1363 SetControlMode(kCurrent);
1364 SetPositionReference(LM_REF_POT);
1365 SetSpeedReference(LM_REF_NONE);
1366 ConfigPotentiometerTurns(1);
1367 SetPID(p, i, d);
1368}
1369
1370/**
1371 * Enable controlling the speed with a feedback loop from a non-quadrature
1372 * encoder.<br>
1373 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1374 * CANJaguar#EnableControl(double)} to enable the device.
1375 *
1376 * @param encoder The constant CANJaguar::Encoder
1377 * @param codesPerRev The counts per revolution on the encoder.
1378 * @param p The proportional gain of the Jaguar's PID controller.
1379 * @param i The integral gain of the Jaguar's PID controller.
1380 * @param d The differential gain of the Jaguar's PID controller.
1381 */
1382void CANJaguar::SetSpeedMode(CANJaguar::EncoderStruct, uint16_t codesPerRev,
1383 double p, double i, double d) {
1384 SetControlMode(kSpeed);
1385 SetPositionReference(LM_REF_NONE);
1386 SetSpeedReference(LM_REF_ENCODER);
1387 ConfigEncoderCodesPerRev(codesPerRev);
1388 SetPID(p, i, d);
1389}
1390
1391/**
1392* Enable controlling the speed with a feedback loop from a quadrature
1393* encoder.<br>
1394* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1395* CANJaguar#EnableControl(double)} to enable the device.
1396*
1397* @param encoder The constant CANJaguar::QuadEncoder
1398* @param codesPerRev The counts per revolution on the encoder.
1399* @param p The proportional gain of the Jaguar's PID controller.
1400* @param i The integral gain of the Jaguar's PID controller.
1401* @param d The differential gain of the Jaguar's PID controller.
1402*/
1403void CANJaguar::SetSpeedMode(CANJaguar::QuadEncoderStruct, uint16_t codesPerRev,
1404 double p, double i, double d) {
1405 SetControlMode(kSpeed);
1406 SetPositionReference(LM_REF_ENCODER);
1407 SetSpeedReference(LM_REF_QUAD_ENCODER);
1408 ConfigEncoderCodesPerRev(codesPerRev);
1409 SetPID(p, i, d);
1410}
1411
1412/**
1413 * Enable controlling the position with a feedback loop using an encoder.<br>
1414 * After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1415 * CANJaguar#EnableControl(double)} to enable the device.
1416 *
1417 * @param encoder The constant CANJaguar::QuadEncoder
1418 * @param codesPerRev The counts per revolution on the encoder.
1419 * @param p The proportional gain of the Jaguar's PID controller.
1420 * @param i The integral gain of the Jaguar's PID controller.
1421 * @param d The differential gain of the Jaguar's PID controller.
1422 *
1423 */
1424void CANJaguar::SetPositionMode(CANJaguar::QuadEncoderStruct,
1425 uint16_t codesPerRev, double p, double i,
1426 double d) {
1427 SetControlMode(kPosition);
1428 SetPositionReference(LM_REF_ENCODER);
1429 ConfigEncoderCodesPerRev(codesPerRev);
1430 SetPID(p, i, d);
1431}
1432
1433/**
1434* Enable controlling the position with a feedback loop using a
1435* potentiometer.<br>
1436* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1437* CANJaguar#EnableControl(double)} to enable the device.
1438* @param p The proportional gain of the Jaguar's PID controller.
1439* @param i The integral gain of the Jaguar's PID controller.
1440* @param d The differential gain of the Jaguar's PID controller.
1441*/
1442void CANJaguar::SetPositionMode(CANJaguar::PotentiometerStruct, double p,
1443 double i, double d) {
1444 SetControlMode(kPosition);
1445 SetPositionReference(LM_REF_POT);
1446 ConfigPotentiometerTurns(1);
1447 SetPID(p, i, d);
1448}
1449
1450/**
1451* Enable controlling the motor voltage without any position or speed
1452* feedback.<br>
1453* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1454* CANJaguar#EnableControl(double)} to enable the device.
1455*/
1456void CANJaguar::SetVoltageMode() {
1457 SetControlMode(kVoltage);
1458 SetPositionReference(LM_REF_NONE);
1459 SetSpeedReference(LM_REF_NONE);
1460}
1461
1462/**
1463* Enable controlling the motor voltage with speed feedback from a
1464* non-quadrature encoder and no position feedback.<br>
1465* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1466* CANJaguar#EnableControl(double)} to enable the device.
1467*
1468* @param encoder The constant CANJaguar::Encoder
1469* @param codesPerRev The counts per revolution on the encoder
1470*/
1471void CANJaguar::SetVoltageMode(CANJaguar::EncoderStruct, uint16_t codesPerRev) {
1472 SetControlMode(kVoltage);
1473 SetPositionReference(LM_REF_NONE);
1474 SetSpeedReference(LM_REF_ENCODER);
1475 ConfigEncoderCodesPerRev(codesPerRev);
1476}
1477
1478/**
1479* Enable controlling the motor voltage with position and speed feedback from a
1480* quadrature encoder.<br>
1481* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1482* CANJaguar#EnableControl(double)} to enable the device.
1483*
1484* @param encoder The constant CANJaguar::QuadEncoder
1485* @param codesPerRev The counts per revolution on the encoder
1486*/
1487void CANJaguar::SetVoltageMode(CANJaguar::QuadEncoderStruct,
1488 uint16_t codesPerRev) {
1489 SetControlMode(kVoltage);
1490 SetPositionReference(LM_REF_ENCODER);
1491 SetSpeedReference(LM_REF_QUAD_ENCODER);
1492 ConfigEncoderCodesPerRev(codesPerRev);
1493}
1494
1495/**
1496* Enable controlling the motor voltage with position feedback from a
1497* potentiometer and no speed feedback.<br>
1498* After calling this you must call {@link CANJaguar#EnableControl()} or {@link
1499* CANJaguar#EnableControl(double)} to enable the device.
1500*
1501* @param potentiometer The constant CANJaguar::Potentiometer
1502*/
1503void CANJaguar::SetVoltageMode(CANJaguar::PotentiometerStruct) {
1504 SetControlMode(kVoltage);
1505 SetPositionReference(LM_REF_POT);
1506 SetSpeedReference(LM_REF_NONE);
1507 ConfigPotentiometerTurns(1);
1508}
1509
1510/**
1511 * Used internally. In order to set the control mode see the methods listed
1512 * below.
1513 * Change the control mode of this Jaguar object.
1514 *
1515 * After changing modes, configure any PID constants or other settings needed
1516 * and then EnableControl() to actually change the mode on the Jaguar.
1517 *
1518 * @param controlMode The new mode.
1519 */
1520void CANJaguar::SetControlMode(ControlMode controlMode) {
1521 // Disable the previous mode
1522 DisableControl();
1523
Brian Silverman1a675112016-02-20 20:42:49 -05001524 if ((controlMode == kFollower) || (controlMode == kMotionProfile))
Brian Silverman26e4e522015-12-17 01:56:40 -05001525 wpi_setWPIErrorWithContext(IncompatibleMode,
1526 "The Jaguar only supports Current, Voltage, "
1527 "Position, Speed, and Percent (Throttle) "
1528 "modes.");
1529
1530 // Update the local mode
1531 m_controlMode = controlMode;
1532 m_controlModeVerified = false;
1533
1534 HALReport(HALUsageReporting::kResourceType_CANJaguar, m_deviceNumber,
1535 m_controlMode);
1536}
1537
1538/**
1539 * Get the active control mode from the Jaguar.
1540 *
1541 * Ask the Jag what mode it is in.
1542 *
1543 * @return ControlMode that the Jag is in.
1544 */
1545CANJaguar::ControlMode CANJaguar::GetControlMode() const {
1546 return m_controlMode;
1547}
1548
1549/**
1550 * Get the voltage at the battery input terminals of the Jaguar.
1551 *
1552 * @return The bus voltage in volts.
1553 */
1554float CANJaguar::GetBusVoltage() const {
1555 updatePeriodicStatus();
1556 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1557
1558 return m_busVoltage;
1559}
1560
1561/**
1562 * Get the voltage being output from the motor terminals of the Jaguar.
1563 *
1564 * @return The output voltage in volts.
1565 */
1566float CANJaguar::GetOutputVoltage() const {
1567 updatePeriodicStatus();
1568 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1569
1570 return m_outputVoltage;
1571}
1572
1573/**
1574 * Get the current through the motor terminals of the Jaguar.
1575 *
1576 * @return The output current in amps.
1577 */
1578float CANJaguar::GetOutputCurrent() const {
1579 updatePeriodicStatus();
1580 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1581
1582 return m_outputCurrent;
1583}
1584
1585/**
1586 * Get the internal temperature of the Jaguar.
1587 *
1588 * @return The temperature of the Jaguar in degrees Celsius.
1589 */
1590float CANJaguar::GetTemperature() const {
1591 updatePeriodicStatus();
1592 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1593
1594 return m_temperature;
1595}
1596
1597/**
1598 * Get the position of the encoder or potentiometer.
1599 *
1600 * @return The position of the motor in rotations based on the configured
1601 * feedback.
1602 * @see CANJaguar#ConfigPotentiometerTurns(int)
1603 * @see CANJaguar#ConfigEncoderCodesPerRev(int)
1604 */
1605double CANJaguar::GetPosition() const {
1606 updatePeriodicStatus();
1607 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1608
1609 return m_position;
1610}
1611
1612/**
1613 * Get the speed of the encoder.
1614 *
1615 * @return The speed of the motor in RPM based on the configured feedback.
1616 */
1617double CANJaguar::GetSpeed() const {
1618 updatePeriodicStatus();
1619 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1620
1621 return m_speed;
1622}
1623
1624/**
1625 * Get the status of the forward limit switch.
1626 *
1627 * @return The motor is allowed to turn in the forward direction when true.
1628 */
1629bool CANJaguar::GetForwardLimitOK() const {
1630 updatePeriodicStatus();
1631 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1632
1633 return m_limits & kForwardLimit;
1634}
1635
1636/**
1637 * Get the status of the reverse limit switch.
1638 *
1639 * @return The motor is allowed to turn in the reverse direction when true.
1640 */
1641bool CANJaguar::GetReverseLimitOK() const {
1642 updatePeriodicStatus();
1643 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1644
1645 return m_limits & kReverseLimit;
1646}
1647
1648/**
1649 * Get the status of any faults the Jaguar has detected.
1650 *
1651 * @return A bit-mask of faults defined by the "Faults" enum.
1652 * @see #kCurrentFault
1653 * @see #kBusVoltageFault
1654 * @see #kTemperatureFault
1655 * @see #kGateDriverFault
1656 */
1657uint16_t CANJaguar::GetFaults() const {
1658 updatePeriodicStatus();
1659 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
1660
1661 return m_faults;
1662}
1663
1664/**
1665 * Set the maximum voltage change rate.
1666 *
1667 * When in PercentVbus or Voltage output mode, the rate at which the voltage
1668 * changes can
1669 * be limited to reduce current spikes. Set this to 0.0 to disable rate
1670 * limiting.
1671 *
1672 * @param rampRate The maximum rate of voltage change in Percent Voltage mode in
1673 * V/s.
1674 */
1675void CANJaguar::SetVoltageRampRate(double rampRate) {
1676 uint8_t dataBuffer[8];
1677 uint8_t dataSize;
1678 uint32_t message;
1679
1680 switch (m_controlMode) {
1681 case kPercentVbus:
1682 dataSize = packPercentage(
1683 dataBuffer, rampRate / (m_maxOutputVoltage * kControllerRate));
1684 message = LM_API_VOLT_SET_RAMP;
1685 break;
1686 case kVoltage:
1687 dataSize = packFXP8_8(dataBuffer, rampRate / kControllerRate);
1688 message = LM_API_VCOMP_COMP_RAMP;
1689 break;
1690 default:
1691 wpi_setWPIErrorWithContext(
1692 IncompatibleMode,
1693 "SetVoltageRampRate only applies in Voltage and Percent mode");
1694 return;
1695 }
1696
1697 sendMessage(message, dataBuffer, dataSize);
1698
1699 m_voltageRampRate = rampRate;
1700 m_voltageRampRateVerified = false;
1701}
1702
1703/**
1704 * Get the version of the firmware running on the Jaguar.
1705 *
1706 * @return The firmware version. 0 if the device did not respond.
1707 */
1708uint32_t CANJaguar::GetFirmwareVersion() const { return m_firmwareVersion; }
1709
1710/**
1711 * Get the version of the Jaguar hardware.
1712 *
1713 * @return The hardware version. 1: Jaguar, 2: Black Jaguar
1714 */
1715uint8_t CANJaguar::GetHardwareVersion() const { return m_hardwareVersion; }
1716
1717/**
1718 * Configure what the controller does to the H-Bridge when neutral (not driving
1719 * the output).
1720 *
1721 * This allows you to override the jumper configuration for brake or coast.
1722 *
1723 * @param mode Select to use the jumper setting or to override it to coast or
1724 * brake.
1725 */
1726void CANJaguar::ConfigNeutralMode(NeutralMode mode) {
1727 uint8_t dataBuffer[8];
1728
1729 // Set the neutral mode
1730 sendMessage(LM_API_CFG_BRAKE_COAST, dataBuffer, sizeof(uint8_t));
1731
1732 m_neutralMode = mode;
1733 m_neutralModeVerified = false;
1734}
1735
1736/**
1737 * Configure how many codes per revolution are generated by your encoder.
1738 *
1739 * @param codesPerRev The number of counts per revolution in 1X mode.
1740 */
1741void CANJaguar::ConfigEncoderCodesPerRev(uint16_t codesPerRev) {
1742 uint8_t dataBuffer[8];
1743
1744 // Set the codes per revolution mode
1745 packint16_t(dataBuffer, codesPerRev);
1746 sendMessage(LM_API_CFG_ENC_LINES, dataBuffer, sizeof(uint16_t));
1747
1748 m_encoderCodesPerRev = codesPerRev;
1749 m_encoderCodesPerRevVerified = false;
1750}
1751
1752/**
1753 * Configure the number of turns on the potentiometer.
1754 *
1755 * There is no special support for continuous turn potentiometers.
1756 * Only integer numbers of turns are supported.
1757 *
1758 * @param turns The number of turns of the potentiometer.
1759 */
1760void CANJaguar::ConfigPotentiometerTurns(uint16_t turns) {
1761 uint8_t dataBuffer[8];
1762 uint8_t dataSize;
1763
1764 // Set the pot turns
1765 dataSize = packint16_t(dataBuffer, turns);
1766 sendMessage(LM_API_CFG_POT_TURNS, dataBuffer, dataSize);
1767
1768 m_potentiometerTurns = turns;
1769 m_potentiometerTurnsVerified = false;
1770}
1771
1772/**
1773 * Configure Soft Position Limits when in Position Controller mode.
1774 *
1775 * When controlling position, you can add additional limits on top of the limit
1776 switch inputs
1777 * that are based on the position feedback. If the position limit is reached or
1778 the
1779 * switch is opened, that direction will be disabled.
1780 *
1781
1782 * @param forwardLimitPosition The position that if exceeded will disable the
1783 forward direction.
1784 * @param reverseLimitPosition The position that if exceeded will disable the
1785 reverse direction.
1786 */
1787void CANJaguar::ConfigSoftPositionLimits(double forwardLimitPosition,
1788 double reverseLimitPosition) {
1789 ConfigLimitMode(kLimitMode_SoftPositionLimits);
1790 ConfigForwardLimit(forwardLimitPosition);
1791 ConfigReverseLimit(reverseLimitPosition);
1792}
1793
1794/**
1795 * Disable Soft Position Limits if previously enabled.
1796 *
1797 * Soft Position Limits are disabled by default.
1798 */
1799void CANJaguar::DisableSoftPositionLimits() {
1800 ConfigLimitMode(kLimitMode_SwitchInputsOnly);
1801}
1802
1803/**
1804 * Set the limit mode for position control mode.
1805 *
1806 * Use ConfigSoftPositionLimits or DisableSoftPositionLimits to set this
1807 * automatically.
1808 */
1809void CANJaguar::ConfigLimitMode(LimitMode mode) {
1810 uint8_t dataBuffer[8];
1811
1812 dataBuffer[0] = mode;
1813 sendMessage(LM_API_CFG_LIMIT_MODE, dataBuffer, sizeof(uint8_t));
1814
1815 m_limitMode = mode;
1816 m_limitModeVerified = false;
1817}
1818
1819/**
1820* Set the position that if exceeded will disable the forward direction.
1821*
1822* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
1823*/
1824void CANJaguar::ConfigForwardLimit(double forwardLimitPosition) {
1825 uint8_t dataBuffer[8];
1826 uint8_t dataSize;
1827
1828 dataSize = packFXP16_16(dataBuffer, forwardLimitPosition);
1829 dataBuffer[dataSize++] = 1;
1830 sendMessage(LM_API_CFG_LIMIT_FWD, dataBuffer, dataSize);
1831
1832 m_forwardLimit = forwardLimitPosition;
1833 m_forwardLimitVerified = false;
1834}
1835
1836/**
1837* Set the position that if exceeded will disable the reverse direction.
1838*
1839* Use ConfigSoftPositionLimits to set this and the limit mode automatically.
1840*/
1841void CANJaguar::ConfigReverseLimit(double reverseLimitPosition) {
1842 uint8_t dataBuffer[8];
1843 uint8_t dataSize;
1844
1845 dataSize = packFXP16_16(dataBuffer, reverseLimitPosition);
1846 dataBuffer[dataSize++] = 0;
1847 sendMessage(LM_API_CFG_LIMIT_REV, dataBuffer, dataSize);
1848
1849 m_reverseLimit = reverseLimitPosition;
1850 m_reverseLimitVerified = false;
1851}
1852
1853/**
1854 * Configure the maximum voltage that the Jaguar will ever output.
1855 *
1856 * This can be used to limit the maximum output voltage in all modes so that
1857 * motors which cannot withstand full bus voltage can be used safely.
1858 *
1859 * @param voltage The maximum voltage output by the Jaguar.
1860 */
1861void CANJaguar::ConfigMaxOutputVoltage(double voltage) {
1862 uint8_t dataBuffer[8];
1863 uint8_t dataSize;
1864
1865 dataSize = packFXP8_8(dataBuffer, voltage);
1866 sendMessage(LM_API_CFG_MAX_VOUT, dataBuffer, dataSize);
1867
1868 m_maxOutputVoltage = voltage;
1869 m_maxOutputVoltageVerified = false;
1870}
1871
1872/**
1873 * Configure how long the Jaguar waits in the case of a fault before resuming
1874 * operation.
1875 *
1876 * Faults include over temerature, over current, and bus under voltage.
1877 * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds.
1878 *
1879 * @param faultTime The time to wait before resuming operation, in seconds.
1880 */
1881void CANJaguar::ConfigFaultTime(float faultTime) {
1882 uint8_t dataBuffer[8];
1883 uint8_t dataSize;
1884
1885 if (faultTime < 0.5)
1886 faultTime = 0.5;
1887 else if (faultTime > 3.0)
1888 faultTime = 3.0;
1889
1890 // Message takes ms
1891 dataSize = packint16_t(dataBuffer, (int16_t)(faultTime * 1000.0));
1892 sendMessage(LM_API_CFG_FAULT_TIME, dataBuffer, dataSize);
1893
1894 m_faultTime = faultTime;
1895 m_faultTimeVerified = false;
1896}
1897
1898/**
1899 * Update all the motors that have pending sets in the syncGroup.
1900 *
1901 * @param syncGroup A bitmask of groups to generate synchronous output.
1902 */
1903void CANJaguar::UpdateSyncGroup(uint8_t syncGroup) {
1904 sendMessageHelper(CAN_MSGID_API_SYNC, &syncGroup, sizeof(syncGroup),
1905 CAN_SEND_PERIOD_NO_REPEAT);
1906}
1907
1908void CANJaguar::SetExpiration(float timeout) {
1909 if (m_safetyHelper) m_safetyHelper->SetExpiration(timeout);
1910}
1911
1912float CANJaguar::GetExpiration() const {
1913 if (!m_safetyHelper) return 0.0;
1914 return m_safetyHelper->GetExpiration();
1915}
1916
1917bool CANJaguar::IsAlive() const {
1918 if (!m_safetyHelper) return false;
1919 return m_safetyHelper->IsAlive();
1920}
1921
1922bool CANJaguar::IsSafetyEnabled() const {
1923 if (!m_safetyHelper) return false;
1924 return m_safetyHelper->IsSafetyEnabled();
1925}
1926
1927void CANJaguar::SetSafetyEnabled(bool enabled) {
1928 if (m_safetyHelper) m_safetyHelper->SetSafetyEnabled(enabled);
1929}
1930
1931void CANJaguar::GetDescription(std::ostringstream& desc) const {
1932 desc << "CANJaguar ID " << m_deviceNumber;
1933}
1934
1935uint8_t CANJaguar::GetDeviceID() const { return m_deviceNumber; }
1936
1937/**
Brian Silverman1a675112016-02-20 20:42:49 -05001938 * Common interface for stopping the motor until the next Set() command
Brian Silverman26e4e522015-12-17 01:56:40 -05001939 * Part of the MotorSafety interface
1940 *
1941 * @deprecated Call DisableControl instead.
1942 */
Brian Silverman1a675112016-02-20 20:42:49 -05001943void CANJaguar::StopMotor() {
1944 m_stopped = true;
1945}
Brian Silverman26e4e522015-12-17 01:56:40 -05001946
1947/**
1948* Common interface for inverting direction of a speed controller.
1949* Only works in PercentVbus, speed, and Voltage modes.
1950* @param isInverted The state of inversion, true is inverted
1951*/
1952void CANJaguar::SetInverted(bool isInverted) { m_isInverted = isInverted; }
1953
1954/**
1955 * Common interface for the inverting direction of a speed controller.
1956 *
1957 * @return isInverted The state of inversion, true is inverted.
1958 *
1959 */
1960bool CANJaguar::GetInverted() const { return m_isInverted; }
1961
1962void CANJaguar::ValueChanged(ITable* source, llvm::StringRef key,
1963 std::shared_ptr<nt::Value> value, bool isNew) {
1964 if(key == "Mode" && value->IsDouble()) SetControlMode(static_cast<CANSpeedController::ControlMode>(value->GetDouble()));
1965 if(IsModePID(m_controlMode) && value->IsDouble()) {
1966 if(key == "p") SetP(value->GetDouble());
1967 if(key == "i") SetI(value->GetDouble());
1968 if(key == "d") SetD(value->GetDouble());
1969 }
1970 if(key == "Enabled" && value->IsBoolean()) {
1971 if (value->GetBoolean()) {
1972 EnableControl();
1973 } else {
1974 DisableControl();
1975 }
1976 }
1977 if(key == "Value" && value->IsDouble()) Set(value->GetDouble());
1978}
1979
1980bool CANJaguar::IsModePID(CANSpeedController::ControlMode mode) const {
1981 return mode == kCurrent || mode == kSpeed || mode == kPosition;
1982}
1983
1984void CANJaguar::UpdateTable() {
1985 if (m_table != nullptr) {
1986 m_table->PutString("~TYPE~", "CANSpeedController");
1987 m_table->PutString("Type", "CANJaguar");
Brian Silverman1a675112016-02-20 20:42:49 -05001988 m_table->PutNumber("Mode", m_controlMode);
Brian Silverman26e4e522015-12-17 01:56:40 -05001989 if (IsModePID(m_controlMode)) {
1990 m_table->PutNumber("p", GetP());
1991 m_table->PutNumber("i", GetI());
1992 m_table->PutNumber("d", GetD());
1993 }
1994 m_table->PutBoolean("Enabled", m_controlEnabled);
1995 m_table->PutNumber("Value", Get());
1996 }
1997}
1998
1999void CANJaguar::StartLiveWindowMode() {
2000 if (m_table != nullptr) {
2001 m_table->AddTableListener(this, true);
2002 }
2003}
2004
2005void CANJaguar::StopLiveWindowMode() {
2006 if (m_table != nullptr) {
2007 m_table->RemoveTableListener(this);
2008 }
2009}
2010
2011std::string CANJaguar::GetSmartDashboardType() const {
2012 return "CANSpeedController";
2013}
2014
2015void CANJaguar::InitTable(std::shared_ptr<ITable> subTable) {
2016 m_table = subTable;
2017 UpdateTable();
2018}
2019
2020std::shared_ptr<ITable> CANJaguar::GetTable() const { return m_table; }