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