dbee0bc2ed269223195ca8bad6a236c797c6a0d1
[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 #include <ao_pwm.h>
27
28 #define WIDTH   AO_ST7565_WIDTH
29 #define HEIGHT  AO_ST7565_HEIGHT
30 #define STRIDE  AO_BITMAP_STRIDE(WIDTH)
31
32 static uint32_t image[STRIDE * HEIGHT];
33
34 static struct ao_bitmap fb = {
35         .base = image,
36         .stride = STRIDE,
37         .width = WIDTH,
38         .height = HEIGHT,
39         .damage = AO_BOX_INIT,
40 };
41
42 static const struct ao_transform logo_transform = {
43         .x_scale = 48, .x_off = 2,
44         .y_scale = 48, .y_off = 0,
45 };
46
47 static const struct ao_transform show_transform = {
48         .x_scale = 36, .x_off = 100,
49         .y_scale = 36, .y_off = 0,
50 };
51
52 #define BIG_FONT BitstreamVeraSans_Roman_58_font
53 #define VOLT_FONT BitstreamVeraSans_Roman_58_font
54 #define SMALL_FONT BitstreamVeraSans_Roman_12_font
55 #define TINY_FONT BitstreamVeraSans_Roman_10_font
56 #define LOGO_FONT BenguiatGothicStd_Bold_26_font
57
58 #define LABEL_Y         (int16_t) (SMALL_FONT.ascent)
59 #define VALUE_Y         (int16_t) (LABEL_Y + BIG_FONT.ascent + 5)
60 #define BOX_X           2
61 #define PAD_X           90
62 #define BOX_LABEL_X     30
63 #define VOLT_LABEL_X    25
64 #define RSSI_LABEL_X    15
65 #define PAD_LABEL_X     95
66 #define SEP_X           (PAD_X - 8)
67 #define SCAN_X          (WIDTH - 100) / 2
68 #define SCAN_Y          50
69 #define SCAN_HEIGHT     3
70 #define FOUND_Y         63
71 #define FOUND_X         6
72 #define FOUND_WIDTH     (WIDTH - 6)
73 #define CONTRAST_LABEL_X        37
74 #define CONTRAST_WIDTH  100
75 #define CONTRAST_X      (WIDTH - CONTRAST_WIDTH) / 2
76 #define CONTRAST_Y      20
77 #define CONTRAST_HEIGHT 20
78 #define CONTRAST_VALUE_X        64
79 #define CONTRAST_VALUE_Y        (CONTRAST_Y + CONTRAST_HEIGHT + SMALL_FONT.ascent + 3)
80 #define BACKLIGHT_LABEL_X       37
81 #define BACKLIGHT_WIDTH 100
82 #define BACKLIGHT_X     (WIDTH - BACKLIGHT_WIDTH) / 2
83 #define BACKLIGHT_Y     20
84 #define BACKLIGHT_HEIGHT        20
85 #define BACKLIGHT_VALUE_X       64
86 #define BACKLIGHT_VALUE_Y       (BACKLIGHT_Y + BACKLIGHT_HEIGHT + SMALL_FONT.ascent + 3)
87 #define INFO_START_Y    ((int16_t) (SMALL_FONT.ascent + 2))
88 #define INFO_STEP_Y     ((int16_t) (SMALL_FONT.ascent + 3))
89
90 #define AO_LCO_DRAG_RACE_START_TIME     AO_SEC_TO_TICKS(5)
91 #define AO_LCO_DRAG_RACE_STOP_TIME      AO_SEC_TO_TICKS(2)
92
93 /* UI values */
94 static uint8_t  ao_lco_select_mode;
95 static uint8_t  ao_lco_event_debug;
96
97 #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)
98 #define AO_LCO_SELECT_PAD       0
99 #define AO_LCO_SELECT_BOX       1
100
101 static uint8_t  ao_lco_display_mutex;
102
103 static void
104 _ao_lco_show_pad(uint8_t pad)
105 {
106         char    str[5];
107
108         snprintf(str, sizeof(str), "%d", pad);
109         ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY);
110         ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY);
111 }
112
113 static void
114 _ao_lco_show_box(int16_t box)
115 {
116         char    str[7];
117
118         snprintf(str, sizeof(str), "%2d", box);
119         ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
120         ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY);
121 }
122
123 static void
124 _ao_lco_show_voltage(uint16_t decivolts, const char *label)
125 {
126         char    str[7];
127
128         PRINTD("voltage %d\n", decivolts);
129         snprintf(str, sizeof(str), "%2d.%d", decivolts / 10, decivolts % 10);
130         ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY);
131         ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, label, AO_BLACK, AO_COPY);
132 }
133
134 static void
135 _ao_lco_batt_voltage(void)
136 {
137         struct ao_adc   packet;
138         int16_t         decivolt;
139
140         ao_adc_single_get(&packet);
141         decivolt = ao_battery_decivolt(packet.v_batt);
142         _ao_lco_show_voltage((uint16_t) decivolt, "LCO battery");
143         ao_st7565_update(&fb);
144 }
145
146 static void
147 _ao_lco_show_contrast(void)
148 {
149         char buf[8];
150         uint8_t brightness = ao_st7565_get_brightness();
151         int16_t contrast = (int16_t) (brightness * CONTRAST_WIDTH / AO_LCO_MAX_CONTRAST);
152         int16_t width;
153
154         ao_text(&fb, &SMALL_FONT, CONTRAST_LABEL_X, LABEL_Y, "Contrast", AO_BLACK, AO_COPY);
155         ao_rect(&fb, CONTRAST_X, CONTRAST_Y, contrast, CONTRAST_HEIGHT, AO_BLACK, AO_COPY);
156         /* this "knows" that CONTRAST_WIDTH == 100 */
157         snprintf(buf, sizeof(buf), "%d %%", contrast);
158         width = ao_text_width(&SMALL_FONT, buf);
159         ao_text(&fb, &SMALL_FONT, BACKLIGHT_VALUE_X - width / 2, BACKLIGHT_VALUE_Y, buf, AO_BLACK, AO_COPY);
160 }
161
162 static void
163 _ao_lco_show_backlight(void)
164 {
165         char buf[8];
166         int32_t backlight = ao_lco_get_backlight();
167         int16_t value = (int16_t) (backlight * BACKLIGHT_WIDTH / AO_LCO_MAX_BACKLIGHT);
168         int16_t width;
169
170         ao_text(&fb, &SMALL_FONT, BACKLIGHT_LABEL_X, LABEL_Y, "Backlight", AO_BLACK, AO_COPY);
171         ao_rect(&fb, BACKLIGHT_X, BACKLIGHT_Y, value, BACKLIGHT_HEIGHT, AO_BLACK, AO_COPY);
172         /* this "knows" that BACKLIGHT_WIDTH == 100 */
173         snprintf(buf, sizeof(buf), "%d %%", value);
174         width = ao_text_width(&SMALL_FONT, buf);
175         ao_text(&fb, &SMALL_FONT, BACKLIGHT_VALUE_X - width / 2, BACKLIGHT_VALUE_Y, buf, AO_BLACK, AO_COPY);
176 }
177
178 static int16_t info_y;
179
180 static void
181 _ao_lco_info(const char *format, ...)
182 {
183         va_list a;
184         char    buf[20];
185         va_start(a, format);
186         vsnprintf(buf, sizeof(buf), format, a);
187         va_end(a);
188         ao_text(&fb, &SMALL_FONT, 0, info_y, buf, AO_BLACK, AO_COPY);
189         info_y += INFO_STEP_Y;
190 }
191
192 static void
193 _ao_lco_show_info(void)
194 {
195         info_y = INFO_START_Y;
196         ao_logo_poly(&fb, &show_transform, AO_BLACK, AO_COPY);
197         _ao_lco_info("%s", ao_product);
198         _ao_lco_info("Version: %s", ao_version);
199         _ao_lco_info("Serial: %d", ao_serial_number);
200         _ao_lco_info("Callsign: %s", ao_config.callsign);
201         _ao_lco_info("Frequency: %ld.%03d",
202                      ao_config.frequency / 1000,
203                      (int) (ao_config.frequency % 1000));
204 }
205
206 void
207 ao_lco_show(void)
208 {
209         ao_mutex_get(&ao_lco_display_mutex);
210         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
211         switch (ao_lco_box) {
212         case AO_LCO_LCO_VOLTAGE:
213                 _ao_lco_batt_voltage();
214                 break;
215         case AO_LCO_CONTRAST:
216                 _ao_lco_show_contrast();
217                 break;
218         case AO_LCO_BACKLIGHT:
219                 _ao_lco_show_backlight();
220                 break;
221         case AO_LCO_INFO:
222                 _ao_lco_show_info();
223                 break;
224         default:
225                 if (ao_lco_pad == AO_LCO_PAD_VOLTAGE) {
226                         _ao_lco_show_voltage(ao_pad_query.battery, "Pad battery");
227                 } else {
228                         _ao_lco_show_pad(ao_lco_pad);
229                         _ao_lco_show_box(ao_lco_box);
230                         ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY);
231                 }
232                 break;
233         }
234         ao_st7565_update(&fb);
235         ao_mutex_put(&ao_lco_display_mutex);
236 }
237
238 static void
239 ao_lco_set_select(void)
240 {
241         if (ao_lco_armed) {
242                 ao_led_off(AO_LED_PAD);
243                 ao_led_off(AO_LED_BOX);
244         } else {
245                 switch (ao_lco_select_mode) {
246                 case AO_LCO_SELECT_PAD:
247                         ao_led_off(AO_LED_BOX);
248                         ao_led_on(AO_LED_PAD);
249                         break;
250                 case AO_LCO_SELECT_BOX:
251                         ao_led_off(AO_LED_PAD);
252                         ao_led_on(AO_LED_BOX);
253                         break;
254                 default:
255                         break;
256                 }
257         }
258 }
259
260
261 void
262 ao_lco_set_contrast(int32_t contrast)
263 {
264         ao_st7565_set_brightness((uint8_t) contrast);
265 }
266
267 int32_t
268 ao_lco_get_contrast(void)
269 {
270         return (int32_t) ao_st7565_get_brightness();
271 }
272
273 static uint16_t ao_backlight;
274
275 void
276 ao_lco_set_backlight(int32_t backlight)
277 {
278         ao_backlight = (uint16_t) backlight;
279         ao_pwm_set(AO_LCD_BL_PWM_CHAN, ao_backlight);
280 }
281
282 int32_t
283 ao_lco_get_backlight(void)
284 {
285         return (int32_t) ao_backlight;
286 }
287
288 static struct ao_task   ao_lco_drag_task;
289
290 static void
291 ao_lco_drag_monitor(void)
292 {
293         AO_TICK_TYPE    delay = ~0UL;
294         AO_TICK_TYPE    now;
295
296         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
297         for (;;) {
298                 PRINTD("Drag monitor count %d delay %lu\n", ao_lco_drag_beep_count, (unsigned long) delay);
299                 if (delay == (AO_TICK_TYPE) ~0)
300                         ao_sleep(&ao_lco_drag_beep_count);
301                 else
302                         ao_sleep_for(&ao_lco_drag_beep_count, delay);
303
304                 delay = ~0UL;
305                 now = ao_time();
306                 delay = ao_lco_drag_warn_check(now, delay);
307                 delay = ao_lco_drag_beep_check(now, delay);
308         }
309 }
310
311 static void
312 ao_lco_input(void)
313 {
314         static struct ao_event  event;
315
316         for (;;) {
317                 ao_event_get(&event);
318                 PRINTE("event type %d unit %d value %ld\n",
319                        event.type, event.unit, (long) event.value);
320                 switch (event.type) {
321                 case AO_EVENT_QUADRATURE:
322                         switch (event.unit) {
323                         case AO_QUADRATURE_SELECT:
324                                 if (!ao_lco_armed) {
325                                         switch (ao_lco_select_mode) {
326                                         case AO_LCO_SELECT_PAD:
327                                                 ao_lco_step_pad((int8_t) event.value);
328                                                 break;
329                                         case AO_LCO_SELECT_BOX:
330                                                 ao_lco_step_box((int8_t) event.value);
331                                                 break;
332                                         default:
333                                                 break;
334                                         }
335                                 }
336                                 break;
337                         }
338                         break;
339                 case AO_EVENT_BUTTON:
340                         switch (event.unit) {
341                         case AO_BUTTON_ARM:
342                                 ao_lco_set_armed((uint8_t) event.value);
343                                 ao_lco_set_select();
344                                 break;
345                         case AO_BUTTON_FIRE:
346                                 if (ao_lco_armed)
347                                         ao_lco_set_firing((uint8_t) event.value);
348                                 break;
349                         case AO_BUTTON_DRAG_SELECT:
350                                 if (event.value)
351                                         ao_lco_toggle_drag();
352                                 break;
353                         case AO_BUTTON_DRAG_MODE:
354                                 if (event.value)
355                                         ao_lco_drag_enable();
356                                 else
357                                         ao_lco_drag_disable();
358                                 break;
359                         case AO_BUTTON_ENCODER_SELECT:
360                                 if (event.value) {
361                                         if (!ao_lco_armed) {
362                                                 ao_lco_select_mode = 1 - ao_lco_select_mode;
363                                                 ao_lco_set_select();
364                                         }
365                                 }
366                                 break;
367                         }
368                         break;
369                 }
370         }
371 }
372
373 /*
374  * Light up everything for a second at power on to let the user
375  * visually inspect the system for correct operation
376  */
377 static void
378 ao_lco_display_test(void)
379 {
380         ao_led_on(AO_LEDS_AVAILABLE);
381         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_BLACK, AO_COPY);
382         ao_st7565_update(&fb);
383         ao_delay(AO_MS_TO_TICKS(250));
384         ao_led_off(AO_LEDS_AVAILABLE);
385 }
386
387 static struct ao_task ao_lco_input_task;
388 static struct ao_task ao_lco_monitor_task;
389 static struct ao_task ao_lco_arm_warn_task;
390 static struct ao_task ao_lco_igniter_status_task;
391
392 static int16_t  found_x;
393
394 void
395 ao_lco_search_start(void)
396 {
397         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY);
398         ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY);
399         found_x = FOUND_X;
400 }
401
402 void
403 ao_lco_search_box_check(int16_t box)
404 {
405         if (box > 0)
406                 ao_rect(&fb, SCAN_X, SCAN_Y, box, SCAN_HEIGHT, AO_BLACK, AO_COPY);
407         ao_st7565_update(&fb);
408 }
409
410 void
411 ao_lco_search_box_present(int16_t box)
412 {
413         char    str[8];
414         if (found_x < FOUND_WIDTH)
415         {
416                 snprintf(str, sizeof(str), "%s%02u", found_x ? ", " : "", box);
417                 found_x = ao_text(&fb, &TINY_FONT, found_x, FOUND_Y, str, AO_BLACK, AO_COPY);
418         }
419 }
420
421 void
422 ao_lco_search_done(void)
423 {
424         ao_st7565_update(&fb);
425 }
426
427 static void
428 ao_lco_main(void)
429 {
430         ao_lco_display_test();
431         ao_lco_search();
432         ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
433         ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
434         ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
435         ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race");
436         ao_lco_monitor();
437 }
438
439 #if DEBUG
440 static void
441 ao_lco_set_debug(void)
442 {
443         uint32_t r = ao_cmd_decimal();
444         if (ao_cmd_status == ao_cmd_success){
445                 ao_lco_debug = r & 1;
446                 ao_lco_event_debug = (r & 2) >> 1;
447         }
448 }
449
450 const struct ao_cmds ao_lco_cmds[] = {
451         { ao_lco_set_debug,     "D <0 off, 1 on>\0Debug" },
452         { ao_lco_search,        "s\0Search for pad boxes" },
453         { ao_lco_pretend,       "p\0Pretend there are lots of pad boxes" },
454         { 0, NULL }
455 };
456 #endif
457
458 void
459 ao_lco_init(void)
460 {
461         ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor");
462 #if DEBUG
463         ao_cmd_register(&ao_lco_cmds[0]);
464 #endif
465 }