aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
Diffstat (limited to 'spaghetti-monster')
-rw-r--r--spaghetti-monster/anduril/anduril-manual.txt20
-rw-r--r--spaghetti-monster/anduril/anduril.c222
-rw-r--r--spaghetti-monster/anduril/cfg-blf-lantern.h10
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d18.h2
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4.h2
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h2
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47-219.h2
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47.h6
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66.h2
-rw-r--r--spaghetti-monster/anduril/cfg-fw3a.h4
-rw-r--r--spaghetti-monster/fsm-adc.c111
-rw-r--r--spaghetti-monster/fsm-misc.c7
-rw-r--r--spaghetti-monster/fsm-misc.h3
-rw-r--r--spaghetti-monster/fsm-ramping.c27
-rw-r--r--spaghetti-monster/fsm-ramping.h1
15 files changed, 266 insertions, 155 deletions
diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt
index 96640f6..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.
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.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