blob: c1acc5f241ea14c90a9e02c933c448548deafd05 [file] [log] [blame]
Ariv Diggi0af59c02023-10-07 13:15:39 -07001#include <unistd.h>
2
3#include <array>
4#include <chrono>
5#include <cinttypes>
6#include <cmath>
7#include <cstdio>
8#include <cstring>
9#include <functional>
10#include <memory>
11#include <mutex>
12#include <thread>
13
14#include "ctre/phoenix/CANifier.h"
15
16#include "frc971/wpilib/ahal/AnalogInput.h"
17#include "frc971/wpilib/ahal/Counter.h"
18#include "frc971/wpilib/ahal/DigitalGlitchFilter.h"
19#include "frc971/wpilib/ahal/DriverStation.h"
20#include "frc971/wpilib/ahal/Encoder.h"
21#include "frc971/wpilib/ahal/Servo.h"
22#include "frc971/wpilib/ahal/TalonFX.h"
23#include "frc971/wpilib/ahal/VictorSP.h"
24#undef ERROR
25
26#include "ctre/phoenix/cci/Diagnostics_CCI.h"
27#include "ctre/phoenix/motorcontrol/can/TalonFX.h"
28#include "ctre/phoenix/motorcontrol/can/TalonSRX.h"
29#include "ctre/phoenix6/TalonFX.hpp"
30
31#include "aos/commonmath.h"
32#include "aos/containers/sized_array.h"
33#include "aos/events/event_loop.h"
34#include "aos/events/shm_event_loop.h"
35#include "aos/init.h"
36#include "aos/logging/logging.h"
37#include "aos/realtime.h"
38#include "aos/time/time.h"
39#include "aos/util/log_interval.h"
40#include "aos/util/phased_loop.h"
41#include "aos/util/wrapping_counter.h"
42#include "frc971/autonomous/auto_mode_generated.h"
43#include "frc971/can_configuration_generated.h"
44#include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
45#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
46#include "frc971/input/robot_state_generated.h"
47#include "frc971/queues/gyro_generated.h"
48#include "frc971/wpilib/ADIS16448.h"
49#include "frc971/wpilib/buffered_pcm.h"
50#include "frc971/wpilib/buffered_solenoid.h"
51#include "frc971/wpilib/dma.h"
52#include "frc971/wpilib/drivetrain_writer.h"
53#include "frc971/wpilib/encoder_and_potentiometer.h"
54#include "frc971/wpilib/joystick_sender.h"
55#include "frc971/wpilib/logging_generated.h"
56#include "frc971/wpilib/loop_output_handler.h"
57#include "frc971/wpilib/pdp_fetcher.h"
58#include "frc971/wpilib/sensor_reader.h"
59#include "frc971/wpilib/wpilib_robot_base.h"
60#include "y2023_bot3/constants.h"
61#include "y2023_bot3/control_loops/superstructure/led_indicator.h"
62#include "y2023_bot3/control_loops/superstructure/superstructure_output_generated.h"
63#include "y2023_bot3/control_loops/superstructure/superstructure_position_generated.h"
64
65DEFINE_bool(ctre_diag_server, false,
66 "If true, enable the diagnostics server for interacting with "
67 "devices on the CAN bus using Phoenix Tuner");
68
69using ::aos::monotonic_clock;
70using ::y2023_bot3::constants::Values;
71namespace superstructure = ::y2023_bot3::control_loops::superstructure;
72namespace drivetrain = ::y2023_bot3::control_loops::drivetrain;
73namespace chrono = ::std::chrono;
74using std::make_unique;
75
76namespace y2023_bot3 {
77namespace wpilib {
78namespace {
79
80constexpr double kMaxBringupPower = 12.0;
81
82// TODO(Brian): Fix the interpretation of the result of GetRaw here and in the
83// DMA stuff and then removing the * 2.0 in *_translate.
84// The low bit is direction.
85
86double drivetrain_velocity_translate(double in) {
87 return (((1.0 / in) / Values::kDrivetrainCyclesPerRevolution()) *
88 (2.0 * M_PI)) *
89 Values::kDrivetrainEncoderRatio() *
90 control_loops::drivetrain::kWheelRadius;
91}
92
93constexpr double kMaxFastEncoderPulsesPerSecond = std::max({
94 Values::kMaxDrivetrainEncoderPulsesPerSecond(),
95});
96static_assert(kMaxFastEncoderPulsesPerSecond <= 1300000,
97 "fast encoders are too fast");
98
99} // namespace
100
101static constexpr int kCANFalconCount = 6;
102static constexpr units::frequency::hertz_t kCANUpdateFreqHz = 200_Hz;
103
104class Falcon {
105 public:
106 Falcon(int device_id, std::string canbus,
107 std::vector<ctre::phoenix6::BaseStatusSignal *> *signals)
108 : talon_(device_id, canbus),
109 device_id_(device_id),
110 device_temp_(talon_.GetDeviceTemp()),
111 supply_voltage_(talon_.GetSupplyVoltage()),
112 supply_current_(talon_.GetSupplyCurrent()),
113 torque_current_(talon_.GetTorqueCurrent()),
114 position_(talon_.GetPosition()),
115 duty_cycle_(talon_.GetDutyCycle()) {
116 // device temp is not timesynced so don't add it to the list of signals
117 device_temp_.SetUpdateFrequency(kCANUpdateFreqHz);
118
119 CHECK_NOTNULL(signals);
120
121 supply_voltage_.SetUpdateFrequency(kCANUpdateFreqHz);
122 signals->push_back(&supply_voltage_);
123
124 supply_current_.SetUpdateFrequency(kCANUpdateFreqHz);
125 signals->push_back(&supply_current_);
126
127 torque_current_.SetUpdateFrequency(kCANUpdateFreqHz);
128 signals->push_back(&torque_current_);
129
130 position_.SetUpdateFrequency(kCANUpdateFreqHz);
131 signals->push_back(&position_);
132
133 duty_cycle_.SetUpdateFrequency(kCANUpdateFreqHz);
134 signals->push_back(&duty_cycle_);
135 }
136
137 void PrintConfigs() {
138 ctre::phoenix6::configs::TalonFXConfiguration configuration;
139 ctre::phoenix::StatusCode status =
140 talon_.GetConfigurator().Refresh(configuration);
141 if (!status.IsOK()) {
142 AOS_LOG(ERROR, "Failed to get falcon configuration: %s: %s",
143 status.GetName(), status.GetDescription());
144 }
145 AOS_LOG(INFO, "configuration: %s", configuration.ToString().c_str());
146 }
147
148 void WriteConfigs(ctre::phoenix6::signals::InvertedValue invert) {
149 inverted_ = invert;
150
151 ctre::phoenix6::configs::CurrentLimitsConfigs current_limits;
152 current_limits.StatorCurrentLimit =
153 constants::Values::kDrivetrainStatorCurrentLimit();
154 current_limits.StatorCurrentLimitEnable = true;
155 current_limits.SupplyCurrentLimit =
156 constants::Values::kDrivetrainSupplyCurrentLimit();
157 current_limits.SupplyCurrentLimitEnable = true;
158
159 ctre::phoenix6::configs::MotorOutputConfigs output_configs;
160 output_configs.NeutralMode =
161 ctre::phoenix6::signals::NeutralModeValue::Brake;
162 output_configs.DutyCycleNeutralDeadband = 0;
163
164 output_configs.Inverted = inverted_;
165
166 ctre::phoenix6::configs::TalonFXConfiguration configuration;
167 configuration.CurrentLimits = current_limits;
168 configuration.MotorOutput = output_configs;
169
170 ctre::phoenix::StatusCode status =
171 talon_.GetConfigurator().Apply(configuration);
172 if (!status.IsOK()) {
173 AOS_LOG(ERROR, "Failed to set falcon configuration: %s: %s",
174 status.GetName(), status.GetDescription());
175 }
176
177 PrintConfigs();
178 }
179
180 ctre::phoenix6::hardware::TalonFX *talon() { return &talon_; }
181
182 flatbuffers::Offset<frc971::control_loops::CANFalcon> WritePosition(
183 flatbuffers::FlatBufferBuilder *fbb) {
184 frc971::control_loops::CANFalcon::Builder builder(*fbb);
185 builder.add_id(device_id_);
186 builder.add_device_temp(device_temp());
187 builder.add_supply_voltage(supply_voltage());
188 builder.add_supply_current(supply_current());
189 builder.add_torque_current(torque_current());
190 builder.add_duty_cycle(duty_cycle());
191
192 double invert =
193 (inverted_ == ctre::phoenix6::signals::InvertedValue::Clockwise_Positive
194 ? 1
195 : -1);
196
197 builder.add_position(
198 constants::Values::DrivetrainCANEncoderToMeters(position()) * invert);
199
200 return builder.Finish();
201 }
202
203 int device_id() const { return device_id_; }
204 float device_temp() const { return device_temp_.GetValue().value(); }
205 float supply_voltage() const { return supply_voltage_.GetValue().value(); }
206 float supply_current() const { return supply_current_.GetValue().value(); }
207 float torque_current() const { return torque_current_.GetValue().value(); }
208 float duty_cycle() const { return duty_cycle_.GetValue().value(); }
209 float position() const { return position_.GetValue().value(); }
210
211 // returns the monotonic timestamp of the latest timesynced reading in the
212 // timebase of the the syncronized CAN bus clock.
213 int64_t GetTimestamp() {
214 std::chrono::nanoseconds latest_timestamp =
215 torque_current_.GetTimestamp().GetTime();
216
217 return latest_timestamp.count();
218 }
219
220 void RefreshNontimesyncedSignals() { device_temp_.Refresh(); };
221
222 private:
223 ctre::phoenix6::hardware::TalonFX talon_;
224 int device_id_;
225
226 ctre::phoenix6::signals::InvertedValue inverted_;
227
228 ctre::phoenix6::StatusSignal<units::temperature::celsius_t> device_temp_;
229 ctre::phoenix6::StatusSignal<units::voltage::volt_t> supply_voltage_;
230 ctre::phoenix6::StatusSignal<units::current::ampere_t> supply_current_,
231 torque_current_;
232 ctre::phoenix6::StatusSignal<units::angle::turn_t> position_;
233 ctre::phoenix6::StatusSignal<units::dimensionless::scalar_t> duty_cycle_;
234};
235
236class CANSensorReader {
237 public:
238 CANSensorReader(
239 aos::EventLoop *event_loop,
240 std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry)
241 : event_loop_(event_loop),
242 signals_(signals_registry.begin(), signals_registry.end()),
243 can_position_sender_(
244 event_loop
245 ->MakeSender<frc971::control_loops::drivetrain::CANPosition>(
246 "/drivetrain")) {
247 event_loop->SetRuntimeRealtimePriority(40);
248 event_loop->SetRuntimeAffinity(aos::MakeCpusetFromCpus({1}));
249 timer_handler_ = event_loop->AddTimer([this]() { Loop(); });
250 timer_handler_->set_name("CANSensorReader Loop");
251
252 event_loop->OnRun([this]() {
253 timer_handler_->Schedule(event_loop_->monotonic_now(),
254 1 / kCANUpdateFreqHz);
255 });
256 }
257
258 void set_falcons(std::shared_ptr<Falcon> right_front,
259 std::shared_ptr<Falcon> right_back,
260 std::shared_ptr<Falcon> right_under,
261 std::shared_ptr<Falcon> left_front,
262 std::shared_ptr<Falcon> left_back,
263 std::shared_ptr<Falcon> left_under) {
264 right_front_ = std::move(right_front);
265 right_back_ = std::move(right_back);
266 right_under_ = std::move(right_under);
267 left_front_ = std::move(left_front);
268 left_back_ = std::move(left_back);
269 left_under_ = std::move(left_under);
270 }
271
272 private:
273 void Loop() {
274 ctre::phoenix::StatusCode status =
275 ctre::phoenix6::BaseStatusSignal::WaitForAll(2000_ms, signals_);
276
277 if (!status.IsOK()) {
278 AOS_LOG(ERROR, "Failed to read signals from falcons: %s: %s",
279 status.GetName(), status.GetDescription());
280 }
281
282 auto builder = can_position_sender_.MakeBuilder();
283
284 for (auto falcon : {right_front_, right_back_, right_under_, left_front_,
285 left_back_, left_under_}) {
286 falcon->RefreshNontimesyncedSignals();
287 }
288
289 aos::SizedArray<flatbuffers::Offset<frc971::control_loops::CANFalcon>,
290 kCANFalconCount>
291 falcons;
292
293 for (auto falcon : {right_front_, right_back_, right_under_, left_front_,
294 left_back_, left_under_}) {
295 falcons.push_back(falcon->WritePosition(builder.fbb()));
296 }
297
298 auto falcons_list =
299 builder.fbb()
300 ->CreateVector<
301 flatbuffers::Offset<frc971::control_loops::CANFalcon>>(falcons);
302
303 frc971::control_loops::drivetrain::CANPosition::Builder
304 can_position_builder =
305 builder
306 .MakeBuilder<frc971::control_loops::drivetrain::CANPosition>();
307
308 can_position_builder.add_falcons(falcons_list);
309 can_position_builder.add_timestamp(right_front_->GetTimestamp());
310 can_position_builder.add_status(static_cast<int>(status));
311
312 builder.CheckOk(builder.Send(can_position_builder.Finish()));
313 }
314
315 aos::EventLoop *event_loop_;
316
317 const std::vector<ctre::phoenix6::BaseStatusSignal *> signals_;
318 aos::Sender<frc971::control_loops::drivetrain::CANPosition>
319 can_position_sender_;
320
321 std::shared_ptr<Falcon> right_front_, right_back_, right_under_, left_front_,
322 left_back_, left_under_;
323
324 // Pointer to the timer handler used to modify the wakeup.
325 ::aos::TimerHandler *timer_handler_;
326};
327
328// Class to send position messages with sensor readings to our loops.
329class SensorReader : public ::frc971::wpilib::SensorReader {
330 public:
331 SensorReader(::aos::ShmEventLoop *event_loop,
332 std::shared_ptr<const Values> values,
333 CANSensorReader *can_sensor_reader)
334 : ::frc971::wpilib::SensorReader(event_loop),
335 values_(std::move(values)),
336 auto_mode_sender_(
337 event_loop->MakeSender<::frc971::autonomous::AutonomousMode>(
338 "/autonomous")),
339 superstructure_position_sender_(
340 event_loop->MakeSender<superstructure::Position>(
341 "/superstructure")),
342 drivetrain_position_sender_(
343 event_loop
344 ->MakeSender<::frc971::control_loops::drivetrain::Position>(
345 "/drivetrain")),
346 gyro_sender_(event_loop->MakeSender<::frc971::sensors::GyroReading>(
347 "/drivetrain")),
348 can_sensor_reader_(can_sensor_reader) {
349 // Set to filter out anything shorter than 1/4 of the minimum pulse width
350 // we should ever see.
351 UpdateFastEncoderFilterHz(kMaxFastEncoderPulsesPerSecond);
352 event_loop->SetRuntimeAffinity(aos::MakeCpusetFromCpus({0}));
353 }
354
355 void Start() override { AddToDMA(&imu_yaw_rate_reader_); }
356
357 // Auto mode switches.
358 void set_autonomous_mode(int i, ::std::unique_ptr<frc::DigitalInput> sensor) {
359 autonomous_modes_.at(i) = ::std::move(sensor);
360 }
361
362 void set_yaw_rate_input(::std::unique_ptr<frc::DigitalInput> sensor) {
363 imu_yaw_rate_input_ = ::std::move(sensor);
364 imu_yaw_rate_reader_.set_input(imu_yaw_rate_input_.get());
365 }
366
367 void RunIteration() override {
368 superstructure_reading_->Set(true);
369
370 {
371 auto builder = drivetrain_position_sender_.MakeBuilder();
372 frc971::control_loops::drivetrain::Position::Builder drivetrain_builder =
373 builder.MakeBuilder<frc971::control_loops::drivetrain::Position>();
374 drivetrain_builder.add_left_encoder(
375 constants::Values::DrivetrainEncoderToMeters(
376 drivetrain_left_encoder_->GetRaw()));
377 drivetrain_builder.add_left_speed(
378 drivetrain_velocity_translate(drivetrain_left_encoder_->GetPeriod()));
379
380 drivetrain_builder.add_right_encoder(
381 -constants::Values::DrivetrainEncoderToMeters(
382 drivetrain_right_encoder_->GetRaw()));
383 drivetrain_builder.add_right_speed(-drivetrain_velocity_translate(
384 drivetrain_right_encoder_->GetPeriod()));
385
386 builder.CheckOk(builder.Send(drivetrain_builder.Finish()));
387 }
388
389 {
390 auto builder = gyro_sender_.MakeBuilder();
391 ::frc971::sensors::GyroReading::Builder gyro_reading_builder =
392 builder.MakeBuilder<::frc971::sensors::GyroReading>();
393 // +/- 2000 deg / sec
394 constexpr double kMaxVelocity = 4000; // degrees / second
395 constexpr double kVelocityRadiansPerSecond =
396 kMaxVelocity / 360 * (2.0 * M_PI);
397
398 // Only part of the full range is used to prevent being 100% on or off.
399 constexpr double kScaledRangeLow = 0.1;
400 constexpr double kScaledRangeHigh = 0.9;
401
402 constexpr double kPWMFrequencyHz = 200;
403 double velocity_duty_cycle =
404 imu_yaw_rate_reader_.last_width() * kPWMFrequencyHz;
405
406 constexpr double kDutyCycleScale =
407 1 / (kScaledRangeHigh - kScaledRangeLow);
408 // scale from 0.1 - 0.9 to 0 - 1
409 double rescaled_velocity_duty_cycle =
410 (velocity_duty_cycle - kScaledRangeLow) * kDutyCycleScale;
411
412 if (!std::isnan(rescaled_velocity_duty_cycle)) {
413 gyro_reading_builder.add_velocity((rescaled_velocity_duty_cycle - 0.5) *
414 kVelocityRadiansPerSecond);
415 }
416 builder.CheckOk(builder.Send(gyro_reading_builder.Finish()));
417 }
418
419 {
420 auto builder = auto_mode_sender_.MakeBuilder();
421
422 uint32_t mode = 0;
423 for (size_t i = 0; i < autonomous_modes_.size(); ++i) {
424 if (autonomous_modes_[i] && autonomous_modes_[i]->Get()) {
425 mode |= 1 << i;
426 }
427 }
428
429 auto auto_mode_builder =
430 builder.MakeBuilder<frc971::autonomous::AutonomousMode>();
431
432 auto_mode_builder.add_mode(mode);
433
434 builder.CheckOk(builder.Send(auto_mode_builder.Finish()));
435 }
436 }
437
438 std::shared_ptr<frc::DigitalOutput> superstructure_reading_;
439
440 void set_superstructure_reading(
441 std::shared_ptr<frc::DigitalOutput> superstructure_reading) {
442 superstructure_reading_ = superstructure_reading;
443 }
444
445 private:
446 std::shared_ptr<const Values> values_;
447
448 aos::Sender<frc971::autonomous::AutonomousMode> auto_mode_sender_;
449 aos::Sender<superstructure::Position> superstructure_position_sender_;
450 aos::Sender<frc971::control_loops::drivetrain::Position>
451 drivetrain_position_sender_;
452 ::aos::Sender<::frc971::sensors::GyroReading> gyro_sender_;
453
454 std::array<std::unique_ptr<frc::DigitalInput>, 2> autonomous_modes_;
455
456 std::unique_ptr<frc::DigitalInput> imu_yaw_rate_input_;
457
458 frc971::wpilib::DMAPulseWidthReader imu_yaw_rate_reader_;
459
460 CANSensorReader *can_sensor_reader_;
461};
462
463class SuperstructureWriter
464 : public ::frc971::wpilib::LoopOutputHandler<superstructure::Output> {
465 public:
466 SuperstructureWriter(aos::EventLoop *event_loop)
467 : frc971::wpilib::LoopOutputHandler<superstructure::Output>(
468 event_loop, "/superstructure") {
469 event_loop->SetRuntimeRealtimePriority(
470 constants::Values::kDrivetrainWriterPriority);
471 }
472
473 std::shared_ptr<frc::DigitalOutput> superstructure_reading_;
474
475 void set_superstructure_reading(
476 std::shared_ptr<frc::DigitalOutput> superstructure_reading) {
477 superstructure_reading_ = superstructure_reading;
478 }
479
480 private:
481 void Stop() override { AOS_LOG(WARNING, "Superstructure output too old.\n"); }
482
483 void Write(const superstructure::Output &output) override { (void)output; }
484
485 static void WriteCan(const double voltage,
486 ::ctre::phoenix::motorcontrol::can::TalonFX *falcon) {
487 falcon->Set(
488 ctre::phoenix::motorcontrol::ControlMode::PercentOutput,
489 std::clamp(voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0);
490 }
491
492 template <typename T>
493 static void WritePwm(const double voltage, T *motor) {
494 motor->SetSpeed(std::clamp(voltage, -kMaxBringupPower, kMaxBringupPower) /
495 12.0);
496 }
497};
498
499class SuperstructureCANWriter
500 : public ::frc971::wpilib::LoopOutputHandler<superstructure::Output> {
501 public:
502 SuperstructureCANWriter(::aos::EventLoop *event_loop)
503 : ::frc971::wpilib::LoopOutputHandler<superstructure::Output>(
504 event_loop, "/superstructure") {
505 event_loop->SetRuntimeRealtimePriority(
506 constants::Values::kSuperstructureCANWriterPriority);
507
508 event_loop->OnRun([this]() { WriteConfigs(); });
509 };
510
511 void HandleCANConfiguration(const frc971::CANConfiguration &configuration) {
512 if (configuration.reapply()) {
513 WriteConfigs();
514 }
515 }
516
517 private:
518 void WriteConfigs() {}
519
520 void Write(const superstructure::Output &output) override { (void)output; }
521
522 void Stop() override {
523 AOS_LOG(WARNING, "Superstructure CAN output too old.\n");
524 ctre::phoenix6::controls::DutyCycleOut stop_command(0.0);
525 stop_command.UpdateFreqHz = 0_Hz;
526 stop_command.EnableFOC = true;
527 }
528
529 double SafeSpeed(double voltage) {
530 return (::aos::Clip(voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0);
531 }
532};
533
534class DrivetrainWriter : public ::frc971::wpilib::LoopOutputHandler<
535 ::frc971::control_loops::drivetrain::Output> {
536 public:
537 DrivetrainWriter(::aos::EventLoop *event_loop)
538 : ::frc971::wpilib::LoopOutputHandler<
539 ::frc971::control_loops::drivetrain::Output>(event_loop,
540 "/drivetrain") {
541 event_loop->SetRuntimeRealtimePriority(
542 constants::Values::kDrivetrainWriterPriority);
543
544 event_loop->OnRun([this]() { WriteConfigs(); });
545 }
546
547 void set_falcons(std::shared_ptr<Falcon> right_front,
548 std::shared_ptr<Falcon> right_back,
549 std::shared_ptr<Falcon> right_under,
550 std::shared_ptr<Falcon> left_front,
551 std::shared_ptr<Falcon> left_back,
552 std::shared_ptr<Falcon> left_under) {
553 right_front_ = std::move(right_front);
554 right_back_ = std::move(right_back);
555 right_under_ = std::move(right_under);
556 left_front_ = std::move(left_front);
557 left_back_ = std::move(left_back);
558 left_under_ = std::move(left_under);
559 }
560
561 void set_right_inverted(ctre::phoenix6::signals::InvertedValue invert) {
562 right_inverted_ = invert;
563 }
564
565 void set_left_inverted(ctre::phoenix6::signals::InvertedValue invert) {
566 left_inverted_ = invert;
567 }
568
569 void HandleCANConfiguration(const frc971::CANConfiguration &configuration) {
570 for (auto falcon : {right_front_, right_back_, right_under_, left_front_,
571 left_back_, left_under_}) {
572 falcon->PrintConfigs();
573 }
574 if (configuration.reapply()) {
575 WriteConfigs();
576 }
577 }
578
579 private:
580 void WriteConfigs() {
581 for (auto falcon :
582 {right_front_.get(), right_back_.get(), right_under_.get()}) {
583 falcon->WriteConfigs(right_inverted_);
584 }
585
586 for (auto falcon :
587 {left_front_.get(), left_back_.get(), left_under_.get()}) {
588 falcon->WriteConfigs(left_inverted_);
589 }
590 }
591
592 void Write(
593 const ::frc971::control_loops::drivetrain::Output &output) override {
594 ctre::phoenix6::controls::DutyCycleOut left_control(
595 SafeSpeed(output.left_voltage()));
596 left_control.UpdateFreqHz = 0_Hz;
597 left_control.EnableFOC = true;
598
599 ctre::phoenix6::controls::DutyCycleOut right_control(
600 SafeSpeed(output.right_voltage()));
601 right_control.UpdateFreqHz = 0_Hz;
602 right_control.EnableFOC = true;
603
604 for (auto falcon :
605 {left_front_.get(), left_back_.get(), left_under_.get()}) {
606 ctre::phoenix::StatusCode status =
607 falcon->talon()->SetControl(left_control);
608
609 if (!status.IsOK()) {
610 AOS_LOG(ERROR, "Failed to write control to falcon: %s: %s",
611 status.GetName(), status.GetDescription());
612 }
613 }
614
615 for (auto falcon :
616 {right_front_.get(), right_back_.get(), right_under_.get()}) {
617 ctre::phoenix::StatusCode status =
618 falcon->talon()->SetControl(right_control);
619
620 if (!status.IsOK()) {
621 AOS_LOG(ERROR, "Failed to write control to falcon: %s: %s",
622 status.GetName(), status.GetDescription());
623 }
624 }
625 }
626
627 void Stop() override {
628 AOS_LOG(WARNING, "drivetrain output too old\n");
629 ctre::phoenix6::controls::DutyCycleOut stop_command(0.0);
630 stop_command.UpdateFreqHz = 0_Hz;
631 stop_command.EnableFOC = true;
632
633 for (auto falcon :
634 {right_front_.get(), right_back_.get(), right_under_.get(),
635 left_front_.get(), left_back_.get(), left_under_.get()}) {
636 falcon->talon()->SetControl(stop_command);
637 }
638 }
639
640 double SafeSpeed(double voltage) {
641 return (::aos::Clip(voltage, -kMaxBringupPower, kMaxBringupPower) / 12.0);
642 }
643
644 ctre::phoenix6::signals::InvertedValue left_inverted_, right_inverted_;
645 std::shared_ptr<Falcon> right_front_, right_back_, right_under_, left_front_,
646 left_back_, left_under_;
647};
648
649class WPILibRobot : public ::frc971::wpilib::WPILibRobotBase {
650 public:
651 ::std::unique_ptr<frc::Encoder> make_encoder(int index) {
652 return make_unique<frc::Encoder>(10 + index * 2, 11 + index * 2, false,
653 frc::Encoder::k4X);
654 }
655
656 void Run() override {
657 std::shared_ptr<const Values> values =
658 std::make_shared<const Values>(constants::MakeValues());
659
660 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
661 aos::configuration::ReadConfig("aos_config.json");
662
663 // Thread 1.
664 ::aos::ShmEventLoop joystick_sender_event_loop(&config.message());
665 ::frc971::wpilib::JoystickSender joystick_sender(
666 &joystick_sender_event_loop);
667 AddLoop(&joystick_sender_event_loop);
668
669 // Thread 2.
670 ::aos::ShmEventLoop pdp_fetcher_event_loop(&config.message());
671 ::frc971::wpilib::PDPFetcher pdp_fetcher(&pdp_fetcher_event_loop);
672 AddLoop(&pdp_fetcher_event_loop);
673
674 std::shared_ptr<frc::DigitalOutput> superstructure_reading =
675 make_unique<frc::DigitalOutput>(25);
676
677 std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry;
678 std::shared_ptr<Falcon> right_front =
679 std::make_shared<Falcon>(1, "Drivetrain Bus", &signals_registry);
680 std::shared_ptr<Falcon> right_back =
681 std::make_shared<Falcon>(2, "Drivetrain Bus", &signals_registry);
682 std::shared_ptr<Falcon> right_under =
683 std::make_shared<Falcon>(3, "Drivetrain Bus", &signals_registry);
684 std::shared_ptr<Falcon> left_front =
685 std::make_shared<Falcon>(4, "Drivetrain Bus", &signals_registry);
686 std::shared_ptr<Falcon> left_back =
687 std::make_shared<Falcon>(5, "Drivetrain Bus", &signals_registry);
688 std::shared_ptr<Falcon> left_under =
689 std::make_shared<Falcon>(6, "Drivetrain Bus", &signals_registry);
690
691 // Thread 3.
692 ::aos::ShmEventLoop can_sensor_reader_event_loop(&config.message());
693 can_sensor_reader_event_loop.set_name("CANSensorReader");
694 CANSensorReader can_sensor_reader(&can_sensor_reader_event_loop,
695 std::move(signals_registry));
696
697 can_sensor_reader.set_falcons(right_front, right_back, right_under,
698 left_front, left_back, left_under);
699
700 AddLoop(&can_sensor_reader_event_loop);
701
702 // Thread 4.
703 ::aos::ShmEventLoop sensor_reader_event_loop(&config.message());
704 SensorReader sensor_reader(&sensor_reader_event_loop, values,
705 &can_sensor_reader);
706 sensor_reader.set_pwm_trigger(true);
707 sensor_reader.set_drivetrain_left_encoder(make_encoder(1));
708 sensor_reader.set_drivetrain_right_encoder(make_encoder(0));
709 sensor_reader.set_superstructure_reading(superstructure_reading);
710 sensor_reader.set_yaw_rate_input(make_unique<frc::DigitalInput>(0));
711
712 AddLoop(&sensor_reader_event_loop);
713
714 // Thread 5.
715 // Set up CAN.
716 if (!FLAGS_ctre_diag_server) {
717 c_Phoenix_Diagnostics_SetSecondsToStart(-1);
718 c_Phoenix_Diagnostics_Dispose();
719 }
720
721 ctre::phoenix::platform::can::CANComm_SetRxSchedPriority(
722 constants::Values::kDrivetrainRxPriority, true, "Drivetrain Bus");
723 ctre::phoenix::platform::can::CANComm_SetTxSchedPriority(
724 constants::Values::kDrivetrainTxPriority, true, "Drivetrain Bus");
725
726 ::aos::ShmEventLoop can_output_event_loop(&config.message());
727 can_output_event_loop.set_name("CANOutputWriter");
728 DrivetrainWriter drivetrain_writer(&can_output_event_loop);
729
730 drivetrain_writer.set_falcons(right_front, right_back, right_under,
731 left_front, left_back, left_under);
732 drivetrain_writer.set_right_inverted(
733 ctre::phoenix6::signals::InvertedValue::Clockwise_Positive);
734 drivetrain_writer.set_left_inverted(
735 ctre::phoenix6::signals::InvertedValue::CounterClockwise_Positive);
736
737 can_output_event_loop.MakeWatcher(
738 "/roborio",
739 [&drivetrain_writer](const frc971::CANConfiguration &configuration) {
740 drivetrain_writer.HandleCANConfiguration(configuration);
741 });
742
743 AddLoop(&can_output_event_loop);
744
745 // Thread 6
746 // Set up superstructure output.
747 ::aos::ShmEventLoop output_event_loop(&config.message());
748 output_event_loop.set_name("PWMOutputWriter");
749 SuperstructureWriter superstructure_writer(&output_event_loop);
750
751 superstructure_writer.set_superstructure_reading(superstructure_reading);
752
753 AddLoop(&output_event_loop);
754
755 // Thread 7
756 // Set up led_indicator.
757 ::aos::ShmEventLoop led_indicator_event_loop(&config.message());
758 led_indicator_event_loop.set_name("LedIndicator");
759 control_loops::superstructure::LedIndicator led_indicator(
760 &led_indicator_event_loop);
761 AddLoop(&led_indicator_event_loop);
762
763 RunLoops();
764 }
765};
766
767} // namespace wpilib
768} // namespace y2023_bot3
769
770AOS_ROBOT_CLASS(::y2023_bot3::wpilib::WPILibRobot);