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