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