#include "tk-attiny.h" // typedef uint8_t (*EventCallbackPtr)(EventPtr event, uint16_t arg); typedef uint8_t EventCallback(EventPtr event, uint16_t arg); // FIXME: Why does a state need a number? Why not just a function pointer? // (I don't think it actually needs a number...) typedef struct State { uint8_t num; EventCallback event_callback; } State; typedef struct State* StatePtr; volatile StatePtr current_state; #define EV_MAX_LEN 16 volatile uint8_t current_event[EV_MAX_LEN]; volatile int16_t voltage; #ifdef USE_THERMAL_REGULATION volatile int16_t temperature; #endif #define A_ENTER_STATE 1 #define A_LEAVE_STATE 2 #define A_TICK 3 #define A_PRESS 4 #define A_HOLD_START 5 #define A_HOLD_TICK 6 #define A_RELEASE 7 #define A_RELEASE_TIMEOUT 8 // TODO: add events for over/under-heat conditions (with parameter for severity) #define A_OVERHEATING 9 #define A_UNDERHEATING 10 // TODO: add events for low voltage conditions #define A_VOLTAGE_LOW 11 #define A_VOLTAGE_CRITICAL 12 // 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) // Event types typedef PROGMEM const uint8_t Event; typedef Event* EventPtr; Event EV_enter_state[] = { A_ENTER_STATE, 0 } ; Event EV_leave_state[] = { A_LEAVE_STATE, 0 } ; Event EV_tick[] = { A_TICK, 0 } ; Event EV_press[] = { A_PRESS, 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_press_release[] = { A_PRESS, A_RELEASE, 0 }; #define EV_1click EV_press_release_timeout Event EV_press_release_timeout[] = { A_PRESS, A_RELEASE, A_RELEASE_TIMEOUT, 0 }; #define EV_hold EV_press_hold // FIXME: Should holds use "start+tick" or just "tick" with a tick number? // Or "start+tick" with a tick number? Event EV_press_hold[] = { A_PRESS, A_HOLD_TIMEOUT, 0 }; Event EV_press_hold_release[] = { A_PRESS, A_HOLD_TIMEOUT, A_RELEASE, 0 }; Event EV_press_release_press[] = { A_PRESS, A_RELEASE, A_PRESS, 0 }; Event EV_press_release_press_release[] = { A_PRESS, A_RELEASE, A_PRESS, A_RELEASE, 0 }; #define EV_2clicks EV_press_release_press_release_timeout Event EV_press_release_press_release_timeout[] = { A_PRESS, A_RELEASE, A_PRESS, A_RELEASE, A_RELEASE_TIMEOUT, 0 }; // ... and so on // A list of event types for easy iteration EventPtr event_sequences[] = { EV_press, EV_press_release, EV_press_release_timeout, EV_press_release_press, EV_press_release_press_release, EV_press_release_press_release_timeout, // ... }; // TODO: move this to a separate UI-specific file /* State states[] = { }; */ // TODO? add events to a queue when inside an interrupt // instead of calling the event functions directly? // (then empty the queue in main loop?) // TODO? new delay() functions which handle queue consumption? // TODO? new interruptible delay() functions? //static uint8_t ticks_since_last_event = 0; // TODO: 16 bits? void WDT_tick() { timer ++; //static uint8_t hold_ticks = 0; // TODO: 16 bits? // callback on each timer tick emit(EV_tick, timer); // if time since last event exceeds timeout, // append timeout to current event sequence, then // send event to current state callback // //hold_event(ticks) // //emit(EV_press_hold, hold_ticks); // emit_current(hold_ticks); // or // //release_timeout() // //emit(EV_press_release_timeout, 0); // emit_current(0); // add 4-step voltage / temperature thing? // with averaged values, // once every N ticks? } void button_change_interrupt() { // TODO: debounce a little //ticks_since_last_event = 0; // something happened // TODO: add event to current sequence // check if sequence matches any defined sequences // if so, send event to current state callback emit_current(0); } uint8_t emit_current(uint16_t arg) { uint8_t err = 1; for (uint8_t i=0; i