ReduxLib C++ 2026.1.2
Loading...
Searching...
No Matches
Canandmag.h
1// Copyright (c) Bagholders of Redux Robotics and other contributors.
2// This is open source and can be modified and shared under the Mozilla Public License v2.0.
3
4#pragma once
5#include <cinttypes>
6#include <unordered_map>
7#include <mutex>
8#include <condition_variable>
9#include <optional>
10#include <units/angle.h>
11#include <units/angular_velocity.h>
12#include <units/time.h>
13#include <units/temperature.h>
14#include "redux/canand/CanandDevice.h"
15#include "redux/canand/CanandEventLoop.h"
16#include "redux/canand/CanandSettingsManager.h"
17#include "redux/canand/CooldownWarning.h"
18#include "redux/canand/MessageBus.h"
19#include "redux/frames/Frame.h"
20
21#include "redux/sensors/canandmag/CanandmagDetails.h"
22#include "redux/sensors/canandmag/CanandmagFaults.h"
23#include "redux/sensors/canandmag/CanandmagSettings.h"
24#include "redux/sensors/canandmag/CanandmagStatus.h"
25
26/**
27 * Namespace for all classes relating to the Canandmag
28*/
30
31/**
32 * Class for the CAN interface of the <a href="https://docs.reduxrobotics.com/canandmag/index.html">Canandmag.</a>
33 *
34 * <p>
35 * If you are using a Canandmag with Spark Max or Talon with the PWM output, see
36 * <a href="https://docs.reduxrobotics.com/canandmag/spark-max.html">our Spark Max docs</a>
37 * or
38 * <a href="https://docs.reduxrobotics.com/canandmag/talon-srx.html">our Talon SRX docs</a>
39 * for information on how to use the encoder with the Rev and CTRE APIs.
40 * </p>
41 *
42 * <p>
43 * The C++ vendordep uses the <a href="https://docs.wpilib.org/en/stable/docs/software/basic-programming/cpp-units.html">units library</a>
44 * for all dimensioned values, including settings.
45 * </p>
46 *
47 * <p>
48 * Operations that receive data from the device (position, velocity, faults, temperature) generally do not block.
49 * The object receives data asynchronously from the CAN packet receive thread and reads thus return the last data received.
50 * </p>
51 * <p>
52 * Operations that set settings or change offsets will generally wait for up to 20ms by default as they will usually
53 * wait for a confirmation packet to be received in response -- unless the blocking timeout is set to zero, in which case
54 * the operation swill not block.
55 * </p>
56 *
57 * Example code:
58 * ```cpp
59 * Canandmag canandmag{0}; // instantiates with encoder id 0
60 *
61 * // Reading the Canandmag
62 * canandmag.GetPosition(); // returns a multi-turn relative position, in rotations (turns)
63 * canandmag.GetAbsPosition(); // returns an absolute position bounded from [0..1) over one rotation
64 * canandmag.GetVelocity(); // returns measured velocity in rotations per second
65 *
66 * // Updating position
67 * canandmag.SetPosition(-3.5_tr); // sets the relative position to -3.5 turns (does not persist on reboot)
68 * canandmag.SetAbsPosition(330_deg, 0_s); // sets the absolute position to 330 degrees without blocking for confirmation (persists on reboot)
69 * canandmag.ZeroAll(); // sets both the relative and absolute position to zero
70 *
71 * // Changing configuration
72 * CanandmagSettings settings;
73 * settings.SetVelocityFilterWidth(25_ms); // sets the velocity filter averaging period to 25 ms
74 * settings.SetInvertDirection(true); // make positive be clockwise instead of ccw opposite the sensor face
75 * canandmag.SetSettings(settings, 20_ms); // apply the new settings to the device, with maximum 20 ms timeout per settings operation
76 *
77 * // Faults
78 * canandmag.ClearStickyFaults(); // clears all sticky faults (including the power cycle flag). This call does not block.
79 *
80 * // this flag will always be true on boot until the sticky faults have been cleared,
81 * // so if this prints true the encoder has rebooted sometime between ClearStickyFaults and now.
82 * CanandmagFaults faults = canandmag.GetStickyFaults(); // fetches faults
83 * fmt::print("Encoder rebooted: {}\n", faults.powerCycle);
84 *
85 * // Timestamped data
86 * redux::frames::FrameData<units::turn_t> posFrameData = canandmag.GetPositionFrame().GetFrameData(); // gets current position + timestamp together
87 * posFrameData.GetValue(); // fetched position in rotations
88 * posFrameData.GetTimestamp(); // timestamp of the previous position
89 * ```
90 *
91 */
93 public:
94 /**
95 * Constructor with the device's id. This object will be constant with respect to whatever CAN id assigned to it,
96 * so if a device changes id it may change which device this object reads from.
97 * @param canID the device id to use
98 * @param bus the message bus to use. Defaults to "halcan"
99 */
100 Canandmag(int canID, std::string bus = "halcan");
102 // functions related to core functionality
103
104 /**
105 * Gets the current integrated position in rotations.
106 *
107 * <p> This value does not wrap around, so turning a sensed axle multiple rotations will return multiple sensed rotations of position.
108 * By default, positive is in the counter-clockwise direction from the sensor face.
109 * </p>
110 * <p> On encoder power-on, unlike the absolute value, this value will always initialize to zero. </p>
111 * @return signed relative position in rotations (range [-131072.0..131071.999938396484])
112 */
113 units::turn_t GetPosition();
114
115 /**
116 * Gets the current absolute position of the encoder, in a scaled value from 0 inclusive to 1 exclusive.
117 * By default, higher values are in the counter-clockwise direction from the sensor face.
118 * <p> This value will persist across encoder power cycles making it appropriate for swerves/arms/etc. </p>
119 * @return absolute position in fraction of a rotation [0..1)
120 */
121 units::turn_t GetAbsPosition();
122
123 /**
124 * Sets the new relative (multi-turn) position of the encoder to the given value.
125 *
126 * <p>
127 * Note that this does not update the absolute position, and this value is lost on a power cycle. To update the absolute position,
128 * use Canandmag::SetAbsPosition
129 * </p>
130 * @param newPosition new position in rotations
131 * @param timeout maximum time to wait for the operation to be confirmed (default 0.020 seconds). Set to 0 to not check (and not block).
132 * @return true on success, false on timeout
133 */
134 bool SetPosition(units::turn_t newPosition, units::second_t timeout = 20_ms);
135
136 /**
137 * Sets the new absolute position value for the encoder which will (by default) persist across reboots.
138 *
139 * @param newPosition new absolute position in fraction of a rotation (acceptable range [0..1))
140 * @param timeout maximum time to wait for the operation to be confirmed (default 0.020 seconds). Set to 0 to not check (and not block).
141 * @param ephemeral if true, set the setting ephemerally -- the new zero offset will not persist on power cycle.
142 * @return true on success, false on timeout
143 */
144 bool SetAbsPosition(units::turn_t newPosition, units::second_t timeout = 20_ms, bool ephemeral = false);
145
146 /**
147 * Sets both the current absolute and relative encoder position to 0 -- generally equivalent to pressing the physical zeroing button on the encoder.
148 * @param timeout maximum time in seconds to wait for each operation to be confirmed (there are 2 ops for zeroing both absolute and relative positions,
149 * so the wait is up to 2x timouet). Set to 0 to not check (and not block).
150 * @return true on success, false on timeout
151 */
152 bool ZeroAll(units::second_t timeout = 20_ms);
153
154 /**
155 * Returns the measured velocity in rotations per second.
156 * @return velocity, in rotations (turns) per second
157 */
158 units::turns_per_second_t GetVelocity();
159
160 /**
161 * Returns whether the encoder magnet is in range of the sensor or not.
162 * This can be seen visually on the sensor -- a green LED is in range, whereas
163 * a red LED is out of range.
164 *
165 * @return whether the output shaft magnet is in range.
166 */
168
169 // functions related to diagonstic data
170
171 /**
172 * Fetches sticky faults.
173 * Sticky faults are the active faults, except once set they do not become unset until ClearStickyFaults() is called.
174 *
175 * @return CanandmagFaults of the sticky faults
176 */
178
179 /**
180 * Fetches active faults.
181 * Active faults are only active for as long as the error state exists.
182 *
183 * @return CanandmagFaults of the active faults
184 */
186
187 /**
188 * Get onboard encoder temperature readings in degrees Celsius.
189 * @return temperature in degrees Celsius
190 */
191 units::celsius_t GetTemperature();
192
193 /**
194 * Get the contents of the previous status packet, which includes active faults, sticky faults, and temperature.
195 * @return device status as a status struct
196 */
197 inline CanandmagStatus GetStatus() { return status.GetValue(); }
198
199 /**
200 * Clears sticky faults.
201 *
202 * <p>It is recommended to clear this during initialization, so one can check if the encoder loses power during operation later. </p>
203 * <p>This call does not block, so it may take up to the next status frame (default every 1000 ms) for the sticky faults to be updated.</p>
204 */
206
207 /**
208 * Controls "party mode" -- an encoder identification tool that blinks the onboard LED
209 * various colors at a user-specified strobe period.
210 * The strobe period of the LED will be (50 milliseconds * level). Setting this to 0 disables party mode.
211 *
212 * This function does not block.
213 *
214 * @param level the party level value to set.
215 */
216 void SetPartyMode(uint8_t level);
217
218 // functions relating to settings
219
220 /**
221 * Fetches the Canandmag's current configuration in a blocking manner.
222 * This function will need to block for at least 0.2-0.3 seconds waiting for the encoder to reply, so it is best
223 * to put this in an init function, rather than the main loop.
224 *
225 * <p> <b>Note that unlike v2023, this function will always return a settings object,
226 * but they may be incomplete settings!</b> </p>
227 *
228 * You will need to do something like this to unwrap/verify the result:
229 *
230 * ```cpp
231 * // device declaration
232 * Canandmag canandmag{0};
233 *
234 * // in your init/other sequence
235 * CanandmagSettings stg = canandmag.GetSettings();
236 * if (stg.AllSettingsReceived()) {
237 * // do your thing here
238 * } else {
239 * // handle missing settings
240 * }
241 * ```
242 *
243 * <p>Advanced users can use this function to retry settings missed from StartFetchSettings: </p>
244 * ```cpp
245 * // device declaration
246 * Canandmag canandmag{0};
247 * enc.StartFetchSettings(); // send a "fetch settings command"
248 *
249 * // wait some amount of time
250 * CanandmagSettings stg = enc.GetSettingsAsync();
251 * stg.AllSettingsReceived(); // may or may not be true
252 *
253 * stg = enc.GetSettings(0_ms, 20_ms, 3); // Retry getitng the missing settings.
254 * stg.AllSettingsReceived(); // far more likely to be true
255 * ```
256 *
257 * @param timeout maximum number of seconds to wait for a settings operation before timing out (default 350_ms)
258 * @param missingTimeout maximum number of seconds to wait for each settings retry before giving up
259 * @param attempts number of attempts to try and fetch values missing from the first pass
260 * @return Received set of CanandmagSettings of device configuration.
261 */
262 inline CanandmagSettings GetSettings(units::second_t timeout = 350_ms, units::second_t missingTimeout = 20_ms, uint32_t attempts = 3) {
263 return stg.GetSettings(timeout, missingTimeout, attempts);
264 }
265
266 /**
267 * Tells the Canandmag to begin transmitting its settings; once they are all transmitted (after ~200-300ms),
268 * the values can be retrieved through the Canandmag::GetSettingsAsync() function call
269 */
270 inline void StartFetchSettings() { stg.StartFetchSettings(); }
271
272 /**
273 * Non-blockingly returns a {@link CanandmagSettings} object of the most recent known settings values received from the encoder.
274 *
275 * <p> <b>Most users will probably want to use Canandmag::GetSettings() instead. </b> </p>
276 *
277 * One can call this after a Canandmag::StartFetchSettings() call, and use CanandmagSettings::AllSettingsReceived()
278 * to check if/when all values have been seen. As an example:
279 *
280 * ```cpp
281 *
282 * // device declaration
283 * Canandmag enc{0};
284 *
285 * // somewhere in an init function
286 * enc.StartFetchSettings();
287 *
288 * // ...
289 * // somewhere in a loop function
290 *
291 * CanandmagSettings stg = enc.GetSettingsAsync();
292 * if (stg.AllSettingsReceived()) {
293 * // do something with the returned settings
294 * fmt::print("Encoder velocity frame period: {}\n", *stg.GetVelocityFramePeriod());
295 * }
296 * ```
297 *
298 *
299 * If this is called after Canandmag::SetSettings(), this method will return a settings object where only
300 * the fields where the encoder has echoed the new values back will be populated. To illustrate this, consider the following:
301 * ```cpp
302 * // device declaration
303 * Canandmag enc{0};
304 *
305 * // somewhere in a loop
306 * CanandmagSettings stg_set;
307 * stg_set.SetVelocityFramePeriod(100_ms);
308 * enc.SetSettings(stg_set);
309 * CanandmagSettings stg_get = enc.GetSettingsAsync();
310 *
311 * // will likely return std::nullopt, as the device likely hasn't already responded to the settings set request
312 * stg_get.GetVelocityFramePeriod();
313 *
314 * // after up to 100 ms...
315 * stg_get = enc.GetSettingsAsync();
316 *
317 * // will likely be a value equivalent to 100_ms, may still be std::nullopt if the device is disconnected, so be careful of blind dereferences
318 * stg_get.GetVelocityFramePeriod();
319 * ```
320 *
321 * @return CanandmagSettings of currently known settings
322 */
323 inline CanandmagSettings GetSettingsAsync() { return stg.GetKnownSettings(); }
324
325 /**
326 * Applies the settings from a CanandmagSettings object to the Canandmag.
327 * For more information, see the CanandmagSettings class documentation.
328 *
329 * Example:
330 * ```cpp
331 * CanandmagSettings stg;
332 * Canandmag enc{0};
333 * // After configuring the settings object...
334 *
335 * CanandmagSettings failed = enc.SetSettings(stg);
336 * if (failed.IsEmpty()) {
337 * // success
338 * } else {
339 * // handle failed settings
340 * }
341 * ```
342 *
343 * @param settings the CanandmagSettings to update the encoder with
344 * @param timeout maximum time in seconds to wait for each setting to be confirmed. (default 0.020s, set to 0 to not check and not block).
345 * @param attempts the maxinum number of attempts to write each individual settings
346 * @return CanandmagSettings object of unsuccessfully set settings.
347 */
348 inline CanandmagSettings SetSettings(CanandmagSettings& settings, units::second_t timeout = 20_ms, uint32_t attempts = 3) {
349 return stg.SetSettings(settings, timeout, attempts);
350 }
351
352 /**
353 * Resets the encoder to factory defaults, and then wait for all settings to be broadcasted
354 * back.
355 * @param clearZero whether to clear the zero offset from the encoder's memory as well
356 * @param timeout how long to wait for the new settings to be confirmed by the encoder in
357 * seconds (suggested at least 0.35 seconds)
358 * @return CanandmagSettings object of received settings.
359 * Use CanandmagSettings.AllSettingsReceived() to verify success.
360 */
361 inline CanandmagSettings ResetFactoryDefaults(bool clearZero = false, units::second_t timeout = 350_ms) {
362 uint8_t val = ((clearZero) ? details::SettingCommand::kResetFactoryDefault
364 return stg.SendReceiveSettingCommand(val, timeout, true);
365 }
366
367 /**
368 * Returns the CanandSettingsManager associated with this device.
369 *
370 * The CanandSettingsManager is an internal helper object.
371 * Teams are typically not expected to use it except for advanced cases (e.g. custom settings
372 * wrappers)
373 * @return internal settings manager handle
374 */
378
379 /**
380 * Returns the current relative position frame, which includes CAN timestamp data.
381 * redux::canand::FrameData objects are immutable.
382 * @return the current position frame, which will hold the current position in the same units as Canandmag::GetPosition()
383 */
385
386 /**
387 * Returns the current absolute position frame, which includes CAN timestamp data.
388 * @return the current position frame, which will hold the current position in the same units as Canandmag::getAbsPosition()
389 */
391
392 /**
393 * Returns the current velocity frame, which includes CAN timestamp data.
394 * @return the current velocity frame, which will hold the current velocity in the same units as Canandmag::getVelocity()
395 */
397
398 /**
399 * Returns a handle to the current status frame, which includes CAN timestamp data.
400 * @return the current status frame, as a CanandmagStatus record.
401 */
403
404
405 // functions that directly modify settings
408 inline std::string GetDeviceClassName() override { return "Canandmag"; };
412
413 /** number of encoder ticks per rotation */
414 static constexpr double kCountsPerRotation = 16384;
415
416 /** number of velocity ticks per rotation per second */
417 static constexpr double kCountsPerRotationPerSecond = 1024;
418
419 protected:
420
421 /** internal Frame variable holding current relative position state */
423
424 /** internal Frame variable holding current absolute position state */
426
427 /** internal Frame variable holding current velocity state */
429
430 /** internal Frame variable holding current status value state */
432
433 /** internal settings manager */
435 private:
436
437 bool dataRecvOnce{false};
438 units::second_t lastMessageTime{0_s};
439 redux::canand::CooldownWarning setAbsPositionWarning{1_s, 5};
441
442};
443
444
445}
Definition CanandAddress.h:62
Definition CanandDevice.h:35
Definition CanandMessage.h:26
Definition CanandSettingsManager.h:82
Definition CooldownWarning.h:15
Definition Frame.h:52
Definition CanandmagFaults.h:13
Definition CanandmagSettings.h:54
Definition Canandmag.h:92
bool SetPosition(units::turn_t newPosition, units::second_t timeout=20_ms)
redux::canand::CanandSettingsManager< CanandmagSettings > stg
Definition Canandmag.h:434
static constexpr double kCountsPerRotation
Definition Canandmag.h:414
CanandmagSettings ResetFactoryDefaults(bool clearZero=false, units::second_t timeout=350_ms)
Definition Canandmag.h:361
bool ZeroAll(units::second_t timeout=20_ms)
redux::canand::CanandSettingsManager< CanandmagSettings > & GetInternalSettingsManager()
Definition Canandmag.h:375
redux::frames::Frame< CanandmagStatus > status
Definition Canandmag.h:431
CanandmagSettings SetSettings(CanandmagSettings &settings, units::second_t timeout=20_ms, uint32_t attempts=3)
Definition Canandmag.h:348
CanandmagSettings GetSettings(units::second_t timeout=350_ms, units::second_t missingTimeout=20_ms, uint32_t attempts=3)
Definition Canandmag.h:262
redux::frames::Frame< CanandmagStatus > & GetStatusFrame()
Definition Canandmag.h:402
void HandleMessage(redux::canand::CanandMessage &msg) override
redux::frames::Frame< units::turn_t > & GetAbsPositionFrame()
Definition Canandmag.h:390
redux::canand::CanandFirmwareVersion GetMinimumFirmwareVersion() override
Definition Canandmag.h:409
CanandmagStatus GetStatus()
Definition Canandmag.h:197
redux::frames::Frame< units::turn_t > position
Definition Canandmag.h:422
static constexpr double kCountsPerRotationPerSecond
Definition Canandmag.h:417
redux::frames::Frame< units::turn_t > absPosition
Definition Canandmag.h:425
std::string GetDeviceClassName() override
Definition Canandmag.h:408
Canandmag(int canID, std::string bus="halcan")
void StartFetchSettings()
Definition Canandmag.h:270
redux::frames::Frame< units::turns_per_second_t > & GetVelocityFrame()
Definition Canandmag.h:396
units::turns_per_second_t GetVelocity()
CanandmagSettings GetSettingsAsync()
Definition Canandmag.h:323
redux::frames::Frame< units::turns_per_second_t > velocity
Definition Canandmag.h:428
bool SetAbsPosition(units::turn_t newPosition, units::second_t timeout=20_ms, bool ephemeral=false)
redux::canand::CanandAddress & GetAddress() override
redux::frames::Frame< units::turn_t > & GetPositionFrame()
Definition Canandmag.h:384
@ kResetFactoryDefaultKeepZero
Definition CanandmagDetails.h:76
@ kResetFactoryDefault
Definition CanandmagDetails.h:72
void RemoveCANListener(CanandDevice *device)
Definition Canandmag.h:29
Definition CanandFirmwareVersion.h:17
Definition CanandmagStatus.h:13