2024-Robot
Robot code for 2024 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
12#include <ctre/phoenix6/TalonFX.hpp>
13
16#include "status_frame_config.h"
17
18namespace argos_lib {
19 namespace falcon_config {
20
21 HAS_MEMBER(forwardLimit_deviceID)
22 HAS_MEMBER(forwardLimit_normalState)
23 HAS_MEMBER(forwardLimit_source)
24 HAS_MEMBER(inverted)
25 HAS_MEMBER(neutralDeadband)
26 HAS_MEMBER(neutralMode)
27 HAS_MEMBER(nominalOutputForward)
28 HAS_MEMBER(nominalOutputReverse)
29 HAS_MEMBER(peakOutputForward)
30 HAS_MEMBER(peakOutputReverse)
31 HAS_MEMBER(pid0_kP)
32 HAS_MEMBER(pid0_kI)
33 HAS_MEMBER(pid0_kD)
34 HAS_MEMBER(pid0_kS)
35 HAS_MEMBER(pid0_kV)
36 HAS_MEMBER(pid0_kA)
37 HAS_MEMBER(pid0_kG)
38 HAS_MEMBER(pid0_gravityType)
39 HAS_MEMBER(pid1_kP)
40 HAS_MEMBER(pid1_kI)
41 HAS_MEMBER(pid1_kD)
42 HAS_MEMBER(pid1_kS)
43 HAS_MEMBER(pid1_kV)
44 HAS_MEMBER(pid1_kA)
45 HAS_MEMBER(pid1_gravityType)
46 HAS_MEMBER(pid1_kG)
47 HAS_MEMBER(reverseLimit_deviceID)
48 HAS_MEMBER(reverseLimit_normalState)
49 HAS_MEMBER(reverseLimit_source)
50 HAS_MEMBER(rotorToSensorRatio)
51 HAS_MEMBER(selectedSensor)
52 HAS_MEMBER(selectedSensor_addr)
53 HAS_MEMBER(sensorToMechanismRatio)
54 HAS_MEMBER(statorCurrentLimit)
55 HAS_MEMBER(statusFrameMotorMode)
56 HAS_MEMBER(supplyCurrentLimit)
57 HAS_MEMBER(supplyCurrentThreshold)
58 HAS_MEMBER(supplyCurrentThresholdTime)
59
106 template <typename T>
107 bool FalconConfig(ctre::phoenix6::hardware::TalonFX& motorController, units::millisecond_t configTimeout) {
108 ctre::phoenix6::configs::TalonFXConfiguration config;
109
110 if constexpr (has_inverted<T>{}) {
111 config.MotorOutput.Inverted = T::inverted;
112 }
113 if constexpr (has_neutralMode<T>{}) {
114 config.MotorOutput.NeutralMode = T::neutralMode;
115 }
116 if constexpr (has_selectedSensor_addr<T>{}) {
117 static_assert(has_selectedSensor<T>{} &&
118 T::selectedSensor != ctre::phoenix6::signals::FeedbackSensorSourceValue::RotorSensor,
119 "Remote sensor required when address provided");
120 static_assert(T::selectedSensor_addr.address >= 0, "Remote sensor address must be non-negative");
121 config.Feedback.FeedbackRemoteSensorID = T::selectedSensor_addr.address;
122 }
123 if constexpr (has_peakOutputForward<T>{}) {
124 config.MotorOutput.PeakForwardDutyCycle = T::peakOutputForward;
125 }
126 if constexpr (has_peakOutputReverse<T>{}) {
127 config.MotorOutput.PeakReverseDutyCycle = T::peakOutputReverse;
128 }
129 if constexpr (has_selectedSensor<T>{}) {
130 config.Feedback.FeedbackSensorSource = T::selectedSensor;
131 if constexpr (T::selectedSensor == ctre::phoenix6::signals::FeedbackSensorSourceValue::FusedCANcoder) {
132 static_assert(has_sensorToMechanismRatio<T>{}, "Fused CANcoder mode requires sensor to mechanism ratio");
133 static_assert(has_rotorToSensorRatio<T>{}, "Fused CANcoder mode requires rotor to sensor ratio");
134 static_assert(T::sensorToMechanismRatio > 0, "sensorToMechanismRatio must be a positive value");
135 static_assert(T::rotorToSensorRatio > 0, "rotorToSensorRatio must be a positive value");
136 config.Feedback.SensorToMechanismRatio = T::sensorToMechanismRatio;
137 config.Feedback.RotorToSensorRatio = T::rotorToSensorRatio;
138 }
139 }
140 if constexpr (has_pid0_kP<T>{}) {
141 config.Slot0.kP = T::pid0_kP;
142 }
143 if constexpr (has_pid0_kI<T>{}) {
144 config.Slot0.kI = T::pid0_kI;
145 }
146 if constexpr (has_pid0_kD<T>{}) {
147 config.Slot0.kD = T::pid0_kD;
148 }
149 if constexpr (has_pid0_kS<T>{}) {
150 config.Slot0.kS = T::pid0_kS;
151 }
152 if constexpr (has_pid0_kV<T>{}) {
153 config.Slot0.kV = T::pid0_kV;
154 }
155 if constexpr (has_pid0_kA<T>{}) {
156 config.Slot0.kA = T::pid0_kA;
157 }
158 if constexpr (has_pid0_kG<T>{} && has_pid0_gravityType<T>{}) {
159 config.Slot0.kG = T::pid0_kG;
160 config.Slot0.GravityType = T::pid0_gravityType;
161 }
162 if constexpr (has_pid1_kP<T>{}) {
163 config.Slot1.kP = T::pid1_kP;
164 }
165 if constexpr (has_pid1_kI<T>{}) {
166 config.Slot1.kI = T::pid1_kI;
167 }
168 if constexpr (has_pid1_kD<T>{}) {
169 config.Slot1.kD = T::pid1_kD;
170 }
171 if constexpr (has_pid1_kS<T>{}) {
172 config.Slot1.kS = T::pid1_kS;
173 }
174 if constexpr (has_pid1_kV<T>{}) {
175 config.Slot1.kV = T::pid1_kV;
176 }
177 if constexpr (has_pid1_kA<T>{}) {
178 config.Slot1.kA = T::pid1_kA;
179 }
180 if constexpr (has_pid1_kG<T>{} && has_pid1_gravityType<T>{}) {
181 config.Slot1.kG = T::pid1_kG;
182 config.Slot1.GravityType = T::pid1_gravityType;
183 }
186 config.CurrentLimits.SupplyCurrentLimitEnable = true;
187 if constexpr (has_supplyCurrentLimit<T>{}) {
188 constexpr units::ampere_t currentLimit = T::supplyCurrentLimit;
189 static_assert(currentLimit.to<double>() > 0, "Supply current limit must be positive");
190 config.CurrentLimits.SupplyCurrentLimit = currentLimit.to<double>();
191 }
192 if constexpr (has_supplyCurrentThreshold<T>{}) {
193 constexpr units::ampere_t currentThreshold = T::supplyCurrentThreshold;
194 static_assert(currentThreshold.to<double>() > 0, "Supply current threshold must be positive");
195 config.CurrentLimits.SupplyCurrentThreshold = currentThreshold.to<double>();
196 }
197 if constexpr (has_supplyCurrentThresholdTime<T>{}) {
198 constexpr units::second_t currentThresholdTime = T::supplyCurrentThresholdTime;
199 static_assert(currentThresholdTime.to<double>() >= 0, "Supply current threshold time must be non-negative");
200 static_assert(currentThresholdTime.to<double>() <= 1.275, "Current duration must be less than 1.275");
201 config.CurrentLimits.SupplyTimeThreshold = currentThresholdTime.to<double>();
202 }
203 }
204 if constexpr (has_statorCurrentLimit<T>{}) {
205 config.CurrentLimits.StatorCurrentLimitEnable = true;
206 constexpr units::ampere_t currentLimit = T::statorCurrentLimit;
207 static_assert(currentLimit.to<double>() > 0, "Stator current limit must be positive");
208 config.CurrentLimits.StatorCurrentLimit = currentLimit.to<double>();
209 }
212 if constexpr (has_forwardLimit_source<T>{}) {
213 constexpr ctre::phoenix6::signals::ForwardLimitSourceValue source = T::forwardLimit_source;
214 if constexpr (source != ctre::phoenix6::signals::ForwardLimitSourceValue::Disabled &&
215 source != ctre::phoenix6::signals::ForwardLimitSourceValue::LimitSwitchPin) {
216 static_assert(has_forwardLimit_deviceID<T>{}, "Forward limit switch requires remote source device ID");
217 }
218 if constexpr (source != ctre::phoenix6::signals::ForwardLimitSourceValue::Disabled) {
219 static_assert(has_forwardLimit_normalState<T>{},
220 "Forward limit switch configuration requires both source and normal state");
221 config.HardwareLimitSwitch.ForwardLimitEnable = true;
222 }
223 config.HardwareLimitSwitch.ForwardLimitSource = T::forwardLimit_source;
224 }
225 if constexpr (has_forwardLimit_deviceID<T>{}) {
226 static_assert(has_forwardLimit_source<T>{} &&
227 T::forwardLimit_source != ctre::phoenix6::signals::ForwardLimitSourceValue::Disabled &&
228 T::forwardLimit_source != ctre::phoenix6::signals::ForwardLimitSourceValue::LimitSwitchPin,
229 "Forward limit switch device ID has no effect when limit source is not remote");
230 config.HardwareLimitSwitch.ForwardLimitRemoteSensorID = T::forwardLimit_deviceID;
231 }
232 if constexpr (has_forwardLimit_normalState<T>{}) {
233 if constexpr (T::forwardLimit_normalState != ctre::phoenix6::signals::ForwardLimitSourceValue::Disabled) {
234 static_assert(has_forwardLimit_source<T>{}, "Forward limit switch source required");
235 }
236 config.HardwareLimitSwitch.ForwardLimitType = T::forwardLimit_normalState;
237 }
238 } else {
239 config.HardwareLimitSwitch.ForwardLimitEnable = false;
240 }
243 if constexpr (has_reverseLimit_source<T>{}) {
244 constexpr ctre::phoenix6::signals::ReverseLimitSourceValue source = T::reverseLimit_source;
245 if constexpr (source != ctre::phoenix6::signals::ReverseLimitSourceValue::Disabled &&
246 source != ctre::phoenix6::signals::ReverseLimitSourceValue::LimitSwitchPin) {
247 static_assert(has_reverseLimit_deviceID<T>{}, "Reverse limit switch requires remote source device ID");
248 }
249 if constexpr (source != ctre::phoenix6::signals::ReverseLimitSourceValue::Disabled) {
250 static_assert(has_reverseLimit_normalState<T>{},
251 "Reverse limit switch configuration requires both source and normal state");
252 config.HardwareLimitSwitch.ReverseLimitEnable = true;
253 }
254 config.HardwareLimitSwitch.ReverseLimitSource = T::reverseLimit_source;
255 }
256 if constexpr (has_reverseLimit_deviceID<T>{}) {
257 static_assert(has_reverseLimit_source<T>{} &&
258 T::reverseLimit_source != ctre::phoenix6::signals::ReverseLimitSourceValue::Disabled &&
259 T::reverseLimit_source != ctre::phoenix6::signals::ReverseLimitSourceValue::LimitSwitchPin,
260 "Reverse limit switch device ID has no effect when limit source is not remote");
261 config.HardwareLimitSwitch.ReverseLimitRemoteSensorID = T::reverseLimit_deviceID;
262 }
263 if constexpr (has_reverseLimit_normalState<T>{}) {
264 if constexpr (T::reverseLimit_normalState != ctre::phoenix6::signals::ReverseLimitSourceValue::Disabled) {
265 static_assert(has_reverseLimit_source<T>{}, "Reverse limit switch source required");
266 }
267 config.HardwareLimitSwitch.ReverseLimitType = T::reverseLimit_normalState;
268 }
269 } else {
270 config.HardwareLimitSwitch.ReverseLimitEnable = false;
271 }
272 if constexpr (has_neutralDeadband<T>{}) {
273 static_assert(T::neutralDeadband >= 0.001, "Neutral deadband must be greater than 0.001 (0.1%)");
274 static_assert(T::neutralDeadband <= 0.25, "Neutral deadband must be less than 0.25 (25%)");
275 config.MotorOutput.DutyCycleNeutralDeadband = T::neutralDeadband;
276 }
277
278 if constexpr (has_statusFrameMotorMode<T>()) {
279 argos_lib::status_frame_config::SetMotorStatusFrameRates(motorController, T::statusFrameMotorMode);
280 }
281
282 auto retVal = motorController.GetConfigurator().Apply(config, configTimeout);
283 if (0 != retVal) {
284 std::cout << "Error code (" << motorController.GetDeviceID() << "): " << retVal << '\n';
285 }
286
287 return 0 != retVal;
288 }
289
301 template <typename CompetitionConfig, typename PracticeConfig>
302 bool FalconConfig(ctre::phoenix6::hardware::TalonFX& motorController,
303 units::millisecond_t configTimeout,
304 argos_lib::RobotInstance instance) {
305 switch (instance) {
307 return FalconConfig<CompetitionConfig>(motorController, configTimeout);
308 break;
310 return FalconConfig<PracticeConfig>(motorController, configTimeout);
311 break;
312 }
313 return false;
314 }
315
316 } // namespace falcon_config
317} // 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(ctre::phoenix6::hardware::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:107
void SetMotorStatusFrameRates(BaseTalon &motor, MotorPresetMode motorMode)
Set motor controller status frame update periods based on the motor preset.
Definition status_frame_config.cpp:9
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:24
Definition falcon_config.h:26
Definition falcon_config.h:36
Definition falcon_config.h:33
Definition falcon_config.h:37
Definition falcon_config.h:32
Definition falcon_config.h:31
Definition falcon_config.h:34
Definition falcon_config.h:35
Definition falcon_config.h:44
Definition falcon_config.h:41
Definition falcon_config.h:46
Definition falcon_config.h:40
Definition falcon_config.h:39
Definition falcon_config.h:42
Definition falcon_config.h:43