1 // Copyright 2017 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 //
27 // User interface.
28 
29 #include "tides2/ui.h"
30 
31 #include <algorithm>
32 
33 #include "stmlib/system/system_clock.h"
34 
35 #include "tides2/factory_test.h"
36 
37 using namespace std;
38 using namespace stmlib;
39 
40 const int32_t kLongPressDuration = 1200;
41 
42 namespace tides {
43 
44 /* static */
45 const LedColor Ui::palette_[4] = {
46   LED_COLOR_GREEN,
47   LED_COLOR_YELLOW,
48   LED_COLOR_RED,
49   LED_COLOR_OFF
50 };
51 
Init(Settings * settings,FactoryTest * factory_test)52 void Ui::Init(Settings* settings, FactoryTest* factory_test) {
53   leds_.Init();
54   switches_.Init();
55 
56   system_clock.Init();
57 
58   settings_ = settings;
59   factory_test_ = factory_test;
60   mode_ = UI_MODE_NORMAL;
61 
62   if (switches_.pressed_immediate(SWITCH_SHIFT)) {
63     State* state = settings_->mutable_state();
64     if (state->color_blind == 1) {
65       state->color_blind = 0;
66     } else {
67       state->color_blind = 1;
68     }
69     settings_->SaveState();
70   }
71 
72   queue_.Init();
73 
74   fill(&press_time_[0], &press_time_[SWITCH_LAST], 0);
75   fill(&ignore_release_[0], &ignore_release_[SWITCH_LAST], false);
76 }
77 
Poll()78 void Ui::Poll() {
79   system_clock.Tick();
80   UpdateLEDs();
81 
82   switches_.Debounce();
83 
84   for (int i = 0; i < SWITCH_LAST; ++i) {
85     Switch s = Switch(i);
86     if (switches_.just_pressed(s)) {
87       queue_.AddEvent(CONTROL_SWITCH, i, 0);
88       press_time_[i] = system_clock.milliseconds();
89       ignore_release_[i] = false;
90     }
91     if (switches_.pressed(s) && !ignore_release_[i]) {
92       int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
93       if (pressed_time > kLongPressDuration) {
94         queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
95         ignore_release_[i] = true;
96       }
97     }
98     if (switches_.released(s) && !ignore_release_[i]) {
99       queue_.AddEvent(
100           CONTROL_SWITCH,
101           i,
102           system_clock.milliseconds() - press_time_[i] + 1);
103       ignore_release_[i] = true;
104     }
105   }
106 }
107 
MakeColor(uint8_t value,bool color_blind)108 LedColor Ui::MakeColor(uint8_t value, bool color_blind) {
109   LedColor color = palette_[value];
110   if (color_blind) {
111     uint8_t pwm_counter = system_clock.milliseconds() & 15;
112     uint8_t triangle = (system_clock.milliseconds() >> 5) & 31;
113     triangle = triangle < 16 ? triangle : 31 - triangle;
114 
115     if (value == 0) {
116       color = pwm_counter < (4 + (triangle >> 2))
117           ? LED_COLOR_GREEN
118           : LED_COLOR_OFF;
119     } else if (value == 1) {
120       color = LED_COLOR_YELLOW;
121     } else if (value == 2) {
122       color = pwm_counter == 0 ? LED_COLOR_RED : LED_COLOR_OFF;
123     }
124   }
125   return color;
126 }
127 
UpdateLEDs()128 void Ui::UpdateLEDs() {
129   leds_.Clear();
130 
131   bool blink = system_clock.milliseconds() & 256;
132 
133   switch (mode_) {
134     case UI_MODE_NORMAL:
135       {
136         const State& s = settings_->state();
137         bool color_blind = s.color_blind == 1;
138 
139         leds_.set(LED_MODE, MakeColor(s.mode, color_blind));
140         leds_.set(LED_RANGE, MakeColor(s.range, color_blind));
141         leds_.set(LED_SHIFT, MakeColor((s.output_mode + 3) % 4, color_blind));
142       }
143       break;
144 
145   case UI_MODE_CALIBRATION_C1:
146     leds_.set(LED_RANGE, blink ? LED_COLOR_YELLOW : LED_COLOR_OFF);
147     break;
148 
149   case UI_MODE_CALIBRATION_C3:
150     leds_.set(LED_SHIFT, blink ? LED_COLOR_YELLOW : LED_COLOR_OFF);
151     break;
152 
153   case UI_MODE_FACTORY_TEST:
154     {
155       size_t counter = (system_clock.milliseconds() >> 8) % 3;
156       for (size_t i = 0; i < 3; ++i) {
157         leds_.set(Led(i), palette_[counter]);
158       }
159     }
160     break;
161   }
162   leds_.Write();
163 }
164 
OnSwitchPressed(const Event & e)165 void Ui::OnSwitchPressed(const Event& e) {
166 
167 }
168 
OnSwitchReleased(const Event & e)169 void Ui::OnSwitchReleased(const Event& e) {
170   if (mode_ == UI_MODE_NORMAL && e.data >= kLongPressDuration) {
171     if ((e.control_id == SWITCH_RANGE && switches_.pressed(SWITCH_SHIFT)) ||
172         (e.control_id == SWITCH_SHIFT && switches_.pressed(SWITCH_RANGE))) {
173       mode_ = UI_MODE_CALIBRATION_C1;
174       factory_test_->Calibrate(0, 1.0f, 3.0f);
175       ignore_release_[SWITCH_RANGE] = ignore_release_[SWITCH_SHIFT] = true;
176     }
177   } else if (mode_ == UI_MODE_CALIBRATION_C1) {
178     factory_test_->Calibrate(1, 1.0f, 3.0f);
179     mode_ = UI_MODE_CALIBRATION_C3;
180   } else if (mode_ == UI_MODE_CALIBRATION_C3) {
181     factory_test_->Calibrate(2, 1.0f, 3.0f);
182     mode_ = UI_MODE_NORMAL;
183   } else {
184     State* s = settings_->mutable_state();
185     switch (e.control_id) {
186       case SWITCH_MODE:
187         s->mode = (s->mode + 1) % 3;
188         break;
189       case SWITCH_RANGE:
190         s->range = (s->range + 1) % 3;
191         break;
192       case SWITCH_SHIFT:
193         s->output_mode = (s->output_mode + 1) % 4;
194         break;
195     }
196     settings_->SaveState();
197   }
198 }
199 
DoEvents()200 void Ui::DoEvents() {
201   while (queue_.available()) {
202     Event e = queue_.PullEvent();
203     if (e.control_type == CONTROL_SWITCH) {
204       if (e.data == 0) {
205         OnSwitchPressed(e);
206       } else {
207         OnSwitchReleased(e);
208       }
209     }
210   }
211 
212   if (queue_.idle_time() > 1000) {
213     queue_.Touch();
214   }
215 }
216 
217 }  // namespace tides
218