2bd3c5009e198fc971dd1a73e417de59b6d1f333
[fw/altos] / src / telelco-v3.0 / ao_lco_v3.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20 #include <ao_lco.h>
21 #include <ao_event.h>
22 #include <ao_quadrature.h>
23 #include <ao_radio_cmac.h>
24 #include <ao_st7565.h>
25 #include <ao_adc_single.h>
26
27 #define WIDTH   AO_ST7565_WIDTH
28 #define HEIGHT  AO_ST7565_HEIGHT
29 #define STRIDE  AO_BITMAP_STRIDE(WIDTH)
30
31 static uint32_t image[STRIDE * HEIGHT];
32
33 static struct ao_bitmap fb = {
34         .base = image,
35         .stride = STRIDE,
36         .width = WIDTH,
37         .height = HEIGHT,
38         .damage = AO_BOX_INIT,
39 };
40
41 static const struct ao_transform logo_transform = {
42         .x_scale = 48, .x_off = 2,
43         .y_scale = 48, .y_off = 0,
44 };
45
46 #define BIG_FONT BitstreamVeraSans_Roman_58_font
47 #define VOLT_FONT BitstreamVeraSans_Roman_58_font
48 #define SMALL_FONT BitstreamVeraSans_Roman_12_font
49 #define TINY_FONT BitstreamVeraSans_Roman_10_font
50 #define LOGO_FONT BenguiatGothicStd_Bold_26_font
51
52 #define LABEL_Y         (int16_t) (SMALL_FONT.ascent)
53 #define VALUE_Y         (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
54 #define BOX_X           2
55 #define PAD_X           90
56 #define BOX_LABEL_X     30
57 #define VOLT_LABEL_X    25
58 #define RSSI_LABEL_X    15
59 #define PAD_LABEL_X     95
60 #define SEP_X           (PAD_X - 8)
61 #define SCAN_X          (WIDTH - 100) / 2
62 #define SCAN_Y          50
63 #define SCAN_HEIGHT     3
64 #define FOUND_Y         63
65 #define FOUND_X         6
66 #define FOUND_WIDTH     (WIDTH - 6)
67
68 #define AO_LCO_DRAG_RACE_START_TIME     AO_SEC_TO_TICKS(5)
69 #define AO_LCO_DRAG_RACE_STOP_TIME      AO_SEC_TO_TICKS(2)
70
71 /* UI values */
72 static uint8_t  ao_lco_select_mode;
73 static uint8_t  ao_lco_event_debug;
74
75 #define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
76 #define AO_LCO_SELECT_PAD       0
77 #define AO_LCO_SELECT_BOX       1
78
79 static uint8_t  ao_lco_display_mutex;
80
81 static void
82 _ao_lco_show_pad(uint8_t pad)
83 {
84         char    str[5];
85
86         snprintf(str, sizeof(str), "%d", pad);
87         ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
88         ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
89 }
90
91 static void
92 _ao_lco_show_box(uint16_t box)
93 {
94         char    str[7];
95
96         snprintf(str, sizeof(str), "%2d", box);
97         ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
98         ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
99 }
100
101 static void
102 _ao_lco_show_voltage(uint16_t decivolts, const char *label)
103 {
104         char    str[7];
105
106         PRINTD("voltage %d\n", decivolts);
107         snprintf(str, sizeof(str), "%2d.%d", decivolts / 10, decivolts % 10);
108         ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
109         ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, label, AO_BLACK, AO_COPY);
110 }
111
112 static void
113 _ao_lco_batt_voltage(void)
114 {
115         struct ao_adc   packet;
116         int16_t         decivolt;
117
118         ao_adc_single_get(&packet);
119         decivolt = ao_battery_decivolt(packet.v_batt);
120         _ao_lco_show_voltage((uint16_t) decivolt, "LCO battery");
121         ao_st7565_update(&fb);
122 }
123
124 void
125 ao_lco_show(void)
126 {
127         ao_mutex_get(&ao_lco_display_mutex);
128         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
129         if (ao_lco_box == AO_LCO_LCO_VOLTAGE) {
130                 _ao_lco_batt_voltage();
131         } else if (ao_lco_pad == AO_LCO_PAD_VOLTAGE) {
132                 _ao_lco_show_voltage(ao_pad_query.battery, "Pad battery");
133         } else {
134                 _ao_lco_show_pad(ao_lco_pad);
135                 _ao_lco_show_box(ao_lco_box);
136                 ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
137         }
138         ao_st7565_update(&fb);
139         ao_mutex_put(&ao_lco_display_mutex);
140 }
141
142 static void
143 ao_lco_set_select(void)
144 {
145         if (ao_lco_armed) {
146                 ao_led_off(AO_LED_PAD);
147                 ao_led_off(AO_LED_BOX);
148         } else {
149                 switch (ao_lco_select_mode) {
150                 case AO_LCO_SELECT_PAD:
151                         ao_led_off(AO_LED_BOX);
152                         ao_led_on(AO_LED_PAD);
153                         break;
154                 case AO_LCO_SELECT_BOX:
155                         ao_led_off(AO_LED_PAD);
156                         ao_led_on(AO_LED_BOX);
157                         break;
158                 default:
159                         break;
160                 }
161         }
162 }
163
164
165 static struct ao_task   ao_lco_drag_task;
166
167 static void
168 ao_lco_drag_monitor(void)
169 {
170         AO_TICK_TYPE    delay = ~0UL;
171         AO_TICK_TYPE    now;
172
173         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
174         for (;;) {
175                 PRINTD("Drag monitor count %d delay %lu\n", ao_lco_drag_beep_count, (unsigned long) delay);
176                 if (delay == (AO_TICK_TYPE) ~0)
177                         ao_sleep(&ao_lco_drag_beep_count);
178                 else
179                         ao_sleep_for(&ao_lco_drag_beep_count, delay);
180
181                 delay = ~0UL;
182                 now = ao_time();
183                 delay = ao_lco_drag_warn_check(now, delay);
184                 delay = ao_lco_drag_beep_check(now, delay);
185         }
186 }
187
188 static void
189 ao_lco_input(void)
190 {
191         static struct ao_event  event;
192
193         for (;;) {
194                 ao_event_get(&event);
195                 PRINTE("event type %d unit %d value %ld\n",
196                        event.type, event.unit, (long) event.value);
197                 switch (event.type) {
198                 case AO_EVENT_QUADRATURE:
199                         switch (event.unit) {
200                         case AO_QUADRATURE_SELECT:
201                                 if (!ao_lco_armed) {
202                                         switch (ao_lco_select_mode) {
203                                         case AO_LCO_SELECT_PAD:
204                                                 ao_lco_step_pad((int8_t) event.value);
205                                                 break;
206                                         case AO_LCO_SELECT_BOX:
207                                                 ao_lco_step_box((int8_t) event.value);
208                                                 break;
209                                         default:
210                                                 break;
211                                         }
212                                 }
213                                 break;
214                         }
215                         break;
216                 case AO_EVENT_BUTTON:
217                         switch (event.unit) {
218                         case AO_BUTTON_ARM:
219                                 ao_lco_set_armed((uint8_t) event.value);
220                                 ao_lco_set_select();
221                                 break;
222                         case AO_BUTTON_FIRE:
223                                 if (ao_lco_armed)
224                                         ao_lco_set_firing((uint8_t) event.value);
225                                 break;
226                         case AO_BUTTON_DRAG_SELECT:
227                                 if (event.value)
228                                         ao_lco_toggle_drag();
229                                 break;
230                         case AO_BUTTON_DRAG_MODE:
231                                 if (event.value)
232                                         ao_lco_drag_enable();
233                                 else
234                                         ao_lco_drag_disable();
235                                 break;
236                         case AO_BUTTON_ENCODER_SELECT:
237                                 if (event.value) {
238                                         if (!ao_lco_armed) {
239                                                 ao_lco_select_mode = 1 - ao_lco_select_mode;
240                                                 ao_lco_set_select();
241                                         }
242                                 }
243                                 break;
244                         }
245                         break;
246                 }
247         }
248 }
249
250 /*
251  * Light up everything for a second at power on to let the user
252  * visually inspect the system for correct operation
253  */
254 static void
255 ao_lco_display_test(void)
256 {
257         ao_led_on(AO_LEDS_AVAILABLE);
258         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_BLACK, AO_COPY);
259         ao_st7565_update(&fb);
260         ao_delay(AO_MS_TO_TICKS(250));
261         ao_led_off(AO_LEDS_AVAILABLE);
262 }
263
264 static struct ao_task ao_lco_input_task;
265 static struct ao_task ao_lco_monitor_task;
266 static struct ao_task ao_lco_arm_warn_task;
267 static struct ao_task ao_lco_igniter_status_task;
268
269 static int16_t  found_x;
270
271 void
272 ao_lco_search_start(void)
273 {
274         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
275         ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
276         found_x = FOUND_X;
277 }
278
279 void
280 ao_lco_search_box_check(uint16_t box)
281 {
282         if (box > 0)
283                 ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) box, SCAN_HEIGHT, AO_BLACK, AO_COPY);
284         ao_st7565_update(&fb);
285 }
286
287 void
288 ao_lco_search_box_present(uint16_t box)
289 {
290         char    str[8];
291         if (found_x < FOUND_WIDTH)
292         {
293                 snprintf(str, sizeof(str), "%s%02u", found_x ? ", " : "", box);
294                 found_x = ao_text(&fb, &TINY_FONT, found_x, FOUND_Y, str, AO_BLACK, AO_COPY);
295         }
296 }
297
298 void
299 ao_lco_search_done(void)
300 {
301         ao_st7565_update(&fb);
302 }
303
304 static void
305 ao_lco_batt_voltage(void)
306 {
307         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
308         _ao_lco_batt_voltage();
309         ao_st7565_update(&fb);
310         ao_delay(AO_MS_TO_TICKS(1000));
311 }
312
313 static void
314 ao_lco_main(void)
315 {
316         ao_lco_display_test();
317         ao_lco_batt_voltage();
318         ao_lco_search();
319         ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
320         ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
321         ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
322         ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race");
323         ao_lco_monitor();
324 }
325
326 #if DEBUG
327 static void
328 ao_lco_set_debug(void)
329 {
330         uint32_t r = ao_cmd_decimal();
331         if (ao_cmd_status == ao_cmd_success){
332                 ao_lco_debug = r & 1;
333                 ao_lco_event_debug = (r & 2) >> 1;
334         }
335 }
336
337 const struct ao_cmds ao_lco_cmds[] = {
338         { ao_lco_set_debug,     "D <0 off, 1 on>\0Debug" },
339         { ao_lco_search,        "s\0Search for pad boxes" },
340         { ao_lco_pretend,       "p\0Pretend there are lots of pad boxes" },
341         { 0, NULL }
342 };
343 #endif
344
345 void
346 ao_lco_init(void)
347 {
348         ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
349 #if DEBUG
350         ao_cmd_register(&ao_lco_cmds[0]);
351 #endif
352 }