blob: 1738ea85c16e6b49a37b42fe945d25123bc72e55 [file] [log] [blame]
Austin Schuh010eb812014-10-25 18:06:49 -07001#include <stdio.h>
2#include <string.h>
3#include <thread>
4#include <mutex>
5#include <unistd.h>
6#include <inttypes.h>
7
8#include "aos/prime/output/motor_output.h"
9#include "aos/common/controls/output_check.q.h"
Brian Silvermanda45b6c2014-12-28 11:36:50 -080010#include "aos/common/messages/robot_state.q.h"
Austin Schuh010eb812014-10-25 18:06:49 -070011#include "aos/common/controls/sensor_generation.q.h"
12#include "aos/common/logging/logging.h"
13#include "aos/common/logging/queue_logging.h"
Austin Schuh010eb812014-10-25 18:06:49 -070014#include "aos/common/time.h"
15#include "aos/common/util/log_interval.h"
16#include "aos/common/util/phased_loop.h"
17#include "aos/common/util/wrapping_counter.h"
Austin Schuh010eb812014-10-25 18:06:49 -070018#include "aos/linux_code/init.h"
19
20#include "frc971/control_loops/drivetrain/drivetrain.q.h"
21#include "frc971/control_loops/claw/claw.q.h"
22#include "frc971/control_loops/shooter/shooter.q.h"
23#include "frc971/constants.h"
24#include "frc971/queues/other_sensors.q.h"
25#include "frc971/queues/to_log.q.h"
26
Brian Silvermanda45b6c2014-12-28 11:36:50 -080027#include "frc971/wpilib/hall_effect.h"
28#include "frc971/wpilib/joystick_sender.h"
29
Brian Silvermancb77f232014-12-19 21:48:36 -080030#include "Encoder.h"
Brian Silvermancb77f232014-12-19 21:48:36 -080031#include "Talon.h"
32#include "DriverStation.h"
33#include "AnalogInput.h"
34#include "Solenoid.h"
35#include "Compressor.h"
36#include "RobotBase.h"
Austin Schuh010eb812014-10-25 18:06:49 -070037
38#ifndef M_PI
39#define M_PI 3.14159265358979323846
40#endif
41
42using ::aos::util::SimpleLogInterval;
43using ::frc971::control_loops::drivetrain;
44using ::frc971::sensors::other_sensors;
45using ::frc971::sensors::gyro_reading;
46using ::aos::util::WrappingCounter;
47
48namespace frc971 {
Brian Silvermanda45b6c2014-12-28 11:36:50 -080049namespace wpilib {
Austin Schuh010eb812014-10-25 18:06:49 -070050
51class priority_mutex {
52 public:
53 typedef pthread_mutex_t *native_handle_type;
54
55 // TODO(austin): Write a test case for the mutex, and make the constructor
56 // constexpr.
57 priority_mutex() {
58 pthread_mutexattr_t attr;
Austin Schuh010eb812014-10-25 18:06:49 -070059#ifdef NDEBUG
Brian Silvermanda45b6c2014-12-28 11:36:50 -080060#error "Won't let assert_perror be no-op ed"
Austin Schuh010eb812014-10-25 18:06:49 -070061#endif
Austin Schuhdb516032014-12-28 00:12:38 -080062 // Turn on priority inheritance.
Austin Schuh010eb812014-10-25 18:06:49 -070063 assert_perror(pthread_mutexattr_init(&attr));
64 assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
65 assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
66
67 assert_perror(pthread_mutex_init(native_handle(), &attr));
68
69 assert_perror(pthread_mutexattr_destroy(&attr));
70 }
71
72 ~priority_mutex() { pthread_mutex_destroy(&handle_); }
73
74 void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
75 bool try_lock() {
76 int ret = pthread_mutex_trylock(&handle_);
77 if (ret == 0) {
78 return true;
79 } else if (ret == EBUSY) {
80 return false;
81 } else {
82 assert_perror(ret);
83 }
84 }
85 void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
86
87 native_handle_type native_handle() { return &handle_; }
88
89 private:
90 DISALLOW_COPY_AND_ASSIGN(priority_mutex);
91 pthread_mutex_t handle_;
92};
93
Brian Silvermanda45b6c2014-12-28 11:36:50 -080094// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -070095class EdgeCounter {
96 public:
97 EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
98 priority_mutex *mutex)
99 : priority_(priority),
100 encoder_(encoder),
101 input_(input),
102 mutex_(mutex),
103 run_(true),
104 any_interrupt_count_(0) {
105 thread_.reset(new ::std::thread(::std::ref(*this)));
106 }
107
108 // Waits for interrupts, locks the mutex, and updates the internal state.
109 // Updates the any_interrupt_count count when the interrupt comes in without
110 // the lock.
Austin Schuhdb516032014-12-28 00:12:38 -0800111 void operator()() {
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800112 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700113
114 input_->RequestInterrupts();
115 input_->SetUpSourceEdge(true, true);
116
117 {
118 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
119 current_value_ = input_->GetHall();
120 }
121
122 InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
123 while (run_) {
124 result = input_->WaitForInterrupt(
125 0.1, result != InterruptableSensorBase::kTimeout);
126 if (result == InterruptableSensorBase::kTimeout) {
127 continue;
128 }
129 ++any_interrupt_count_;
130
131 ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
132 int32_t encoder_value = encoder_->GetRaw();
133 bool hall_value = input_->GetHall();
134 if (current_value_ != hall_value) {
135 if (hall_value) {
136 ++positive_interrupt_count_;
137 last_positive_encoder_value_ = encoder_value;
138 } else {
139 ++negative_interrupt_count_;
140 last_negative_encoder_value_ = encoder_value;
141 }
142 } else {
143 LOG(WARNING, "Detected spurious edge on %d. Dropping it.\n",
144 input_->GetChannel());
145 }
146
147 current_value_ = hall_value;
148 }
149 }
150
151 // Updates the internal hall effect value given this new observation.
152 // The mutex provided at construction time must be held during this operation.
153 void set_polled_value(bool value) {
154 polled_value_ = value;
155 bool miss_match = (value != current_value_);
156 if (miss_match && last_miss_match_) {
157 current_value_ = value;
158 last_miss_match_ = false;
159 } else {
160 last_miss_match_ = miss_match;
161 }
162 }
163
164 // Signals the thread to quit next time it gets an interrupt.
165 void Quit() {
166 run_ = false;
167 thread_->join();
168 }
169
170 // Returns the total number of interrupts since construction time. This
171 // should be done without the mutex held.
172 int any_interrupt_count() const { return any_interrupt_count_; }
173 // Returns the current interrupt edge counts and encoder values.
174 // The mutex provided at construction time must be held during this operation.
175 int positive_interrupt_count() const { return positive_interrupt_count_; }
176 int negative_interrupt_count() const { return negative_interrupt_count_; }
177 int32_t last_positive_encoder_value() const {
178 return last_positive_encoder_value_;
179 }
180 int32_t last_negative_encoder_value() const {
181 return last_negative_encoder_value_;
182 }
183 // Returns the current polled value.
184 bool polled_value() const { return polled_value_; }
185
186 private:
187 int priority_;
188 Encoder *encoder_;
189 HallEffect *input_;
190 priority_mutex *mutex_;
191 ::std::atomic<bool> run_;
192
193 ::std::atomic<int> any_interrupt_count_;
194
195 // The following variables represent the current state. They must be
196 // synchronized by mutex_;
197 bool current_value_ = false;
198 bool polled_value_ = false;
199 bool last_miss_match_ = true;
200 int positive_interrupt_count_ = 0;
201 int negative_interrupt_count_ = 0;
202 int32_t last_positive_encoder_value_ = 0;
203 int32_t last_negative_encoder_value_ = 0;
204
205 ::std::unique_ptr<::std::thread> thread_;
206};
207
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800208// This class will synchronize sampling edges on a bunch of HallEffects with
Austin Schuh010eb812014-10-25 18:06:49 -0700209// the periodic poll.
210//
211// The data is provided to subclasses by calling SaveState when the state is
212// consistent and ready.
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800213//
214// TODO(brian): Split this out into a separate file once DMA is in.
Austin Schuh010eb812014-10-25 18:06:49 -0700215template <int num_sensors>
216class PeriodicHallSynchronizer {
217 public:
218 PeriodicHallSynchronizer(
219 const char *name, int priority, int interrupt_priority,
220 ::std::unique_ptr<Encoder> encoder,
221 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> *sensors)
222 : name_(name),
223 priority_(priority),
224 encoder_(::std::move(encoder)),
225 run_(true) {
226 for (int i = 0; i < num_sensors; ++i) {
227 sensors_[i] = ::std::move((*sensors)[i]);
228 edge_counters_[i] = ::std::unique_ptr<EdgeCounter>(new EdgeCounter(
229 interrupt_priority, encoder_.get(), sensors_[i].get(), &mutex_));
230 }
231 }
232
233 const char *name() const { return name_.c_str(); }
234
235 void StartThread() { thread_.reset(new ::std::thread(::std::ref(*this))); }
236
237 // Called when the state is consistent and up to date.
238 virtual void SaveState() = 0;
239
240 // Starts a sampling iteration. See RunIteration for usage.
241 void StartIteration() {
242 // Start by capturing the current interrupt counts.
243 for (int i = 0; i < num_sensors; ++i) {
244 interrupt_counts_[i] = edge_counters_[i]->any_interrupt_count();
245 }
246
247 {
248 // Now, update the encoder and sensor values.
249 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
250 encoder_value_ = encoder_->GetRaw();
251 for (int i = 0; i < num_sensors; ++i) {
252 edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
253 }
254 }
255 }
256
257 // Attempts to finish a sampling iteration. See RunIteration for usage.
258 // Returns true if the iteration succeeded, and false otherwise.
259 bool TryFinishingIteration() {
260 // Make sure no interrupts have occurred while we were waiting. If they
261 // have, we are in an inconsistent state and need to try again.
262 ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
263 bool retry = false;
264 for (int i = 0; i < num_sensors; ++i) {
265 retry = retry || (interrupt_counts_[i] !=
266 edge_counters_[i]->any_interrupt_count());
267 }
268 if (!retry) {
269 SaveState();
270 return true;
271 }
272 LOG(WARNING, "Got an interrupt while sampling encoder %s, retrying\n",
273 name());
274 return false;
275 }
276
277 void RunIteration() {
278 while (true) {
279 StartIteration();
280
281 // Wait more than the amount of time it takes for a digital input change
282 // to go from visible to software to having triggered an interrupt.
283 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
284
285 if (TryFinishingIteration()) {
286 return;
287 }
288 }
289 }
290
291 void operator()() {
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800292 ::aos::SetCurrentThreadRealtimePriority(priority_);
Austin Schuh010eb812014-10-25 18:06:49 -0700293 while (run_) {
294 ::aos::time::PhasedLoopXMS(10, 9000);
295 RunIteration();
296 }
297 }
298
299 void Quit() {
300 run_ = false;
301 for (int i = 0; i < num_sensors; ++i) {
302 edge_counters_[i]->Quit();
303 }
304 if (thread_) {
305 thread_->join();
306 }
307 }
308
309 protected:
310 // These values are only safe to fetch from inside SaveState()
311 int32_t encoder_value() const { return encoder_value_; }
312 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> &edge_counters() {
313 return edge_counters_;
314 }
315
316 private:
317 // A descriptive name for error messages.
318 ::std::string name_;
319 // The priority of the polling thread.
320 int priority_;
321 // The Encoder to sample.
322 ::std::unique_ptr<Encoder> encoder_;
323 // A list of all the digital inputs.
324 ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
325 // The mutex used to synchronize all the state.
326 priority_mutex mutex_;
327 ::std::atomic<bool> run_;
328
329 // The state.
330 // The current encoder value.
331 int32_t encoder_value_ = 0;
332 // The current edge counters.
333 ::std::array<::std::unique_ptr<EdgeCounter>, num_sensors> edge_counters_;
334
335 ::std::unique_ptr<::std::thread> thread_;
336 ::std::array<int, num_sensors> interrupt_counts_;
337};
338
339double drivetrain_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800340 return static_cast<double>(in) /
341 (256.0 /*cpr*/ * 2.0 /*2x. Stupid WPILib*/) *
342 (18.0 / 50.0 /*output stage*/) * (56.0 / 30.0 /*encoder gears*/)
343 // * constants::GetValues().drivetrain_encoder_ratio
344 *
345 (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
Austin Schuh010eb812014-10-25 18:06:49 -0700346}
347
348static const double kVcc = 5.15;
349
350double gyro_translate(int64_t in) {
351 return in / 16.0 / 1000.0 / (180.0 / M_PI);
352}
353
354float hall_translate(const constants::ShifterHallEffect &k, float in_low,
355 float in_high) {
356 const float low_ratio =
357 0.5 * (in_low - static_cast<float>(k.low_gear_low)) /
358 static_cast<float>(k.low_gear_middle - k.low_gear_low);
359 const float high_ratio =
360 0.5 + 0.5 * (in_high - static_cast<float>(k.high_gear_middle)) /
361 static_cast<float>(k.high_gear_high - k.high_gear_middle);
362
363 // Return low when we are below 1/2, and high when we are above 1/2.
364 if (low_ratio + high_ratio < 1.0) {
365 return low_ratio;
366 } else {
367 return high_ratio;
368 }
369}
370
371double claw_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800372 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) /
373 (18.0 / 48.0 /*encoder gears*/) / (12.0 / 60.0 /*chain reduction*/) *
374 (M_PI / 180.0) *
375 2.0 /*TODO(austin): Debug this, encoders read too little*/;
Austin Schuh010eb812014-10-25 18:06:49 -0700376}
377
378double shooter_translate(int32_t in) {
Austin Schuhdb516032014-12-28 00:12:38 -0800379 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
380 16 /*sprocket teeth*/ * 0.375 /*chain pitch*/
381 * (2.54 / 100.0 /*in to m*/);
Austin Schuh010eb812014-10-25 18:06:49 -0700382}
383
Austin Schuh010eb812014-10-25 18:06:49 -0700384// This class sends out half of the claw position state at 100 hz.
385class HalfClawHallSynchronizer : public PeriodicHallSynchronizer<3> {
386 public:
387 // Constructs a HalfClawHallSynchronizer.
388 //
389 // priority is the priority of the polling thread.
390 // interrupt_priority is the priority of the interrupt threads.
391 // encoder is the encoder to read.
392 // sensors is an array of hall effect sensors. The sensors[0] is the front
Austin Schuhdb516032014-12-28 00:12:38 -0800393 // sensor, sensors[1] is the calibration sensor, sensors[2] is the back
394 // sensor.
Austin Schuh010eb812014-10-25 18:06:49 -0700395 HalfClawHallSynchronizer(
396 const char *name, int priority, int interrupt_priority,
397 ::std::unique_ptr<Encoder> encoder,
398 ::std::array<::std::unique_ptr<HallEffect>, 3> *sensors, bool reversed)
399 : PeriodicHallSynchronizer<3>(name, priority, interrupt_priority,
400 ::std::move(encoder), sensors),
401 reversed_(reversed) {}
402
403 void set_position(control_loops::HalfClawPosition *half_claw_position) {
404 half_claw_position_ = half_claw_position;
405 }
406
407 // Saves the state so that it can be sent if it was synchronized.
408 virtual void SaveState() {
409 const auto &front = edge_counters()[0];
410 half_claw_position_->front.current = front->polled_value();
411 half_claw_position_->front.posedge_count =
412 front->positive_interrupt_count();
413 half_claw_position_->front.negedge_count =
414 front->negative_interrupt_count();
415
416 const auto &calibration = edge_counters()[1];
417 half_claw_position_->calibration.current = calibration->polled_value();
418 half_claw_position_->calibration.posedge_count =
419 calibration->positive_interrupt_count();
420 half_claw_position_->calibration.negedge_count =
421 calibration->negative_interrupt_count();
422
423 const auto &back = edge_counters()[2];
424 half_claw_position_->back.current = back->polled_value();
425 half_claw_position_->back.posedge_count = back->positive_interrupt_count();
426 half_claw_position_->back.negedge_count = back->negative_interrupt_count();
427
428 const double multiplier = reversed_ ? -1.0 : 1.0;
429
430 half_claw_position_->position =
431 multiplier * claw_translate(encoder_value());
432
433 // We assume here that we can only have 1 sensor have a posedge per cycle.
434 {
435 half_claw_position_->posedge_value =
436 last_half_claw_position_.posedge_value;
437 int posedge_changes = 0;
438 if (half_claw_position_->front.posedge_count !=
439 last_half_claw_position_.front.posedge_count) {
440 ++posedge_changes;
441 half_claw_position_->posedge_value =
442 multiplier * claw_translate(front->last_positive_encoder_value());
443 LOG(INFO, "Got a front posedge\n");
444 }
445
446 if (half_claw_position_->back.posedge_count !=
447 last_half_claw_position_.back.posedge_count) {
448 ++posedge_changes;
449 half_claw_position_->posedge_value =
450 multiplier * claw_translate(back->last_positive_encoder_value());
451 LOG(INFO, "Got a back posedge\n");
452 }
453
454 if (half_claw_position_->calibration.posedge_count !=
455 last_half_claw_position_.calibration.posedge_count) {
456 ++posedge_changes;
457 half_claw_position_->posedge_value =
458 multiplier *
459 claw_translate(calibration->last_positive_encoder_value());
460 LOG(INFO, "Got a calibration posedge\n");
461 }
462
463 if (posedge_changes > 1) {
464 LOG(WARNING, "Found more than 1 positive edge on %s\n", name());
465 }
466 }
467
468 {
469 half_claw_position_->negedge_value =
470 last_half_claw_position_.negedge_value;
471 int negedge_changes = 0;
472 if (half_claw_position_->front.negedge_count !=
473 last_half_claw_position_.front.negedge_count) {
474 ++negedge_changes;
475 half_claw_position_->negedge_value =
476 multiplier * claw_translate(front->last_negative_encoder_value());
477 LOG(INFO, "Got a front negedge\n");
478 }
479
480 if (half_claw_position_->back.negedge_count !=
481 last_half_claw_position_.back.negedge_count) {
482 ++negedge_changes;
483 half_claw_position_->negedge_value =
484 multiplier * claw_translate(back->last_negative_encoder_value());
485 LOG(INFO, "Got a back negedge\n");
486 }
487
488 if (half_claw_position_->calibration.negedge_count !=
489 last_half_claw_position_.calibration.negedge_count) {
490 ++negedge_changes;
491 half_claw_position_->negedge_value =
492 multiplier *
493 claw_translate(calibration->last_negative_encoder_value());
494 LOG(INFO, "Got a calibration negedge\n");
495 }
496
497 if (negedge_changes > 1) {
498 LOG(WARNING, "Found more than 1 negative edge on %s\n", name());
499 }
500 }
501
502 last_half_claw_position_ = *half_claw_position_;
503 }
504
505 private:
506 control_loops::HalfClawPosition *half_claw_position_;
507 control_loops::HalfClawPosition last_half_claw_position_;
508 bool reversed_;
509};
510
Austin Schuh010eb812014-10-25 18:06:49 -0700511// This class sends out the shooter position state at 100 hz.
512class ShooterHallSynchronizer : public PeriodicHallSynchronizer<2> {
513 public:
514 // Constructs a ShooterHallSynchronizer.
515 //
516 // priority is the priority of the polling thread.
517 // interrupt_priority is the priority of the interrupt threads.
518 // encoder is the encoder to read.
519 // sensors is an array of hall effect sensors. The sensors[0] is the proximal
520 // sensor, sensors[1] is the distal sensor.
521 // shooter_plunger is the plunger.
522 // shooter_latch is the latch.
523 ShooterHallSynchronizer(
524 int priority, int interrupt_priority, ::std::unique_ptr<Encoder> encoder,
525 ::std::array<::std::unique_ptr<HallEffect>, 2> *sensors,
526 ::std::unique_ptr<HallEffect> shooter_plunger,
527 ::std::unique_ptr<HallEffect> shooter_latch)
528 : PeriodicHallSynchronizer<2>("shooter", priority, interrupt_priority,
529 ::std::move(encoder), sensors),
530 shooter_plunger_(::std::move(shooter_plunger)),
531 shooter_latch_(::std::move(shooter_latch)) {}
532
533 // Saves the state so that it can be sent if it was synchronized.
534 virtual void SaveState() {
535 auto shooter_position =
536 control_loops::shooter_queue_group.position.MakeMessage();
537
538 shooter_position->plunger = shooter_plunger_->GetHall();
539 shooter_position->latch = shooter_latch_->GetHall();
540 shooter_position->position = shooter_translate(encoder_value());
541
542 {
543 const auto &proximal_edge_counter = edge_counters()[0];
544 shooter_position->pusher_proximal.current =
545 proximal_edge_counter->polled_value();
546 shooter_position->pusher_proximal.posedge_count =
547 proximal_edge_counter->positive_interrupt_count();
548 shooter_position->pusher_proximal.negedge_count =
549 proximal_edge_counter->negative_interrupt_count();
550 shooter_position->pusher_proximal.posedge_value = shooter_translate(
551 proximal_edge_counter->last_positive_encoder_value());
552 }
553
554 {
Austin Schuhdb516032014-12-28 00:12:38 -0800555 const auto &distal_edge_counter = edge_counters()[1];
556 shooter_position->pusher_distal.current =
557 distal_edge_counter->polled_value();
558 shooter_position->pusher_distal.posedge_count =
559 distal_edge_counter->positive_interrupt_count();
560 shooter_position->pusher_distal.negedge_count =
561 distal_edge_counter->negative_interrupt_count();
562 shooter_position->pusher_distal.posedge_value =
563 shooter_translate(distal_edge_counter->last_positive_encoder_value());
Austin Schuh010eb812014-10-25 18:06:49 -0700564 }
565
566 shooter_position.Send();
567 }
568
569 private:
570 ::std::unique_ptr<HallEffect> shooter_plunger_;
571 ::std::unique_ptr<HallEffect> shooter_latch_;
572};
573
Austin Schuh010eb812014-10-25 18:06:49 -0700574class SensorReader {
575 public:
576 SensorReader()
577 : auto_selector_analog_(new AnalogInput(4)),
Brian Silvermancb77f232014-12-19 21:48:36 -0800578 left_encoder_(new Encoder(11, 10, false, Encoder::k2X)), // E0
579 right_encoder_(new Encoder(13, 12, false, Encoder::k2X)), // E1
580 low_left_drive_hall_(new AnalogInput(1)),
581 high_left_drive_hall_(new AnalogInput(0)),
582 low_right_drive_hall_(new AnalogInput(2)),
583 high_right_drive_hall_(new AnalogInput(3)),
584 shooter_plunger_(new HallEffect(8)), // S3
585 shooter_latch_(new HallEffect(9)), // S4
586 shooter_distal_(new HallEffect(7)), // S2
587 shooter_proximal_(new HallEffect(6)), // S1
588 shooter_encoder_(new Encoder(14, 15)), // E2
589 claw_top_front_hall_(new HallEffect(4)), // R2
590 claw_top_calibration_hall_(new HallEffect(3)), // R3
591 claw_top_back_hall_(new HallEffect(5)), // R1
592 claw_top_encoder_(new Encoder(17, 16)), // E3
Austin Schuh010eb812014-10-25 18:06:49 -0700593 // L2 Middle Left hall effect sensor.
Brian Silvermancb77f232014-12-19 21:48:36 -0800594 claw_bottom_front_hall_(new HallEffect(1)),
Austin Schuh010eb812014-10-25 18:06:49 -0700595 // L3 Bottom Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800596 claw_bottom_calibration_hall_(new HallEffect(0)),
Austin Schuh010eb812014-10-25 18:06:49 -0700597 // L1 Top Left hall effect sensor
Brian Silvermancb77f232014-12-19 21:48:36 -0800598 claw_bottom_back_hall_(new HallEffect(2)),
599 claw_bottom_encoder_(new Encoder(21, 20)), // E5
Austin Schuh010eb812014-10-25 18:06:49 -0700600 run_(true) {
601 filter_.SetPeriodNanoSeconds(100000);
602 filter_.Add(shooter_plunger_.get());
603 filter_.Add(shooter_latch_.get());
604 filter_.Add(shooter_distal_.get());
605 filter_.Add(shooter_proximal_.get());
606 filter_.Add(claw_top_front_hall_.get());
607 filter_.Add(claw_top_calibration_hall_.get());
608 filter_.Add(claw_top_back_hall_.get());
609 filter_.Add(claw_bottom_front_hall_.get());
610 filter_.Add(claw_bottom_calibration_hall_.get());
611 filter_.Add(claw_bottom_back_hall_.get());
612 printf("Filtering all hall effect sensors with a %" PRIu64
613 " nanosecond window\n",
614 filter_.GetPeriodNanoSeconds());
615 }
616
617 void operator()() {
618 const int kPriority = 30;
619 const int kInterruptPriority = 55;
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800620 ::aos::SetCurrentThreadRealtimePriority(kPriority);
Austin Schuh010eb812014-10-25 18:06:49 -0700621
622 ::std::array<::std::unique_ptr<HallEffect>, 2> shooter_sensors;
623 shooter_sensors[0] = ::std::move(shooter_proximal_);
624 shooter_sensors[1] = ::std::move(shooter_distal_);
625 ShooterHallSynchronizer shooter(
626 kPriority, kInterruptPriority, ::std::move(shooter_encoder_),
627 &shooter_sensors, ::std::move(shooter_plunger_),
628 ::std::move(shooter_latch_));
629 shooter.StartThread();
630
631 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_top_sensors;
632 claw_top_sensors[0] = ::std::move(claw_top_front_hall_);
633 claw_top_sensors[1] = ::std::move(claw_top_calibration_hall_);
634 claw_top_sensors[2] = ::std::move(claw_top_back_hall_);
635 HalfClawHallSynchronizer top_claw("top_claw", kPriority, kInterruptPriority,
636 ::std::move(claw_top_encoder_),
637 &claw_top_sensors, false);
638
639 ::std::array<::std::unique_ptr<HallEffect>, 3> claw_bottom_sensors;
640 claw_bottom_sensors[0] = ::std::move(claw_bottom_front_hall_);
641 claw_bottom_sensors[1] = ::std::move(claw_bottom_calibration_hall_);
642 claw_bottom_sensors[2] = ::std::move(claw_bottom_back_hall_);
643 HalfClawHallSynchronizer bottom_claw(
644 "bottom_claw", kPriority, kInterruptPriority,
645 ::std::move(claw_bottom_encoder_), &claw_bottom_sensors, true);
646
647 while (run_) {
648 ::aos::time::PhasedLoopXMS(10, 9000);
649 RunIteration();
650
651 auto claw_position =
652 control_loops::claw_queue_group.position.MakeMessage();
653 bottom_claw.set_position(&claw_position->bottom);
654 top_claw.set_position(&claw_position->top);
655 while (true) {
656 bottom_claw.StartIteration();
657 top_claw.StartIteration();
658
659 // Wait more than the amount of time it takes for a digital input change
660 // to go from visible to software to having triggered an interrupt.
661 ::aos::time::SleepFor(::aos::time::Time::InUS(120));
662
Austin Schuhdb516032014-12-28 00:12:38 -0800663 if (bottom_claw.TryFinishingIteration() &&
664 top_claw.TryFinishingIteration()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700665 break;
666 }
667 }
668
669 claw_position.Send();
670 }
671 shooter.Quit();
672 top_claw.Quit();
673 bottom_claw.Quit();
674 }
675
676 void RunIteration() {
677 //::aos::time::TimeFreezer time_freezer;
678 DriverStation *ds = DriverStation::GetInstance();
679
680 bool bad_gyro = true;
681 // TODO(brians): Switch to LogInterval for these things.
682 /*
683 if (data->uninitialized_gyro) {
684 LOG(DEBUG, "uninitialized gyro\n");
685 bad_gyro = true;
686 } else if (data->zeroing_gyro) {
687 LOG(DEBUG, "zeroing gyro\n");
688 bad_gyro = true;
689 } else if (data->bad_gyro) {
690 LOG(ERROR, "bad gyro\n");
691 bad_gyro = true;
692 } else if (data->old_gyro_reading) {
693 LOG(WARNING, "old/bad gyro reading\n");
694 bad_gyro = true;
695 } else {
696 bad_gyro = false;
697 }
698 */
699
700 if (!bad_gyro) {
701 // TODO(austin): Read the gyro.
702 gyro_reading.MakeWithBuilder().angle(0).Send();
703 }
704
Austin Schuhdb516032014-12-28 00:12:38 -0800705 if (ds->IsSysActive()) {
Austin Schuh010eb812014-10-25 18:06:49 -0700706 auto message = ::aos::controls::output_check_received.MakeMessage();
707 // TODO(brians): Actually read a pulse value from the roboRIO.
708 message->pwm_value = 0;
709 message->pulse_length = -1;
710 LOG_STRUCT(DEBUG, "received", *message);
711 message.Send();
712 }
713
714 ::frc971::sensors::auto_mode.MakeWithBuilder()
715 .voltage(auto_selector_analog_->GetVoltage())
716 .Send();
717
718 // TODO(austin): Calibrate the shifter constants again.
719 drivetrain.position.MakeWithBuilder()
720 .right_encoder(drivetrain_translate(right_encoder_->GetRaw()))
721 .left_encoder(-drivetrain_translate(left_encoder_->GetRaw()))
722 .left_shifter_position(
723 hall_translate(constants::GetValues().left_drive,
724 low_left_drive_hall_->GetVoltage(),
725 high_left_drive_hall_->GetVoltage()))
726 .right_shifter_position(
727 hall_translate(constants::GetValues().right_drive,
728 low_right_drive_hall_->GetVoltage(),
729 high_right_drive_hall_->GetVoltage()))
730 .battery_voltage(ds->GetBatteryVoltage())
731 .Send();
732
733 // Signal that we are allive.
734 ::aos::controls::sensor_generation.MakeWithBuilder()
735 .reader_pid(getpid())
736 .cape_resets(0)
737 .Send();
738 }
739
740 void Quit() { run_ = false; }
741
742 private:
743 ::std::unique_ptr<AnalogInput> auto_selector_analog_;
744
745 ::std::unique_ptr<Encoder> left_encoder_;
746 ::std::unique_ptr<Encoder> right_encoder_;
747 ::std::unique_ptr<AnalogInput> low_left_drive_hall_;
748 ::std::unique_ptr<AnalogInput> high_left_drive_hall_;
749 ::std::unique_ptr<AnalogInput> low_right_drive_hall_;
750 ::std::unique_ptr<AnalogInput> high_right_drive_hall_;
751
752 ::std::unique_ptr<HallEffect> shooter_plunger_;
753 ::std::unique_ptr<HallEffect> shooter_latch_;
754 ::std::unique_ptr<HallEffect> shooter_distal_;
755 ::std::unique_ptr<HallEffect> shooter_proximal_;
756 ::std::unique_ptr<Encoder> shooter_encoder_;
757
758 ::std::unique_ptr<HallEffect> claw_top_front_hall_;
759 ::std::unique_ptr<HallEffect> claw_top_calibration_hall_;
760 ::std::unique_ptr<HallEffect> claw_top_back_hall_;
761 ::std::unique_ptr<Encoder> claw_top_encoder_;
762
763 ::std::unique_ptr<HallEffect> claw_bottom_front_hall_;
764 ::std::unique_ptr<HallEffect> claw_bottom_calibration_hall_;
765 ::std::unique_ptr<HallEffect> claw_bottom_back_hall_;
766 ::std::unique_ptr<Encoder> claw_bottom_encoder_;
767
768 ::std::atomic<bool> run_;
769 DigitalGlitchFilter filter_;
770};
771
772class MotorWriter {
773 public:
774 MotorWriter()
775 : right_drivetrain_talon_(new Talon(2)),
776 left_drivetrain_talon_(new Talon(5)),
777 shooter_talon_(new Talon(6)),
778 top_claw_talon_(new Talon(1)),
779 bottom_claw_talon_(new Talon(0)),
780 left_tusk_talon_(new Talon(4)),
781 right_tusk_talon_(new Talon(3)),
782 intake1_talon_(new Talon(7)),
783 intake2_talon_(new Talon(8)),
784 left_shifter_(new Solenoid(6)),
785 right_shifter_(new Solenoid(7)),
786 shooter_latch_(new Solenoid(5)),
787 shooter_brake_(new Solenoid(4)),
788 compressor_(new Compressor()) {
789 compressor_->SetClosedLoopControl(true);
Austin Schuhdb516032014-12-28 00:12:38 -0800790 // right_drivetrain_talon_->EnableDeadbandElimination(true);
791 // left_drivetrain_talon_->EnableDeadbandElimination(true);
792 // shooter_talon_->EnableDeadbandElimination(true);
793 // top_claw_talon_->EnableDeadbandElimination(true);
794 // bottom_claw_talon_->EnableDeadbandElimination(true);
795 // left_tusk_talon_->EnableDeadbandElimination(true);
796 // right_tusk_talon_->EnableDeadbandElimination(true);
797 // intake1_talon_->EnableDeadbandElimination(true);
798 // intake2_talon_->EnableDeadbandElimination(true);
Austin Schuh010eb812014-10-25 18:06:49 -0700799 }
800
801 // Maximum age of an output packet before the motors get zeroed instead.
802 static const int kOutputMaxAgeMS = 20;
803 static constexpr ::aos::time::Time kOldLogInterval =
804 ::aos::time::Time::InSeconds(0.5);
805
806 void Run() {
807 //::aos::time::Time::EnableMockTime();
808 while (true) {
809 //::aos::time::Time::UpdateMockTime();
810 // 200 hz loop
811 ::aos::time::PhasedLoopXMS(5, 1000);
812 //::aos::time::Time::UpdateMockTime();
813
814 no_robot_state_.Print();
815 fake_robot_state_.Print();
816 sending_failed_.Print();
817
818 RunIteration();
819 }
820 }
821
822 virtual void RunIteration() {
823 ::aos::robot_state.FetchLatest();
824 if (!::aos::robot_state.get()) {
825 LOG_INTERVAL(no_robot_state_);
826 return;
827 }
828 if (::aos::robot_state->fake) {
829 LOG_INTERVAL(fake_robot_state_);
830 return;
831 }
832
833 // TODO(austin): Write the motor values out when they change! One thread
834 // per queue.
835 // TODO(austin): Figure out how to synchronize everything to the PWM update
836 // rate, or get the pulse to go out clocked off of this code. That would be
837 // awesome.
838 {
839 static auto &drivetrain = ::frc971::control_loops::drivetrain.output;
840 drivetrain.FetchLatest();
841 if (drivetrain.IsNewerThanMS(kOutputMaxAgeMS)) {
842 LOG_STRUCT(DEBUG, "will output", *drivetrain);
843 left_drivetrain_talon_->Set(-drivetrain->left_voltage / 12.0);
844 right_drivetrain_talon_->Set(drivetrain->right_voltage / 12.0);
845 left_shifter_->Set(drivetrain->left_high);
846 right_shifter_->Set(drivetrain->right_high);
847 } else {
848 left_drivetrain_talon_->Disable();
849 right_drivetrain_talon_->Disable();
850 LOG_INTERVAL(drivetrain_old_);
851 }
852 drivetrain_old_.Print();
853 }
854
855 {
856 static auto &shooter =
857 ::frc971::control_loops::shooter_queue_group.output;
858 shooter.FetchLatest();
859 if (shooter.IsNewerThanMS(kOutputMaxAgeMS)) {
860 LOG_STRUCT(DEBUG, "will output", *shooter);
861 shooter_talon_->Set(shooter->voltage / 12.0);
862 shooter_latch_->Set(!shooter->latch_piston);
863 shooter_brake_->Set(!shooter->brake_piston);
864 } else {
865 shooter_talon_->Disable();
866 shooter_brake_->Set(false); // engage the brake
867 LOG_INTERVAL(shooter_old_);
868 }
869 shooter_old_.Print();
870 }
871
872 {
873 static auto &claw = ::frc971::control_loops::claw_queue_group.output;
874 claw.FetchLatest();
875 if (claw.IsNewerThanMS(kOutputMaxAgeMS)) {
876 LOG_STRUCT(DEBUG, "will output", *claw);
877 intake1_talon_->Set(claw->intake_voltage / 12.0);
878 intake2_talon_->Set(claw->intake_voltage / 12.0);
879 bottom_claw_talon_->Set(-claw->bottom_claw_voltage / 12.0);
880 top_claw_talon_->Set(claw->top_claw_voltage / 12.0);
881 left_tusk_talon_->Set(claw->tusk_voltage / 12.0);
882 right_tusk_talon_->Set(-claw->tusk_voltage / 12.0);
883 } else {
884 intake1_talon_->Disable();
885 intake2_talon_->Disable();
886 bottom_claw_talon_->Disable();
887 top_claw_talon_->Disable();
888 left_tusk_talon_->Disable();
889 right_tusk_talon_->Disable();
890 LOG_INTERVAL(claw_old_);
891 }
892 claw_old_.Print();
893 }
894 }
895
896 SimpleLogInterval drivetrain_old_ =
897 SimpleLogInterval(kOldLogInterval, WARNING, "drivetrain too old");
898 SimpleLogInterval shooter_old_ =
899 SimpleLogInterval(kOldLogInterval, WARNING, "shooter too old");
900 SimpleLogInterval claw_old_ =
901 SimpleLogInterval(kOldLogInterval, WARNING, "claw too old");
902
903 ::std::unique_ptr<Talon> right_drivetrain_talon_;
904 ::std::unique_ptr<Talon> left_drivetrain_talon_;
905 ::std::unique_ptr<Talon> shooter_talon_;
906 ::std::unique_ptr<Talon> top_claw_talon_;
907 ::std::unique_ptr<Talon> bottom_claw_talon_;
908 ::std::unique_ptr<Talon> left_tusk_talon_;
909 ::std::unique_ptr<Talon> right_tusk_talon_;
910 ::std::unique_ptr<Talon> intake1_talon_;
911 ::std::unique_ptr<Talon> intake2_talon_;
912
913 ::std::unique_ptr<Solenoid> left_shifter_;
914 ::std::unique_ptr<Solenoid> right_shifter_;
915 ::std::unique_ptr<Solenoid> shooter_latch_;
916 ::std::unique_ptr<Solenoid> shooter_brake_;
917
918 ::std::unique_ptr<Compressor> compressor_;
919
920 ::aos::util::SimpleLogInterval no_robot_state_ =
921 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), INFO,
922 "no robot state -> not outputting");
923 ::aos::util::SimpleLogInterval fake_robot_state_ =
924 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.5), DEBUG,
925 "fake robot state -> not outputting");
926 ::aos::util::SimpleLogInterval sending_failed_ =
927 ::aos::util::SimpleLogInterval(::aos::time::Time::InSeconds(0.1), WARNING,
928 "sending outputs failed");
929};
930
931constexpr ::aos::time::Time MotorWriter::kOldLogInterval;
932
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800933} // namespace wpilib
Austin Schuh010eb812014-10-25 18:06:49 -0700934} // namespace frc971
935
936class WPILibRobot : public RobotBase {
937 public:
938 virtual void StartCompetition() {
939 ::aos::Init();
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800940 ::frc971::wpilib::MotorWriter writer;
941 ::frc971::wpilib::SensorReader reader;
Austin Schuh010eb812014-10-25 18:06:49 -0700942 ::std::thread reader_thread(::std::ref(reader));
Brian Silvermanda45b6c2014-12-28 11:36:50 -0800943 ::frc971::wpilib::JoystickSender joystick_sender;
Austin Schuh010eb812014-10-25 18:06:49 -0700944 ::std::thread joystick_thread(::std::ref(joystick_sender));
945 writer.Run();
946 LOG(ERROR, "Exiting WPILibRobot\n");
947 reader.Quit();
948 reader_thread.join();
949 joystick_sender.Quit();
950 joystick_thread.join();
951 ::aos::Cleanup();
952 }
953};
954
Austin Schuhdb516032014-12-28 00:12:38 -0800955
Austin Schuh010eb812014-10-25 18:06:49 -0700956START_ROBOT_CLASS(WPILibRobot);