diff options
| author | Selene ToyKeeper | 2020-07-22 01:48:22 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2020-07-22 01:48:22 -0600 |
| commit | 4f8a7d66a6f98fc51d3cb75e9906c049faffe14a (patch) | |
| tree | e503ac174d211e317d5dcd00035a6731dcf88b2e /spaghetti-monster | |
| parent | started refactoring anduril into individual files... (diff) | |
| download | anduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.tar.gz anduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.tar.bz2 anduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.zip | |
more progress on refactoring Anduril into separate files... nearly done with the initial split
Diffstat (limited to '')
30 files changed, 2016 insertions, 1357 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index d69be54..23f14d5 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -54,126 +54,68 @@ #include incfile(CONFIGFILE) -////////// Include headers which need to be before FSM ////////// +/********* Include headers which need to be before FSM *********/ + +// enable FSM features needed by strobe modes +#include "ramping-fsm.h" + #ifdef USE_FACTORY_RESET #include "factory-reset-fsm.h" #endif +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode-fsm.h" +#endif + // enable FSM features needed by strobe modes #include "strobes-fsm.h" +// figure out how many bytes of eeprom are needed, +// based on which UI features are enabled +#include "load-save-config-fsm.h" -// brightness to use when no memory is set -#ifndef DEFAULT_LEVEL -#define DEFAULT_LEVEL MAX_1x7135 -#endif -// thermal properties, if not defined per-driver -#ifndef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to -#endif -#ifndef THERM_FASTER_LEVEL - #ifdef MAX_Nx7135 - #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high - #else - #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high - #endif -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments -#endif +/********* bring in FSM / SpaghettiMonster *********/ +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending +#include "spaghetti-monster.h" -/********* Configure SpaghettiMonster *********/ -#define USE_DELAY_ZERO -#define USE_RAMPING -#ifndef RAMP_LENGTH -#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file -#endif -#define MAX_BIKING_LEVEL 120 // should be 127 or less -#define USE_BATTCHECK -#if defined(USE_SIMPLE_UI) -// start in the simple UI after each factory reset? -#ifndef DEFAULT_SIMPLE_UI_ACTIVE -#define DEFAULT_SIMPLE_UI_ACTIVE 1 -#endif -#ifndef DEFAULT_SIMPLE_UI_FLOOR -#define DEFAULT_SIMPLE_UI_FLOOR 22 -#endif -#ifndef DEFAULT_SIMPLE_UI_CEIL -#define DEFAULT_SIMPLE_UI_CEIL (MAX_1x7135+20) -#endif -#ifndef DEFAULT_SIMPLE_UI_STEPS -#define DEFAULT_SIMPLE_UI_STEPS 3 -#endif -#endif +/********* Include all the regular app headers *********/ -#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending +#include "off-state.h" +#include "ramping.h" +#include "load-save-config.h" +#include "config-mode.h" +#include "misc.h" -// auto-detect how many eeprom bytes -#define USE_EEPROM -typedef enum { - ramp_style_e, - #ifdef USE_RAMP_CONFIG - ramp_smooth_floor_e, - ramp_smooth_ceil_e, - ramp_discrete_floor_e, - ramp_discrete_ceil_e, - ramp_discrete_steps_e, - #endif - #ifdef USE_MANUAL_MEMORY - manual_memory_e, - #endif - #ifdef USE_TINT_RAMPING - tint_e, - #endif - #ifdef USE_STROBE_STATE - strobe_type_e, - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - strobe_delays_0_e, - strobe_delays_1_e, - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_brightness_e, - #endif - #ifdef USE_BEACON_MODE - beacon_seconds_e, - #endif - #ifdef USE_SIMPLE_UI - simple_ui_active_e, - simple_ui_floor_e, - simple_ui_ceil_e, - simple_ui_steps_e, - #endif - #ifdef USE_THERMAL_REGULATION - therm_ceil_e, - therm_cal_offset_e, - #endif - #ifdef USE_INDICATOR_LED - indicator_led_mode_e, - #endif - #ifdef USE_AUX_RGB_LEDS - rgb_led_off_mode_e, - rgb_led_lockout_mode_e, - #endif - eeprom_indexes_e_END -} eeprom_indexes_e; -#define EEPROM_BYTES eeprom_indexes_e_END +#ifdef USE_VERSION_CHECK +#include "version-check-mode.h" +#endif -#ifdef START_AT_MEMORIZED_LEVEL -#define USE_EEPROM_WL -#define EEPROM_WL_BYTES 1 +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode.h" #endif -#include "spaghetti-monster.h" +#ifdef USE_GOODNIGHT_MODE +#include "goodnight-mode.h" +#endif +#ifdef USE_BEACON_MODE +#include "beacon.h" +#endif -////////// Include all the extra headers ////////// +#ifdef USE_THERMAL_REGULATION +#include "tempcheck-mode.h" +#endif -#include "config-mode.h" +#ifdef USE_LOCKOUT_MODE #include "lockout.h" +#endif + +#ifdef USE_MOMENTARY_MODE +#include "momentary-mode.h" +#endif #ifdef USE_TINT_RAMPING #include "tint-ramping.h" @@ -183,64 +125,15 @@ typedef enum { #include "factory-reset.h" #endif -#ifdef USE_BEACON_MODE -#include "beacon.h" -#endif - // this one detects its own enable/disable settings #include "strobes.h" - -// configure the timing of turning on/off in regular ramp mode -// press: react as soon as the button is pressed -#define B_PRESS_T 0 -// release: react as soon as the button is released -#define B_RELEASE_T 1 -// timeout: react as soon as we're sure the user isn't doing a double-click -#define B_TIMEOUT_T 2 -// defaults are release on, timeout off -#ifndef B_TIMING_ON -//#define B_TIMING_ON B_PRESS_T -#define B_TIMING_ON B_RELEASE_T -#endif -#ifndef B_TIMING_OFF -//#define B_TIMING_OFF B_RELEASE_T -#define B_TIMING_OFF B_TIMEOUT_T +#ifdef USE_SOS_MODE +#include "sos-mode.h" #endif - // FSM states -uint8_t off_state(Event event, uint16_t arg); -// ramping mode and its related config mode -uint8_t steady_state(Event event, uint16_t arg); -#ifdef USE_RAMP_CONFIG -uint8_t ramp_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_BLINK_NUM -uint8_t tempcheck_state(Event event, uint16_t arg); -uint8_t thermal_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_GOODNIGHT_MODE -// 1-hour ramp down from low, then automatic off -uint8_t goodnight_state(Event event, uint16_t arg); -#endif -#ifdef USE_SOS_MODE_IN_BLINKY_GROUP -// automatic SOS emergency signal -uint8_t sos_state(Event event, uint16_t arg); -#endif -#ifdef USE_MOMENTARY_MODE -// momentary / signalling mode -uint8_t momentary_state(Event event, uint16_t arg); -uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe -uint8_t momentary_active = 0; // boolean, true if active *right now* -#endif -void blink_confirm(uint8_t num); -void blip(); #if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) void indicator_blink(uint8_t arg); #endif @@ -284,91 +177,6 @@ uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT; uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT; #endif -// remember stuff even after battery was changed -void load_config(); -void save_config(); -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl(); -#endif - -// default ramp options if not overridden earlier per-driver -#ifndef RAMP_STYLE -#define RAMP_STYLE 0 // smooth default -#endif -#ifndef RAMP_SMOOTH_FLOOR - #define RAMP_SMOOTH_FLOOR 1 -#endif -#ifndef RAMP_SMOOTH_CEIL - #if PWM_CHANNELS == 3 - #define RAMP_SMOOTH_CEIL MAX_Nx7135 - #else - #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 - #endif -#endif -#ifndef RAMP_DISCRETE_FLOOR - #define RAMP_DISCRETE_FLOOR 20 -#endif -#ifndef RAMP_DISCRETE_CEIL - #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#endif -#ifndef RAMP_DISCRETE_STEPS - #define RAMP_DISCRETE_STEPS 7 -#endif - -// mile marker(s) partway up the ramp -// default: blink only at border between regulated and FET -#ifdef BLINK_AT_RAMP_MIDDLE - #if PWM_CHANNELS >= 3 - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 - #ifndef BLINK_AT_RAMP_MIDDLE_2 - #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 - #endif - #endif - #else - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 - #endif - #endif -#endif - -// brightness control -uint8_t memorized_level = DEFAULT_LEVEL; -#ifdef USE_MANUAL_MEMORY -uint8_t manual_memory = 0; -#endif -#ifdef USE_SIMPLE_UI -// whether to enable the simplified interface or not -uint8_t simple_ui_active = DEFAULT_SIMPLE_UI_ACTIVE; -#endif -// smooth vs discrete ramping -uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete -// current values, regardless of style -uint8_t ramp_floor = RAMP_SMOOTH_FLOOR; -uint8_t ramp_ceil = RAMP_SMOOTH_CEIL; -// per style -uint8_t ramp_floors[] = { - RAMP_SMOOTH_FLOOR, - RAMP_DISCRETE_FLOOR, - #ifdef USE_SIMPLE_UI - DEFAULT_SIMPLE_UI_FLOOR, - #endif - }; -uint8_t ramp_ceils[] = { - RAMP_SMOOTH_CEIL, - RAMP_DISCRETE_CEIL, - #ifdef USE_SIMPLE_UI - DEFAULT_SIMPLE_UI_CEIL, - #endif - }; -uint8_t ramp_stepss[] = { - 0, - RAMP_DISCRETE_STEPS, - #ifdef USE_SIMPLE_UI - DEFAULT_SIMPLE_UI_STEPS, - #endif - }; -uint8_t ramp_discrete_step_size; // don't set this #ifdef USE_INDICATOR_LED // bits 2-3 control lockout mode @@ -386,998 +194,60 @@ uint8_t ramp_discrete_step_size; // don't set this #endif #endif -// calculate the nearest ramp level which would be valid at the moment -// (is a no-op for smooth ramp, but limits discrete ramp to only the -// correct levels for the user's config) -uint8_t nearest_level(int16_t target); -// ensure ramp globals are correct -void ramp_update_config(); -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -void set_level_and_therm_target(uint8_t level); -#else -#define set_level_and_therm_target(level) set_level(level) -#endif - -#ifdef USE_VERSION_CHECK -#define USE_BLINK_DIGIT -#include "version.h" -const PROGMEM uint8_t version_number[] = VERSION_NUMBER; -uint8_t version_check_state(Event event, uint16_t arg); -#endif - -#ifdef USE_RAMP_CONFIG -void ramp_config_save(); -#endif -#ifdef USE_THERMAL_REGULATION -void thermal_config_save(); -#endif - -////////// Include all the extra sources ////////// +/********* Include all the app logic source files *********/ // (is a bit weird to do things this way, // but it saves a lot of space by letting use use the -fwhole-program flag) +#include "off-state.c" +#include "ramping.c" +#include "load-save-config.c" #include "config-mode.c" -#include "lockout.c" +#include "misc.c" -#ifdef USE_TINT_RAMPING -#include "tint-ramping.c" -#endif - -#ifdef USE_FACTORY_RESET -#include "factory-reset.c" -#endif - -#ifdef USE_BEACON_MODE -#include "beacon.c" +#ifdef USE_VERSION_CHECK +#include "version-check-mode.c" #endif -#ifdef USE_STROBE_STATE -#include "strobes.c" +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode.c" #endif - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(rgb_led_off_mode, 0); - #endif - // sleep while off (lower power use) - // (unless delay requested; give the ADC some time to catch up) - if (! arg) { go_to_standby = 1; } - return MISCHIEF_MANAGED; - } - // go back to sleep eventually if we got bumped but didn't leave "off" state - else if (event == EV_tick) { - if (arg > HOLD_TIMEOUT) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(rgb_led_off_mode, arg); - #endif - } - return MISCHIEF_MANAGED; - } - #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) - // blink the indicator LED, maybe - else if (event == EV_sleep_tick) { - #ifdef USE_INDICATOR_LED - if ((indicator_led_mode & 0b00000011) == 0b00000011) { - indicator_blink(arg); - } - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(rgb_led_off_mode, arg); - #endif - return MISCHIEF_MANAGED; - } - #endif - #if (B_TIMING_ON == B_PRESS_T) - // hold (initially): go to lowest level (floor), but allow abort for regular click - else if (event == EV_click1_press) { - set_level(nearest_level(1)); - return MISCHIEF_MANAGED; - } - #endif // B_TIMING_ON == B_PRESS_T - // hold: go to lowest level - else if (event == EV_click1_hold) { - #if (B_TIMING_ON == B_PRESS_T) - #ifdef MOON_TIMING_HINT - if (arg == 0) { - // let the user know they can let go now to stay at moon - blip(); - } else - #endif - #else // B_RELEASE_T or B_TIMEOUT_T - set_level(nearest_level(1)); - #endif - // don't start ramping immediately; - // give the user time to release at moon level - //if (arg >= HOLD_TIMEOUT) { // smaller - if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent - set_state(steady_state, 1); - } - return MISCHIEF_MANAGED; - } - // hold, release quickly: go to lowest level (floor) - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return MISCHIEF_MANAGED; - } - #if (B_TIMING_ON != B_TIMEOUT_T) - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - #ifdef USE_MANUAL_MEMORY - if (manual_memory) - set_level(nearest_level(manual_memory)); - else - #endif - set_level(nearest_level(memorized_level)); - return MISCHIEF_MANAGED; - } - #endif // if (B_TIMING_ON != B_TIMEOUT_T) - // 1 click: regular mode - else if (event == EV_1click) { - #ifdef USE_MANUAL_MEMORY - if (manual_memory) - set_state(steady_state, manual_memory); - else - #endif - set_state(steady_state, memorized_level); - return MISCHIEF_MANAGED; - } - // click, hold: go to highest level (ceiling) (for ramping down) - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return MISCHIEF_MANAGED; - } - // 2 clicks: highest mode (ceiling) - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return MISCHIEF_MANAGED; - } - // 3 clicks (initial press): off, to prep for later events - else if (event == EV_click3_press) { - set_level(0); - return MISCHIEF_MANAGED; - } - #ifdef USE_BATTCHECK - // 3 clicks: battcheck mode / blinky mode group 1 - else if (event == EV_3clicks) { - set_state(battcheck_state, 0); - return MISCHIEF_MANAGED; - } - #endif - // 4 clicks: soft lockout - else if (event == EV_4clicks) { - blink_confirm(2); - set_state(lockout_state, 0); - return MISCHIEF_MANAGED; - } - #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET) - // 13 clicks and hold the last click: invoke factory reset (reboot) - else if (event == EV_click13_hold) { - reboot(); - return MISCHIEF_MANAGED; - } - #endif - #ifdef USE_VERSION_CHECK - // 15+ clicks: show the version number - else if (event == EV_15clicks) { - set_state(version_check_state, 0); - return MISCHIEF_MANAGED; - } - #endif - - #ifdef USE_SIMPLE_UI - // 8 clicks, but hold last click: turn simple UI off - else if ((event == EV_click8_hold) && (!arg)) { - blink_confirm(1); - simple_ui_active = 0; - return MISCHIEF_MANAGED; - } - - ////////// Every action below here is blocked in the simple UI ////////// - if (simple_ui_active) { - return EVENT_NOT_HANDLED; - } - // 8 clicks: enable simple UI - else if (event == EV_8clicks) { - blink_confirm(1); - simple_ui_active = 1; - return MISCHIEF_MANAGED; - } - #endif - - // click, click, long-click: strobe mode - #ifdef USE_STROBE_STATE - else if (event == EV_click3_hold) { - set_state(strobe_state, 0); - return MISCHIEF_MANAGED; - } - #elif defined(USE_BORING_STROBE_STATE) - else if (event == EV_click3_hold) { - set_state(boring_strobe_state, 0); - return MISCHIEF_MANAGED; - } - #endif - #ifdef USE_MOMENTARY_MODE - // 5 clicks: momentary mode - else if (event == EV_5clicks) { - blink_confirm(1); - set_state(momentary_state, 0); - return MISCHIEF_MANAGED; - } - #endif - #ifdef USE_INDICATOR_LED - // 7 clicks: change indicator LED mode - else if (event == EV_7clicks) { - uint8_t mode = (indicator_led_mode & 3) + 1; - #ifdef TICK_DURING_STANDBY - mode = mode & 3; - #else - mode = mode % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; - indicator_led(mode); - save_config(); - return MISCHIEF_MANAGED; - } - #elif defined(USE_AUX_RGB_LEDS) - // 7 clicks: change RGB aux LED pattern - else if (event == EV_7clicks) { - uint8_t mode = (rgb_led_off_mode >> 4) + 1; - mode = mode % RGB_LED_NUM_PATTERNS; - rgb_led_off_mode = (mode << 4) | (rgb_led_off_mode & 0x0f); - rgb_led_update(rgb_led_off_mode, 0); - save_config(); - blink_confirm(1); - return MISCHIEF_MANAGED; - } - // 7 clicks (hold last): change RGB aux LED color - else if (event == EV_click7_hold) { - setting_rgb_mode_now = 1; - if (0 == (arg & 0x3f)) { - uint8_t mode = (rgb_led_off_mode & 0x0f) + 1; - mode = mode % RGB_LED_NUM_COLORS; - rgb_led_off_mode = mode | (rgb_led_off_mode & 0xf0); - //save_config(); - } - rgb_led_update(rgb_led_off_mode, arg); - return MISCHIEF_MANAGED; - } - else if (event == EV_click7_hold_release) { - setting_rgb_mode_now = 0; - save_config(); - return MISCHIEF_MANAGED; - } - #endif // end 7 clicks - #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION) - // 10 clicks: thermal config mode - else if (event == EV_10clicks) { - push_state(thermal_config_state, 0); - return MISCHIEF_MANAGED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -uint8_t steady_state(Event event, uint16_t arg) { - #ifdef USE_REVERSING - static int8_t ramp_direction = 1; - #endif - #if (B_TIMING_OFF == B_RELEASE_T) - // if the user double clicks, we need to abort turning off, - // and this stores the level to return to - static uint8_t level_before_off = 0; - #endif - - // make sure ramp globals are correct... - // ... but they already are; no need to do it here - //ramp_update_config(); - //nearest_level(1); // same effect, takes less space - - uint8_t mode_min = ramp_floor; - uint8_t mode_max = ramp_ceil; - uint8_t step_size; - if (ramp_style) { step_size = ramp_discrete_step_size; } - else { step_size = 1; } - - // turn LED on when we first enter the mode - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE) - momentary_mode = 0; // 0 = ramping, 1 = strobes - #endif - // if we just got back from config mode, go back to memorized level - if (event == EV_reenter_state) { - arg = memorized_level; - } - // remember this level, unless it's moon or turbo - if ((arg > mode_min) && (arg < mode_max)) - memorized_level = arg; - // use the requested level even if not memorized - arg = nearest_level(arg); - set_level_and_therm_target(arg); - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - return MISCHIEF_MANAGED; - } - #if (B_TIMING_OFF == B_RELEASE_T) - // 1 click (early): off, if configured for early response - else if (event == EV_click1_release) { - level_before_off = actual_level; - set_level_and_therm_target(0); - return MISCHIEF_MANAGED; - } - // 2 clicks (early): abort turning off, if configured for early response - else if (event == EV_click2_press) { - set_level_and_therm_target(level_before_off); - return MISCHIEF_MANAGED; - } - #endif // if (B_TIMING_OFF == B_RELEASE_T) - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - uint8_t turbo_level; - #ifdef USE_SIMPLE_UI - if (simple_ui_active) { turbo_level = mode_max; } - else - #endif - turbo_level = MAX_LEVEL; - - if (actual_level < turbo_level) { - // true turbo, not the mode-specific ceiling - set_level_and_therm_target(turbo_level); - } - else { - set_level_and_therm_target(memorized_level); - } - return MISCHIEF_MANAGED; - } - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return MISCHIEF_MANAGED; - } - #ifdef USE_REVERSING - // fix ramp direction on first frame if necessary - if (!arg) { - // make it ramp down instead, if already at max - if (actual_level >= mode_max) { ramp_direction = -1; } - // make it ramp up if already at min - // (off->hold->stepped_min->release causes this state) - else if (actual_level <= mode_min) { ramp_direction = 1; } - } - // if the button is stuck, err on the side of safety and ramp down - else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) { - ramp_direction = -1; - } - // if the button is still stuck, lock the light - else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) { - blip(); - set_state(lockout_state, 0); - } - memorized_level = nearest_level((int16_t)actual_level \ - + (step_size * ramp_direction)); - #else - memorized_level = nearest_level((int16_t)actual_level + step_size); - #endif - #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_CEIL - || (memorized_level == mode_max) - #endif - #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) - || (memorized_level == mode_min) - #endif - )) { - blip(); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - blip(); - } - #endif - set_level_and_therm_target(memorized_level); - return MISCHIEF_MANAGED; - } - #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL) - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - #ifdef USE_REVERSING - ramp_direction = -ramp_direction; - #endif - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - return MISCHIEF_MANAGED; - } - #endif - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return MISCHIEF_MANAGED; - } - // TODO? make it ramp up instead, if already at min? - memorized_level = nearest_level((int16_t)actual_level - step_size); - #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_FLOOR - || (memorized_level == mode_min) - #endif - )) { - blip(); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - blip(); - } - #endif - set_level_and_therm_target(memorized_level); - return MISCHIEF_MANAGED; - } - #ifdef START_AT_MEMORIZED_LEVEL - // click, release, hold, release: save new ramp level (if necessary) - else if (event == EV_click2_hold_release) { - save_config_wl(); - return MISCHIEF_MANAGED; - } - #endif - - #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) - else if (event == EV_tick) { - #ifdef USE_REVERSING - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - #endif - #ifdef USE_SET_LEVEL_GRADUALLY - int16_t diff = gradual_target - actual_level; - static uint16_t ticks_since_adjust = 0; - ticks_since_adjust++; - if (diff) { - uint16_t ticks_per_adjust = 256; - if (diff < 0) { - //diff = -diff; - if (actual_level > THERM_FASTER_LEVEL) { - #ifdef THERM_HARD_TURBO_DROP - ticks_per_adjust >>= 2; - #endif - ticks_per_adjust >>= 2; - } - } else { - // rise at half speed - ticks_per_adjust <<= 1; - } - while (diff) { - ticks_per_adjust >>= 1; - //diff >>= 1; - diff /= 2; // because shifting produces weird behavior - } - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; - } - } - #endif // ifdef USE_SET_LEVEL_GRADUALLY - return MISCHIEF_MANAGED; - } - #endif - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - #if 0 - blip(); - #endif - #ifdef THERM_HARD_TURBO_DROP - //if (actual_level > THERM_FASTER_LEVEL) { - if (actual_level == MAX_LEVEL) { - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(THERM_FASTER_LEVEL); - target_level = THERM_FASTER_LEVEL; - #else - set_level_and_therm_target(THERM_FASTER_LEVEL); - #endif - } else - #endif - if (actual_level > MIN_THERM_STEPDOWN) { - int16_t stepdown = actual_level - arg; - if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; - else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepdown); - #else - set_level(stepdown); - #endif - } - return MISCHIEF_MANAGED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - #if 0 - blip(); - #endif - if (actual_level < target_level) { - //int16_t stepup = actual_level + (arg>>1); - int16_t stepup = actual_level + arg; - if (stepup > target_level) stepup = target_level; - else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepup); - #else - set_level(stepup); - #endif - } - return MISCHIEF_MANAGED; - } - #ifdef USE_SET_LEVEL_GRADUALLY - // temperature is within target window - // (so stop trying to adjust output) - else if (event == EV_temperature_okay) { - // if we're still adjusting output... stop after the current step - if (gradual_target > actual_level) - gradual_target = actual_level + 1; - else if (gradual_target < actual_level) - gradual_target = actual_level - 1; - return MISCHIEF_MANAGED; - } - #endif // ifdef USE_SET_LEVEL_GRADUALLY - #endif // ifdef USE_THERMAL_REGULATION - - ////////// Every action below here is blocked in the simple UI ////////// - #ifdef USE_SIMPLE_UI - if (simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - // 3 clicks: toggle smooth vs discrete ramping - else if (event == EV_3clicks) { - ramp_style = !ramp_style; - save_config(); - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - blip(); - memorized_level = nearest_level(actual_level); - set_level_and_therm_target(memorized_level); - return MISCHIEF_MANAGED; - } - - #ifdef USE_MANUAL_MEMORY - else if (event == EV_5clicks) { - manual_memory = actual_level; - save_config(); - blip(); - return MISCHIEF_MANAGED; - } - else if (event == EV_click5_hold) { - if (0 == arg) { - manual_memory = 0; - save_config(); - blip(); - } - return MISCHIEF_MANAGED; - } - #endif - - #ifdef USE_RAMP_CONFIG - // 7 clicks: configure this ramp mode - else if (event == EV_7clicks) { - push_state(ramp_config_state, 0); - return MISCHIEF_MANAGED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_SOS_MODE -#ifdef USE_SOS_MODE_IN_BLINKY_GROUP -uint8_t sos_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // 2 clicks: next mode - else if (event == EV_2clicks) { - #ifdef USE_THERMAL_REGULATION - set_state(tempcheck_state, 0); - #else - set_state(battcheck_state, 0); - #endif - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} +#ifdef USE_GOODNIGHT_MODE +#include "goodnight-mode.c" #endif -void sos_blink(uint8_t num, uint8_t dah) { - #define DIT_LENGTH 200 - for (; num > 0; num--) { - set_level(memorized_level); - nice_delay_ms(DIT_LENGTH); - if (dah) { // dah is 3X as long as a dit - nice_delay_ms(DIT_LENGTH*2); - } - set_level(0); - // one "off" dit between blinks - nice_delay_ms(DIT_LENGTH); - } - // three "off" dits (or one "dah") between letters - // (except for SOS, which is collectively treated as a single "letter") - //nice_delay_ms(DIT_LENGTH*2); -} - -inline void sos_mode_iter() { - // one iteration of main loop() - //nice_delay_ms(1000); - sos_blink(3, 0); // S - sos_blink(3, 1); // O - sos_blink(3, 0); // S - nice_delay_ms(2000); -} -#endif // #ifdef USE_SOS_MODE - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - ////////// Every action below here is blocked in the simple UI ////////// - #ifdef USE_SIMPLE_UI - if (simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - #ifdef USE_GOODNIGHT_MODE - // 2 clicks: goodnight mode - else if (event == EV_2clicks) { - set_state(goodnight_state, 0); - return MISCHIEF_MANAGED; - } - #elif defined(USE_BEACON_MODE) - // 2 clicks: beacon mode - else if (event == EV_2clicks) { - set_state(beacon_state, 0); - return MISCHIEF_MANAGED; - } - #elif defined(USE_THERMAL_REGULATION) - // 2 clicks: tempcheck mode - else if (event == EV_2clicks) { - set_state(tempcheck_state, 0); - return MISCHIEF_MANAGED; - } - #endif - return EVENT_NOT_HANDLED; -} +#ifdef USE_BEACON_MODE +#include "beacon.c" #endif - #ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - #ifdef USE_BATTCHECK - // 2 clicks: battcheck mode - else if (event == EV_2clicks) { - set_state(battcheck_state, 0); - return MISCHIEF_MANAGED; - } - #endif - // 7 clicks: thermal config mode - else if (event == EV_7clicks) { - push_state(thermal_config_state, 0); - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} +#include "tempcheck-mode.c" #endif - -#ifdef USE_GOODNIGHT_MODE -#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) -uint8_t goodnight_state(Event event, uint16_t arg) { - static uint16_t ticks_since_stepdown = 0; - // blink on start - if (event == EV_enter_state) { - ticks_since_stepdown = 0; - blink_confirm(2); - set_level(GOODNIGHT_LEVEL); - return MISCHIEF_MANAGED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // 2 clicks: next mode - else if (event == EV_2clicks) { - #ifdef USE_BEACON_MODE - set_state(beacon_state, 0); - #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP) - set_state(sos_state, 0); - #elif defined(USE_THERMAL_REGULATION) - set_state(tempcheck_state, 0); - #endif - return MISCHIEF_MANAGED; - } - // tick: step down (maybe) or off (maybe) - else if (event == EV_tick) { - if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) { - ticks_since_stepdown = 0; - set_level(actual_level-1); - if (! actual_level) { - #if 0 // test blink, to help measure timing - set_level(MAX_LEVEL>>2); - delay_4ms(8/2); - set_level(0); - #endif - set_state(off_state, 0); - } - } - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} +#ifdef USE_LOCKOUT_MODE +#include "lockout.c" #endif - #ifdef USE_MOMENTARY_MODE -uint8_t momentary_state(Event event, uint16_t arg) { - // TODO: momentary strobe here? (for light painting) - - // init strobe mode, if relevant - #ifdef USE_STROBE_STATE - if ((event == EV_enter_state) && (momentary_mode == 1)) { - strobe_state(event, arg); - } - #endif - - // light up when the button is pressed; go dark otherwise - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - momentary_active = 1; - // 0 = ramping, 1 = strobes - if (momentary_mode == 0) { - set_level(memorized_level); - } - return MISCHIEF_MANAGED; - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - momentary_active = 0; - set_level(0); - //go_to_standby = 1; // sleep while light is off - return MISCHIEF_MANAGED; - } - - // Sleep, dammit! (but wait a few seconds first) - // (because standby mode uses such little power that it can interfere - // with exiting via tailcap loosen+tighten unless you leave power - // disconnected for several seconds, so we want to be awake when that - // happens to speed up the process) - else if (event == EV_tick) { - #ifdef USE_STROBE_STATE - if (momentary_active) { - // 0 = ramping, 1 = strobes - if (momentary_mode == 1) { - return strobe_state(event, arg); - } - } - else { - #endif - if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds - go_to_standby = 1; // sleep while light is off - // turn off lighted button - #ifdef USE_INDICATOR_LED - indicator_led(0); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(0, 0); - #endif - } - #ifdef USE_STROBE_STATE - } - #endif - return MISCHIEF_MANAGED; - } - - return EVENT_NOT_HANDLED; -} +#include "momentary-mode.c" #endif - -#ifdef USE_VERSION_CHECK -uint8_t version_check_state(Event event, uint16_t arg) { - return EVENT_NOT_HANDLED; -} +#ifdef USE_TINT_RAMPING +#include "tint-ramping.c" #endif - -#ifdef USE_RAMP_CONFIG -void ramp_config_save() { - // parse values - uint8_t val; - uint8_t style = ramp_style; - // TODO: detect if we're configuring the simple UI - - val = config_state_values[0]; - if (val) { ramp_floors[style] = val; } - - val = config_state_values[1]; - if (val) { ramp_ceils[style] = MAX_LEVEL + 1 - val; } - - if (ramp_style) { // discrete / stepped ramp - val = config_state_values[2]; - if (val) ramp_stepss[style] = val; - } -} - -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, - num_config_steps, ramp_config_save); -} -#endif // #ifdef USE_RAMP_CONFIG - - -#ifdef USE_THERMAL_REGULATION -void thermal_config_save() { - // parse values - uint8_t val; - - // calibrate room temperature - val = config_state_values[0]; - if (val) { - int8_t rawtemp = temperature - therm_cal_offset; - therm_cal_offset = val - rawtemp; - reset_thermal_history = 1; // invalidate all recent temperature data - } - - val = config_state_values[1]; - if (val) { - // set maximum heat limit - therm_ceil = 30 + val - 1; - } - if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; -} - -uint8_t thermal_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 2, thermal_config_save); -} -#endif // #ifdef USE_THERMAL_REGULATION - - -// find the ramp level closest to the target, -// using only the levels which are allowed in the current state -uint8_t nearest_level(int16_t target) { - ramp_update_config(); - - // bounds check - // using int16_t here saves us a bunch of logic elsewhere, - // by allowing us to correct for numbers < 0 or > 255 in one central place - uint8_t mode_min = ramp_floor; - uint8_t mode_max = ramp_ceil; - if (target < mode_min) return mode_min; - if (target > mode_max) return mode_max; - // the rest isn't relevant for smooth ramping - if (! ramp_style) return target; - - uint8_t ramp_range = mode_max - mode_min; - uint8_t num_steps = ramp_stepss[1 + simple_ui_active]; - ramp_discrete_step_size = ramp_range / (num_steps-1); - uint8_t this_level = mode_min; - - for(uint8_t i=0; i<num_steps; i++) { - this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1)); - int16_t diff = target - this_level; - if (diff < 0) diff = -diff; - if (diff <= (ramp_discrete_step_size>>1)) - return this_level; - } - return this_level; -} - -// ensure ramp globals are correct -void ramp_update_config() { - uint8_t which = ramp_style; - if (simple_ui_active) { which = 2; } - - ramp_floor = ramp_floors[which]; - ramp_ceil = ramp_ceils[which]; -} - -#ifdef USE_THERMAL_REGULATION -void set_level_and_therm_target(uint8_t level) { - target_level = level; - set_level(level); -} +#ifdef USE_FACTORY_RESET +#include "factory-reset.c" #endif -void blink_confirm(uint8_t num) { - for (; num>0; num--) { - set_level(MAX_LEVEL/4); - delay_4ms(10/4); - set_level(0); - // TODO: only do this delay if num > 1 - delay_4ms(100/4); - } -} +#ifdef USE_STROBE_STATE +#include "strobes.c" +#endif -// Just go dark for a moment to indicate to user that something happened -void blip() { - uint8_t temp = actual_level; - set_level(0); - delay_4ms(3); - set_level(temp); -} +#ifdef USE_SOS_MODE +#include "sos-mode.c" +#endif #if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) @@ -1527,113 +397,6 @@ void rgb_led_voltage_readout(uint8_t bright) { #endif -void load_config() { - if (load_eeprom()) { - ramp_style = eeprom[ramp_style_e]; - #ifdef USE_RAMP_CONFIG - ramp_floors[0] = eeprom[ramp_smooth_floor_e]; - ramp_ceils[0] = eeprom[ramp_smooth_ceil_e]; - ramp_floors[1] = eeprom[ramp_discrete_floor_e]; - ramp_ceils[1] = eeprom[ramp_discrete_ceil_e]; - ramp_stepss[1] = eeprom[ramp_discrete_steps_e]; - #endif - #ifdef USE_MANUAL_MEMORY - manual_memory = eeprom[manual_memory_e]; - #endif - #ifdef USE_TINT_RAMPING - tint = eeprom[tint_e]; - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl? - strobe_delays[0] = eeprom[strobe_delays_0_e]; - strobe_delays[1] = eeprom[strobe_delays_1_e]; - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_brightness = eeprom[bike_flasher_brightness_e]; - #endif - #ifdef USE_BEACON_MODE - beacon_seconds = eeprom[beacon_seconds_e]; - #endif - #ifdef USE_SIMPLE_UI - simple_ui_active = eeprom[simple_ui_active_e]; - ramp_floors[2] = eeprom[simple_ui_floor_e]; - ramp_ceils[2] = eeprom[simple_ui_ceil_e]; - ramp_stepss[2] = eeprom[simple_ui_steps_e]; - #endif - #ifdef USE_THERMAL_REGULATION - therm_ceil = eeprom[therm_ceil_e]; - therm_cal_offset = eeprom[therm_cal_offset_e]; - #endif - #ifdef USE_INDICATOR_LED - indicator_led_mode = eeprom[indicator_led_mode_e]; - #endif - #ifdef USE_AUX_RGB_LEDS - rgb_led_off_mode = eeprom[rgb_led_off_mode_e]; - rgb_led_lockout_mode = eeprom[rgb_led_lockout_mode_e]; - #endif - } - #ifdef START_AT_MEMORIZED_LEVEL - if (load_eeprom_wl()) { - memorized_level = eeprom_wl[0]; - } - #endif -} - -void save_config() { - eeprom[ramp_style_e] = ramp_style; - #ifdef USE_RAMP_CONFIG - eeprom[ramp_smooth_floor_e] = ramp_floors[0]; - eeprom[ramp_smooth_ceil_e] = ramp_ceils[0]; - eeprom[ramp_discrete_floor_e] = ramp_floors[1]; - eeprom[ramp_discrete_ceil_e] = ramp_ceils[1]; - eeprom[ramp_discrete_steps_e] = ramp_stepss[1]; - #endif - #ifdef USE_MANUAL_MEMORY - eeprom[manual_memory_e] = manual_memory; - #endif - #ifdef USE_TINT_RAMPING - eeprom[tint_e] = tint; - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl? - eeprom[strobe_delays_0_e] = strobe_delays[0]; - eeprom[strobe_delays_1_e] = strobe_delays[1]; - #endif - #ifdef USE_BIKE_FLASHER_MODE - eeprom[bike_flasher_brightness_e] = bike_flasher_brightness; - #endif - #ifdef USE_BEACON_MODE - eeprom[beacon_seconds_e] = beacon_seconds; - #endif - #ifdef USE_SIMPLE_UI - eeprom[simple_ui_active_e] = simple_ui_active; - eeprom[simple_ui_floor_e] = ramp_floors[2]; - eeprom[simple_ui_ceil_e] = ramp_ceils[2]; - eeprom[simple_ui_steps_e] = ramp_stepss[2]; - #endif - #ifdef USE_THERMAL_REGULATION - eeprom[therm_ceil_e] = therm_ceil; - eeprom[therm_cal_offset_e] = therm_cal_offset; - #endif - #ifdef USE_INDICATOR_LED - eeprom[indicator_led_mode_e] = indicator_led_mode; - #endif - #ifdef USE_AUX_RGB_LEDS - eeprom[rgb_led_off_mode_e] = rgb_led_off_mode; - eeprom[rgb_led_lockout_mode_e] = rgb_led_lockout_mode; - #endif - - save_eeprom(); -} - -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl() { - eeprom_wl[0] = memorized_level; - save_eeprom_wl(); -} -#endif - - void low_voltage() { StatePtr state = current_state; @@ -1715,19 +478,9 @@ void loop() { #ifdef USE_VERSION_CHECK else if (state == version_check_state) { - for (uint8_t i=0; i<sizeof(version_number)-1; i++) { - blink_digit(pgm_read_byte(version_number + i) - '0'); - nice_delay_ms(300); - } - // FIXME: when user interrupts with button, "off" takes an extra click - // before it'll turn back on, because the click to cancel gets sent - // to the "off" state instead of version_check_state - //while (button_is_pressed()) {} - //empty_event_sequence(); - - set_state(off_state, 0); + version_check_iter(); } - #endif // #ifdef USE_VERSION_CHECK + #endif #ifdef USE_STROBE_STATE else if ((state == strobe_state) @@ -1736,33 +489,7 @@ void loop() { || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) #endif ) { - uint8_t st = strobe_type; - - switch(st) { - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_PARTY_STROBE_MODE - case party_strobe_e: - #endif - #ifdef USE_TACTICAL_STROBE_MODE - case tactical_strobe_e: - #endif - party_tactical_strobe_mode_iter(st); - break; - #endif - - #ifdef USE_LIGHTNING_MODE - case lightning_storm_e: - lightning_storm_iter(); - break; - #endif - - #ifdef USE_BIKE_FLASHER_MODE - case bike_flasher_e: - bike_flasher_iter(); - break; - #endif - } - + strobe_state_iter(); } #endif // #ifdef USE_STROBE_STATE diff --git a/spaghetti-monster/anduril/battcheck-mode-fsm.h b/spaghetti-monster/anduril/battcheck-mode-fsm.h new file mode 100644 index 0000000..8f19e12 --- /dev/null +++ b/spaghetti-monster/anduril/battcheck-mode-fsm.h @@ -0,0 +1,26 @@ +/* + * battcheck-mode-fsm.h: FSM config for battery check mode in Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTCHECK_MODE_FSM_H +#define BATTCHECK_MODE_FSM_H + +#define USE_BATTCHECK + + +#endif diff --git a/spaghetti-monster/anduril/battcheck-mode.c b/spaghetti-monster/anduril/battcheck-mode.c new file mode 100644 index 0000000..3755249 --- /dev/null +++ b/spaghetti-monster/anduril/battcheck-mode.c @@ -0,0 +1,62 @@ +/* + * battcheck-mode.c: Battery check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTCHECK_MODE_C +#define BATTCHECK_MODE_C + +#include "battcheck-mode.h" + +uint8_t battcheck_state(Event event, uint16_t arg) { + ////////// Every action below here is blocked in the simple UI ////////// + #ifdef USE_SIMPLE_UI + if (simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + #ifdef USE_GOODNIGHT_MODE + // 2 clicks: goodnight mode + else if (event == EV_2clicks) { + set_state(goodnight_state, 0); + return MISCHIEF_MANAGED; + } + #elif defined(USE_BEACON_MODE) + // 2 clicks: beacon mode + else if (event == EV_2clicks) { + set_state(beacon_state, 0); + return MISCHIEF_MANAGED; + } + #elif defined(USE_THERMAL_REGULATION) + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + set_state(tempcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/battcheck-mode.h b/spaghetti-monster/anduril/battcheck-mode.h new file mode 100644 index 0000000..f57fa74 --- /dev/null +++ b/spaghetti-monster/anduril/battcheck-mode.h @@ -0,0 +1,26 @@ +/* + * battcheck-mode.h: Battery check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef BATTCHECK_MODE_H +#define BATTCHECK_MODE_H + +uint8_t battcheck_state(Event event, uint16_t arg); + + +#endif diff --git a/spaghetti-monster/anduril/cfg-default.h b/spaghetti-monster/anduril/cfg-default.h index 1405422..ee76c6e 100644 --- a/spaghetti-monster/anduril/cfg-default.h +++ b/spaghetti-monster/anduril/cfg-default.h @@ -58,11 +58,16 @@ // or the user can go back to automatic with click-click-click-click-hold) #define USE_MANUAL_MEMORY +// enable the battery check mode (shows remaining charge) +#define USE_BATTCHECK_MODE // battery readout style (pick one) #define BATTCHECK_VpT //#define BATTCHECK_8bars // FIXME: breaks build //#define BATTCHECK_4bars // FIXME: breaks build +// enable a mode for locking the light for safe carry +#define USE_LOCKOUT_MODE + // enable/disable various strobe modes #define USE_BIKE_FLASHER_MODE #define USE_PARTY_STROBE_MODE diff --git a/spaghetti-monster/anduril/config-mode.h b/spaghetti-monster/anduril/config-mode.h index 7bf51fc..81abf2c 100644 --- a/spaghetti-monster/anduril/config-mode.h +++ b/spaghetti-monster/anduril/config-mode.h @@ -20,8 +20,6 @@ #ifndef CONFIG_MODE_H #define CONFIG_MODE_H -#include "fsm-events.h" - #define MAX_CONFIG_VALUES 3 uint8_t config_state_values[MAX_CONFIG_VALUES]; diff --git a/spaghetti-monster/anduril/goodnight-mode.c b/spaghetti-monster/anduril/goodnight-mode.c new file mode 100644 index 0000000..2fe90e9 --- /dev/null +++ b/spaghetti-monster/anduril/goodnight-mode.c @@ -0,0 +1,72 @@ +/* + * goodnight-mode.c: Goodnight / sunset mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GOODNIGHT_MODE_C +#define GOODNIGHT_MODE_C + +#include "goodnight-mode.h" + +#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) +uint8_t goodnight_state(Event event, uint16_t arg) { + static uint16_t ticks_since_stepdown = 0; + // blink on start + if (event == EV_enter_state) { + ticks_since_stepdown = 0; + blink_confirm(2); + set_level(GOODNIGHT_LEVEL); + return MISCHIEF_MANAGED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: next mode + else if (event == EV_2clicks) { + #ifdef USE_BEACON_MODE + set_state(beacon_state, 0); + #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP) + set_state(sos_state, 0); + #elif defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #endif + return MISCHIEF_MANAGED; + } + // tick: step down (maybe) or off (maybe) + else if (event == EV_tick) { + if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) { + ticks_since_stepdown = 0; + set_level(actual_level-1); + if (! actual_level) { + #if 0 // test blink, to help measure timing + set_level(MAX_LEVEL>>2); + delay_4ms(8/2); + set_level(0); + #endif + set_state(off_state, 0); + } + } + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/goodnight-mode.h b/spaghetti-monster/anduril/goodnight-mode.h new file mode 100644 index 0000000..3bb5272 --- /dev/null +++ b/spaghetti-monster/anduril/goodnight-mode.h @@ -0,0 +1,27 @@ +/* + * goodnight-mode.h: Goodnight / sunset for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GOODNIGHT_MODE_H +#define GOODNIGHT_MODE_H + +// 1-hour ramp down from low, then automatic off +uint8_t goodnight_state(Event event, uint16_t arg); + + +#endif diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h new file mode 100644 index 0000000..7760048 --- /dev/null +++ b/spaghetti-monster/anduril/load-save-config-fsm.h @@ -0,0 +1,80 @@ +/* + * load-save-config-fsm.h: FSM config for eeprom configuration in Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LOAD_SAVE_CONFIG_FSM_H +#define LOAD_SAVE_CONFIG_FSM_H + +// auto-detect how many eeprom bytes +#define USE_EEPROM +typedef enum { + ramp_style_e, + #ifdef USE_RAMP_CONFIG + ramp_smooth_floor_e, + ramp_smooth_ceil_e, + ramp_discrete_floor_e, + ramp_discrete_ceil_e, + ramp_discrete_steps_e, + #endif + #ifdef USE_MANUAL_MEMORY + manual_memory_e, + #endif + #ifdef USE_TINT_RAMPING + tint_e, + #endif + #ifdef USE_STROBE_STATE + strobe_type_e, + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + strobe_delays_0_e, + strobe_delays_1_e, + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_brightness_e, + #endif + #ifdef USE_BEACON_MODE + beacon_seconds_e, + #endif + #ifdef USE_SIMPLE_UI + simple_ui_active_e, + simple_ui_floor_e, + simple_ui_ceil_e, + simple_ui_steps_e, + #endif + #ifdef USE_THERMAL_REGULATION + therm_ceil_e, + therm_cal_offset_e, + #endif + #ifdef USE_INDICATOR_LED + indicator_led_mode_e, + #endif + #ifdef USE_AUX_RGB_LEDS + rgb_led_off_mode_e, + rgb_led_lockout_mode_e, + #endif + eeprom_indexes_e_END +} eeprom_indexes_e; +#define EEPROM_BYTES eeprom_indexes_e_END + +#ifdef START_AT_MEMORIZED_LEVEL +#define USE_EEPROM_WL +#define EEPROM_WL_BYTES 1 +#endif + + +#endif diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c new file mode 100644 index 0000000..8cfb69d --- /dev/null +++ b/spaghetti-monster/anduril/load-save-config.c @@ -0,0 +1,134 @@ +/* + * load-save-config.c: Load/save/eeprom functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LOAD_SAVE_CONFIG_C +#define LOAD_SAVE_CONFIG_C + +#include "load-save-config-fsm.h" +#include "load-save-config.h" + +void load_config() { + if (load_eeprom()) { + ramp_style = eeprom[ramp_style_e]; + #ifdef USE_RAMP_CONFIG + ramp_floors[0] = eeprom[ramp_smooth_floor_e]; + ramp_ceils[0] = eeprom[ramp_smooth_ceil_e]; + ramp_floors[1] = eeprom[ramp_discrete_floor_e]; + ramp_ceils[1] = eeprom[ramp_discrete_ceil_e]; + ramp_stepss[1] = eeprom[ramp_discrete_steps_e]; + #endif + #ifdef USE_MANUAL_MEMORY + manual_memory = eeprom[manual_memory_e]; + #endif + #ifdef USE_TINT_RAMPING + tint = eeprom[tint_e]; + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl? + strobe_delays[0] = eeprom[strobe_delays_0_e]; + strobe_delays[1] = eeprom[strobe_delays_1_e]; + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_brightness = eeprom[bike_flasher_brightness_e]; + #endif + #ifdef USE_BEACON_MODE + beacon_seconds = eeprom[beacon_seconds_e]; + #endif + #ifdef USE_SIMPLE_UI + simple_ui_active = eeprom[simple_ui_active_e]; + ramp_floors[2] = eeprom[simple_ui_floor_e]; + ramp_ceils[2] = eeprom[simple_ui_ceil_e]; + ramp_stepss[2] = eeprom[simple_ui_steps_e]; + #endif + #ifdef USE_THERMAL_REGULATION + therm_ceil = eeprom[therm_ceil_e]; + therm_cal_offset = eeprom[therm_cal_offset_e]; + #endif + #ifdef USE_INDICATOR_LED + indicator_led_mode = eeprom[indicator_led_mode_e]; + #endif + #ifdef USE_AUX_RGB_LEDS + rgb_led_off_mode = eeprom[rgb_led_off_mode_e]; + rgb_led_lockout_mode = eeprom[rgb_led_lockout_mode_e]; + #endif + } + #ifdef START_AT_MEMORIZED_LEVEL + if (load_eeprom_wl()) { + memorized_level = eeprom_wl[0]; + } + #endif +} + +void save_config() { + eeprom[ramp_style_e] = ramp_style; + #ifdef USE_RAMP_CONFIG + eeprom[ramp_smooth_floor_e] = ramp_floors[0]; + eeprom[ramp_smooth_ceil_e] = ramp_ceils[0]; + eeprom[ramp_discrete_floor_e] = ramp_floors[1]; + eeprom[ramp_discrete_ceil_e] = ramp_ceils[1]; + eeprom[ramp_discrete_steps_e] = ramp_stepss[1]; + #endif + #ifdef USE_MANUAL_MEMORY + eeprom[manual_memory_e] = manual_memory; + #endif + #ifdef USE_TINT_RAMPING + eeprom[tint_e] = tint; + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl? + eeprom[strobe_delays_0_e] = strobe_delays[0]; + eeprom[strobe_delays_1_e] = strobe_delays[1]; + #endif + #ifdef USE_BIKE_FLASHER_MODE + eeprom[bike_flasher_brightness_e] = bike_flasher_brightness; + #endif + #ifdef USE_BEACON_MODE + eeprom[beacon_seconds_e] = beacon_seconds; + #endif + #ifdef USE_SIMPLE_UI + eeprom[simple_ui_active_e] = simple_ui_active; + eeprom[simple_ui_floor_e] = ramp_floors[2]; + eeprom[simple_ui_ceil_e] = ramp_ceils[2]; + eeprom[simple_ui_steps_e] = ramp_stepss[2]; + #endif + #ifdef USE_THERMAL_REGULATION + eeprom[therm_ceil_e] = therm_ceil; + eeprom[therm_cal_offset_e] = therm_cal_offset; + #endif + #ifdef USE_INDICATOR_LED + eeprom[indicator_led_mode_e] = indicator_led_mode; + #endif + #ifdef USE_AUX_RGB_LEDS + eeprom[rgb_led_off_mode_e] = rgb_led_off_mode; + eeprom[rgb_led_lockout_mode_e] = rgb_led_lockout_mode; + #endif + + save_eeprom(); +} + +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl() { + eeprom_wl[0] = memorized_level; + save_eeprom_wl(); +} +#endif + + +#endif + diff --git a/spaghetti-monster/anduril/load-save-config.h b/spaghetti-monster/anduril/load-save-config.h new file mode 100644 index 0000000..29c1a69 --- /dev/null +++ b/spaghetti-monster/anduril/load-save-config.h @@ -0,0 +1,31 @@ +/* + * load-save-config.h: Load/save/eeprom functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LOAD_SAVE_CONFIG_H +#define LOAD_SAVE_CONFIG_H + +// remember stuff even after battery was changed +void load_config(); +void save_config(); +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl(); +#endif + + +#endif diff --git a/spaghetti-monster/anduril/misc.c b/spaghetti-monster/anduril/misc.c new file mode 100644 index 0000000..a03225c --- /dev/null +++ b/spaghetti-monster/anduril/misc.c @@ -0,0 +1,45 @@ +/* + * misc.c: Misc extra functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MISC_C +#define MISC_C + +#include "misc.h" + +void blink_confirm(uint8_t num) { + for (; num>0; num--) { + set_level(MAX_LEVEL/4); + delay_4ms(10/4); + set_level(0); + // TODO: only do this delay if num > 1 + delay_4ms(100/4); + } +} + +// Just go dark for a moment to indicate to user that something happened +void blip() { + uint8_t temp = actual_level; + set_level(0); + delay_4ms(3); + set_level(temp); +} + + +#endif + diff --git a/spaghetti-monster/anduril/misc.h b/spaghetti-monster/anduril/misc.h new file mode 100644 index 0000000..e582a71 --- /dev/null +++ b/spaghetti-monster/anduril/misc.h @@ -0,0 +1,27 @@ +/* + * misc.h: Misc extra functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MISC_H +#define MISC_H + +void blink_confirm(uint8_t num); +void blip(); + + +#endif diff --git a/spaghetti-monster/anduril/momentary-mode.c b/spaghetti-monster/anduril/momentary-mode.c new file mode 100644 index 0000000..8d111c7 --- /dev/null +++ b/spaghetti-monster/anduril/momentary-mode.c @@ -0,0 +1,86 @@ +/* + * momentary-mode.c: Momentary mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MOMENTARY_MODE_C +#define MOMENTARY_MODE_C + +#include "momentary-mode.h" + +uint8_t momentary_state(Event event, uint16_t arg) { + // init strobe mode, if relevant + #ifdef USE_STROBE_STATE + if ((event == EV_enter_state) && (momentary_mode == 1)) { + strobe_state(event, arg); + } + #endif + + // light up when the button is pressed; go dark otherwise + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + momentary_active = 1; + // 0 = ramping, 1 = strobes + if (momentary_mode == 0) { + set_level(memorized_level); + } + return MISCHIEF_MANAGED; + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + momentary_active = 0; + set_level(0); + //go_to_standby = 1; // sleep while light is off + return MISCHIEF_MANAGED; + } + + // Sleep, dammit! (but wait a few seconds first) + // (because standby mode uses such little power that it can interfere + // with exiting via tailcap loosen+tighten unless you leave power + // disconnected for several seconds, so we want to be awake when that + // happens to speed up the process) + else if (event == EV_tick) { + #ifdef USE_STROBE_STATE + if (momentary_active) { + // 0 = ramping, 1 = strobes + if (momentary_mode == 1) { + return strobe_state(event, arg); + } + } + else { + #endif + if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds + go_to_standby = 1; // sleep while light is off + // turn off lighted button + #ifdef USE_INDICATOR_LED + indicator_led(0); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(0, 0); + #endif + } + #ifdef USE_STROBE_STATE + } + #endif + return MISCHIEF_MANAGED; + } + + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/momentary-mode.h b/spaghetti-monster/anduril/momentary-mode.h new file mode 100644 index 0000000..c5ccf0f --- /dev/null +++ b/spaghetti-monster/anduril/momentary-mode.h @@ -0,0 +1,29 @@ +/* + * momentary-mode.h: Momentary mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MOMENTARY_MODE_H +#define MOMENTARY_MODE_H + +// momentary / signalling mode +uint8_t momentary_state(Event event, uint16_t arg); +uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe +uint8_t momentary_active = 0; // boolean, true if active *right now* + + +#endif diff --git a/spaghetti-monster/anduril/off-state.c b/spaghetti-monster/anduril/off-state.c new file mode 100644 index 0000000..9bbdc81 --- /dev/null +++ b/spaghetti-monster/anduril/off-state.c @@ -0,0 +1,261 @@ +/* + * off-state.c: "Off" state for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OFF_STATE_C +#define OFF_STATE_C + +#include "off-state.h" + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(rgb_led_off_mode, 0); + #endif + // sleep while off (lower power use) + // (unless delay requested; give the ADC some time to catch up) + if (! arg) { go_to_standby = 1; } + return MISCHIEF_MANAGED; + } + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > HOLD_TIMEOUT) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(rgb_led_off_mode, arg); + #endif + } + return MISCHIEF_MANAGED; + } + #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) + // blink the indicator LED, maybe + else if (event == EV_sleep_tick) { + #ifdef USE_INDICATOR_LED + if ((indicator_led_mode & 0b00000011) == 0b00000011) { + indicator_blink(arg); + } + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(rgb_led_off_mode, arg); + #endif + return MISCHIEF_MANAGED; + } + #endif + #if (B_TIMING_ON == B_PRESS_T) + // hold (initially): go to lowest level (floor), but allow abort for regular click + else if (event == EV_click1_press) { + set_level(nearest_level(1)); + return MISCHIEF_MANAGED; + } + #endif // B_TIMING_ON == B_PRESS_T + // hold: go to lowest level + else if (event == EV_click1_hold) { + #if (B_TIMING_ON == B_PRESS_T) + #ifdef MOON_TIMING_HINT + if (arg == 0) { + // let the user know they can let go now to stay at moon + blip(); + } else + #endif + #else // B_RELEASE_T or B_TIMEOUT_T + set_level(nearest_level(1)); + #endif + // don't start ramping immediately; + // give the user time to release at moon level + //if (arg >= HOLD_TIMEOUT) { // smaller + if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent + set_state(steady_state, 1); + } + return MISCHIEF_MANAGED; + } + // hold, release quickly: go to lowest level (floor) + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return MISCHIEF_MANAGED; + } + #if (B_TIMING_ON != B_TIMEOUT_T) + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + #ifdef USE_MANUAL_MEMORY + if (manual_memory) + set_level(nearest_level(manual_memory)); + else + #endif + set_level(nearest_level(memorized_level)); + return MISCHIEF_MANAGED; + } + #endif // if (B_TIMING_ON != B_TIMEOUT_T) + // 1 click: regular mode + else if (event == EV_1click) { + #ifdef USE_MANUAL_MEMORY + if (manual_memory) + set_state(steady_state, manual_memory); + else + #endif + set_state(steady_state, memorized_level); + return MISCHIEF_MANAGED; + } + // click, hold: go to highest level (ceiling) (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return MISCHIEF_MANAGED; + } + // 2 clicks: highest mode (ceiling) + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return MISCHIEF_MANAGED; + } + // 3 clicks (initial press): off, to prep for later events + else if (event == EV_click3_press) { + set_level(0); + return MISCHIEF_MANAGED; + } + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group 1 + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif + #ifdef USE_LOCKOUT_MODE + // 4 clicks: soft lockout + else if (event == EV_4clicks) { + blink_confirm(2); + set_state(lockout_state, 0); + return MISCHIEF_MANAGED; + } + #endif + #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET) + // 13 clicks and hold the last click: invoke factory reset (reboot) + else if (event == EV_click13_hold) { + reboot(); + return MISCHIEF_MANAGED; + } + #endif + #ifdef USE_VERSION_CHECK + // 15+ clicks: show the version number + else if (event == EV_15clicks) { + set_state(version_check_state, 0); + return MISCHIEF_MANAGED; + } + #endif + + #ifdef USE_SIMPLE_UI + // 8 clicks, but hold last click: turn simple UI off + else if ((event == EV_click8_hold) && (!arg)) { + blink_confirm(1); + simple_ui_active = 0; + return MISCHIEF_MANAGED; + } + + ////////// Every action below here is blocked in the simple UI ////////// + if (simple_ui_active) { + return EVENT_NOT_HANDLED; + } + // 8 clicks: enable simple UI + else if (event == EV_8clicks) { + blink_confirm(1); + simple_ui_active = 1; + return MISCHIEF_MANAGED; + } + #endif + + // click, click, long-click: strobe mode + #ifdef USE_STROBE_STATE + else if (event == EV_click3_hold) { + set_state(strobe_state, 0); + return MISCHIEF_MANAGED; + } + #elif defined(USE_BORING_STROBE_STATE) + else if (event == EV_click3_hold) { + set_state(boring_strobe_state, 0); + return MISCHIEF_MANAGED; + } + #endif + #ifdef USE_MOMENTARY_MODE + // 5 clicks: momentary mode + else if (event == EV_5clicks) { + blink_confirm(1); + set_state(momentary_state, 0); + return MISCHIEF_MANAGED; + } + #endif + #ifdef USE_INDICATOR_LED + // 7 clicks: change indicator LED mode + else if (event == EV_7clicks) { + uint8_t mode = (indicator_led_mode & 3) + 1; + #ifdef TICK_DURING_STANDBY + mode = mode & 3; + #else + mode = mode % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; + indicator_led(mode); + save_config(); + return MISCHIEF_MANAGED; + } + #elif defined(USE_AUX_RGB_LEDS) + // 7 clicks: change RGB aux LED pattern + else if (event == EV_7clicks) { + uint8_t mode = (rgb_led_off_mode >> 4) + 1; + mode = mode % RGB_LED_NUM_PATTERNS; + rgb_led_off_mode = (mode << 4) | (rgb_led_off_mode & 0x0f); + rgb_led_update(rgb_led_off_mode, 0); + save_config(); + blink_confirm(1); + return MISCHIEF_MANAGED; + } + // 7 clicks (hold last): change RGB aux LED color + else if (event == EV_click7_hold) { + setting_rgb_mode_now = 1; + if (0 == (arg & 0x3f)) { + uint8_t mode = (rgb_led_off_mode & 0x0f) + 1; + mode = mode % RGB_LED_NUM_COLORS; + rgb_led_off_mode = mode | (rgb_led_off_mode & 0xf0); + //save_config(); + } + rgb_led_update(rgb_led_off_mode, arg); + return MISCHIEF_MANAGED; + } + else if (event == EV_click7_hold_release) { + setting_rgb_mode_now = 0; + save_config(); + return MISCHIEF_MANAGED; + } + #endif // end 7 clicks + #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION) + // 10 clicks: thermal config mode + else if (event == EV_10clicks) { + push_state(thermal_config_state, 0); + return MISCHIEF_MANAGED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/off-state.h b/spaghetti-monster/anduril/off-state.h new file mode 100644 index 0000000..2bd7508 --- /dev/null +++ b/spaghetti-monster/anduril/off-state.h @@ -0,0 +1,27 @@ +/* + * off-state.h: "Off" state for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OFF_STATE_H +#define OFF_STATE_H + +// handles when the light is "off" or in standby +uint8_t off_state(Event event, uint16_t arg); + + +#endif diff --git a/spaghetti-monster/anduril/ramping-fsm.h b/spaghetti-monster/anduril/ramping-fsm.h new file mode 100644 index 0000000..97b4321 --- /dev/null +++ b/spaghetti-monster/anduril/ramping-fsm.h @@ -0,0 +1,37 @@ +/* + * ramping-fsm.h: FSM config for ramping functions in Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RAMPING_FSM_H +#define RAMPING_FSM_H + +#define USE_RAMPING + +#ifdef USE_THERMAL_REGULATION +#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments +#endif + +// brightness to use when no memory is set +// FIXME: this is only here to stop an error in fsm-ramping.c, +// which should be fixed by using a different symbol instead +#ifndef DEFAULT_LEVEL +#define DEFAULT_LEVEL MAX_1x7135 +#endif + + +#endif diff --git a/spaghetti-monster/anduril/ramping.c b/spaghetti-monster/anduril/ramping.c new file mode 100644 index 0000000..f2a23c8 --- /dev/null +++ b/spaghetti-monster/anduril/ramping.c @@ -0,0 +1,460 @@ +/* + * ramping.c: Ramping functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RAMPING_C +#define RAMPING_C + +#include "ramping.h" + +uint8_t steady_state(Event event, uint16_t arg) { + #ifdef USE_REVERSING + static int8_t ramp_direction = 1; + #endif + #if (B_TIMING_OFF == B_RELEASE_T) + // if the user double clicks, we need to abort turning off, + // and this stores the level to return to + static uint8_t level_before_off = 0; + #endif + + // make sure ramp globals are correct... + // ... but they already are; no need to do it here + //ramp_update_config(); + //nearest_level(1); // same effect, takes less space + + uint8_t mode_min = ramp_floor; + uint8_t mode_max = ramp_ceil; + uint8_t step_size; + if (ramp_style) { step_size = ramp_discrete_step_size; } + else { step_size = 1; } + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE) + momentary_mode = 0; // 0 = ramping, 1 = strobes + #endif + // if we just got back from config mode, go back to memorized level + if (event == EV_reenter_state) { + arg = memorized_level; + } + // remember this level, unless it's moon or turbo + if ((arg > mode_min) && (arg < mode_max)) + memorized_level = arg; + // use the requested level even if not memorized + arg = nearest_level(arg); + set_level_and_therm_target(arg); + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + return MISCHIEF_MANAGED; + } + #if (B_TIMING_OFF == B_RELEASE_T) + // 1 click (early): off, if configured for early response + else if (event == EV_click1_release) { + level_before_off = actual_level; + set_level_and_therm_target(0); + return MISCHIEF_MANAGED; + } + // 2 clicks (early): abort turning off, if configured for early response + else if (event == EV_click2_press) { + set_level_and_therm_target(level_before_off); + return MISCHIEF_MANAGED; + } + #endif // if (B_TIMING_OFF == B_RELEASE_T) + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + uint8_t turbo_level; + #ifdef USE_SIMPLE_UI + if (simple_ui_active) { turbo_level = mode_max; } + else + #endif + turbo_level = MAX_LEVEL; + + if (actual_level < turbo_level) { + // true turbo, not the mode-specific ceiling + set_level_and_therm_target(turbo_level); + } + else { + set_level_and_therm_target(memorized_level); + } + return MISCHIEF_MANAGED; + } + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return MISCHIEF_MANAGED; + } + #ifdef USE_REVERSING + // fix ramp direction on first frame if necessary + if (!arg) { + // make it ramp down instead, if already at max + if (actual_level >= mode_max) { ramp_direction = -1; } + // make it ramp up if already at min + // (off->hold->stepped_min->release causes this state) + else if (actual_level <= mode_min) { ramp_direction = 1; } + } + // if the button is stuck, err on the side of safety and ramp down + else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) { + ramp_direction = -1; + } + #ifdef USE_LOCKOUT_MODE + // if the button is still stuck, lock the light + else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) { + blip(); + set_state(lockout_state, 0); + } + #endif + memorized_level = nearest_level((int16_t)actual_level \ + + (step_size * ramp_direction)); + #else + memorized_level = nearest_level((int16_t)actual_level + step_size); + #endif + #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_CEIL + || (memorized_level == mode_max) + #endif + #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) + || (memorized_level == mode_min) + #endif + )) { + blip(); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + blip(); + } + #endif + set_level_and_therm_target(memorized_level); + return MISCHIEF_MANAGED; + } + #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL) + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + #ifdef USE_REVERSING + ramp_direction = -ramp_direction; + #endif + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + return MISCHIEF_MANAGED; + } + #endif + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return MISCHIEF_MANAGED; + } + // TODO? make it ramp up instead, if already at min? + memorized_level = nearest_level((int16_t)actual_level - step_size); + #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_FLOOR + || (memorized_level == mode_min) + #endif + )) { + blip(); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + blip(); + } + #endif + set_level_and_therm_target(memorized_level); + return MISCHIEF_MANAGED; + } + #ifdef START_AT_MEMORIZED_LEVEL + // click, release, hold, release: save new ramp level (if necessary) + else if (event == EV_click2_hold_release) { + save_config_wl(); + return MISCHIEF_MANAGED; + } + #endif + + #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) + else if (event == EV_tick) { + #ifdef USE_REVERSING + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + #endif + #ifdef USE_SET_LEVEL_GRADUALLY + int16_t diff = gradual_target - actual_level; + static uint16_t ticks_since_adjust = 0; + ticks_since_adjust++; + if (diff) { + uint16_t ticks_per_adjust = 256; + if (diff < 0) { + //diff = -diff; + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef THERM_HARD_TURBO_DROP + ticks_per_adjust >>= 2; + #endif + ticks_per_adjust >>= 2; + } + } else { + // rise at half speed + ticks_per_adjust <<= 1; + } + while (diff) { + ticks_per_adjust >>= 1; + //diff >>= 1; + diff /= 2; // because shifting produces weird behavior + } + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY + return MISCHIEF_MANAGED; + } + #endif + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + #if 0 + blip(); + #endif + #ifdef THERM_HARD_TURBO_DROP + //if (actual_level > THERM_FASTER_LEVEL) { + if (actual_level == MAX_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + target_level = THERM_FASTER_LEVEL; + #else + set_level_and_therm_target(THERM_FASTER_LEVEL); + #endif + } else + #endif + if (actual_level > MIN_THERM_STEPDOWN) { + int16_t stepdown = actual_level - arg; + if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; + else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepdown); + #else + set_level(stepdown); + #endif + } + return MISCHIEF_MANAGED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + #if 0 + blip(); + #endif + if (actual_level < target_level) { + //int16_t stepup = actual_level + (arg>>1); + int16_t stepup = actual_level + arg; + if (stepup > target_level) stepup = target_level; + else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepup); + #else + set_level(stepup); + #endif + } + return MISCHIEF_MANAGED; + } + #ifdef USE_SET_LEVEL_GRADUALLY + // temperature is within target window + // (so stop trying to adjust output) + else if (event == EV_temperature_okay) { + // if we're still adjusting output... stop after the current step + if (gradual_target > actual_level) + gradual_target = actual_level + 1; + else if (gradual_target < actual_level) + gradual_target = actual_level - 1; + return MISCHIEF_MANAGED; + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY + #endif // ifdef USE_THERMAL_REGULATION + + ////////// Every action below here is blocked in the simple UI ////////// + #ifdef USE_SIMPLE_UI + if (simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + // 3 clicks: toggle smooth vs discrete ramping + else if (event == EV_3clicks) { + ramp_style = !ramp_style; + save_config(); + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + blip(); + memorized_level = nearest_level(actual_level); + set_level_and_therm_target(memorized_level); + return MISCHIEF_MANAGED; + } + + #ifdef USE_MANUAL_MEMORY + else if (event == EV_5clicks) { + manual_memory = actual_level; + save_config(); + blip(); + return MISCHIEF_MANAGED; + } + else if (event == EV_click5_hold) { + if (0 == arg) { + manual_memory = 0; + save_config(); + blip(); + } + return MISCHIEF_MANAGED; + } + #endif + + #ifdef USE_RAMP_CONFIG + // 7 clicks: configure this ramp mode + else if (event == EV_7clicks) { + push_state(ramp_config_state, 0); + return MISCHIEF_MANAGED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_RAMP_CONFIG +void ramp_config_save() { + // parse values + uint8_t val; + uint8_t style = ramp_style; + // TODO: detect if we're configuring the simple UI + + val = config_state_values[0]; + if (val) { ramp_floors[style] = val; } + + val = config_state_values[1]; + if (val) { ramp_ceils[style] = MAX_LEVEL + 1 - val; } + + if (ramp_style) { // discrete / stepped ramp + val = config_state_values[2]; + if (val) ramp_stepss[style] = val; + } +} + +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, + num_config_steps, ramp_config_save); +} +#endif // #ifdef USE_RAMP_CONFIG + + +// find the ramp level closest to the target, +// using only the levels which are allowed in the current state +uint8_t nearest_level(int16_t target) { + ramp_update_config(); + + // bounds check + // using int16_t here saves us a bunch of logic elsewhere, + // by allowing us to correct for numbers < 0 or > 255 in one central place + uint8_t mode_min = ramp_floor; + uint8_t mode_max = ramp_ceil; + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + // the rest isn't relevant for smooth ramping + if (! ramp_style) return target; + + uint8_t ramp_range = mode_max - mode_min; + uint8_t num_steps = ramp_stepss[1 + simple_ui_active]; + ramp_discrete_step_size = ramp_range / (num_steps-1); + uint8_t this_level = mode_min; + + for(uint8_t i=0; i<num_steps; i++) { + this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1)); + int16_t diff = target - this_level; + if (diff < 0) diff = -diff; + if (diff <= (ramp_discrete_step_size>>1)) + return this_level; + } + return this_level; +} + +// ensure ramp globals are correct +void ramp_update_config() { + uint8_t which = ramp_style; + if (simple_ui_active) { which = 2; } + + ramp_floor = ramp_floors[which]; + ramp_ceil = ramp_ceils[which]; +} + +#ifdef USE_THERMAL_REGULATION +void set_level_and_therm_target(uint8_t level) { + target_level = level; + set_level(level); +} +#endif + + +#endif + diff --git a/spaghetti-monster/anduril/ramping.h b/spaghetti-monster/anduril/ramping.h new file mode 100644 index 0000000..b47297d --- /dev/null +++ b/spaghetti-monster/anduril/ramping.h @@ -0,0 +1,179 @@ +/* + * ramping.h: Ramping functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RAMPING_H +#define RAMPING_H + +#ifndef RAMP_LENGTH +#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#endif + +// thermal properties, if not defined per-driver +#ifndef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to +#endif +#ifndef THERM_FASTER_LEVEL + #ifdef MAX_Nx7135 + #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high + #else + #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high + #endif +#endif + +#if defined(USE_SIMPLE_UI) +// start in the simple UI after each factory reset? +#ifndef DEFAULT_SIMPLE_UI_ACTIVE +#define DEFAULT_SIMPLE_UI_ACTIVE 1 +#endif +#ifndef DEFAULT_SIMPLE_UI_FLOOR +#define DEFAULT_SIMPLE_UI_FLOOR 22 +#endif +#ifndef DEFAULT_SIMPLE_UI_CEIL +#define DEFAULT_SIMPLE_UI_CEIL (MAX_1x7135+20) +#endif +#ifndef DEFAULT_SIMPLE_UI_STEPS +#define DEFAULT_SIMPLE_UI_STEPS 3 +#endif +#endif + + +// configure the timing of turning on/off in regular ramp mode +// press: react as soon as the button is pressed +#define B_PRESS_T 0 +// release: react as soon as the button is released +#define B_RELEASE_T 1 +// timeout: react as soon as we're sure the user isn't doing a double-click +#define B_TIMEOUT_T 2 +// defaults are release on, timeout off +#ifndef B_TIMING_ON +//#define B_TIMING_ON B_PRESS_T +#define B_TIMING_ON B_RELEASE_T +#endif +#ifndef B_TIMING_OFF +//#define B_TIMING_OFF B_RELEASE_T +#define B_TIMING_OFF B_TIMEOUT_T +#endif + +// default ramp options if not overridden earlier per-driver +#ifndef RAMP_STYLE +#define RAMP_STYLE 0 // smooth default +#endif +#ifndef RAMP_SMOOTH_FLOOR + #define RAMP_SMOOTH_FLOOR 1 +#endif +#ifndef RAMP_SMOOTH_CEIL + #if PWM_CHANNELS == 3 + #define RAMP_SMOOTH_CEIL MAX_Nx7135 + #else + #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 + #endif +#endif +#ifndef RAMP_DISCRETE_FLOOR + #define RAMP_DISCRETE_FLOOR 20 +#endif +#ifndef RAMP_DISCRETE_CEIL + #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#endif +#ifndef RAMP_DISCRETE_STEPS + #define RAMP_DISCRETE_STEPS 7 +#endif + +// mile marker(s) partway up the ramp +// default: blink only at border between regulated and FET +#ifdef BLINK_AT_RAMP_MIDDLE + #if PWM_CHANNELS >= 3 + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 + #ifndef BLINK_AT_RAMP_MIDDLE_2 + #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 + #endif + #endif + #else + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 + #endif + #endif +#endif + + +// ramping mode and its related config mode +uint8_t steady_state(Event event, uint16_t arg); + +#ifdef USE_RAMP_CONFIG +uint8_t ramp_config_state(Event event, uint16_t arg); +void ramp_config_save(); +#endif + +// calculate the nearest ramp level which would be valid at the moment +// (is a no-op for smooth ramp, but limits discrete ramp to only the +// correct levels for the user's config) +uint8_t nearest_level(int16_t target); + +// ensure ramp globals are correct +void ramp_update_config(); + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +void set_level_and_therm_target(uint8_t level); +#else +#define set_level_and_therm_target(level) set_level(level) +#endif + + +// brightness control +uint8_t memorized_level = DEFAULT_LEVEL; +#ifdef USE_MANUAL_MEMORY +uint8_t manual_memory = 0; +#endif +#ifdef USE_SIMPLE_UI +// whether to enable the simplified interface or not +uint8_t simple_ui_active = DEFAULT_SIMPLE_UI_ACTIVE; +#endif +// smooth vs discrete ramping +uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete +// current values, regardless of style +uint8_t ramp_floor = RAMP_SMOOTH_FLOOR; +uint8_t ramp_ceil = RAMP_SMOOTH_CEIL; +// per style +uint8_t ramp_floors[] = { + RAMP_SMOOTH_FLOOR, + RAMP_DISCRETE_FLOOR, + #ifdef USE_SIMPLE_UI + DEFAULT_SIMPLE_UI_FLOOR, + #endif + }; +uint8_t ramp_ceils[] = { + RAMP_SMOOTH_CEIL, + RAMP_DISCRETE_CEIL, + #ifdef USE_SIMPLE_UI + DEFAULT_SIMPLE_UI_CEIL, + #endif + }; +uint8_t ramp_stepss[] = { + 0, + RAMP_DISCRETE_STEPS, + #ifdef USE_SIMPLE_UI + DEFAULT_SIMPLE_UI_STEPS, + #endif + }; +uint8_t ramp_discrete_step_size; // don't set this + + +#endif diff --git a/spaghetti-monster/anduril/sos-mode.c b/spaghetti-monster/anduril/sos-mode.c new file mode 100644 index 0000000..9f87818 --- /dev/null +++ b/spaghetti-monster/anduril/sos-mode.c @@ -0,0 +1,75 @@ +/* + * sos-mode.c: SOS mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SOS_MODE_C +#define SOS_MODE_C + +#include "sos-mode.h" + +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +uint8_t sos_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: next mode + else if (event == EV_2clicks) { + #ifdef USE_THERMAL_REGULATION + set_state(tempcheck_state, 0); + #else + #ifdef USE_BATTCHECK_MODE + set_state(battcheck_state, 0); + #endif + #endif + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} +#endif + +void sos_blink(uint8_t num, uint8_t dah) { + #define DIT_LENGTH 200 + for (; num > 0; num--) { + set_level(memorized_level); + nice_delay_ms(DIT_LENGTH); + if (dah) { // dah is 3X as long as a dit + nice_delay_ms(DIT_LENGTH*2); + } + set_level(0); + // one "off" dit between blinks + nice_delay_ms(DIT_LENGTH); + } + // three "off" dits (or one "dah") between letters + // (except for SOS, which is collectively treated as a single "letter") + //nice_delay_ms(DIT_LENGTH*2); +} + +inline void sos_mode_iter() { + // one iteration of main loop() + //nice_delay_ms(1000); + sos_blink(3, 0); // S + sos_blink(3, 1); // O + sos_blink(3, 0); // S + nice_delay_ms(2000); +} + + +#endif + diff --git a/spaghetti-monster/anduril/sos-mode.h b/spaghetti-monster/anduril/sos-mode.h new file mode 100644 index 0000000..397aa3f --- /dev/null +++ b/spaghetti-monster/anduril/sos-mode.h @@ -0,0 +1,29 @@ +/* + * sos-mode.h: SOS mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SOS_MODE_H +#define SOS_MODE_H + +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +// automatic SOS emergency signal +uint8_t sos_state(Event event, uint16_t arg); +#endif + + +#endif diff --git a/spaghetti-monster/anduril/strobes-fsm.h b/spaghetti-monster/anduril/strobes-fsm.h index 6514d5f..2eb2d79 100644 --- a/spaghetti-monster/anduril/strobes-fsm.h +++ b/spaghetti-monster/anduril/strobes-fsm.h @@ -25,11 +25,20 @@ #define USE_PSEUDO_RAND #endif +// party strobe uses really short pulses +#ifdef USE_PARTY_STROBE_MODE +#define USE_DELAY_ZERO +#endif + +// candle mode is basically a bunch of stacked random triangle waves +#if defined(USE_CANDLE_MODE) +#define USE_TRIANGLE_WAVE +#endif + // the presence of strobe mode(s) affects how many eeprom bytes we need, // so it's relevant for FSM configuration #if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) #define USE_STROBE_STATE #endif - #endif diff --git a/spaghetti-monster/anduril/strobes.c b/spaghetti-monster/anduril/strobes.c index 42d085d..1037c0e 100644 --- a/spaghetti-monster/anduril/strobes.c +++ b/spaghetti-monster/anduril/strobes.c @@ -162,6 +162,36 @@ uint8_t strobe_state(Event event, uint16_t arg) { #endif return EVENT_NOT_HANDLED; } + +// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe +inline void strobe_state_iter() { + uint8_t st = strobe_type; + + switch(st) { + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_PARTY_STROBE_MODE + case party_strobe_e: + #endif + #ifdef USE_TACTICAL_STROBE_MODE + case tactical_strobe_e: + #endif + party_tactical_strobe_mode_iter(st); + break; + #endif + + #ifdef USE_LIGHTNING_MODE + case lightning_storm_e: + lightning_storm_iter(); + break; + #endif + + #ifdef USE_BIKE_FLASHER_MODE + case bike_flasher_e: + bike_flasher_iter(); + break; + #endif + } +} #endif // ifdef USE_STROBE_STATE #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) @@ -385,12 +415,6 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { } return EVENT_NOT_HANDLED; } - -uint8_t triangle_wave(uint8_t phase) { - uint8_t result = phase << 1; - if (phase > 127) result = 255 - result; - return result; -} #endif // #ifdef USE_CANDLE_MODE diff --git a/spaghetti-monster/anduril/strobes.h b/spaghetti-monster/anduril/strobes.h index 8cfc4a7..c74b4a0 100644 --- a/spaghetti-monster/anduril/strobes.h +++ b/spaghetti-monster/anduril/strobes.h @@ -65,6 +65,7 @@ volatile strobe_mode_te strobe_type = 0; // party and tactical strobes #ifdef USE_STROBE_STATE uint8_t strobe_state(Event event, uint16_t arg); +inline void strobe_state_iter(); #endif #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) @@ -79,13 +80,15 @@ inline void lightning_storm_iter(); // bike mode config options #ifdef USE_BIKE_FLASHER_MODE +#define MAX_BIKING_LEVEL 120 // should be 127 or less volatile uint8_t bike_flasher_brightness = MAX_1x7135; inline void bike_flasher_iter(); #endif #ifdef USE_CANDLE_MODE uint8_t candle_mode_state(Event event, uint16_t arg); -uint8_t triangle_wave(uint8_t phase); +// moved to fsm-misc.c because tint ramping power correction +//uint8_t triangle_wave(uint8_t phase); #ifndef CANDLE_AMPLITUDE #define CANDLE_AMPLITUDE 25 #endif diff --git a/spaghetti-monster/anduril/tempcheck-mode.c b/spaghetti-monster/anduril/tempcheck-mode.c new file mode 100644 index 0000000..b4e95ff --- /dev/null +++ b/spaghetti-monster/anduril/tempcheck-mode.c @@ -0,0 +1,73 @@ +/* + * tempcheck-mode.c: Temperature check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TEMPCHECK_MODE_C +#define TEMPCHECK_MODE_C + +#include "tempcheck-mode.h" + +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + #ifdef USE_BATTCHECK + // 2 clicks: battcheck mode + else if (event == EV_2clicks) { + set_state(battcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif + // 7 clicks: thermal config mode + else if (event == EV_7clicks) { + push_state(thermal_config_state, 0); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + +void thermal_config_save() { + // parse values + uint8_t val; + + // calibrate room temperature + val = config_state_values[0]; + if (val) { + int8_t rawtemp = temperature - therm_cal_offset; + therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data + } + + val = config_state_values[1]; + if (val) { + // set maximum heat limit + therm_ceil = 30 + val - 1; + } + if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} + + +#endif + diff --git a/spaghetti-monster/anduril/tempcheck-mode.h b/spaghetti-monster/anduril/tempcheck-mode.h new file mode 100644 index 0000000..36c41c1 --- /dev/null +++ b/spaghetti-monster/anduril/tempcheck-mode.h @@ -0,0 +1,30 @@ +/* + * tempcheck-mode.h: Temperature check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TEMPCHECK_MODE_H +#define TEMPCHECK_MODE_H + +#define USE_BLINK_NUM // FIXME: this only matters in an earlier header + +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); +void thermal_config_save(); + + +#endif diff --git a/spaghetti-monster/anduril/version-check-mode.c b/spaghetti-monster/anduril/version-check-mode.c new file mode 100644 index 0000000..76efde3 --- /dev/null +++ b/spaghetti-monster/anduril/version-check-mode.c @@ -0,0 +1,47 @@ +/* + * version-check-mode.c: Version check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef VERSION_CHECK_MODE_C +#define VERSION_CHECK_MODE_C + +#include "version-check-mode.h" + +// empty state; logic is handled in FSM loop() instead +uint8_t version_check_state(Event event, uint16_t arg) { + return EVENT_NOT_HANDLED; +} + +// this happens in FSM loop() +inline void version_check_iter() { + for (uint8_t i=0; i<sizeof(version_number)-1; i++) { + blink_digit(pgm_read_byte(version_number + i) - '0'); + nice_delay_ms(300); + } + // FIXME: when user interrupts with button, "off" takes an extra click + // before it'll turn back on, because the click to cancel gets sent + // to the "off" state instead of version_check_state + //while (button_is_pressed()) {} + //empty_event_sequence(); + + set_state(off_state, 0); +} + + +#endif + diff --git a/spaghetti-monster/anduril/version-check-mode.h b/spaghetti-monster/anduril/version-check-mode.h new file mode 100644 index 0000000..7e89d73 --- /dev/null +++ b/spaghetti-monster/anduril/version-check-mode.h @@ -0,0 +1,31 @@ +/* + * version-check-mode.h: Version check mode for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef VERSION_CHECK_MODE_H +#define VERSION_CHECK_MODE_H + +#define USE_BLINK_DIGIT // FIXME: does nothing unless defined earlier + +#include "version.h" +const PROGMEM uint8_t version_number[] = VERSION_NUMBER; +uint8_t version_check_state(Event event, uint16_t arg); +inline void version_check_iter(); + + +#endif diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c index eaa6fb3..82be745 100644 --- a/spaghetti-monster/fsm-misc.c +++ b/spaghetti-monster/fsm-misc.c @@ -222,7 +222,6 @@ void rgb_led_set(uint8_t value) { #endif // ifdef USE_AUX_RGB_LEDS #ifdef USE_TRIANGLE_WAVE -// TODO: remove this, it's in the wrong file uint8_t triangle_wave(uint8_t phase) { uint8_t result = phase << 1; if (phase > 127) result = 255 - result; |
