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