diff options
| -rw-r--r-- | spaghetti-monster/anduril/anduril.c | 116 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-events.c | 96 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-events.h | 607 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-pcint.c | 4 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-states.c | 4 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-states.h | 6 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-wdt.c | 38 |
7 files changed, 253 insertions, 618 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index df478ee..de4f996 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -226,45 +226,45 @@ typedef enum { // FSM states -uint8_t off_state(EventPtr event, uint16_t arg); +uint8_t off_state(Event event, uint16_t arg); // simple numeric entry config menu -uint8_t config_state_base(EventPtr event, uint16_t arg, +uint8_t config_state_base(Event event, uint16_t arg, uint8_t num_config_steps, void (*savefunc)()); #define MAX_CONFIG_VALUES 3 uint8_t config_state_values[MAX_CONFIG_VALUES]; // ramping mode and its related config mode -uint8_t steady_state(EventPtr event, uint16_t arg); -uint8_t ramp_config_state(EventPtr event, uint16_t arg); +uint8_t steady_state(Event event, uint16_t arg); +uint8_t ramp_config_state(Event event, uint16_t arg); // party and tactical strobes #ifdef USE_STROBE_STATE -uint8_t strobe_state(EventPtr event, uint16_t arg); +uint8_t strobe_state(Event event, uint16_t arg); #endif #ifdef USE_BATTCHECK -uint8_t battcheck_state(EventPtr event, uint16_t arg); +uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(EventPtr event, uint16_t arg); -uint8_t thermal_config_state(EventPtr event, uint16_t arg); +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); #endif // 1-hour ramp down from low, then automatic off -uint8_t goodnight_state(EventPtr event, uint16_t arg); +uint8_t goodnight_state(Event event, uint16_t arg); // beacon mode and its related config mode -uint8_t beacon_state(EventPtr event, uint16_t arg); -uint8_t beacon_config_state(EventPtr event, uint16_t arg); +uint8_t beacon_state(Event event, uint16_t arg); +uint8_t beacon_config_state(Event event, uint16_t arg); // soft lockout #define MOON_DURING_LOCKOUT_MODE -uint8_t lockout_state(EventPtr event, uint16_t arg); +uint8_t lockout_state(Event event, uint16_t arg); // momentary / signalling mode -uint8_t momentary_state(EventPtr event, uint16_t arg); +uint8_t momentary_state(Event event, uint16_t arg); #ifdef USE_MUGGLE_MODE // muggle mode, super-simple, hard to exit -uint8_t muggle_state(EventPtr event, uint16_t arg); +uint8_t muggle_state(Event event, uint16_t arg); uint8_t muggle_mode_active = 0; #endif // general helper function for config modes -uint8_t number_entry_state(EventPtr event, uint16_t arg); +uint8_t number_entry_state(Event event, uint16_t arg); // return value from number_entry_state() volatile uint8_t number_entry_value; @@ -387,7 +387,7 @@ uint8_t triangle_wave(uint8_t phase); volatile uint8_t beacon_seconds = 2; -uint8_t off_state(EventPtr event, uint16_t arg) { +uint8_t off_state(Event event, uint16_t arg) { // turn emitter off when entering state if (event == EV_enter_state) { set_level(0); @@ -527,7 +527,7 @@ uint8_t off_state(EventPtr event, uint16_t arg) { } -uint8_t steady_state(EventPtr event, uint16_t arg) { +uint8_t steady_state(Event event, uint16_t arg) { uint8_t mode_min = ramp_smooth_floor; uint8_t mode_max = ramp_smooth_ceil; uint8_t ramp_step_size = 1; @@ -846,7 +846,7 @@ uint8_t steady_state(EventPtr event, uint16_t arg) { #ifdef USE_STROBE_STATE -uint8_t strobe_state(EventPtr event, uint16_t arg) { +uint8_t strobe_state(Event event, uint16_t arg) { // 'st' reduces ROM size by avoiding access to a volatile var // (maybe I should just make it nonvolatile?) strobe_mode_te st = strobe_type; @@ -1059,7 +1059,7 @@ uint8_t strobe_state(EventPtr event, uint16_t arg) { #ifdef USE_BATTCHECK -uint8_t battcheck_state(EventPtr event, uint16_t arg) { +uint8_t battcheck_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1076,7 +1076,7 @@ uint8_t battcheck_state(EventPtr event, uint16_t arg) { #ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(EventPtr event, uint16_t arg) { +uint8_t tempcheck_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1097,7 +1097,7 @@ uint8_t tempcheck_state(EventPtr event, uint16_t arg) { #endif -uint8_t beacon_state(EventPtr event, uint16_t arg) { +uint8_t beacon_state(Event event, uint16_t arg) { // 1 click: off if (event == EV_1click) { set_state(off_state, 0); @@ -1124,7 +1124,7 @@ uint8_t beacon_state(EventPtr event, uint16_t arg) { #define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) -uint8_t goodnight_state(EventPtr event, uint16_t arg) { +uint8_t goodnight_state(Event event, uint16_t arg) { static uint16_t ticks_since_stepdown = 0; // blink on start if (event == EV_enter_state) { @@ -1163,28 +1163,27 @@ uint8_t goodnight_state(EventPtr event, uint16_t arg) { } -uint8_t lockout_state(EventPtr event, uint16_t arg) { +uint8_t lockout_state(Event event, uint16_t arg) { #ifdef MOON_DURING_LOCKOUT_MODE // momentary(ish) moon mode during lockout // not all presses will be counted; // it depends on what is in the master event_sequences table - uint8_t last = 0; - for(uint8_t i=0; pgm_read_byte(event + i) && (i<EV_MAX_LEN); i++) - last = pgm_read_byte(event + i); if (arg == 0) { // Only turn on/off when button state changes - if ((last == A_PRESS) || (last == A_HOLD)) { - #ifdef LOCKOUT_MOON_LOWEST - // Use lowest moon configured - uint8_t lvl = ramp_smooth_floor; - if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; - set_level(lvl); - #else - // Use moon from current ramp - set_level(nearest_level(1)); - #endif - } - else if ((last == A_RELEASE) || (last == A_RELEASE_TIMEOUT)) { - set_level(0); + if (! (event & B_SYSTEM)) { // event is a button click type + if (event & B_PRESS) { // button is being held + #ifdef LOCKOUT_MOON_LOWEST + // Use lowest moon configured + uint8_t lvl = ramp_smooth_floor; + if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; + set_level(lvl); + #else + // Use moon from current ramp + set_level(nearest_level(1)); + #endif + } + else { // button not being held + set_level(0); + } } } #endif @@ -1278,21 +1277,33 @@ uint8_t lockout_state(EventPtr event, uint16_t arg) { } -uint8_t momentary_state(EventPtr event, uint16_t arg) { +uint8_t momentary_state(Event event, uint16_t arg) { // TODO: momentary strobe here? (for light painting) - if (event == EV_click1_press) { + + // light up when the button is pressed; go dark otherwise + #if 0 + if ((event ^ B_SYSTEM) & B_PRESS) { set_level(memorized_level); - empty_event_sequence(); // don't attempt to parse multiple clicks return MISCHIEF_MANAGED; } - - else if (event == EV_release) { + else if (((event ^ B_SYSTEM) & B_PRESS) == 0) { set_level(0); - empty_event_sequence(); // don't attempt to parse multiple clicks + empty_event_sequence(); //go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? return MISCHIEF_MANAGED; } + #else + if (! (event & B_SYSTEM)) { // is a button-related event + if (event & B_PRESS) { // button is pressed + set_level(memorized_level); + } else { // button was released + set_level(0); + empty_event_sequence(); + //go_to_standby = 1; // sleep while light is off + } + return MISCHIEF_MANAGED; + } + #endif // Sleep, dammit! (but wait a few seconds first) // (because standby mode uses such little power that it can interfere @@ -1302,6 +1313,7 @@ uint8_t momentary_state(EventPtr event, uint16_t arg) { else if ((event == EV_tick) && (actual_level == 0)) { if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds go_to_standby = 1; // sleep while light is off + // TODO: lighted button should use lockout config? } return MISCHIEF_MANAGED; } @@ -1311,7 +1323,7 @@ uint8_t momentary_state(EventPtr event, uint16_t arg) { #ifdef USE_MUGGLE_MODE -uint8_t muggle_state(EventPtr event, uint16_t arg) { +uint8_t muggle_state(Event event, uint16_t arg) { static int8_t ramp_direction; static int8_t muggle_off_mode; @@ -1461,7 +1473,7 @@ uint8_t muggle_state(EventPtr event, uint16_t arg) { // ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(EventPtr event, uint16_t arg, +uint8_t config_state_base(Event event, uint16_t arg, uint8_t num_config_steps, void (*savefunc)()) { static uint8_t config_step; @@ -1520,7 +1532,7 @@ void ramp_config_save() { } } -uint8_t ramp_config_state(EventPtr event, uint16_t arg) { +uint8_t ramp_config_state(Event event, uint16_t arg) { uint8_t num_config_steps; num_config_steps = 2 + ramp_style; return config_state_base(event, arg, @@ -1548,7 +1560,7 @@ void thermal_config_save() { if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } -uint8_t thermal_config_state(EventPtr event, uint16_t arg) { +uint8_t thermal_config_state(Event event, uint16_t arg) { return config_state_base(event, arg, 2, thermal_config_save); } @@ -1563,13 +1575,13 @@ void beacon_config_save() { } } -uint8_t beacon_config_state(EventPtr event, uint16_t arg) { +uint8_t beacon_config_state(Event event, uint16_t arg) { return config_state_base(event, arg, 1, beacon_config_save); } -uint8_t number_entry_state(EventPtr event, uint16_t arg) { +uint8_t number_entry_state(Event event, uint16_t arg) { static uint8_t value; static uint8_t blinks_left; static uint8_t entry_step; diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c index ee7bc97..091d4cf 100644 --- a/spaghetti-monster/fsm-events.c +++ b/spaghetti-monster/fsm-events.c @@ -20,25 +20,8 @@ #ifndef FSM_EVENTS_C #define FSM_EVENTS_C -// TODO: maybe compare events by number instead of pointer? -// (number = index in event types array) -// (comparison would use full event content, but send off index to callbacks) -// (saves space by using uint8_t instead of a pointer) -// (also eliminates the need to duplicate single-entry events like for voltage or timer tick) - -// return 1 if (a == b), 0 otherwise -uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b) { - for(uint8_t i=0; (i<EV_MAX_LEN) && (a[i] == pgm_read_byte(b+i)); i++) { - // end of zero-terminated sequence - if (a[i] == 0) return 1; - } - // if we ever fall out, that means something was different - // (or the sequence is too long) - return 0; -} - void empty_event_sequence() { - for(uint8_t i=0; i<EV_MAX_LEN; i++) current_event[i] = 0; + current_event = 0; // when the user completes an input sequence, interrupt any running timers // to cancel any delays currently in progress // This eliminates a whole bunch of extra code: @@ -49,46 +32,41 @@ void empty_event_sequence() { uint8_t push_event(uint8_t ev_type) { ticks_since_last_event = 0; // something happened - uint8_t i; - //uint8_t prev_event = 0; // never push the same event twice in a row - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++) { - // this doesn't actually seem to be necessary any more... - //prev_event = current_event[i]; + + // only click events are sent to this function + //current_event |= B_CLICK; + + // handle button presses + if (ev_type == B_PRESS) { + // set press flag + current_event |= B_PRESS; + // increase click counter + if ((current_event & B_COUNT) < (B_COUNT-1)) { + current_event ++; + return 1; // event pushed + } + return 0; // maximum number of clicks reached } - //if ((i < EV_MAX_LEN) && (prev_event != ev_type)) { - //if (prev_event != ev_type) { - if (i < EV_MAX_LEN) { - current_event[i] = ev_type; + // handle button releases + else if (ev_type == B_RELEASE) { + // clear the press flag + current_event ^= B_PRESS; + // if a "hold" event just ended, set the timeout flag + // to indicate that the event is done and can be cleared + if (current_event & B_HOLD) { current_event |= B_TIMEOUT; } return 1; // event pushed - } else { - // TODO: ... something? } - return 0; // no event pushed -} -// find and return last action in the current event sequence -/* -uint8_t last_event(uint8_t offset) { - uint8_t i; - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++); - if (i == EV_MAX_LEN) return current_event[EV_MAX_LEN-offset]; - else if (i >= offset) return current_event[i-offset]; - return 0; -} -*/ + return 0; // unexpected event type -inline uint8_t last_event_num() { - uint8_t i; - for(i=0; current_event[i] && (i<EV_MAX_LEN); i++); - return i; } -void append_emission(EventPtr event, uint16_t arg) { +void append_emission(Event event, uint16_t arg) { uint8_t i; // find last entry for(i=0; - (i<EMISSION_QUEUE_LEN) && (emissions[i].event != NULL); + (i<EMISSION_QUEUE_LEN) && (emissions[i].event != EV_none); i++) { } // add new entry if (i < EMISSION_QUEUE_LEN) { @@ -105,12 +83,12 @@ void delete_first_emission() { emissions[i].event = emissions[i+1].event; emissions[i].arg = emissions[i+1].arg; } - emissions[i].event = NULL; + emissions[i].event = EV_none; emissions[i].arg = 0; } void process_emissions() { - while (emissions[0].event != NULL) { + while (emissions[0].event != EV_none) { emit_now(emissions[0].event, emissions[0].arg); delete_first_emission(); } @@ -202,7 +180,7 @@ uint8_t nice_delay_s() { */ // Call stacked callbacks for the given event until one handles it. -uint8_t emit_now(EventPtr event, uint16_t arg) { +uint8_t emit_now(Event event, uint16_t arg) { for(int8_t i=state_stack_len-1; i>=0; i--) { uint8_t err = state_stack[i](event, arg); if (! err) return 0; @@ -210,26 +188,10 @@ uint8_t emit_now(EventPtr event, uint16_t arg) { return 1; // event not handled } -void emit(EventPtr event, uint16_t arg) { +void emit(Event event, uint16_t arg) { // add this event to the queue for later, // so we won't use too much time during an interrupt append_emission(event, arg); } -// Search the pre-defined event list for one matching what the user just did, -// and emit it if one was found. -void emit_current_event(uint16_t arg) { - //uint8_t err = 1; - for (uint8_t i=0; i<(sizeof(event_sequences)/sizeof(EventPtr)); i++) { - if (events_match(current_event, event_sequences[i])) { - //DEBUG_FLASH; - //err = emit(event_sequences[i], arg); - //return err; - emit(event_sequences[i], arg); - return; - } - } - //return err; -} - #endif diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h index 0212c2c..d838800 100644 --- a/spaghetti-monster/fsm-events.h +++ b/spaghetti-monster/fsm-events.h @@ -23,10 +23,9 @@ #include <avr/pgmspace.h> // typedefs -typedef PROGMEM const uint8_t Event; -typedef Event * EventPtr; +typedef uint8_t Event; typedef struct Emission { - EventPtr event; + Event event; uint16_t arg; } Emission; @@ -36,11 +35,11 @@ typedef struct Emission { #define MISCHIEF_NOT_MANAGED EVENT_NOT_HANDLED #ifndef MAX_CLICKS -#define MAX_CLICKS 4 +#define MAX_CLICKS 15 #endif #define EV_MAX_LEN ((MAX_CLICKS*2)+3) -uint8_t current_event[EV_MAX_LEN]; +Event current_event; // at 0.016 ms per tick, 255 ticks = 4.08 s static volatile uint16_t ticks_since_last_event = 0; @@ -52,509 +51,175 @@ static volatile uint16_t ticks_since_last_event = 0; #define RELEASE_TIMEOUT 24 #endif -#define A_ENTER_STATE 1 -#define A_LEAVE_STATE 2 -#define A_REENTER_STATE 3 -#define A_TICK 4 -#define A_SLEEP_TICK 5 -#define A_PRESS 6 -#define A_HOLD 7 -#define A_RELEASE 8 -#define A_RELEASE_TIMEOUT 9 -#define A_OVERHEATING 10 -#define A_UNDERHEATING 11 -#define A_VOLTAGE_LOW 12 -//#define A_VOLTAGE_CRITICAL 13 -#define A_DEBUG 255 // test event for debugging +/* Event structure + * Bit 7: 1 for a button input event, 0 for all others. + * If bit 7 is 1: + * Bits 0,1,2,3: Click counter. Up to 15 clicks. + * Bit 4: 1 for a "press" event, 0 for a "release" event. + * Bit 5: 1 for a "hold" event, 0 otherwise. + * Bit 6: 1 for a "timeout" event, 0 otherwise. + * If bit 7 is 0: + * Not yet defined. + */ + +// event masks / bits +#define B_SYSTEM 0b10000000 +#define B_CLICK 0b00000000 +#define B_TIMEOUT 0b01000000 +#define B_HOLD 0b00100000 +#define B_PRESS 0b00010000 +#define B_RELEASE 0b00000000 +#define B_COUNT 0b00001111 +#define B_FLAGS 0b11110000 // Event types -// TODO: make these progmem-only? -Event EV_debug[] = { - A_DEBUG, - 0 } ; -Event EV_enter_state[] = { - A_ENTER_STATE, - 0 } ; -Event EV_leave_state[] = { - A_LEAVE_STATE, - 0 } ; -Event EV_reenter_state[] = { - A_REENTER_STATE, - 0 } ; -Event EV_tick[] = { - A_TICK, - 0 } ; +#define EV_debug (B_SYSTEM|0b01111111) +#define EV_enter_state (B_SYSTEM|0b00001000) +#define EV_leave_state (B_SYSTEM|0b00001001) +#define EV_reenter_state (B_SYSTEM|0b00001010) +#define EV_tick (B_SYSTEM|0b00000001) #ifdef TICK_DURING_STANDBY -Event EV_sleep_tick[] = { - A_SLEEP_TICK, - 0 } ; +#define EV_sleep_tick (B_SYSTEM|0b00000011) #endif #ifdef USE_LVP -Event EV_voltage_low[] = { - A_VOLTAGE_LOW, - 0 } ; +#define EV_voltage_low (B_SYSTEM|0b00000100) #endif #ifdef USE_THERMAL_REGULATION -Event EV_temperature_high[] = { - A_OVERHEATING, - 0 } ; -Event EV_temperature_low[] = { - A_UNDERHEATING, - 0 } ; +#define EV_temperature_high (B_SYSTEM|0b00000101) +#define EV_temperature_low (B_SYSTEM|0b00000110) #endif -Event EV_click1_press[] = { - A_PRESS, - 0 }; + +#define EV_none 0 + // shouldn't normally happen, but UI might reset event while button is down // so a release with no recorded prior hold could be possible -Event EV_release[] = { - A_RELEASE, - 0 }; -Event EV_click1_release[] = { - A_PRESS, - A_RELEASE, - 0 }; +#define EV_release (B_CLICK|B_RELEASE|0) + +#define EV_click1_press (B_CLICK|B_PRESS|1) +#define EV_click1_hold (B_CLICK|B_HOLD|B_PRESS|1) +#define EV_click1_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|1) +#define EV_click1_release (B_CLICK|B_RELEASE|1) +#define EV_click1_complete (B_CLICK|B_TIMEOUT|1) #define EV_1click EV_click1_complete -Event EV_click1_complete[] = { - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; #define EV_hold EV_click1_hold -// FIXME: Should holds use "start+tick" or just "tick" with a tick number? -// Or "start+tick" with a tick number? -Event EV_click1_hold[] = { - A_PRESS, - A_HOLD, - 0 }; -Event EV_click1_hold_release[] = { - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -#if MAX_CLICKS >= 2 -Event EV_click2_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click2_hold[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - 0 }; -Event EV_click2_hold_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -Event EV_click2_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; + +#define EV_click2_press (B_CLICK|B_PRESS|2) +#define EV_click2_hold (B_CLICK|B_HOLD|B_PRESS|2) +#define EV_click2_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|2) +#define EV_click2_release (B_CLICK|B_RELEASE|2) +#define EV_click2_complete (B_CLICK|B_TIMEOUT|2) #define EV_2clicks EV_click2_complete -Event EV_click2_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif // MAX_CLICKS >= 2 -#if MAX_CLICKS >= 3 -Event EV_click3_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click3_hold[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - 0 }; -Event EV_click3_hold_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_HOLD, - A_RELEASE, - 0 }; -Event EV_click3_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; + +#define EV_click3_press (B_CLICK|B_PRESS|3) +#define EV_click3_hold (B_CLICK|B_HOLD|B_PRESS|3) +#define EV_click3_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|3) +#define EV_click3_release (B_CLICK|B_RELEASE|3) +#define EV_click3_complete (B_CLICK|B_TIMEOUT|3) #define EV_3clicks EV_click3_complete -Event EV_click3_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif // MAX_CLICKS >= 3 -#if MAX_CLICKS >= 4 + +#define EV_click4_press (B_CLICK|B_PRESS|4) +#define EV_click4_hold (B_CLICK|B_HOLD|B_PRESS|4) +#define EV_click4_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|4) +#define EV_click4_release (B_CLICK|B_RELEASE|4) +#define EV_click4_complete (B_CLICK|B_TIMEOUT|4) #define EV_4clicks EV_click4_complete -Event EV_click4_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 5 + +#define EV_click5_press (B_CLICK|B_PRESS|5) +#define EV_click5_hold (B_CLICK|B_HOLD|B_PRESS|5) +#define EV_click5_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|5) +#define EV_click5_release (B_CLICK|B_RELEASE|5) +#define EV_click5_complete (B_CLICK|B_TIMEOUT|5) #define EV_5clicks EV_click5_complete -Event EV_click5_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 6 + +#define EV_click6_press (B_CLICK|B_PRESS|6) +#define EV_click6_hold (B_CLICK|B_HOLD|B_PRESS|6) +#define EV_click6_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|6) +#define EV_click6_release (B_CLICK|B_RELEASE|6) +#define EV_click6_complete (B_CLICK|B_TIMEOUT|6) #define EV_6clicks EV_click6_complete -Event EV_click6_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 7 + +#define EV_click7_press (B_CLICK|B_PRESS|7) +#define EV_click7_hold (B_CLICK|B_HOLD|B_PRESS|7) +#define EV_click7_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|7) +#define EV_click7_release (B_CLICK|B_RELEASE|7) +#define EV_click7_complete (B_CLICK|B_TIMEOUT|7) #define EV_7clicks EV_click7_complete -Event EV_click7_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 8 + +#define EV_click8_press (B_CLICK|B_PRESS|8) +#define EV_click8_hold (B_CLICK|B_HOLD|B_PRESS|8) +#define EV_click8_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|8) +#define EV_click8_release (B_CLICK|B_RELEASE|8) +#define EV_click8_complete (B_CLICK|B_TIMEOUT|8) #define EV_8clicks EV_click8_complete -Event EV_click8_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 9 + +#define EV_click9_press (B_CLICK|B_PRESS|9) +#define EV_click9_hold (B_CLICK|B_HOLD|B_PRESS|9) +#define EV_click9_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|9) +#define EV_click9_release (B_CLICK|B_RELEASE|9) +#define EV_click9_complete (B_CLICK|B_TIMEOUT|9) #define EV_9clicks EV_click9_complete -Event EV_click9_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 10 + +#define EV_click10_press (B_CLICK|B_PRESS|10) +#define EV_click10_hold (B_CLICK|B_HOLD|B_PRESS|10) +#define EV_click10_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|10) +#define EV_click10_release (B_CLICK|B_RELEASE|10) +#define EV_click10_complete (B_CLICK|B_TIMEOUT|10) #define EV_10clicks EV_click10_complete -Event EV_click10_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 11 + +#define EV_click11_press (B_CLICK|B_PRESS|11) +#define EV_click11_hold (B_CLICK|B_HOLD|B_PRESS|11) +#define EV_click11_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|11) +#define EV_click11_release (B_CLICK|B_RELEASE|11) +#define EV_click11_complete (B_CLICK|B_TIMEOUT|11) #define EV_11clicks EV_click11_complete -Event EV_click11_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 12 + +#define EV_click12_press (B_CLICK|B_PRESS|12) +#define EV_click12_hold (B_CLICK|B_HOLD|B_PRESS|12) +#define EV_click12_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|12) +#define EV_click12_release (B_CLICK|B_RELEASE|12) +#define EV_click12_complete (B_CLICK|B_TIMEOUT|12) #define EV_12clicks EV_click12_complete -Event EV_click12_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 13 + +#define EV_click13_press (B_CLICK|B_PRESS|13) +#define EV_click13_hold (B_CLICK|B_HOLD|B_PRESS|13) +#define EV_click13_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|13) +#define EV_click13_release (B_CLICK|B_RELEASE|13) +#define EV_click13_complete (B_CLICK|B_TIMEOUT|13) #define EV_13clicks EV_click13_complete -Event EV_click13_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -#if MAX_CLICKS >= 14 + +#define EV_click14_press (B_CLICK|B_PRESS|14) +#define EV_click14_hold (B_CLICK|B_HOLD|B_PRESS|14) +#define EV_click14_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|14) +#define EV_click14_release (B_CLICK|B_RELEASE|14) +#define EV_click14_complete (B_CLICK|B_TIMEOUT|14) #define EV_14clicks EV_click14_complete -Event EV_click14_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#endif -// ... and so on - -// A list of button event types for easy iteration -// TODO: make this progmem-only? -EventPtr event_sequences[] = { - EV_click1_press, - EV_release, - EV_click1_release, - EV_click1_complete, - EV_click1_hold, - EV_click1_hold_release, - #if MAX_CLICKS >= 2 - EV_click2_press, - EV_click2_hold, - EV_click2_hold_release, - EV_click2_release, - EV_click2_complete, - #endif - #if MAX_CLICKS >= 3 - EV_click3_press, - EV_click3_hold, - EV_click3_hold_release, - EV_click3_release, - EV_click3_complete, - #endif - #if MAX_CLICKS >= 4 - EV_click4_complete, - #endif - #if MAX_CLICKS >= 5 - EV_click5_complete, - #endif - #if MAX_CLICKS >= 6 - EV_click6_complete, - #endif - #if MAX_CLICKS >= 7 - EV_click7_complete, - #endif - #if MAX_CLICKS >= 8 - EV_click8_complete, - #endif - #if MAX_CLICKS >= 9 - EV_click9_complete, - #endif - #if MAX_CLICKS >= 10 - EV_click10_complete, - #endif - #if MAX_CLICKS >= 11 - EV_click11_complete, - #endif - #if MAX_CLICKS >= 12 - EV_click12_complete, - #endif - #if MAX_CLICKS >= 13 - EV_click13_complete, - #endif - #if MAX_CLICKS >= 14 - EV_click14_complete, - #endif - // ... -}; - -#define events_match(a,b) compare_event_sequences(a,b) + +#define EV_click15_press (B_CLICK|B_PRESS|15) +#define EV_click15_hold (B_CLICK|B_HOLD|B_PRESS|15) +#define EV_click15_hold_release (B_CLICK|B_TIMEOUT|B_HOLD|B_RELEASE|15) +#define EV_click15_release (B_CLICK|B_RELEASE|15) +#define EV_click15_complete (B_CLICK|B_TIMEOUT|15) +#define EV_15clicks EV_click15_complete + + +#define events_match(a,b) (a == b) // return 1 if (a == b), 0 otherwise -uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b); +#define compare_event_sequences(a,b) (a == b) void empty_event_sequence(); uint8_t push_event(uint8_t ev_type); -// uint8_t last_event(uint8_t offset); -inline uint8_t last_event_num(); #define EMISSION_QUEUE_LEN 16 // no comment about "volatile emissions" volatile Emission emissions[EMISSION_QUEUE_LEN]; -void append_emission(EventPtr event, uint16_t arg); +void append_emission(Event event, uint16_t arg); void delete_first_emission(); void process_emissions(); //#define emit_now emit -uint8_t emit_now(EventPtr event, uint16_t arg); -void emit(EventPtr event, uint16_t arg); -void emit_current_event(uint16_t arg); +uint8_t emit_now(Event event, uint16_t arg); +void emit(Event event, uint16_t arg); +#define emit_current_event(arg) emit(current_event, arg) uint8_t nice_delay_ms(uint16_t ms); //uint8_t nice_delay_4ms(uint8_t ms); diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c index a79572d..acb627d 100644 --- a/spaghetti-monster/fsm-pcint.c +++ b/spaghetti-monster/fsm-pcint.c @@ -75,9 +75,9 @@ void PCINT_inner(uint8_t pressed) { uint8_t pushed; if (pressed) { - pushed = push_event(A_PRESS); + pushed = push_event(B_PRESS); } else { - pushed = push_event(A_RELEASE); + pushed = push_event(B_RELEASE); } // check if sequence matches any defined sequences diff --git a/spaghetti-monster/fsm-states.c b/spaghetti-monster/fsm-states.c index 09ae804..f91dc4b 100644 --- a/spaghetti-monster/fsm-states.c +++ b/spaghetti-monster/fsm-states.c @@ -32,7 +32,7 @@ // TODO: function to call stacked callbacks until one returns "handled" void _set_state(StatePtr new_state, uint16_t arg, - EventPtr exit_event, EventPtr enter_event) { + Event exit_event, Event enter_event) { // call old state-exit hook (don't use stack) if (current_state != NULL) current_state(exit_event, arg); // set new state @@ -82,7 +82,7 @@ uint8_t set_state(StatePtr new_state, uint16_t arg) { #ifndef DONT_USE_DEFAULT_STATE // bottom state on stack // handles default actions for LVP, thermal regulation, etc -uint8_t default_state(EventPtr event, uint16_t arg) { +uint8_t default_state(Event event, uint16_t arg) { if (0) {} // this should get compiled out #ifdef USE_LVP diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h index 6e4a2a0..7d9361b 100644 --- a/spaghetti-monster/fsm-states.h +++ b/spaghetti-monster/fsm-states.h @@ -23,7 +23,7 @@ #include "fsm-adc.h" // typedefs -typedef uint8_t State(EventPtr event, uint16_t arg); +typedef uint8_t State(Event event, uint16_t arg); typedef State * StatePtr; // top of the stack @@ -36,12 +36,12 @@ StatePtr state_stack[STATE_STACK_SIZE]; uint8_t state_stack_len = 0; void _set_state(StatePtr new_state, uint16_t arg, - EventPtr exit_event, EventPtr enter_event); + Event exit_event, Event enter_event); int8_t push_state(StatePtr new_state, uint16_t arg); StatePtr pop_state(); uint8_t set_state(StatePtr new_state, uint16_t arg); #ifndef DONT_USE_DEFAULT_STATE -uint8_t default_state(EventPtr event, uint16_t arg); +uint8_t default_state(Event event, uint16_t arg); #endif #endif diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index e8419bc..cfff1fa 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -85,15 +85,8 @@ ISR(WDT_vect) { // append timeout to current event sequence, then // send event to current state callback - // preload recent events - uint8_t le_num = last_event_num(); - uint8_t last_event = 0; - uint8_t prev_event = 0; - if (le_num >= 1) last_event = current_event[le_num-1]; - if (le_num >= 2) prev_event = current_event[le_num-2]; - // callback on each timer tick - if (last_event == A_HOLD) { + if ((current_event & B_FLAGS) == (B_HOLD | B_PRESS)) { emit(EV_tick, 0); // override tick counter while holding button } else { @@ -101,29 +94,32 @@ ISR(WDT_vect) { } // user held button long enough to count as a long click? - if (last_event == A_PRESS) { - if (ticks_since_last_event >= HOLD_TIMEOUT) { - push_event(A_HOLD); - emit_current_event(0); + if (current_event & B_PRESS) { + // during a "hold", send a hold event each tick, with a timer + if (current_event & B_HOLD) { + emit_current_event(ticks_since_last_event); + } + // has button been down long enough to become a "hold"? + else { + if (ticks_since_last_event >= HOLD_TIMEOUT) { + current_event |= B_HOLD; + emit_current_event(0); + } } } - // user is still holding button, so tick - else if (last_event == A_HOLD) { - emit_current_event(ticks_since_last_event); - } - - // detect completed button presses with expired timeout - else if (last_event == A_RELEASE) { + // event in progress, but button not currently down + else if (current_event) { + // "hold" event just ended // no timeout required when releasing a long-press // TODO? move this logic to PCINT() and simplify things here? - if (prev_event == A_HOLD) { + if (current_event & B_HOLD) { //emit_current_event(0); // should have been emitted by PCINT empty_event_sequence(); } // end and clear event after release timeout else if (ticks_since_last_event >= RELEASE_TIMEOUT) { - push_event(A_RELEASE_TIMEOUT); + current_event |= B_TIMEOUT; emit_current_event(0); empty_event_sequence(); } |
