altos/telelco: Add drag race UI
authorKeith Packard <keithp@keithp.com>
Sun, 30 Aug 2015 00:29:00 +0000 (17:29 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 30 Aug 2015 00:32:54 +0000 (17:32 -0700)
With the unit disarmed, press and hold the fire button for five
seconds to enable drag race mode.

The display will show 'dr' for five seconds and beep five times to
indicate that drag race mode is enabled. The decimal points in the
display will all be displayed as an additional visual aid. Once every
five seconds, it will beep.

With drag race mode enabled, you can select a box/pad pair and press
the 'fire' button to add it to the drag race group. For the current
box, all members of the drag race group will have their continuity
LEDs blink slowly. There will be no indication of continuity in this
mode; you'll want to check that before enabling drag race mode. If you
want to de-select a member of the group, just press the fire button
again. Each time you push the fire button, it will beep out the pad
number added or removed.

Arm the box and you will not be able to add or remove members from the
drag race group. Firing will simultaneously fire all members of the
drag race group.

To disable drag race mode, press and hold the fire button for two
seconds. It will beep twice and turn off the decimal points in the display.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/drivers/ao_lco.c

index 464e05ab363c1bf115a7321cf0859b5d976eccb1..8180c49de1137dc69af68f59702d9aa571e3cd1f 100644 (file)
@@ -36,29 +36,42 @@ static uint8_t      ao_lco_debug;
 #define AO_LCO_BOX_DIGIT_1     1
 #define AO_LCO_BOX_DIGIT_10    2
 
+#define AO_LCO_DRAG_RACE_START_TIME    AO_SEC_TO_TICKS(5)
+#define AO_LCO_DRAG_RACE_STOP_TIME     AO_SEC_TO_TICKS(2)
+
 static uint8_t ao_lco_min_box, ao_lco_max_box;
 static uint8_t ao_lco_selected[AO_PAD_MAX_BOXES];
-static uint8_t ao_lco_armed;
-static uint8_t ao_lco_firing;
 static uint8_t ao_lco_valid[AO_PAD_MAX_BOXES];
 static uint8_t ao_lco_channels[AO_PAD_MAX_BOXES];
 static uint16_t        ao_lco_tick_offset[AO_PAD_MAX_BOXES];
 
+/* UI values */
+static uint8_t ao_lco_armed;
+static uint8_t ao_lco_firing;
+static uint16_t        ao_lco_fire_tick;
+static uint8_t ao_lco_fire_down;
+static uint8_t ao_lco_drag_race;
 static uint8_t ao_lco_pad;
 static uint8_t ao_lco_box;
 static struct ao_pad_query     ao_pad_query;
 
+static uint8_t ao_lco_display_mutex;
+
 static void
 ao_lco_set_pad(uint8_t pad)
 {
-       ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad);
+       ao_mutex_get(&ao_lco_display_mutex);
+       ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad | (ao_lco_drag_race << 4));
+       ao_mutex_put(&ao_lco_display_mutex);
 }
 
 static void
 ao_lco_set_box(uint8_t box)
 {
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10);
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10);
+       ao_mutex_get(&ao_lco_display_mutex);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10 | (ao_lco_drag_race << 4));
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10 | (ao_lco_drag_race << 4));
+       ao_mutex_put(&ao_lco_display_mutex);
 }
 
 static void
@@ -69,9 +82,11 @@ ao_lco_set_voltage(uint16_t decivolts)
        tenths = decivolts % 10;
        ones = (decivolts / 10) % 10;
        tens = (decivolts / 100) % 10;
+       ao_mutex_get(&ao_lco_display_mutex);
        ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths);
        ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10);
        ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens);
+       ao_mutex_put(&ao_lco_display_mutex);
 }
 
 static void
@@ -85,6 +100,33 @@ ao_lco_set_display(void)
        }
 }
 
+#define SEVEN_SEGMENT_d                ((0 << 0) |     \
+                                (0 << 1) |     \
+                                (1 << 2) |     \
+                                (1 << 3) |     \
+                                (1 << 4) |     \
+                                (1 << 5) |     \
+                                (1 << 6))
+
+
+#define SEVEN_SEGMENT_r                ((0 << 0) |     \
+                                (0 << 1) |     \
+                                (0 << 2) |     \
+                                (1 << 3) |     \
+                                (1 << 4) |     \
+                                (0 << 5) |     \
+                                (0 << 6))
+
+static void
+ao_lco_set_display_drag(void)
+{
+       ao_mutex_get(&ao_lco_display_mutex);
+       ao_seven_segment_direct(AO_LCO_BOX_DIGIT_10, SEVEN_SEGMENT_d | 0x80);
+       ao_seven_segment_direct(AO_LCO_BOX_DIGIT_1, SEVEN_SEGMENT_r | 0x80);
+       ao_mutex_put(&ao_lco_display_mutex);
+       ao_lco_set_pad(ao_lco_pad);
+}
+
 #define MASK_SIZE(n)   (((n) + 7) >> 3)
 #define MASK_ID(n)     ((n) >> 3)
 #define MASK_SHIFT(n)  ((n) & 7)
@@ -123,6 +165,188 @@ ao_lco_pad_first(uint8_t box)
        return 0;
 }
 
+static struct ao_task  ao_lco_drag_task;
+static uint8_t         ao_lco_drag_active;
+static uint8_t         ao_lco_drag_beep_count;
+static uint8_t         ao_lco_drag_beep_on;
+static uint16_t                ao_lco_drag_beep_time;
+static uint16_t                ao_lco_drag_warn_time;
+static uint16_t                ao_lco_drag_display_time;
+static uint8_t         ao_lco_drag_display_on;
+
+#define AO_LCO_DRAG_BEEP_TIME  AO_MS_TO_TICKS(50)
+#define AO_LCO_DRAG_WARN_TIME  AO_SEC_TO_TICKS(5)
+
+static void
+ao_lco_drag_beep_start(void)
+{
+       ao_beep(AO_BEEP_HIGH);
+       PRINTD("beep start\n");
+       ao_lco_drag_beep_on = 1;
+       ao_lco_drag_beep_time = ao_time() + AO_LCO_DRAG_BEEP_TIME;
+}
+
+static void
+ao_lco_drag_beep_stop(void)
+{
+       ao_beep(0);
+       PRINTD("beep stop\n");
+       ao_lco_drag_beep_on = 0;
+       if (ao_lco_drag_beep_count) {
+               --ao_lco_drag_beep_count;
+               if (ao_lco_drag_beep_count)
+                       ao_lco_drag_beep_time = ao_time() + AO_LCO_DRAG_BEEP_TIME;
+       }
+}
+
+static void
+ao_lco_drag_beep(uint8_t beeps)
+{
+       PRINTD("beep %d\n", beeps);
+       if (!ao_lco_drag_beep_count)
+               ao_lco_drag_beep_start();
+       ao_lco_drag_beep_count += beeps;
+}
+
+static uint16_t
+ao_lco_drag_beep_check(uint16_t now, uint16_t delay)
+{
+       PRINTD("beep check count %d delta %d\n",
+              ao_lco_drag_beep_count,
+              (int16_t) (now - ao_lco_drag_beep_time));
+       if (ao_lco_drag_beep_count) {
+               if ((int16_t) (now - ao_lco_drag_beep_time) >= 0) {
+                       if (ao_lco_drag_beep_on)
+                               ao_lco_drag_beep_stop();
+                       else
+                               ao_lco_drag_beep_start();
+               }
+       }
+
+       if (ao_lco_drag_beep_count) {
+               if (delay > AO_LCO_DRAG_BEEP_TIME)
+                       delay = AO_LCO_DRAG_BEEP_TIME;
+       }
+       return delay;
+}
+
+static void
+ao_lco_drag_enable(void)
+{
+       PRINTD("Drag enable\n");
+       ao_lco_drag_race = 1;
+       memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+       ao_lco_drag_beep(5);
+       ao_lco_set_display_drag();
+       ao_lco_fire_down = 0;
+       ao_lco_drag_display_on = 1;
+       ao_lco_drag_display_time = ao_time() + AO_SEC_TO_TICKS(5);
+}
+
+static void
+ao_lco_drag_disable(void)
+{
+       PRINTD("Drag disable\n");
+       ao_lco_drag_race = 0;
+       memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+       ao_lco_drag_beep(2);
+       ao_lco_set_display();
+       ao_lco_fire_down = 0;
+}
+
+static uint16_t
+ao_lco_drag_button_check(uint16_t now, uint16_t delay)
+{
+       uint16_t        button_delay = ~0;
+
+       /*
+        * Check to see if the button has been held down long enough
+        * to switch in/out of drag race mode
+        */
+       if (ao_lco_fire_down) {
+               if (ao_lco_drag_race) {
+                       if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_STOP_TIME)
+                               ao_lco_drag_disable();
+                       else
+                               button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_STOP_TIME - now;
+               } else {
+                       if ((int16_t) (now - ao_lco_fire_tick) >= AO_LCO_DRAG_RACE_START_TIME)
+                               ao_lco_drag_enable();
+                       else
+                               button_delay = ao_lco_fire_tick + AO_LCO_DRAG_RACE_START_TIME - now;
+               }
+               if (delay > button_delay)
+                       delay = button_delay;
+       }
+       return delay;
+}
+
+static uint16_t
+ao_lco_drag_warn_check(uint16_t now, uint16_t delay)
+{
+       uint16_t        warn_delay = ~0;
+
+       if (ao_lco_drag_race) {
+               if ((int16_t) (now - ao_lco_drag_warn_time) >= 0) {
+                       ao_lco_drag_beep(1);
+                       ao_lco_drag_warn_time = now + AO_LCO_DRAG_WARN_TIME;
+               }
+               warn_delay = ao_lco_drag_warn_time - now;
+       }
+       if (delay > warn_delay)
+               delay = warn_delay;
+       return delay;
+}
+
+static uint16_t
+ao_lco_drag_display_check(uint16_t now, uint16_t delay)
+{
+       uint16_t        display_delay;
+
+       if (ao_lco_drag_display_on) {
+               if ((int16_t) (now - ao_lco_drag_display_time) >= 0) {
+                       ao_lco_drag_display_on = 0;
+                       ao_lco_set_display();
+               } else {
+                       display_delay = ao_lco_drag_display_time - now;
+                       if (delay > display_delay)
+                               delay = display_delay;
+               }
+       }
+       return delay;
+}
+
+static void
+ao_lco_drag_monitor(void)
+{
+       uint16_t        delay = ~0;
+       uint16_t        now;
+
+       for (;;) {
+               PRINTD("Drag monitor active %d delay %d\n", ao_lco_drag_active, delay);
+               if (delay == (uint16_t) ~0)
+                       ao_sleep(&ao_lco_drag_active);
+               else
+                       ao_sleep_for(&ao_lco_drag_active, delay);
+
+               delay = ~0;
+               if (!ao_lco_drag_active)
+                       continue;
+
+               now = ao_time();
+               delay = ao_lco_drag_button_check(now, delay);
+               delay = ao_lco_drag_warn_check(now, delay);
+               delay = ao_lco_drag_beep_check(now, delay);
+               delay = ao_lco_drag_display_check(now, delay);
+
+               /* check to see if there's anything left to do here */
+               if (!ao_lco_fire_down && !ao_lco_drag_race && !ao_lco_drag_beep_count) {
+                       delay = ~0;
+                       ao_lco_drag_active = 0;
+               }
+       }
+}
+
 static void
 ao_lco_input(void)
 {
@@ -184,17 +408,49 @@ ao_lco_input(void)
                        case AO_BUTTON_ARM:
                                ao_lco_armed = event.value;
                                PRINTD("Armed %d\n", ao_lco_armed);
-                               if (ao_lco_pad != 0) {
-                                       memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
-                                       ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1));
-                                       ao_wakeup(&ao_lco_armed);
+                               if (ao_lco_armed) {
+                                       if (ao_lco_drag_race) {
+                                               uint8_t box;
+
+                                               for (box = ao_lco_min_box; box <= ao_lco_max_box; box++) {
+                                                       if (ao_lco_selected[box]) {
+                                                               ao_wakeup(&ao_lco_armed);
+                                                               break;
+                                                       }
+                                               }
+                                       } else if (ao_lco_pad != 0) {
+                                               memset(ao_lco_selected, 0, sizeof (ao_lco_selected));
+                                               ao_lco_selected[ao_lco_box] = (1 << (ao_lco_pad - 1));
+                                       }
                                }
+                               ao_wakeup(&ao_lco_armed);
                                break;
                        case AO_BUTTON_FIRE:
                                if (ao_lco_armed) {
+                                       ao_lco_fire_down = 0;
                                        ao_lco_firing = event.value;
                                        PRINTD("Firing %d\n", ao_lco_firing);
                                        ao_wakeup(&ao_lco_armed);
+                               } else {
+                                       if (event.value) {
+                                               ao_lco_fire_down = 1;
+                                               ao_lco_fire_tick = ao_time();
+                                               ao_lco_drag_active = 1;
+                                               /* ao_lco_fire_down will already be off if we just enabled drag mode */
+                                               if (ao_lco_fire_down && ao_lco_drag_race) {
+                                                       if (ao_lco_pad != 0) {
+                                                               ao_lco_selected[ao_lco_box] ^= (1 << (ao_lco_pad - 1));
+                                                               PRINTD("Toggle box %d pad %d (pads now %x) to drag race\n",
+                                                                      ao_lco_pad, ao_lco_box, ao_lco_selected[ao_lco_box]);
+                                                               ao_lco_drag_beep(ao_lco_pad);
+                                                       }
+                                               }
+                                               ao_wakeup(&ao_lco_drag_active);
+                                       } else {
+                                               ao_lco_fire_down = 0;
+                                               if (ao_lco_drag_active)
+                                                       ao_wakeup(&ao_lco_drag_active);
+                                       }
                                }
                                break;
                        }
@@ -241,7 +497,7 @@ ao_lco_get_channels(uint8_t box, struct ao_pad_query *query)
                ao_lco_valid[box] = 1;
        } else
                ao_lco_valid[box] = 0;
-       PRINTD("ao_lco_get_channels(%d) valid %d\n", box, ao_lco_valid[box]);
+       PRINTD("ao_lco_get_channels(%d) rssi %d valid %d ret %d\n", box, ao_radio_cmac_rssi, ao_lco_valid[box], r);
        ao_wakeup(&ao_pad_query);
        return ao_lco_valid[box];
 }
@@ -323,6 +579,7 @@ static void
 ao_lco_igniter_status(void)
 {
        uint8_t         c;
+       uint8_t         t = 0;
 
        for (;;) {
                ao_sleep(&ao_pad_query);
@@ -343,18 +600,27 @@ ao_lco_igniter_status(void)
                        ao_led_on(AO_LED_REMOTE_ARM);
                else
                        ao_led_off(AO_LED_REMOTE_ARM);
+
                for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) {
                        uint8_t status;
 
-                       if (ao_pad_query.channels & (1 << c))
-                               status = ao_pad_query.igniter_status[c];
-                       else
-                               status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
-                       if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
-                               ao_led_on(continuity_led[c]);
-                       else
-                               ao_led_off(continuity_led[c]);
+                       if (ao_lco_drag_race) {
+                               if (ao_lco_selected[ao_lco_box] & (1 << c) && t)
+                                       ao_led_on(continuity_led[c]);
+                               else
+                                       ao_led_off(continuity_led[c]);
+                       } else {
+                               if (ao_pad_query.channels & (1 << c))
+                                       status = ao_pad_query.igniter_status[c];
+                               else
+                                       status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
+                               if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
+                                       ao_led_on(continuity_led[c]);
+                               else
+                                       ao_led_off(continuity_led[c]);
+                       }
                }
+               t = 1-t;
        }
 }
 
@@ -379,12 +645,12 @@ ao_lco_monitor(void)
 {
        uint16_t                delay;
        uint8_t                 box;
-       struct ao_pad_query     pad_query;
 
        ao_lco_search();
        ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
        ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
        ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
+       ao_add_task(&ao_lco_drag_task, ao_lco_drag_monitor, "drag race");
        for (;;) {
                PRINTD("monitor armed %d firing %d\n",
                       ao_lco_armed, ao_lco_firing);
@@ -392,19 +658,17 @@ ao_lco_monitor(void)
                if (ao_lco_armed && ao_lco_firing) {
                        ao_lco_ignite();
                } else {
+                       ao_lco_update();
                        if (ao_lco_armed) {
-                               for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+                               for (box = ao_lco_min_box; box <= ao_lco_max_box; box++) {
                                        if (ao_lco_selected[box]) {
                                                PRINTD("Arming box %d pads %x\n",
                                                       box, ao_lco_selected[box]);
-                                               if (!ao_lco_valid[box])
-                                                       ao_lco_get_channels(box, &pad_query);
                                                if (ao_lco_valid[box])
                                                        ao_lco_arm(box, ao_lco_selected[box], ao_lco_tick_offset[ao_lco_box]);
                                        }
                                }
                        }
-                       ao_lco_update();
                }
                if (ao_lco_armed && ao_lco_firing)
                        delay = AO_MS_TO_TICKS(100);