diff options
Diffstat (limited to 'spaghetti-monster')
| -rw-r--r-- | spaghetti-monster/anduril/anduril.c | 132 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-ff-e01.h | 15 | ||||
| -rw-r--r-- | spaghetti-monster/fireflies-ui/Makefile | 2 | ||||
| -rwxr-xr-x | spaghetti-monster/fireflies-ui/build-all.sh | 2 | ||||
| -rw-r--r-- | spaghetti-monster/fireflies-ui/cfg-ff-e01.h | 44 | ||||
| -rw-r--r-- | spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h (renamed from spaghetti-monster/fireflies-ui/cfg-ff-edc-thrower.h) | 16 | ||||
| -rw-r--r-- | spaghetti-monster/fireflies-ui/fireflies-ui.c | 370 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-events.h | 2 |
8 files changed, 467 insertions, 116 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index f9509e7..dffa981 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -31,7 +31,7 @@ // short blip when crossing from "click" to "hold" from off // (helps the user hit moon mode exactly, instead of holding too long // or too short) -#define MOON_TIMING_HINT +#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T // short blips while ramping #define BLINK_AT_RAMP_MIDDLE //#define BLINK_AT_RAMP_FLOOR @@ -42,6 +42,13 @@ // ("hold, release, hold" ramps down instead of up) #define USE_REVERSING +// add a runtime option to switch between automatic memory (default) +// and manual memory (only available if compiled in) +// (manual memory makes 1-click-from-off start at the same level each time) +// (the level can be set explicitly with 5 clicks from on, +// or the user can go back to automatic with click-click-click-click-hold) +#define USE_MANUAL_MEMORY + // battery readout style (pick one) #define BATTCHECK_VpT //#define BATTCHECK_8bars // FIXME: breaks build @@ -145,6 +152,9 @@ typedef enum { ramp_discrete_ceil_e, ramp_discrete_steps_e, #endif + #ifdef USE_MANUAL_MEMORY + manual_memory_e, + #endif #ifdef USE_TINT_RAMPING tint_e, #endif @@ -194,6 +204,24 @@ typedef enum { #include "spaghetti-monster.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 +#endif + + // FSM states uint8_t off_state(Event event, uint16_t arg); // simple numeric entry config menu @@ -316,6 +344,9 @@ void save_config_wl(); #define DEFAULT_LEVEL MAX_1x7135 #endif uint8_t memorized_level = DEFAULT_LEVEL; +#ifdef USE_MANUAL_MEMORY +uint8_t manual_memory = 0; +#endif // smooth vs discrete ramping volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; @@ -436,19 +467,25 @@ uint8_t off_state(Event event, uint16_t arg) { 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 @@ -462,13 +499,25 @@ uint8_t off_state(Event event, uint16_t arg) { 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; } @@ -562,6 +611,11 @@ 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 if (ramp_style) { mode_min = ramp_discrete_floor; mode_max = ramp_discrete_ceil; @@ -589,6 +643,25 @@ uint8_t steady_state(Event event, uint16_t arg) { #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; + #ifdef USE_THERMAL_REGULATION + target_level = 0; + #endif + set_level(0); + return MISCHIEF_MANAGED; + } + // 2 clicks (early): abort turning off, if configured for early response + else if (event == EV_click2_press) { + #ifdef USE_THERMAL_REGULATION + target_level = level_before_off; + #endif + set_level(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); @@ -763,6 +836,20 @@ uint8_t steady_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } #endif + #ifdef USE_MANUAL_MEMORY + else if (event == EV_5clicks) { + manual_memory = actual_level; + save_config(); + blip(); + } + else if (event == EV_click5_hold) { + if (0 == arg) { + manual_memory = 0; + save_config(); + blip(); + } + } + #endif #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) else if (event == EV_tick) { #ifdef USE_REVERSING @@ -794,26 +881,29 @@ uint8_t steady_state(Event event, uint16_t arg) { else { diff = actual_level - gradual_target; } - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } - #endif - while (diff) { - magnitude ++; - diff >>= 1; - } - uint8_t ticks_per_adjust = intervals[magnitude]; - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; + // if there's any adjustment to be made, make it + if (diff) { + uint8_t magnitude = 0; + #ifndef THERM_HARD_TURBO_DROP + // if we're on a really high mode, drop faster + if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } + #endif + while (diff) { + magnitude ++; + diff >>= 1; + } + uint8_t ticks_per_adjust = intervals[magnitude]; + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + //if (!(arg % ticks_per_adjust)) gradual_tick(); } - //if (!(arg % ticks_per_adjust)) gradual_tick(); #ifdef THERM_HARD_TURBO_DROP } #endif - #endif + #endif // ifdef USE_SET_LEVEL_GRADUALLY return MISCHIEF_MANAGED; } #endif @@ -864,7 +954,7 @@ uint8_t steady_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } - #endif + #endif // ifdef USE_THERMAL_REGULATION return EVENT_NOT_HANDLED; } @@ -2074,6 +2164,9 @@ void load_config() { ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e]; ramp_discrete_steps = 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 @@ -2115,6 +2208,9 @@ void save_config() { eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil; eeprom[ramp_discrete_steps_e] = ramp_discrete_steps; #endif + #ifdef USE_MANUAL_MEMORY + eeprom[manual_memory_e] = manual_memory; + #endif #ifdef USE_TINT_RAMPING eeprom[tint_e] = tint; #endif diff --git a/spaghetti-monster/anduril/cfg-ff-e01.h b/spaghetti-monster/anduril/cfg-ff-e01.h new file mode 100644 index 0000000..3b47165 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-ff-e01.h @@ -0,0 +1,15 @@ +// Fireflies E01 SST-40 thrower config options for Anduril +// most of the good stuff is in the FFUI config; just copy it +#include "../fireflies-ui/cfg-ff-e01.h" + +#ifndef BLINK_AT_RAMP_CEILING +#define BLINK_AT_RAMP_CEILING +#endif + +// 20, 38, 56, 75, [93], 111, 130 (93 is highest regulated) +// (9 / 45 / 116 / 248 / 467 / 742 / 1280 + 2140 lm) +#undef RAMP_DISCRETE_STEPS +#define RAMP_DISCRETE_STEPS 7 + +// shortcut for first-time setup +#define USE_TENCLICK_THERMAL_CONFIG diff --git a/spaghetti-monster/fireflies-ui/Makefile b/spaghetti-monster/fireflies-ui/Makefile index 8db198e..0b59898 100644 --- a/spaghetti-monster/fireflies-ui/Makefile +++ b/spaghetti-monster/fireflies-ui/Makefile @@ -2,6 +2,6 @@ all: ./build-all.sh clean: - rm -f *.hex cfg-*.h *~ *.elf *.o + rm -f *.hex cfg-ff-[pr]*.h *~ *.elf *.o .phony: clean diff --git a/spaghetti-monster/fireflies-ui/build-all.sh b/spaghetti-monster/fireflies-ui/build-all.sh index 41d92d0..81ebd97 100755 --- a/spaghetti-monster/fireflies-ui/build-all.sh +++ b/spaghetti-monster/fireflies-ui/build-all.sh @@ -1,6 +1,6 @@ #!/bin/sh -cp -av ../anduril/cfg-ff*.h . +cp -av --no-clobber ../anduril/cfg-ff*.h . UI=fireflies-ui diff --git a/spaghetti-monster/fireflies-ui/cfg-ff-e01.h b/spaghetti-monster/fireflies-ui/cfg-ff-e01.h new file mode 100644 index 0000000..42c23b2 --- /dev/null +++ b/spaghetti-monster/fireflies-ui/cfg-ff-e01.h @@ -0,0 +1,44 @@ +// Fireflies EDC thrower config options for Fireflies UI +// (uses PL47 driver) +#include "hwdef-FF_PL47.h" + +// disable indicator LED; it's hardwired +#ifdef USE_INDICATOR_LED +#undef USE_INDICATOR_LED +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_CEILING + +// ramp shape and size +#define RAMP_LENGTH 150 + +// driver is a FET + 3x7135, ~413 lm at highest regulated level +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 +#define MAX_1x7135 93 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 7 + +#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to + + +// ceiling is level 130/150 (50% power) +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 + +// 20, 56, [93], 130 (93 is highest regulated) +// (8 / 102 / 413 / 1163 + 1930 lm) +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 4 + +// ~25 lm to ~400 lm +#define MUGGLE_FLOOR 30 +#define MUGGLE_CEILING MAX_1x7135 + +// throttle back faster when high +#define THERM_FASTER_LEVEL 130 + diff --git a/spaghetti-monster/fireflies-ui/cfg-ff-edc-thrower.h b/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h index 31be0ef..48f9c15 100644 --- a/spaghetti-monster/fireflies-ui/cfg-ff-edc-thrower.h +++ b/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h @@ -1,20 +1,20 @@ -// Fireflies EDC thrower config options for Fireflies UI +// Fireflies E07-2 config options for Anduril / FFUI +// mostly the same as PL47 #include "cfg-ff-pl47.h" -// disable indicator LED -#undef USE_INDICATOR_LED - -// ceiling is level 130/150 (50% power) +// ceiling is level 130/150 (50% power) #undef RAMP_SMOOTH_CEIL #define RAMP_SMOOTH_CEIL 130 -// 36, 83, 130 (83 is highest regulated) +// 20, 56, 93, 130 (83 is highest regulated) +// (requested config is 1%, 5%, 25%, 50%, double-click-turbo) +// (but this doesn't allow us to hit level 83) #undef RAMP_DISCRETE_FLOOR -#define RAMP_DISCRETE_FLOOR 36 +#define RAMP_DISCRETE_FLOOR 20 #undef RAMP_DISCRETE_CEIL #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #undef RAMP_DISCRETE_STEPS -#define RAMP_DISCRETE_STEPS 3 +#define RAMP_DISCRETE_STEPS 4 // regulate down faster when the FET is active, slower otherwise #undef THERM_FASTER_LEVEL diff --git a/spaghetti-monster/fireflies-ui/fireflies-ui.c b/spaghetti-monster/fireflies-ui/fireflies-ui.c index 0fc2a1d..2c3e60b 100644 --- a/spaghetti-monster/fireflies-ui/fireflies-ui.c +++ b/spaghetti-monster/fireflies-ui/fireflies-ui.c @@ -33,7 +33,7 @@ // or too short) #define MOON_TIMING_HINT // short blips while ramping -#define BLINK_AT_CHANNEL_BOUNDARIES +#define BLINK_AT_RAMP_MIDDLE //#define BLINK_AT_RAMP_FLOOR #define BLINK_AT_RAMP_CEILING //#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode @@ -189,6 +189,9 @@ typedef enum { ramp_discrete_ceil_e, ramp_discrete_steps_e, #endif + #ifdef USE_TINT_RAMPING + tint_e, + #endif #ifdef USE_STROBE_STATE strobe_type_e, #endif @@ -226,6 +229,12 @@ typedef enum { #define USE_PSEUDO_RAND #endif +#if defined(USE_CANDLE_MODE) +#ifndef USE_TRIANGLE_WAVE +#define USE_TRIANGLE_WAVE +#endif +#endif + #include "spaghetti-monster.h" @@ -242,6 +251,10 @@ 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_TINT_RAMPING +// not actually a mode, more of a fallback under other modes +uint8_t tint_ramping_state(Event event, uint16_t arg); +#endif // party and tactical strobes #ifdef USE_STROBE_STATE uint8_t strobe_state(Event event, uint16_t arg); @@ -250,6 +263,7 @@ uint8_t strobe_state(Event event, uint16_t arg); uint8_t boring_strobe_state(Event event, uint16_t arg); volatile uint8_t boring_strobe_type = 0; void sos_blink(uint8_t num, uint8_t dah); +#define strobe_state boring_strobe_state // use the right strobes #define NUM_BORING_STROBES 2 #endif #ifdef USE_BATTCHECK @@ -271,10 +285,12 @@ uint8_t beacon_config_state(Event event, uint16_t arg); // soft lockout #define MOON_DURING_LOCKOUT_MODE // if enabled, 2nd lockout click goes to the other ramp's floor level -//#define LOCKOUT_MOON_FANCY +#define LOCKOUT_MOON_FANCY uint8_t lockout_state(Event event, uint16_t arg); // 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* #ifdef USE_MUGGLE_MODE // muggle mode, super-simple, hard to exit uint8_t muggle_state(Event event, uint16_t arg); @@ -287,6 +303,7 @@ uint8_t number_entry_state(Event event, uint16_t arg); volatile uint8_t number_entry_value; void blink_confirm(uint8_t num); +void blip(); #if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) void indicator_blink(uint8_t arg); #endif @@ -299,6 +316,9 @@ 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 @@ -319,10 +339,30 @@ void save_config_wl(); #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 = MAX_1x7135; +#ifndef DEFAULT_LEVEL +#define DEFAULT_LEVEL MAX_1x7135 +#endif +uint8_t memorized_level = DEFAULT_LEVEL; // smooth vs discrete ramping -volatile uint8_t ramp_style = 0; // 0 = smooth, 1 = discrete +volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL; volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; @@ -400,6 +440,9 @@ volatile uint8_t bike_flasher_brightness = MAX_1x7135; #ifdef USE_CANDLE_MODE uint8_t candle_mode_state(Event event, uint16_t arg); uint8_t triangle_wave(uint8_t phase); +#ifndef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 25 +#endif #endif #ifdef USE_BEACON_MODE @@ -448,10 +491,7 @@ uint8_t off_state(Event event, uint16_t arg) { #ifdef MOON_TIMING_HINT if (arg == 0) { // let the user know they can let go now to stay at moon - uint8_t temp = actual_level; - set_level(0); - delay_4ms(2); - set_level(temp); + blip(); } else #endif // don't start ramping immediately; @@ -531,8 +571,26 @@ uint8_t off_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } #endif - // 7 clicks: temperature check + #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; + } + #endif + // 8 clicks: temperature check + else if (event == EV_8clicks) { set_state(tempcheck_state, 0); return MISCHIEF_MANAGED; } @@ -562,6 +620,7 @@ uint8_t steady_state(Event event, uint16_t arg) { // turn LED on when we first enter the mode if ((event == EV_enter_state) || (event == EV_reenter_state)) { + momentary_mode = 0; // 0 = ramping, 1 = strobes // if we just got back from config mode, go back to memorized level if (event == EV_reenter_state) { arg = memorized_level; @@ -616,8 +675,7 @@ uint8_t steady_state(Event event, uint16_t arg) { #ifdef START_AT_MEMORIZED_LEVEL save_config_wl(); #endif - set_level(0); - delay_4ms(20/4); + blip(); set_level(memorized_level); return MISCHIEF_MANAGED; } @@ -635,9 +693,13 @@ uint8_t steady_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } #ifdef USE_REVERSING - // make it ramp down instead, if already at max - if ((arg <= 1) && (actual_level >= mode_max)) { - ramp_direction = -1; + // 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; } } memorized_level = nearest_level((int16_t)actual_level \ + (ramp_step_size * ramp_direction)); @@ -647,15 +709,15 @@ uint8_t steady_state(Event event, uint16_t arg) { #ifdef USE_THERMAL_REGULATION target_level = memorized_level; #endif - #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_CHANNEL_BOUNDARIES) + #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE) // only blink once for each threshold if ((memorized_level != actual_level) && ( 0 // for easier syntax below - #ifdef BLINK_AT_CHANNEL_BOUNDARIES - || (memorized_level == MAX_1x7135) - #if PWM_CHANNELS >= 3 - || (memorized_level == MAX_Nx7135) + #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_CEILING || (memorized_level == mode_max) @@ -664,8 +726,7 @@ uint8_t steady_state(Event event, uint16_t arg) { || (memorized_level == mode_min) #endif )) { - set_level(0); - delay_4ms(8/4); + blip(); } #endif #if defined(BLINK_AT_STEPS) @@ -679,8 +740,7 @@ uint8_t steady_state(Event event, uint16_t arg) { (memorized_level == nearest) ) { - set_level(0); - delay_4ms(8/4); + blip(); } #endif set_level(memorized_level); @@ -712,22 +772,21 @@ uint8_t steady_state(Event event, uint16_t arg) { #ifdef USE_THERMAL_REGULATION target_level = memorized_level; #endif - #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_CHANNEL_BOUNDARIES) + #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_CHANNEL_BOUNDARIES - || (memorized_level == MAX_1x7135) - #if PWM_CHANNELS >= 3 - || (memorized_level == MAX_Nx7135) + #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 )) { - set_level(0); - delay_4ms(8/4); + blip(); } #endif #if defined(BLINK_AT_STEPS) @@ -741,8 +800,7 @@ uint8_t steady_state(Event event, uint16_t arg) { (memorized_level == nearest) ) { - set_level(0); - delay_4ms(8/4); + blip(); } #endif set_level(memorized_level); @@ -813,10 +871,7 @@ uint8_t steady_state(Event event, uint16_t arg) { // overheating: drop by an amount proportional to how far we are above the ceiling else if (event == EV_temperature_high) { #if 0 - uint8_t foo = actual_level; - set_level(0); - delay_4ms(2); - set_level(foo); + blip(); #endif #ifdef THERM_HARD_TURBO_DROP if (actual_level > THERM_FASTER_LEVEL) { @@ -844,10 +899,7 @@ uint8_t steady_state(Event event, uint16_t arg) { // (proportional to how low we are) else if (event == EV_temperature_low) { #if 0 - uint8_t foo = actual_level; - set_level(0); - delay_4ms(2); - set_level(foo); + blip(); #endif if (actual_level < target_level) { //int16_t stepup = actual_level + (arg>>1); @@ -867,12 +919,79 @@ uint8_t steady_state(Event event, uint16_t arg) { } +#ifdef USE_TINT_RAMPING +uint8_t tint_ramping_state(Event event, uint16_t arg) { + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + + // click, click, hold: change the tint + if (event == EV_click3_hold) { + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_HANDLED; + + // change normal tints + if ((tint_ramp_direction > 0) && (tint < 254)) { + tint += 1; + } + else if ((tint_ramp_direction < 0) && (tint > 1)) { + tint -= 1; + } + // if the user kept pressing long enough, go the final step + if (past_edge_counter == 64) { + past_edge_counter ++; + tint ^= 1; // 0 -> 1, 254 -> 255 + blip(); + } + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + // count up but don't wrap back to zero + if (past_edge_counter < 255) past_edge_counter ++; + } + prev_tint = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (tint == 0) tint_ramp_direction = 1; + else if (tint == 255) tint_ramp_direction = -1; + // remember tint after battery change + save_config(); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} +#endif // ifdef USE_TINT_RAMPING + + #ifdef USE_STROBE_STATE uint8_t strobe_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + // 'st' reduces ROM size by avoiding access to a volatile var // (maybe I should just make it nonvolatile?) strobe_mode_te st = strobe_type; + momentary_mode = 1; // 0 = ramping, 1 = strobes + #ifdef USE_CANDLE_MODE // pass all events to candle mode, when it's active // (the code is in its own pseudo-state to keep things cleaner) @@ -882,11 +1001,11 @@ uint8_t strobe_state(Event event, uint16_t arg) { #endif if (0) {} // placeholder - /* not used any more + // init anything which needs to be initialized else if (event == EV_enter_state) { + ramp_direction = 1; return MISCHIEF_MANAGED; } - */ // 1 click: off else if (event == EV_1click) { set_state(off_state, 0); @@ -911,7 +1030,11 @@ uint8_t strobe_state(Event event, uint16_t arg) { else if (st == party_strobe_e) { #endif if ((arg & 1) == 0) { - if (strobe_delays[st] > 8) strobe_delays[st] --; + uint8_t d = strobe_delays[st]; + d -= ramp_direction; + if (d < 8) d = 8; + else if (d > 254) d = 254; + strobe_delays[st] = d; } } #endif @@ -922,17 +1045,27 @@ uint8_t strobe_state(Event event, uint16_t arg) { // biking mode brighter #ifdef USE_BIKE_FLASHER_MODE else if (st == bike_flasher_e) { - if (bike_flasher_brightness < MAX_BIKING_LEVEL) - bike_flasher_brightness ++; + bike_flasher_brightness += ramp_direction; + if (bike_flasher_brightness < 2) bike_flasher_brightness = 2; + else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL; set_level(bike_flasher_brightness); } #endif return MISCHIEF_MANAGED; } + // reverse ramp direction on hold release + // ... and save new strobe settings + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + save_config(); + return MISCHIEF_MANAGED; + } // click, hold: change speed (go slower) // or change brightness (dimmer) else if (event == EV_click2_hold) { + ramp_direction = 1; + if (0) {} // placeholder // party / tactical strobe slower @@ -963,14 +1096,16 @@ uint8_t strobe_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } // release hold: save new strobe settings - else if ((event == EV_click1_hold_release) - || (event == EV_click2_hold_release)) { + else if (event == EV_click2_hold_release) { save_config(); return MISCHIEF_MANAGED; } #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) // clock tick: bump the random seed else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + pseudo_rand_seed += arg; return MISCHIEF_MANAGED; } @@ -1012,7 +1147,7 @@ inline void lightning_storm_iter() { //rand_time = 1 << (pseudo_rand() % 7); rand_time = pseudo_rand() & 63; brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 - brightness += 1 << (pseudo_rand() & 0x03); // 2 to 80 now + brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; set_level(brightness); @@ -1067,17 +1202,19 @@ inline void bike_flasher_iter() { #ifdef USE_CANDLE_MODE uint8_t candle_mode_state(Event event, uint16_t arg) { - // FIXME: make candle variance magnitude a compile-time option, - // since 20 is sometimes too much or too little, - // depending on the driver type and ramp shape - //#define MAX_CANDLE_LEVEL (RAMP_SIZE-8-6-4) - #define MAX_CANDLE_LEVEL (RAMP_SIZE/2) + static int8_t ramp_direction = 1; + #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) static uint8_t candle_wave1 = 0; static uint8_t candle_wave2 = 0; static uint8_t candle_wave3 = 0; static uint8_t candle_wave2_speed = 0; - static uint8_t candle_wave2_depth = 7; - static uint8_t candle_wave3_depth = 4; + // these should add up to 100 + #define CANDLE_WAVE1_MAXDEPTH 30 + #define CANDLE_WAVE2_MAXDEPTH 45 + #define CANDLE_WAVE3_MAXDEPTH 25 + static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; static uint8_t candle_mode_brightness = 24; static uint8_t candle_mode_timer = 0; #define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds @@ -1085,6 +1222,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { if (event == EV_enter_state) { candle_mode_timer = 0; // in case any time was left over from earlier + ramp_direction = 1; return MISCHIEF_MANAGED; } // 2 clicks: cancel timer @@ -1096,12 +1234,25 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { } // hold: change brightness (brighter) else if (event == EV_click1_hold) { - if (candle_mode_brightness < MAX_CANDLE_LEVEL) - candle_mode_brightness ++; + // ramp away from extremes + if (! arg) { + if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } + else if (candle_mode_brightness <= 1) { ramp_direction = 1; } + } + // change brightness, but not too far + candle_mode_brightness += ramp_direction; + if (candle_mode_brightness < 1) candle_mode_brightness = 1; + else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; + return MISCHIEF_MANAGED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; return MISCHIEF_MANAGED; } // click, hold: change brightness (dimmer) else if (event == EV_click2_hold) { + ramp_direction = 1; if (candle_mode_brightness > 1) candle_mode_brightness --; return MISCHIEF_MANAGED; @@ -1119,10 +1270,13 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { } // clock tick: animate candle brightness else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + // self-timer dims the light during the final minute uint8_t subtract = 0; if (candle_mode_timer == 1) { - subtract = ((candle_mode_brightness+20) + subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE) * ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4)) >> 8; } @@ -1139,7 +1293,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { } // 3-oscillator synth for a relatively organic pattern uint8_t add; - add = ((triangle_wave(candle_wave1) * 8) >> 8) + add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); int8_t brightness = candle_mode_brightness + add - subtract; @@ -1147,6 +1301,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { set_level(brightness); // wave1: slow random LFO + // TODO: make wave slower and more erratic? if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; // wave2: medium-speed erratic LFO candle_wave2 += candle_wave2_speed; @@ -1159,8 +1314,10 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) candle_wave2_depth --; // random sawtooth retrigger - if ((pseudo_rand()) == 0) { - candle_wave2_depth = 7; + if (pseudo_rand() == 0) { + // random amplitude + //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); //candle_wave3_depth = 5; candle_wave2 = 0; } @@ -1168,17 +1325,13 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) candle_wave3_depth --; if ((pseudo_rand() & 0b01111111) == 0) - candle_wave3_depth = 5; + // random amplitude + //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); return MISCHIEF_MANAGED; } 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 @@ -1189,6 +1342,8 @@ uint8_t boring_strobe_state(Event event, uint16_t arg) { // (maybe I should just make it nonvolatile?) uint8_t st = boring_strobe_type; + momentary_mode = 1; // 0 = ramping, 1 = strobes + if (event == EV_enter_state) { return MISCHIEF_MANAGED; } @@ -1492,14 +1647,24 @@ uint8_t lockout_state(Event event, uint16_t arg) { uint8_t momentary_state(Event event, uint16_t arg) { // TODO: momentary strobe here? (for light painting) + // init strobe mode, if relevant + if ((event == EV_enter_state) && (momentary_mode == 1)) { + strobe_state(event, arg); + } + // light up when the button is pressed; go dark otherwise // button is being held if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - set_level(memorized_level); + 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; @@ -1510,10 +1675,18 @@ uint8_t momentary_state(Event event, uint16_t arg) { // 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) && (actual_level == 0)) { - if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds - go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? + else if (event == EV_tick) { + if (momentary_active) { + // 0 = ramping, 1 = strobes + if (momentary_mode == 1) { + return strobe_state(event, arg); + } + } + else { + if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds + go_to_standby = 1; // sleep while light is off + // TODO: lighted button should use lockout config? + } } return MISCHIEF_MANAGED; } @@ -1647,12 +1820,13 @@ uint8_t muggle_state(Event event, uint16_t arg) { #ifdef USE_THERMAL_REGULATION // overheating is handled specially in muggle mode else if(event == EV_temperature_high) { - // don't even try... - // go immediately to the bottom, in case someone put the light on - // maximum while wrapped in dark-colored flammable insulation - // or something, because muggles are cool like that - // memorized_level = MUGGLE_FLOOR; // override memory? maybe not - set_level(MUGGLE_FLOOR); + #if 0 + blip(); + #endif + // step down proportional to the amount of overheating + uint8_t new = actual_level - arg; + if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; } + set_level(new); return MISCHIEF_MANAGED; } #endif @@ -1750,14 +1924,15 @@ void thermal_config_save() { // calibrate room temperature val = config_state_values[0]; if (val) { - int8_t rawtemp = (temperature >> 1) - therm_cal_offset; + 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; + therm_ceil = 30 + val - 1; } if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } @@ -1786,9 +1961,9 @@ uint8_t beacon_config_state(Event event, uint16_t arg) { inline void beacon_mode_iter() { // one iteration of main loop() set_level(memorized_level); - nice_delay_ms(500); + nice_delay_ms(100); set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 500); + nice_delay_ms(((beacon_seconds) * 1000) - 100); } #endif // #ifdef USE_BEACON_MODE @@ -1920,6 +2095,14 @@ void blink_confirm(uint8_t num) { } } +// 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); +} + #if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) // beacon-like mode for the indicator LED @@ -1955,6 +2138,9 @@ void load_config() { ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e]; ramp_discrete_steps = eeprom[ramp_discrete_steps_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]; @@ -1993,6 +2179,9 @@ void save_config() { eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil; eeprom[ramp_discrete_steps_e] = ramp_discrete_steps; #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]; @@ -2092,14 +2281,19 @@ void setup() { load_config(); + #ifdef USE_TINT_RAMPING + // add tint ramping underneath every other state + push_state(tint_ramping_state, 0); + #endif // ifdef USE_TINT_RAMPING + #ifdef USE_MUGGLE_MODE if (muggle_mode_active) push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2); else #endif push_state(off_state, 0); - #endif + #endif // ifdef START_AT_MEMORIZED_LEVEL } @@ -2110,7 +2304,8 @@ void loop() { if (0) {} #ifdef USE_STROBE_STATE - else if (state == strobe_state) { + else if ((state == strobe_state) + || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes uint8_t st = strobe_type; switch(st) { @@ -2142,7 +2337,8 @@ void loop() { #endif // #ifdef USE_STROBE_STATE #ifdef USE_BORING_STROBE_STATE - else if (state == boring_strobe_state) { + else if ((state == boring_strobe_state) + || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes switch(boring_strobe_type) { #ifdef USE_POLICE_STROBE_MODE case 0: // police strobe @@ -2174,7 +2370,7 @@ void loop() { #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state? else if (state == tempcheck_state) { - blink_num(temperature>>1); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h index 0ebcfd0..39ad3aa 100644 --- a/spaghetti-monster/fsm-events.h +++ b/spaghetti-monster/fsm-events.h @@ -43,7 +43,7 @@ static volatile uint16_t ticks_since_last_event = 0; #define HOLD_TIMEOUT 24 #endif #ifndef RELEASE_TIMEOUT -#define RELEASE_TIMEOUT 24 +#define RELEASE_TIMEOUT 18 #endif /* Event structure |
