2023-Robot
Robot code for 2023 FRC season by Argos, FRC team #1756
Loading...
Searching...
No Matches
falcon_config.h
Go to the documentation of this file.
1
4
5#pragma once
6
7#include <units/current.h>
8#include <units/time.h>
9
10#include <iostream>
11
14#include "ctre/Phoenix.h"
15#include "status_frame_config.h"
16
17namespace argos_lib {
18 namespace falcon_config {
19
20 HAS_MEMBER(forwardLimit_deviceID)
21 HAS_MEMBER(forwardLimit_normalState)
22 HAS_MEMBER(forwardLimit_source)
23 HAS_MEMBER(inverted)
24 HAS_MEMBER(neutralDeadband)
25 HAS_MEMBER(neutralMode)
26 HAS_MEMBER(nominalOutputForward)
27 HAS_MEMBER(nominalOutputReverse)
28 HAS_MEMBER(peakOutputForward)
29 HAS_MEMBER(peakOutputReverse)
30 HAS_MEMBER(pid0_allowableError)
31 HAS_MEMBER(pid0_iZone)
32 HAS_MEMBER(pid0_kD)
33 HAS_MEMBER(pid0_kF)
34 HAS_MEMBER(pid0_kI)
35 HAS_MEMBER(pid0_kP)
36 HAS_MEMBER(pid0_selectedSensor)
37 HAS_MEMBER(pid1_allowableError)
38 HAS_MEMBER(pid1_iZone)
39 HAS_MEMBER(pid1_kD)
40 HAS_MEMBER(pid1_kF)
41 HAS_MEMBER(pid1_kI)
42 HAS_MEMBER(pid1_kP)
43 HAS_MEMBER(remoteFilter0_addr)
44 HAS_MEMBER(remoteFilter0_type)
45 HAS_MEMBER(reverseLimit_deviceID)
46 HAS_MEMBER(reverseLimit_normalState)
47 HAS_MEMBER(reverseLimit_source)
48 HAS_MEMBER(sensorPhase)
49 HAS_MEMBER(supplyCurrentLimit)
50 HAS_MEMBER(supplyCurrentThreshold)
51 HAS_MEMBER(supplyCurrentThresholdTime)
52 HAS_MEMBER(statorCurrentLimit)
53 HAS_MEMBER(statorCurrentThreshold)
54 HAS_MEMBER(statorCurrentThresholdTime)
55 HAS_MEMBER(voltCompSat)
56 HAS_MEMBER(statusFrameMotorMode)
57
105 template <typename T>
106 bool FalconConfig(TalonFX& motorController, units::millisecond_t configTimeout) {
107 TalonFXConfiguration config;
108 auto timeout = configTimeout.to<int>();
109
110 if constexpr (has_inverted<T>{}) {
111 motorController.SetInverted(T::inverted);
112 }
113 if constexpr (has_sensorPhase<T>{}) {
114 motorController.SetSensorPhase(T::sensorPhase);
115 }
116 if constexpr (has_neutralMode<T>{}) {
117 motorController.SetNeutralMode(T::neutralMode);
118 }
119 if constexpr (has_voltCompSat<T>{}) {
120 constexpr units::volt_t voltage = T::voltCompSat;
121 config.voltageCompSaturation = voltage.to<double>();
122 motorController.EnableVoltageCompensation(true);
123 } else {
124 motorController.EnableVoltageCompensation(false);
125 }
127 ctre::phoenix::motorcontrol::can::FilterConfiguration filterConfig;
128 filterConfig.remoteSensorDeviceID = T::remoteFilter0_addr.address;
129 filterConfig.remoteSensorSource = T::remoteFilter0_type;
130 config.remoteFilter0 = filterConfig;
131 }
132 if constexpr (has_nominalOutputForward<T>{}) {
133 config.nominalOutputForward = T::nominalOutputForward;
134 }
135 if constexpr (has_nominalOutputReverse<T>{}) {
136 config.nominalOutputReverse = T::nominalOutputReverse;
137 }
138 if constexpr (has_peakOutputForward<T>{}) {
139 config.peakOutputForward = T::peakOutputForward;
140 }
141 if constexpr (has_peakOutputReverse<T>{}) {
142 config.peakOutputReverse = T::peakOutputReverse;
143 }
144 if constexpr (has_pid0_selectedSensor<T>{}) {
145 config.primaryPID.selectedFeedbackSensor = T::pid0_selectedSensor;
146 }
147 if constexpr (has_pid0_kP<T>{}) {
148 config.slot0.kP = T::pid0_kP;
149 }
150 if constexpr (has_pid0_kI<T>{}) {
151 config.slot0.kI = T::pid0_kI;
152 }
153 if constexpr (has_pid0_kD<T>{}) {
154 config.slot0.kD = T::pid0_kD;
155 }
156 if constexpr (has_pid0_kF<T>{}) {
157 config.slot0.kF = T::pid0_kF;
158 }
159 if constexpr (has_pid0_iZone<T>{}) {
160 config.slot0.integralZone = T::pid0_iZone;
161 }
162 if constexpr (has_pid0_allowableError<T>{}) {
163 config.slot0.allowableClosedloopError = T::pid0_allowableError;
164 }
165 if constexpr (has_pid1_kP<T>{}) {
166 config.slot1.kP = T::pid1_kP;
167 }
168 if constexpr (has_pid1_kI<T>{}) {
169 config.slot1.kI = T::pid1_kI;
170 }
171 if constexpr (has_pid1_kD<T>{}) {
172 config.slot1.kD = T::pid1_kD;
173 }
174 if constexpr (has_pid1_kF<T>{}) {
175 config.slot1.kF = T::pid1_kF;
176 }
177 if constexpr (has_pid1_iZone<T>{}) {
178 config.slot1.integralZone = T::pid1_iZone;
179 }
180 if constexpr (has_pid1_allowableError<T>{}) {
181 config.slot1.allowableClosedloopError = T::pid1_allowableError;
182 }
185 config.supplyCurrLimit.enable = true;
186 if constexpr (has_supplyCurrentLimit<T>{}) {
187 constexpr units::ampere_t currentLimit = T::supplyCurrentLimit;
188 static_assert(currentLimit.to<double>() > 0, "Supply current limit must be positive");
189 config.supplyCurrLimit.currentLimit = currentLimit.to<double>();
190 }
191 if constexpr (has_supplyCurrentThreshold<T>{}) {
192 constexpr units::ampere_t currentThreshold = T::supplyCurrentThreshold;
193 static_assert(currentThreshold.to<double>() > 0, "Supply current threshold must be positive");
194 config.supplyCurrLimit.triggerThresholdCurrent = currentThreshold.to<double>();
195 }
196 if constexpr (has_supplyCurrentThresholdTime<T>{}) {
197 constexpr units::second_t currentThresholdTime = T::supplyCurrentThresholdTime;
198 static_assert(currentThresholdTime.to<double>() >= 0, "Supply current threshold time must be non-negative");
199 config.supplyCurrLimit.triggerThresholdTime = currentThresholdTime.to<double>();
200 }
201 }
204 config.statorCurrLimit.enable = true;
205 if constexpr (has_statorCurrentLimit<T>{}) {
206 constexpr units::ampere_t currentLimit = T::statorCurrentLimit;
207 static_assert(currentLimit.to<double>() > 0, "Stator current limit must be positive");
208 config.statorCurrLimit.currentLimit = currentLimit.to<double>();
209 }
210 if constexpr (has_statorCurrentThreshold<T>{}) {
211 constexpr units::ampere_t currentThreshold = T::statorCurrentThreshold;
212 static_assert(currentThreshold.to<double>() > 0, "Stator current threshold must be positive");
213 config.statorCurrLimit.triggerThresholdCurrent = currentThreshold.to<double>();
214 }
215 if constexpr (has_statorCurrentThresholdTime<T>{}) {
216 constexpr units::second_t currentThresholdTime = T::statorCurrentThresholdTime;
217 static_assert(currentThresholdTime.to<double>() >= 0, "Stator current threshold time must be non-negative");
218 config.statorCurrLimit.triggerThresholdTime = currentThresholdTime.to<double>();
219 }
220 }
223 if constexpr (has_forwardLimit_source<T>{}) {
224 constexpr ctre::phoenix::motorcontrol::LimitSwitchSource source = T::forwardLimit_source;
225 if constexpr (source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated &&
226 source != ctre::phoenix::motorcontrol::LimitSwitchSource_FeedbackConnector) {
227 static_assert(has_forwardLimit_deviceID<T>{}, "Forward limit switch requires remote source device ID");
228 }
229 if constexpr (source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated) {
230 static_assert(has_forwardLimit_normalState<T>{} &&
231 T::forwardLimit_normalState != ctre::phoenix::motorcontrol::LimitSwitchNormal_Disabled,
232 "Forward limit switch configuration requires both source and normal state");
233 }
234 config.forwardLimitSwitchSource = T::forwardLimit_source;
235 }
236 if constexpr (has_forwardLimit_deviceID<T>{}) {
237 static_assert(has_forwardLimit_source<T>{} &&
238 T::forwardLimit_source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated &&
239 T::forwardLimit_source != ctre::phoenix::motorcontrol::LimitSwitchSource_FeedbackConnector,
240 "Forward limit switch device ID has no effect when limit source is not remote");
241 config.forwardLimitSwitchDeviceID = T::forwardLimit_deviceID;
242 }
243 if constexpr (has_forwardLimit_normalState<T>{}) {
244 if constexpr (T::forwardLimit_normalState != ctre::phoenix::motorcontrol::LimitSwitchNormal_Disabled) {
245 static_assert(has_forwardLimit_source<T>{}, "Forward limit switch source required");
246 }
247 config.forwardLimitSwitchNormal = T::forwardLimit_normalState;
248 }
249 }
252 if constexpr (has_reverseLimit_source<T>{}) {
253 constexpr ctre::phoenix::motorcontrol::LimitSwitchSource source = T::reverseLimit_source;
254 if constexpr (source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated &&
255 source != ctre::phoenix::motorcontrol::LimitSwitchSource_FeedbackConnector) {
256 static_assert(has_reverseLimit_deviceID<T>{}, "Reverse limit switch requires remote source device ID");
257 }
258 if constexpr (source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated) {
259 static_assert(has_reverseLimit_normalState<T>{} &&
260 T::reverseLimit_normalState != ctre::phoenix::motorcontrol::LimitSwitchNormal_Disabled,
261 "Reverse limit switch configuration requires both source and normal state");
262 }
263 config.reverseLimitSwitchSource = T::reverseLimit_source;
264 }
265 if constexpr (has_reverseLimit_deviceID<T>{}) {
266 static_assert(has_reverseLimit_source<T>{} &&
267 T::reverseLimit_source != ctre::phoenix::motorcontrol::LimitSwitchSource_Deactivated &&
268 T::reverseLimit_source != ctre::phoenix::motorcontrol::LimitSwitchSource_FeedbackConnector,
269 "Reverse limit switch device ID has no effect when limit source is not remote");
270 config.reverseLimitSwitchDeviceID = T::reverseLimit_deviceID;
271 }
272 if constexpr (has_reverseLimit_normalState<T>{}) {
273 if constexpr (T::reverseLimit_normalState != ctre::phoenix::motorcontrol::LimitSwitchNormal_Disabled) {
274 static_assert(has_reverseLimit_source<T>{}, "Reverse limit switch source required");
275 }
276 config.reverseLimitSwitchNormal = T::reverseLimit_normalState;
277 }
278 }
279 if constexpr (has_neutralDeadband<T>{}) {
280 static_assert(T::neutralDeadband >= 0.001, "Neutral deadband must be greater than 0.001 (0.1%)");
281 static_assert(T::neutralDeadband <= 0.25, "Neutral deadband must be less than 0.25 (25%)");
282 config.neutralDeadband = T::neutralDeadband;
283 }
284
285 if constexpr (has_statusFrameMotorMode<T>()) {
286 argos_lib::status_frame_config::SetMotorStatusFrameRates(motorController, T::statusFrameMotorMode);
287 }
288
289 auto retVal = motorController.ConfigAllSettings(config, timeout);
290 if (0 != retVal) {
291 std::cout << "Error code (" << motorController.GetDeviceID() << "): " << retVal << '\n';
292 }
293
294 return 0 != retVal;
295 }
296
308 template <typename CompetitionConfig, typename PracticeConfig>
309 bool FalconConfig(WPI_TalonFX& motorController,
310 units::millisecond_t configTimeout,
311 argos_lib::RobotInstance instance) {
312 switch (instance) {
314 return FalconConfig<CompetitionConfig>(motorController, configTimeout);
315 break;
317 return FalconConfig<PracticeConfig>(motorController, configTimeout);
318 break;
319 }
320 return false;
321 }
322
323 } // namespace falcon_config
324} // namespace argos_lib
#define HAS_MEMBER(X)
Helper function generator to detect if a namespace has a member defined.
Definition compile_time_member_check.h:14
bool FalconConfig(TalonFX &motorController, units::millisecond_t configTimeout)
Configures a CTRE Falcon with only the fields provided. All other fields are given the factory defaul...
Definition falcon_config.h:106
void SetMotorStatusFrameRates(BaseTalon &motor, MotorPresetMode motorMode)
Set motor controller status frame update periods based on the motor preset.
Definition status_frame_config.cpp:7
Definition swap_controllers_command.h:12
RobotInstance
Differentiates between practice robot and competition robot.
Definition config_types.h:13
@ Competition
Competition robot.
@ Practice
Practice robot.
Definition custom_units.h:11
Definition falcon_config.h:23
Definition falcon_config.h:25
Definition falcon_config.h:31
Definition falcon_config.h:32
Definition falcon_config.h:33
Definition falcon_config.h:34
Definition falcon_config.h:35
Definition falcon_config.h:38
Definition falcon_config.h:39
Definition falcon_config.h:40
Definition falcon_config.h:41
Definition falcon_config.h:42
Definition falcon_config.h:48
Definition falcon_config.h:55