From 73dd2e2c73c42f6ce949b4aa7992f63610962c37 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Aug 2015 10:50:30 -0700 Subject: altoslib: Add companion telemetry data support This got lost in the great telemetry rewrite Signed-off-by: Keith Packard --- altoslib/AltosState.java | 5 ---- altoslib/AltosTelemetryCompanion.java | 54 +++++++++++++++++++++++++++++++++++ altoslib/AltosTelemetryStandard.java | 2 -- altoslib/Makefile.am | 1 + 4 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 altoslib/AltosTelemetryCompanion.java diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index 4edae54a..cf4fb9b0 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -1358,11 +1358,6 @@ public class AltosState implements Cloneable, Serializable { } } - public void make_companion (int nchannels) { - if (companion == null) - companion = new AltosCompanion(nchannels); - } - public void set_companion(AltosCompanion companion) { this.companion = companion; } diff --git a/altoslib/AltosTelemetryCompanion.java b/altoslib/AltosTelemetryCompanion.java new file mode 100644 index 00000000..96125aca --- /dev/null +++ b/altoslib/AltosTelemetryCompanion.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2015 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +public class AltosTelemetryCompanion extends AltosTelemetryStandard { + AltosCompanion companion; + + static final public int max_channels = 12; + + public AltosTelemetryCompanion(int[] bytes) { + super(bytes); + + int channels = uint8(7); + + if (channels > max_channels) + channels = max_channels; + + companion = new AltosCompanion(channels); + + companion.tick = tick; + companion.board_id = uint8(5); + companion.update_period = uint8(6); + + if (channels == 0) + companion.companion_data = null; + else { + companion.companion_data = new int[channels]; + + for (int i = 0; i < channels; i++) + companion.companion_data[i] = uint16(8 + i * 2); + } + } + + public void update_state(AltosState state) { + super.update_state(state); + + state.set_companion(companion); + } +} diff --git a/altoslib/AltosTelemetryStandard.java b/altoslib/AltosTelemetryStandard.java index 27561826..c2a5ab3c 100644 --- a/altoslib/AltosTelemetryStandard.java +++ b/altoslib/AltosTelemetryStandard.java @@ -68,11 +68,9 @@ public abstract class AltosTelemetryStandard extends AltosTelemetry { case packet_type_satellite: telem = new AltosTelemetrySatellite(bytes); break; -/* case packet_type_companion: telem = new AltosTelemetryCompanion(bytes); break; -*/ case packet_type_mega_sensor: telem = new AltosTelemetryMegaSensor(bytes); break; diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index a6b178fa..d1f8f265 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -96,6 +96,7 @@ altoslib_JAVA = \ AltosStateUpdate.java \ AltosTelemetry.java \ AltosTelemetryConfiguration.java \ + AltosTelemetryCompanion.java \ AltosTelemetryFile.java \ AltosTelemetryIterable.java \ AltosTelemetryLegacy.java \ -- cgit v1.2.3 From b4064bf63bb95c58d74869f4ff3e440370d64692 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Aug 2015 10:51:11 -0700 Subject: ao-telem: Add companion packet telemetry data printing Signed-off-by: Keith Packard --- ao-tools/ao-telem/ao-telem.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ao-tools/ao-telem/ao-telem.c b/ao-tools/ao-telem/ao-telem.c index da873838..05a69542 100644 --- a/ao-tools/ao-telem/ao-telem.c +++ b/ao-tools/ao-telem/ao-telem.c @@ -173,6 +173,15 @@ main (int argc, char **argv) telem.mega_sensor.mag_y, telem.mega_sensor.mag_z); break; + case AO_TELEMETRY_COMPANION: + printf("board_id %3d update_period %3d channels %2d", + telem.companion.board_id, + telem.companion.update_period, + telem.companion.channels); + for (c = 0; c < telem.companion.channels; c++) + printf(" %6d", telem.companion.companion_data[c]); + printf("\n"); + break; case AO_TELEMETRY_MEGA_DATA: printf ("state %1d v_batt %5d v_pyro %5d ", telem.mega_data.state, @@ -182,7 +191,7 @@ main (int argc, char **argv) printf ("s%1d %5d ", c, telem.mega_data.sense[c] | (telem.mega_data.sense[c] << 8)); - + printf ("ground_pres %5d ground_accel %5d accel_plus %5d accel_minus %5d ", telem.mega_data.ground_pres, telem.mega_data.ground_accel, -- cgit v1.2.3 From f491eec1b950e4ad35a535db254a27a3dd2ad430 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Aug 2015 23:02:04 -0700 Subject: altos: Add HAS_COMPANION to new boards with companion connectors Thanks much to Rob Derstadt for discovering this bug! TeleMetrum v2.0, TeleMega, EasyMega, TeleBalloon v2.0 all have companion connectors and yet HAS_COMPANION was not defined. This disabled companion telemetry packets on these products, but otherwise didn't have any effect. Signed-off-by: Keith Packard --- src/drivers/ao_companion.c | 4 ++++ src/easymega-v1.0/ao_pins.h | 1 + src/teleballoon-v2.0/ao_pins.h | 1 + src/telemega-v0.1/ao_pins.h | 1 + src/telemega-v1.0/ao_pins.h | 1 + src/telemetrum-v2.0/ao_pins.h | 1 + 6 files changed, 9 insertions(+) diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c index 7e02939b..5f07e8b0 100644 --- a/src/drivers/ao_companion.c +++ b/src/drivers/ao_companion.c @@ -23,6 +23,10 @@ #define ao_spi_fast(b) #endif +#if !HAS_COMPANION +#error HAS_COMPANION not set in ao_companion.c +#endif + #define COMPANION_SELECT() do { \ ao_spi_get_bit(AO_COMPANION_CS_PORT, \ AO_COMPANION_CS_PIN, \ diff --git a/src/easymega-v1.0/ao_pins.h b/src/easymega-v1.0/ao_pins.h index d6490ba5..a5e55638 100644 --- a/src/easymega-v1.0/ao_pins.h +++ b/src/easymega-v1.0/ao_pins.h @@ -78,6 +78,7 @@ #define HAS_RADIO 0 #define HAS_TELEMETRY 0 #define HAS_APRS 0 +#define HAS_COMPANION 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ diff --git a/src/teleballoon-v2.0/ao_pins.h b/src/teleballoon-v2.0/ao_pins.h index a369070f..b62b5580 100644 --- a/src/teleballoon-v2.0/ao_pins.h +++ b/src/teleballoon-v2.0/ao_pins.h @@ -75,6 +75,7 @@ #define HAS_RADIO 1 #define HAS_TELEMETRY 1 #define HAS_APRS 1 +#define HAS_COMPANION 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ diff --git a/src/telemega-v0.1/ao_pins.h b/src/telemega-v0.1/ao_pins.h index 2616e906..7ccc6085 100644 --- a/src/telemega-v0.1/ao_pins.h +++ b/src/telemega-v0.1/ao_pins.h @@ -79,6 +79,7 @@ #define HAS_RADIO 1 #define HAS_TELEMETRY 1 #define HAS_APRS 1 +#define HAS_COMPANION 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ diff --git a/src/telemega-v1.0/ao_pins.h b/src/telemega-v1.0/ao_pins.h index 77b753d1..664546c2 100644 --- a/src/telemega-v1.0/ao_pins.h +++ b/src/telemega-v1.0/ao_pins.h @@ -79,6 +79,7 @@ #define HAS_RADIO 1 #define HAS_TELEMETRY 1 #define HAS_APRS 1 +#define HAS_COMPANION 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ diff --git a/src/telemetrum-v2.0/ao_pins.h b/src/telemetrum-v2.0/ao_pins.h index a9a4b243..fbb38df2 100644 --- a/src/telemetrum-v2.0/ao_pins.h +++ b/src/telemetrum-v2.0/ao_pins.h @@ -75,6 +75,7 @@ #define HAS_RADIO 1 #define HAS_TELEMETRY 1 #define HAS_APRS 1 +#define HAS_COMPANION 1 #define HAS_SPI_1 1 #define SPI_1_PA5_PA6_PA7 1 /* Barometer */ -- cgit v1.2.3 From 368f87918547f89e7eb2a92990621e75e07a3b25 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 29 Aug 2015 13:18:46 -0700 Subject: altos/telefire: No reply for ARMED. Ignore time for FIRE. This will let us do drag races by letting the LCO arm multiple boxes and fire them all with a single command. Signed-off-by: Keith Packard --- src/drivers/ao_pad.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index ffe46c68..419ea8d3 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -288,8 +288,7 @@ ao_pad(void) PRINTD ("armed\n"); ao_pad_armed = command.channels; ao_pad_arm_time = ao_time(); - - /* fall through ... */ + break; case AO_LAUNCH_QUERY: if (command.box != ao_pad_box) { @@ -320,13 +319,6 @@ ao_pad(void) ao_pad_arm_time, ao_time()); break; } - time_difference = command.tick - ao_time(); - if (time_difference < 0) - time_difference = -time_difference; - if (time_difference > 10) { - PRINTD ("time different too large %d\n", time_difference); - break; - } PRINTD ("ignite\n"); ao_pad_ignite = ao_pad_armed; ao_pad_arm_time = ao_time(); -- cgit v1.2.3 From 2839796ca5ace5f0c79643afc1a868893246b621 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 29 Aug 2015 13:20:16 -0700 Subject: altos: Provide direct segment driving interface for LCD displays This exposes a simple bit-mask for setting the seven segments instead of just allowing hex-decimal values. Signed-off-by: Keith Packard --- src/drivers/ao_seven_segment.c | 17 ++++++++++++----- src/drivers/ao_seven_segment.h | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/drivers/ao_seven_segment.c b/src/drivers/ao_seven_segment.c index 961fbb84..f1339ee5 100644 --- a/src/drivers/ao_seven_segment.c +++ b/src/drivers/ao_seven_segment.c @@ -168,10 +168,20 @@ static const uint8_t ao_segments[] = { (0 << 6), /* F */ }; + void -ao_seven_segment_set(uint8_t digit, uint8_t value) +ao_seven_segment_direct(uint8_t digit, uint8_t segments) { uint8_t s; + + for (s = 0; s <= 7; s++) + ao_lcd_set(digit, s, !!(segments & (1 << s))); + ao_lcd_flush(); +} + +void +ao_seven_segment_set(uint8_t digit, uint8_t value) +{ uint8_t segments; if (value == AO_SEVEN_SEGMENT_CLEAR) @@ -183,10 +193,7 @@ ao_seven_segment_set(uint8_t digit, uint8_t value) if (value & 0x10) segments |= (1 << 7); } - - for (s = 0; s <= 7; s++) - ao_lcd_set(digit, s, !!(segments & (1 << s))); - ao_lcd_flush(); + ao_seven_segment_direct(digit, segments); } void diff --git a/src/drivers/ao_seven_segment.h b/src/drivers/ao_seven_segment.h index 5b29deaf..f997f3b5 100644 --- a/src/drivers/ao_seven_segment.h +++ b/src/drivers/ao_seven_segment.h @@ -22,6 +22,9 @@ #define AO_SEVEN_SEGMENT_CLEAR 0xff +void +ao_seven_segment_direct(uint8_t digit, uint8_t segments); + void ao_seven_segment_set(uint8_t digit, uint8_t value); -- cgit v1.2.3 From 55c1be449ef7ce389a3d94686051d272c858bee4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 29 Aug 2015 13:21:19 -0700 Subject: altos/telelco: Infrastructure changes for drag racing This makes the lco management code support arming multiple pads and firing all of them at once. The UI code does not expose this yet. Signed-off-by: Keith Packard --- src/drivers/ao_lco.c | 119 ++++++++++++++++++++++++---------------------- src/drivers/ao_lco_cmd.c | 2 +- src/drivers/ao_lco_func.c | 11 ++--- src/drivers/ao_lco_func.h | 2 +- 4 files changed, 68 insertions(+), 66 deletions(-) diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c index b8698a80..464e05ab 100644 --- a/src/drivers/ao_lco.c +++ b/src/drivers/ao_lco.c @@ -37,14 +37,15 @@ static uint8_t ao_lco_debug; #define AO_LCO_BOX_DIGIT_10 2 static uint8_t ao_lco_min_box, ao_lco_max_box; -static uint8_t ao_lco_pad; -static uint8_t ao_lco_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; -static uint8_t ao_lco_got_channels; -static uint16_t ao_lco_tick_offset; +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]; +static uint8_t ao_lco_pad; +static uint8_t ao_lco_box; static struct ao_pad_query ao_pad_query; static void @@ -99,25 +100,25 @@ ao_lco_box_present(uint8_t box) } static uint8_t -ao_lco_pad_present(uint8_t pad) +ao_lco_pad_present(uint8_t box, uint8_t pad) { - if (!ao_lco_got_channels || !ao_pad_query.channels) - return pad == 0; /* voltage measurement is always valid */ if (pad == 0) return 1; + if (!ao_lco_channels[box]) + return 0; if (pad > AO_PAD_MAX_CHANNELS) return 0; - return (ao_pad_query.channels >> (pad - 1)) & 1; + return (ao_lco_channels[box] >> (pad - 1)) & 1; } static uint8_t -ao_lco_pad_first(void) +ao_lco_pad_first(uint8_t box) { uint8_t pad; for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++) - if (ao_lco_pad_present(pad)) + if (ao_lco_pad_present(box, pad)) return pad; return 0; } @@ -148,7 +149,7 @@ ao_lco_input(void) new_pad = AO_PAD_MAX_CHANNELS; if (new_pad == ao_lco_pad) break; - } while (!ao_lco_pad_present(new_pad)); + } while (!ao_lco_pad_present(ao_lco_box, new_pad)); if (new_pad != ao_lco_pad) { ao_lco_pad = new_pad; ao_lco_set_display(); @@ -171,7 +172,7 @@ ao_lco_input(void) if (ao_lco_box != new_box) { ao_lco_box = new_box; ao_lco_pad = 1; - ao_lco_got_channels = 0; + ao_lco_channels[ao_lco_box] = 0; ao_lco_set_display(); } } @@ -183,7 +184,11 @@ ao_lco_input(void) case AO_BUTTON_ARM: ao_lco_armed = event.value; PRINTD("Armed %d\n", ao_lco_armed); - ao_wakeup(&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); + } break; case AO_BUTTON_FIRE: if (ao_lco_armed) { @@ -225,37 +230,36 @@ static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = { #endif }; -static void -ao_lco_update(void) +static uint8_t +ao_lco_get_channels(uint8_t box, struct ao_pad_query *query) { int8_t r; - uint8_t c; - r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset); + r = ao_lco_query(box, query, &ao_lco_tick_offset[box]); if (r == AO_RADIO_CMAC_OK) { - c = ao_lco_got_channels; - ao_lco_got_channels = 1; - ao_lco_valid = 1; - if (!c) { + ao_lco_channels[box] = query->channels; + 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]); + ao_wakeup(&ao_pad_query); + return ao_lco_valid[box]; +} + +static void +ao_lco_update(void) +{ + uint8_t previous_valid = ao_lco_valid[ao_lco_box]; + + if (ao_lco_get_channels(ao_lco_box, &ao_pad_query)) { + if (!previous_valid) { if (ao_lco_pad != 0) - ao_lco_pad = ao_lco_pad_first(); + ao_lco_pad = ao_lco_pad_first(ao_lco_box); ao_lco_set_display(); } if (ao_lco_pad == 0) ao_lco_set_display(); - } else - ao_lco_valid = 0; - -#if 0 - PRINTD("lco_query success arm_status %d i0 %d i1 %d i2 %d i3 %d\n", - query.arm_status, - query.igniter_status[0], - query.igniter_status[1], - query.igniter_status[2], - query.igniter_status[3]); -#endif - PRINTD("ao_lco_update valid %d\n", ao_lco_valid); - ao_wakeup(&ao_pad_query); + } } static void @@ -309,8 +313,8 @@ ao_lco_search(void) ao_lco_box = ao_lco_min_box; else ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0; - ao_lco_valid = 0; - ao_lco_got_channels = 0; + memset(ao_lco_valid, 0, sizeof (ao_lco_valid)); + memset(ao_lco_channels, 0, sizeof (ao_lco_channels)); ao_lco_pad = 1; ao_lco_set_display(); } @@ -322,8 +326,8 @@ ao_lco_igniter_status(void) for (;;) { ao_sleep(&ao_pad_query); - PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid); - if (!ao_lco_valid) { + PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid[ao_lco_box]); + if (!ao_lco_valid[ao_lco_box]) { ao_led_on(AO_LED_RED); ao_led_off(AO_LED_GREEN|AO_LED_AMBER); continue; @@ -374,33 +378,32 @@ static void 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"); for (;;) { - PRINTD("monitor armed %d firing %d offset %d\n", - ao_lco_armed, ao_lco_firing, ao_lco_tick_offset); + PRINTD("monitor armed %d firing %d\n", + ao_lco_armed, ao_lco_firing); if (ao_lco_armed && ao_lco_firing) { - PRINTD("Firing box %d pad %d: valid %d\n", - ao_lco_box, ao_lco_pad, ao_lco_valid); - if (!ao_lco_valid) - ao_lco_update(); - if (ao_lco_valid && ao_lco_pad) - ao_lco_ignite(ao_lco_box, 1 << (ao_lco_pad - 1), ao_lco_tick_offset); - } else if (ao_lco_armed) { - PRINTD("Arming box %d pad %d\n", - ao_lco_box, ao_lco_pad); - if (!ao_lco_valid) - ao_lco_update(); - if (ao_lco_pad) { - ao_lco_arm(ao_lco_box, 1 << (ao_lco_pad - 1), ao_lco_tick_offset); - ao_delay(AO_MS_TO_TICKS(30)); - ao_lco_update(); - } + ao_lco_ignite(); } else { + if (ao_lco_armed) { + for (box = 0; box < AO_PAD_MAX_BOXES; 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) diff --git a/src/drivers/ao_lco_cmd.c b/src/drivers/ao_lco_cmd.c index acbf589a..6a365687 100644 --- a/src/drivers/ao_lco_cmd.c +++ b/src/drivers/ao_lco_cmd.c @@ -62,7 +62,7 @@ lco_arm(void) static void lco_ignite(void) { - ao_lco_ignite(lco_box, lco_channels, tick_offset); + ao_lco_ignite(); } static void diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c index 32c00068..08d45467 100644 --- a/src/drivers/ao_lco_func.c +++ b/src/drivers/ao_lco_func.c @@ -44,7 +44,7 @@ ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset) } #endif ao_mutex_get(&ao_lco_mutex); - command.tick = ao_time() - *tick_offset; + command.tick = ao_time(); command.box = box; command.cmd = AO_LAUNCH_QUERY; command.channels = 0; @@ -70,14 +70,13 @@ ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset) } void -ao_lco_ignite(uint16_t box, uint8_t channels, uint16_t tick_offset) +ao_lco_ignite(void) { ao_mutex_get(&ao_lco_mutex); - command.tick = ao_time() - tick_offset; - command.box = box; + command.tick = 0; + command.box = 0; command.cmd = AO_LAUNCH_FIRE; - command.channels = channels; + command.channels = 0; ao_radio_cmac_send(&command, sizeof (command)); ao_mutex_put(&ao_lco_mutex); } - diff --git a/src/drivers/ao_lco_func.h b/src/drivers/ao_lco_func.h index dccf602a..42754352 100644 --- a/src/drivers/ao_lco_func.h +++ b/src/drivers/ao_lco_func.h @@ -27,6 +27,6 @@ void ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset); void -ao_lco_ignite(uint16_t box, uint8_t channels, uint16_t tick_offset); +ao_lco_ignite(void); #endif /* _AO_LCO_FUNC_H_ */ -- cgit v1.2.3 From dda3f459eaff8d4e41cb44584c8ef77b8e2b3b1c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 29 Aug 2015 17:29:00 -0700 Subject: altos/telelco: Add drag race UI 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 --- src/drivers/ao_lco.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 287 insertions(+), 23 deletions(-) diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c index 464e05ab..8180c49d 100644 --- a/src/drivers/ao_lco.c +++ b/src/drivers/ao_lco.c @@ -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); -- cgit v1.2.3