diff options
Diffstat (limited to 'spaghetti-monster')
| -rw-r--r-- | spaghetti-monster/anduril/anduril-manual.txt | 23 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/anduril.c | 222 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-blf-lantern.h | 10 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-emisar-d18.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-emisar-d4.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-emisar-d4s.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-ff-pl47-219.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-ff-pl47.h | 6 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-ff-rot66.h | 2 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-fw3a-219.h | 9 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-fw3a.h | 4 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-adc.c | 111 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-misc.c | 7 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-misc.h | 3 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-ramping.c | 27 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-ramping.h | 1 |
16 files changed, 278 insertions, 155 deletions
diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt index cd4fe3e..3684b50 100644 --- a/spaghetti-monster/anduril/anduril-manual.txt +++ b/spaghetti-monster/anduril/anduril-manual.txt @@ -223,11 +223,23 @@ Momentary Mode Click 5 times from Off to enter Momentary mode. This locks the flashlight into a single-mode interface where the LEDs are only on when -the button is held down. It is intended for Morse code and other -signalling tasks. +the button is held down. It is intended for Morse code, light painting, +and other tasks where the light should be on only for a short time and +probably in a pattern. -Brightness is the last-ramped level, so adjust that before entering -momentary mode. +Momentary mode does either a steady brightness level or a strobe. To +select which one, go to the mode you want to use, adjust the brightness +and speed and other settings, then turn the light off. Then click 5 +times to enter momentary mode. + +Supported momentary modes are steady (normal ramping mode) and strobes +(everything in the "strobe" mode group). + +In steady mode, brightness is the last-ramped level, so adjust that +before entering momentary mode. + +In momentary strobe mode, the settings are copied from the last-used +strobe mode, such as party strobe, tactical strobe, or lightning. To exit this mode, physically disconnect power by unscrewing the tailcap or battery tube. @@ -253,6 +265,9 @@ prevent children from exiting the mode by unscrewing the tailcap. However, be sure to supervise children whenever they are using any powerful tools, including a bright flashlight. +Note: If the light steps down in muggle mode, it probably needs to have +the temperature sensor calibrated. Use thermal config mode to do this. + Configuration Modes ------------------- diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index e3ff0cb..f9509e7 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -185,6 +185,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" @@ -234,10 +240,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); @@ -250,6 +258,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 @@ -262,6 +271,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 @@ -305,7 +317,7 @@ void save_config_wl(); #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; @@ -434,10 +446,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(3); - set_level(temp); + blip(); } else #endif // don't start ramping immediately; @@ -561,6 +570,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; @@ -615,8 +625,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; } @@ -667,8 +676,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) @@ -682,8 +690,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); @@ -729,8 +736,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) @@ -744,8 +750,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); @@ -816,10 +821,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) { @@ -847,10 +849,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); @@ -873,23 +872,53 @@ 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) { - //if ((arg & 1) == 0) { // ramp slower - if ((tint_ramp_direction > 0) && (tint < 255)) { - tint += 1; - } - else if ((tint_ramp_direction < 0) && (tint > 0)) { - tint -= 1; - } - set_level(actual_level); - //} + // 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; @@ -905,10 +934,14 @@ uint8_t tint_ramping_state(Event event, uint16_t arg) { #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) @@ -918,11 +951,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); @@ -947,7 +980,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 @@ -958,17 +995,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 @@ -999,14 +1046,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; } @@ -1048,7 +1097,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); @@ -1103,6 +1152,7 @@ inline void bike_flasher_iter() { #ifdef USE_CANDLE_MODE uint8_t candle_mode_state(Event event, uint16_t arg) { + 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; @@ -1122,6 +1172,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 @@ -1133,12 +1184,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; @@ -1156,6 +1220,9 @@ 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) { @@ -1215,12 +1282,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 @@ -1522,14 +1583,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; @@ -1540,10 +1611,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; } @@ -1677,12 +1756,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 @@ -1780,14 +1860,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; } @@ -1816,9 +1897,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 @@ -1950,6 +2031,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 @@ -2151,7 +2240,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) { @@ -2215,7 +2305,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/anduril/cfg-blf-lantern.h b/spaghetti-monster/anduril/cfg-blf-lantern.h index 3d4c762..9467397 100644 --- a/spaghetti-monster/anduril/cfg-blf-lantern.h +++ b/spaghetti-monster/anduril/cfg-blf-lantern.h @@ -44,9 +44,10 @@ // set floor and ceiling as far apart as possible // because this lantern isn't overpowered +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped #define RAMP_SMOOTH_FLOOR 1 #define RAMP_SMOOTH_CEIL 150 -#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_FLOOR 10 #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 5 @@ -68,9 +69,10 @@ #ifdef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_MIDDLE #endif -#ifdef BLINK_AT_RAMP_CEILING -#undef BLINK_AT_RAMP_CEILING -#endif #ifdef BLINK_AT_RAMP_FLOOR #undef BLINK_AT_RAMP_FLOOR #endif +// except the top... blink at the top +#ifndef BLINK_AT_RAMP_CEILING +#define BLINK_AT_RAMP_CEILING +#endif diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h index fcea43a..16fbacd 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d18.h +++ b/spaghetti-monster/anduril/cfg-emisar-d18.h @@ -43,4 +43,4 @@ // stop panicking at about ~40% power or ~5000 lm #define THERM_FASTER_LEVEL 125 // optional, makes initial turbo step-down faster so first peak isn't as hot -#define THERM_HARD_TURBO_DROP +//#define THERM_HARD_TURBO_DROP diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h index 2f4df49..de8f796 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4.h @@ -13,7 +13,7 @@ // optional, makes initial turbo step-down faster so first peak isn't as hot // the D4 runs very very hot, so be extra careful -#define THERM_HARD_TURBO_DROP +//#define THERM_HARD_TURBO_DROP // stop panicking at ~30% power or ~1200 lm #define THERM_FASTER_LEVEL 105 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h index e26634a..d19a514 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4s.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h @@ -36,7 +36,7 @@ #undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 #endif #define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high +#define THERM_FASTER_LEVEL (RAMP_SIZE-1) // don't throttle back faster when high // no need to be extra-careful on this light #ifdef THERM_HARD_TURBO_DROP diff --git a/spaghetti-monster/anduril/cfg-ff-pl47-219.h b/spaghetti-monster/anduril/cfg-ff-pl47-219.h index 3ded2aa..ad66adb 100644 --- a/spaghetti-monster/anduril/cfg-ff-pl47-219.h +++ b/spaghetti-monster/anduril/cfg-ff-pl47-219.h @@ -3,5 +3,7 @@ // to avoid destroying the LEDs #include "cfg-ff-pl47.h" +#undef PWM1_LEVELS #undef PWM2_LEVELS +#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,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,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,255,255,255,255,255,255,255,255,255,255,255 #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,1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,19,20,22,23,25,27,28,30,31,33,35,37,39,41,43,45,47,50,52,55,57,60,63,65,68,71,74,77,80,83,87,90,93,97,101,105,108,112,116,121,125,129,134,139,143,148,153,159,164,169 diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h index f9c8974..7a81c25 100644 --- a/spaghetti-monster/anduril/cfg-ff-pl47.h +++ b/spaghetti-monster/anduril/cfg-ff-pl47.h @@ -59,10 +59,12 @@ //#define MUGGLE_CEILING 90 // regulate down faster when the FET is active, slower otherwise -#define THERM_FASTER_LEVEL 130 // throttle back faster when high +#define THERM_FASTER_LEVEL 135 // throttle back faster when high // play it safe, don't try to regulate above the recommended safe level -#define THERM_HARD_TURBO_DROP +#ifdef THERM_HARD_TURBO_DROP +#undef THERM_HARD_TURBO_DROP +#endif // don't do this #undef BLINK_AT_RAMP_MIDDLE diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h index 6073ce0..2a90343 100644 --- a/spaghetti-monster/anduril/cfg-ff-rot66.h +++ b/spaghetti-monster/anduril/cfg-ff-rot66.h @@ -39,7 +39,7 @@ #define THERM_FASTER_LEVEL 130 // throttle back faster when high // play it safe, don't try to regulate above the recommended safe level -#define THERM_HARD_TURBO_DROP +//#define THERM_HARD_TURBO_DROP // don't do this #undef BLINK_AT_RAMP_MIDDLE diff --git a/spaghetti-monster/anduril/cfg-fw3a-219.h b/spaghetti-monster/anduril/cfg-fw3a-219.h new file mode 100644 index 0000000..1d8e4a1 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-fw3a-219.h @@ -0,0 +1,9 @@ +// FW3A-219 config options for Anduril +#include "cfg-fw3a.h" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 +#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,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM3_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,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,8,13,19,25,30,36,42,48,54,61,67,73,80,86,93,100,107,114,120,128 diff --git a/spaghetti-monster/anduril/cfg-fw3a.h b/spaghetti-monster/anduril/cfg-fw3a.h index dd77137..a28d12a 100644 --- a/spaghetti-monster/anduril/cfg-fw3a.h +++ b/spaghetti-monster/anduril/cfg-fw3a.h @@ -15,5 +15,5 @@ // stop panicking at about 3A or ~1100 lm, this light is a hotrod #define THERM_FASTER_LEVEL MAX_Nx7135 -// optional, makes initial turbo step-down faster so first peak isn't as hot -#define THERM_HARD_TURBO_DROP + +#define USE_TENCLICK_THERMAL_CONFIG diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 8decd9d..bcf49ed 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -89,20 +89,16 @@ ISR(ADC_vect) { // thermal declarations #ifdef USE_THERMAL_REGULATION - #define NUM_THERMAL_VALUES 8 - #define NUM_THERMAL_VALUES_HISTORY 16 - #define NUM_THERMAL_PROJECTED_HISTORY 8 + #define NUM_THERMAL_VALUES_HISTORY 8 #define ADC_STEPS 4 - static int16_t temperature_values[NUM_THERMAL_VALUES]; // last few readings in C - static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; // 14.1 fixed-point - static int16_t projected_temperature_history[NUM_THERMAL_PROJECTED_HISTORY]; // 14.1 fixed-point - static uint8_t projected_temperature_history_counter = 0; + static uint8_t history_step = 0; // don't update history as often + static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; static uint8_t temperature_timer = 0; static uint8_t overheat_lowpass = 0; static uint8_t underheat_lowpass = 0; #define TEMPERATURE_TIMER_START (THERMAL_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events - #define OVERHEAT_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second - #define UNDERHEAT_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second + #define OVERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds + #define UNDERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds #else #define ADC_STEPS 2 #endif @@ -190,33 +186,17 @@ ISR(ADC_vect) { // prime on first execution if (reset_thermal_history) { reset_thermal_history = 0; - for(uint8_t i=0; i<NUM_THERMAL_VALUES; i++) - temperature_values[i] = temp; + temperature = temp; for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++) - temperature_history[i] = temp<<1; - for(uint8_t i=0; i<NUM_THERMAL_PROJECTED_HISTORY; i++) - projected_temperature_history[i] = temp<<1; - temperature = temp<<1; + temperature_history[i] = temp; } else { // update our current temperature estimate - uint8_t i; - int16_t total=0; - - // rotate array - // FIXME: just move the index, don't move the values? - for(i=0; i<NUM_THERMAL_VALUES-1; i++) { - temperature_values[i] = temperature_values[i+1]; - total += temperature_values[i]; + // crude lowpass filter + // (limit rate of change to 1 degree per measurement) + if (temp > temperature) { + temperature ++; + } else if (temp < temperature) { + temperature --; } - temperature_values[i] = temp; - total += temp; - - // Divide back to original range: - //temperature = total >> 2; - // More precise method: use noise as extra precision - // (values are now basically fixed-point, signed 13.2) - //temperature = total; - // 14.1 is less prone to overflows - temperature = total >> 2; } // guess what the temperature will be in a few seconds @@ -230,18 +210,20 @@ ISR(ADC_vect) { // how far ahead should we predict? #define THERM_PREDICTION_STRENGTH 4 // how proportional should the adjustments be? - #define THERM_DIFF_ATTENUATION 3 // acceptable temperature window size in C - #define THERM_WINDOW_SIZE 10 + #define THERM_WINDOW_SIZE 5 // highest temperature allowed - // (convert configured value to 14.1 fixed-point) - #define THERM_CEIL (((int16_t)therm_ceil)<<1) - // bottom of target temperature window (14.1 fixed-point) - #define THERM_FLOOR (THERM_CEIL - (THERM_WINDOW_SIZE<<1)) - - // rotate measurements and add a new one - for (i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { - temperature_history[i] = temperature_history[i+1]; + #define THERM_CEIL ((int16_t)therm_ceil) + // bottom of target temperature window + #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE) + + // if it's time to rotate the thermal history, do it + history_step ++; + if (0 == (history_step & 7)) { + // rotate measurements and add a new one + for (i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { + temperature_history[i] = temperature_history[i+1]; + } } temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t; @@ -249,25 +231,16 @@ ISR(ADC_vect) { // diff = rate of temperature change //diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0]; diff = t - temperature_history[0]; + // slight bias toward zero; ignore very small changes (noise) + for (uint8_t z=0; z<3; z++) { + if (diff < 0) diff ++; + if (diff > 0) diff --; + } // projected_temperature = current temp extended forward by amplified rate of change //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH); pt = projected_temperature = t + (diff<<THERM_PREDICTION_STRENGTH); - - // store prediction for later averaging - projected_temperature_history[projected_temperature_history_counter] = pt; - projected_temperature_history_counter = (projected_temperature_history_counter + 1) & (NUM_THERMAL_PROJECTED_HISTORY-1); } - // average prediction to reduce noise - int16_t avg_projected_temperature = 0; - uint8_t i; - for (i = 0; - (i < NUM_THERMAL_PROJECTED_HISTORY) && (avg_projected_temperature < 16000); - i++) - avg_projected_temperature += projected_temperature_history[i]; - avg_projected_temperature /= NUM_THERMAL_PROJECTED_HISTORY; - //avg_projected_temperature /= i; - // cancel counters if appropriate if (pt > THERM_FLOOR) { underheat_lowpass = 0; // we're probably not too cold @@ -280,7 +253,7 @@ ISR(ADC_vect) { } else { // it has been long enough since the last warning // Too hot? - if (avg_projected_temperature > THERM_CEIL) { + if (pt > THERM_CEIL) { if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) { overheat_lowpass ++; } else { @@ -288,16 +261,14 @@ ISR(ADC_vect) { overheat_lowpass = 0; temperature_timer = TEMPERATURE_TIMER_START; // how far above the ceiling? - int16_t howmuch = (avg_projected_temperature - THERM_CEIL) >> THERM_DIFF_ATTENUATION; - if (howmuch > 0) { - // try to send out a warning - emit(EV_temperature_high, howmuch); - } + int16_t howmuch = pt - THERM_CEIL; + // try to send out a warning + emit(EV_temperature_high, howmuch); } } // Too cold? - else if (avg_projected_temperature < THERM_FLOOR) { + else if (pt < THERM_FLOOR) { if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) { underheat_lowpass ++; } else { @@ -305,13 +276,11 @@ ISR(ADC_vect) { underheat_lowpass = 0; temperature_timer = TEMPERATURE_TIMER_START; // how far below the floor? - int16_t howmuch = (THERM_FLOOR - avg_projected_temperature) >> THERM_DIFF_ATTENUATION; - if (howmuch > 0) { - // try to send out a warning (unless voltage is low) - // (LVP and underheat warnings fight each other) - if (voltage > VOLTAGE_LOW) - emit(EV_temperature_low, howmuch); - } + int16_t howmuch = THERM_FLOOR - pt; + // try to send out a warning (unless voltage is low) + // (LVP and underheat warnings fight each other) + if (voltage > VOLTAGE_LOW) + emit(EV_temperature_low, howmuch); } } } diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c index e61fe00..9f953fa 100644 --- a/spaghetti-monster/fsm-misc.c +++ b/spaghetti-monster/fsm-misc.c @@ -146,5 +146,12 @@ void indicator_led_auto() { */ #endif // USE_INDICATOR_LED +#ifdef USE_TRIANGLE_WAVE +uint8_t triangle_wave(uint8_t phase) { + uint8_t result = phase << 1; + if (phase > 127) result = 255 - result; + return result; +} +#endif #endif diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h index 4e0eb4f..6e41b6c 100644 --- a/spaghetti-monster/fsm-misc.h +++ b/spaghetti-monster/fsm-misc.h @@ -46,5 +46,8 @@ uint8_t blink(uint8_t num, uint8_t speed); void indicator_led(uint8_t lvl); #endif +#ifdef USE_TRIANGLE_WAVE +uint8_t triangle_wave(uint8_t phase); +#endif #endif diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c index ee816dd..082f8c9 100644 --- a/spaghetti-monster/fsm-ramping.c +++ b/spaghetti-monster/fsm-ramping.c @@ -65,8 +65,31 @@ void set_level(uint8_t level) { // and a global tint value uint8_t brightness = pgm_read_byte(pwm1_levels + level); uint8_t warm_PWM, cool_PWM; - cool_PWM = (((uint16_t)tint * (uint16_t)brightness) + 127) / 255; - warm_PWM = brightness - cool_PWM; + + // auto-tint modes + uint8_t mytint; + #if 1 + // perceptual by ramp level + if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; } + else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); } + #else + // linear with power level + //if (tint == 0) { mytint = brightness; } + //else if (tint == 255) { mytint = 255 - brightness; } + #endif + // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198) + else { mytint = (tint * 100 / 99) - 1; } + + // middle tints sag, so correct for that effect + uint16_t base_PWM = brightness; + // correction is only necessary when PWM is fast + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((uint16_t)brightness) * 26 / 64) * triangle_wave(mytint) / 255); + } + + cool_PWM = (((uint16_t)mytint * (uint16_t)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; PWM1_LVL = warm_PWM; PWM2_LVL = cool_PWM; diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h index 4ce8015..dcc3b74 100644 --- a/spaghetti-monster/fsm-ramping.h +++ b/spaghetti-monster/fsm-ramping.h @@ -28,6 +28,7 @@ volatile uint8_t actual_level = 0; #ifdef USE_TINT_RAMPING uint8_t tint = 128; +#define USE_TRIANGLE_WAVE #endif #ifdef USE_SET_LEVEL_GRADUALLY |
