ReduxLib C++ 2024.1.1-beta0
Loading...
Searching...
No Matches
Canandcolor.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 <mutex>
7#include <condition_variable>
8#include <optional>
9#include <units/time.h>
10#include <units/temperature.h>
11#include "redux/canand/CanandDevice.h"
12#include "redux/canand/CanandEventLoop.h"
13#include "redux/canand/CanandSettingsManager.h"
14#include "redux/frames/Frame.h"
15
16#include "redux/sensors/canandcolor/CanandcolorData.h"
17#include "redux/sensors/canandcolor/CanandcolorDetails.h"
18#include "redux/sensors/canandcolor/CanandcolorDigout.h"
19#include "redux/sensors/canandcolor/CanandcolorDigoutSlot.h"
20#include "redux/sensors/canandcolor/CanandcolorProximityConfig.h"
21#include "redux/sensors/canandcolor/CanandcolorSettings.h"
22
23
24/**
25 * Namespace for all classes relating to the Canandcolor
26*/
28/**
29 * Class for the CAN interface of the <a href="https://docs.reduxrobotics.com/canandcolor/index.html">Canandcolor.</a>
30 *
31 *
32 * <p>
33 * Operations that receive data from the device (proximity, color, faults, temperature) generally do not block.
34 * The object receives data asynchronously from the CAN packet receive thread and reads thus return the last data received.
35 * </p>
36 * <p>
37 * Operations that set settings or change offsets will generally wait for up to 50ms by default as they will usually
38 * wait for a confirmation packet to be received in response -- unless the blocking timeout is set to zero, in which case
39 * the operations will not block.
40 * </p>
41 *
42 * Example code:
43 * ```cpp
44 * Canandcolor canandcolor{0}; // device id 0
45 *
46 * // Reading the Canandcolor
47 * canandcolor.GetProximity(); // returns a proximity value [0..1). Larger values are distances closer to the sensor.
48 *
49 * // these are all bounded [0..1)
50 * canandcolor.GetRed();
51 * canandcolor.GetGreen();
52 * canandcolor.GetBlue();
53 * canandcolor.GetWhite();
54 *
55 *
56 * // Changing configuration
57 * CanandcolorSettings settings{};
58 * settings.SetProximityFramePeriod(10_ms); // set the proximity frame period to be sent every 10 ms
59 * settings.SetColorFramePeriod(40_ms); // set the position frame period to be sent every 40 ms
60 * settings.SetColorConfig(CanandcolorColorPeriod::k40ms); // set the color sensor integration to happen over 80 milliseconds
61 * canandcolor.SetSettings(settings, 50_ms); // apply the new settings to the device, with maximum 50 ms timeout per settings op
62 *
63 * // Faults
64 * canandcolor.ClearStickyFaults(); // clears all sticky faults (including the power cycle flag). This call does not block.
65 *
66 * // The power cycle flag will always be true on boot until the sticky faults have been cleared,
67 * // so if this is true the device has rebooted sometime between clearStickyFaults and now.
68 * CanandcolorFaults faults = canandcolor.GetStickyFaults(); // fetches faults
69 * fmt::print("Canandcolor rebooted: {}\n", faults.powerCycle);
70 *
71 * // Timestamped data
72 * redux::frames::FrameData<double> proxFrame = canandcolor.GetProximityFrame().GetFrameData(); // gets current proximity + timestamp together
73 * proxFrame.GetValue(); // fetched position in rotations
74 * proxFrame.GetTimestamp(); // timestamp of the previous position
75 * ```
76 */
78 protected:
79
80 // internal state
81 /** internal Frame variable holding current proximity state */
83
84 /** internal Frame variable holding current color state */
86
87 /** internal Frame variable holding current digital output state */
89
90 /** internal Frame variable holding current status value state */
92
93 /** internal settings manager */
95
96 private:
97
99 units::second_t lastMessageTime{0_s};
100 public:
101
102 /**
103 * Instantiates a new Canandcolor object. This object will be constant with respect to whatever CAN id assigned to it,
104 * so if a device changes id it may change which device this object reads from.
105 * @param canID the device id to use [0..63]
106 */
107 Canandcolor(int canID);
109
110 /**
111 * Gets the currently sensed proximity normalized between [0..1].
112 * Proximity decreases as objects get further away from the sensor face and increases as they approach the sensor.
113 * Proximities that are too close will saturate at 1.0.
114 *
115 * <p>
116 * Note that proximity is not given a unit as different materials and sensor configurations can greatly vary how proximity translates to actual distance.
117 * It is generally presumed that users will have to finetune specific thresholds for applications anyway and units may not be meaningful or accurate.
118 * </p>
119 *
120 * @return proximity value (range [0..1])
121 */
122 double GetProximity();
123
124 /**
125 * Red intensity, normalized [0..1) where 0 is none and 1 is as bright as possible.
126 * @return red intensity [0..1)
127 */
128 double GetRed();
129
130 /**
131 * Green intensity, normalized [0..1) where 0 is none and 1 is as bright as possible.
132 * @return green intensity [0..1)
133 */
134 double GetGreen();
135
136 /**
137 * Blue intensity, normalized [0..1) where 0 is none and 1 is as bright as possible.
138 * @return blue intensity [0..1)
139 */
140 double GetBlue();
141
142 /**
143 * White intensity, normalized [0..1)
144 * This can be used as a proxy for proximity at ranges too close for the normmal proximity sensor to give useful values.
145 * @return absolute position in fraction of a rotation [0..1)
146 */
147 double GetWhite();
148
149 /**
150 * Returns a CanandcolorColorData object which can also convert to HSV.
151 * @return color object
152 */
154
155 /**
156 * Returns a DigoutSlotState object representing the current state of the digital outputs.
157 * @return color object
158 */
160
161 /**
162 * Returns sticky faults.
163 * Sticky faults are the active faults, except once set they do not become unset until ClearStickyFaults()
164 * is called.
165 *
166 * @return CanandcolorFaults of the sticky faults.
167 */
169
170 /**
171 * Returns an object representing currently active faults.
172 * Active faults are only active for as long as the error state exists.
173 *
174 * @return CanandcolorFaults of the active faults
175 */
177
178 /**
179 * Get onboard device temperature readings in degrees Celsius.
180 * @return temperature in degrees Celsius
181 */
182 units::celsius_t GetTemperature();
183
184 /**
185 * Clears sticky faults.
186 *
187 * <p>It is recommended to clear this during initialization, so one can check if the device loses power during operation later. </p>
188 * <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. To check for validity,
189 * use CanandcolorFaults::FaultsValid() for faults returned by GetStickyFaults()</p>
190 */
192
193 /**
194 * Controls "party mode" -- an device identification tool that blinks the onboard LED
195 * various colors at a user-specified strobe period.
196 * The strobe period of the LED will be (50 milliseconds * level). Setting this to 0 disables party mode.
197 *
198 * This function does not block.
199 *
200 * @param level the party level value to set.
201 */
202 void SetPartyMode(uint8_t level);
203
204
205 /**
206 * Fetches the Canandcolor's current configuration in a blocking manner.
207 * This function will block for at up to 0.5 seconds waiting for the device to reply, so it is best
208 * to put this in an init function, rather than the main loop.
209 *
210 * @param timeout maximum number of seconds to wait for settings before giving up
211 * @param missingTimeout maximum number of seconds to wait for each settings retry before giving up
212 * @param attempts number of attempts to try and fetch values missing from the first pass
213 * @return Received set of CanandcolorSettings of device configuration.
214 */
215 inline CanandcolorSettings GetSettings(units::second_t timeout = 500_ms, units::second_t missingTimeout = 50_ms, uint32_t attempts = 3) {
216 return stg.GetSettings(timeout, missingTimeout, attempts);
217 };
218
219 /**
220 * Sets whether or not the onboard lamp LED is to be powered.
221 *
222 * <p><b>This value does not persist on device reboot!</b>
223 * Use CanandcolorSettings.SetLampLED and Canandcolor.SetSettings to set this persistently. <p>
224 *
225 * <p> The LED can also be physically turned off regardless of setting with the onboard switch. </p>
226 *
227 * <p> The LED is useful for measuring the color of objects that do not themselves emit light (e.g. most game pieces)
228 * or for using the white channel to estimate very close proximity. </p>
229 *
230 * By factory default the lamp is enabled.
231 * @param lamp whether to enable or disable the lamp LED
232 */
233 void SetLampLED(bool lamp);
234
235 /**
236 * Sets the brightness of the onboard lamp LED.
237 *
238 * <p><b>This value does not persist on device reboot!</b>
239 * Use CanandcolorSettings.SetLampLEDBrightness and Canandcolor.SetSettings to set this persistently.
240 * </p>
241 * By factory default this setting is set to max brightness (1.0)
242 * @param brightness scaled brightness from 0.0 (off) to 1.0 (max brightness)
243 */
244 void SetLampLEDBrightness(double brightness);
245
246 /**
247 * Tells the Canandcolor to begin transmitting its settings; once they are all transmitted (after ~200-300ms),
248 * the values can be retrieved from Canandcolor::GetSettingsAsync()
249 */
250 inline void StartFetchSettings() { return stg.StartFetchSettings(); };
251
252 /**
253 * Non-blockingly returns a CanandcolorSettings object of the most recent known settings values received from the device.
254 *
255 * <p> <b>Most users will probably want to use Canandcolor::GetSettings instead. </b> </p>
256 *
257 * One can call this after a Canandcolor::StartFetchSettings() call, and use CanandcolorSettings::AllSettingsReceived()
258 * to check if/when all values have been seen. As an example:
259 *
260 * ```cpp
261 *
262 * // somewhere in an init function
263 * Canandcolor canandcolor{0};
264 * canandcolor.StartFetchSettings();
265 *
266 * // ...
267 * // somewhere in a loop function
268 *
269 * if (canandcolor.GetSettingsAsync().AllSettingsReceived()) {
270 * // do something with the settings object
271 * fmt::print("Canandcolor lamp enabled: {}\n", *canandcolor.GetSettingsAsync().GetLampLED());
272 * }
273 * ```
274 *
275 *
276 * If this is called after Canandcolor::SetSettings(CanandcolorSettings), this method will return a settings object where only
277 * the fields where the device has echoed the new values back will be populated. To illustrate this, consider the following:
278 * ```cpp
279 * // somewhere in an init function
280 * Canandcolor canandcolor{0};
281 *
282 * CanandcolorSettings stg{};
283 * stg.SetProximityFramePeriod(0.1_s);
284 * canandcolor.SetSettings(stg);
285 * canandcolor.StartFetchSettings();
286 *
287 * // right after the fetch...
288 * canandcolor.GetSettingsAsync().GetProximityFramePeriod(); // will likely return nullopt as the device hasn't confirmed the previous transaction
289 *
290 * // after up to ~300 ms...
291 * canandcolor.GetSettingsAsync().GetProximityFramePeriod(); // will likely return 100 ms
292 * ```
293 *
294 * @return CanandcolorSettings object of known settings
295 */
296 inline CanandcolorSettings GetSettingsAsync() { return stg.GetKnownSettings(); }
297
298 /**
299 * Applies the settings from a CanandcolorSettings object to the device.
300 * For more information, see the CanandcolorSettings class documentation.
301 *
302 * Example:
303 * ```cpp
304 * CanandcolorSettings stg;
305 * Canandcolor color{0};
306 * // After configuring the settings object...
307 *
308 * CanandcolorSettings failed = color.SetSettings(stg);
309 * if (failed.IsEmpty()) {
310 * // success
311 * } else {
312 * // handle failed settings
313 * }
314 * ```
315 *
316 * @param settings the CanandcolorSettings to update the device with
317 * @param timeout maximum time in seconds to wait for each setting to be confirmed (default 50 ms). Set to 0 to not check (and not block).
318 * @param attempts the maxinum number of attempts to write each individual settings
319 * @return CanandcolorSettings object of unsuccessfully set settings.
320 */
321 inline CanandcolorSettings SetSettings(CanandcolorSettings& settings, units::second_t timeout = 50_ms, uint32_t attempts = 3) {
322 return stg.SetSettings(settings, timeout, attempts);
323 }
324
325 /**
326 * Sets an indexed slot on a Canandcolor digital output.
327 *
328 * These condition slots are used to determine the value of the digital outputs.
329 * For more information on how these slots work, see https://docs.reduxrobotics.com/canandcolor/programming/digital-ports.html
330 *
331 * @param digout The digital output associated with the slot to write
332 * @param slotIndex The index of the slot to write to (0-15)
333 * @param slotConfig The actual slot data (see DigoutSlot)
334 * @param timeout The timeout to wait for a confirmation message (default 50_ms, set to 0_s to not block)
335 * @return whether or not the slot update was successful
336 */
337 bool SetDigoutSlot(CanandcolorDigout digout, uint8_t slotIndex, const digout::DigoutSlot& slotConfig, units::second_t timeout = 50_ms);
338
339 /**
340 * Fetches a digout slot's configuration.
341 *
342 * These condition slots are used to determine the value of the digital outputs.
343 * For more information on how these slots work, see https://docs.reduxrobotics.com/canandcolor/programming/digital-ports.html
344 *
345 * @param digout digital output associated with the slot to fetch from
346 * @param slotIndex The index of the slot to fetch from (0-15)
347 * @param timeout The timeout to wait for the slot to be retrieved (default 50_ms, set to 0_s to not block)
348 * @return std::optional with DigoutSlot object on success, nullopt on failure
349 */
350 std::optional<digout::DigoutSlot> GetDigoutSlot(CanandcolorDigout digout, uint8_t slotIndex, units::second_t timeout = 50_ms);
351
352 /**
353 * Clears all configured "slots" on the specified digital output.
354 * @param digout the digital output to clear slots on
355 */
357
358 /**
359 * Resets the device to factory defaults, and then wait for all settings to be broadcasted back.
360 * @param timeout how long to wait for the new settings to be confirmed by the device in seconds (suggested at least 0.35 seconds)
361 * @return CanandcoderSettings object of received settings. Use AllSettingsReceived to verify success.
362 */
363 inline CanandcolorSettings ResetFactoryDefaults(units::second_t timeout = 500_ms) {
364 return stg.SendReceiveSettingCommand(details::SettingCommand::kResetFactoryDefault, timeout, true);
365 }
366
367 /**
368 * Returns the proximity reading frame.
369 * @return the proximity reading frame, which will hold the current proximity in the same units as ::GetProximity()
370 * @see Frame
371 */
373
374 /**
375 * Returns the color reading frame, which includes CAN timestamp data.
376 * @return the color reading frame, which will hold timestamped color readings
377 * @see Frame
378 */
380
381 /**
382 * Returns the digital output state frame, which includes CAN timestamp data.
383 * @return the digital output state frame
384 */
386
387 /**
388 * Returns the current status frame, which includes CAN timestamp data.
389 * FrameData objects are immutable.
390 * @return the current status frame, as a CanandcolorStatus record.
391 */
393
394 /* various behind the scenes stuff */
397 inline std::string GetDeviceClassName() override { return "Canandcolor"; };
399 return redux::canand::CanandFirmwareVersion{2024, 1, 0};
400 }
401
402};
403}
Definition: CanandAddress.h:60
Definition: CanandDevice.h:35
Definition: CanandMessage.h:26
Definition: CanandSettingsManager.h:78
Definition: Frame.h:89
Definition: CanandcolorData.h:117
Definition: CanandcolorSettings.h:53
Definition: CanandcolorData.h:179
Definition: Canandcolor.h:77
CanandcolorSettings GetSettingsAsync()
Definition: Canandcolor.h:296
redux::canand::CanandSettingsManager< CanandcolorSettings > stg
Definition: Canandcolor.h:94
void HandleMessage(redux::canand::CanandMessage &msg) override
redux::frames::Frame< double > & GetProximityFrame()
Definition: Canandcolor.h:372
void ClearAllDigoutSlots(CanandcolorDigout digout)
redux::canand::CanandFirmwareVersion GetMinimumFirmwareVersion() override
Definition: Canandcolor.h:398
redux::frames::Frame< digout::DigoutSlotState > digout
Definition: Canandcolor.h:88
digout::DigoutSlotState GetDigoutState()
void SetLampLEDBrightness(double brightness)
redux::frames::Frame< CanandcolorColorData > & GetColorFrame()
Definition: Canandcolor.h:379
redux::frames::Frame< CanandcolorStatus > & GetStatusFrame()
Definition: Canandcolor.h:392
void StartFetchSettings()
Definition: Canandcolor.h:250
std::optional< digout::DigoutSlot > GetDigoutSlot(CanandcolorDigout digout, uint8_t slotIndex, units::second_t timeout=50_ms)
redux::frames::Frame< CanandcolorColorData > color
Definition: Canandcolor.h:85
CanandcolorSettings GetSettings(units::second_t timeout=500_ms, units::second_t missingTimeout=50_ms, uint32_t attempts=3)
Definition: Canandcolor.h:215
redux::frames::Frame< digout::DigoutSlotState > & GetDigoutFrame()
Definition: Canandcolor.h:385
redux::canand::CanandAddress & GetAddress() override
CanandcolorSettings SetSettings(CanandcolorSettings &settings, units::second_t timeout=50_ms, uint32_t attempts=3)
Definition: Canandcolor.h:321
std::string GetDeviceClassName() override
Definition: Canandcolor.h:397
bool SetDigoutSlot(CanandcolorDigout digout, uint8_t slotIndex, const digout::DigoutSlot &slotConfig, units::second_t timeout=50_ms)
redux::frames::Frame< CanandcolorStatus > status
Definition: Canandcolor.h:91
redux::frames::Frame< double > proximity
Definition: Canandcolor.h:82
CanandcolorSettings ResetFactoryDefaults(units::second_t timeout=500_ms)
Definition: Canandcolor.h:363
Definition: CanandcolorDigoutSlot.h:18
Definition: CanandcolorDigoutSlot.h:160
void RemoveCANListener(CanandDevice *device)
@ kResetFactoryDefault
Definition: CanandcolorDetails.h:90
Definition: Canandcolor.h:27
CanandcolorDigout
Definition: CanandcolorDigout.h:19
Definition: CanandFirmwareVersion.h:17