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