From 5631564b329d0445fb282e5e387217ba4e4ff191 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 23 Aug 2017 19:22:22 -0600 Subject: Added thermal regulation to SpaghettiMonster / Baton. Made some LVP values configurable. Removed high_temperature() / low_temperature() shortcuts for now. --- spaghetti-monster/baton.c | 36 ++++++++++- spaghetti-monster/fsm-adc.c | 136 +++++++++++++++++++++++++++++++++++++++-- spaghetti-monster/fsm-adc.h | 41 +++++++++++-- spaghetti-monster/fsm-states.c | 2 + 4 files changed, 206 insertions(+), 9 deletions(-) diff --git a/spaghetti-monster/baton.c b/spaghetti-monster/baton.c index 5ab35be..6b694e4 100644 --- a/spaghetti-monster/baton.c +++ b/spaghetti-monster/baton.c @@ -19,6 +19,8 @@ #define FSM_EMISAR_D4_LAYOUT #define USE_LVP +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 #define USE_DEBUG_BLINK #define USE_DELAY_MS #define USE_DELAY_ZERO @@ -37,6 +39,9 @@ uint8_t party_strobe_state(EventPtr event, uint16_t arg); // brightness control uint8_t memorized_level = 1; uint8_t actual_level = 0; +#ifdef USE_THERMAL_REGULATION +uint8_t target_level = 0; +#endif void set_level(uint8_t lvl) { actual_level = lvl; @@ -94,6 +99,9 @@ uint8_t steady_state(EventPtr event, uint16_t arg) { if ((arg > 0) && (arg < MAX_LEVEL)) memorized_level = arg; // use the requested level even if not memorized + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif set_level(arg); return 0; } @@ -106,10 +114,17 @@ uint8_t steady_state(EventPtr event, uint16_t arg) { else if (event == EV_2clicks) { if (actual_level < MAX_LEVEL) { memorized_level = actual_level; // in case we're on moon + #ifdef USE_THERMAL_REGULATION + target_level = MAX_LEVEL; + #endif set_level(MAX_LEVEL); } - else + else { + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif set_level(memorized_level); + } return 0; } // 3 clicks: go to strobe modes @@ -121,10 +136,29 @@ uint8_t steady_state(EventPtr event, uint16_t arg) { else if (event == EV_click1_hold) { if ((arg % HOLD_TIMEOUT) == 0) { memorized_level = (actual_level+1) % (MAX_LEVEL+1); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif set_level(memorized_level); } return 0; } + #ifdef USE_THERMAL_REGULATION + // overheating: drop by 1 level + else if (event == EV_temperature_high) { + if (actual_level > 1) { + set_level(actual_level - 1); + } + return 0; + } + // underheating: increase by 1 level if we're lower than the target + else if (event == EV_temperature_low) { + if (actual_level < target_level) { + set_level(actual_level + 1); + } + return 0; + } + #endif return 1; } diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 11468b9..8af3487 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -35,9 +35,18 @@ inline void ADC_off() { ADCSRA &= ~(1<> 2; + // More precise method: use noise as extra precision + // (values are now basically fixed-point, signed 13.2) + temperature = total; + } + + // guess what the temperature will be in a few seconds + { + uint8_t i; + int16_t diff; + + // algorithm tweaking; not really intended to be modified + // how far ahead should we predict? + #define THERM_PREDICTION_STRENGTH 4 + // how proportional should the adjustments be? + #define THERM_DIFF_ATTENUATION 4 + // acceptable temperature window size in C + #define THERM_WINDOW_SIZE 8 + // highest temperature allowed + // (convert configured value to 13.2 fixed-point) + #define THERM_CEIL (therm_ceil<<2) + // bottom of target temperature window (13.2 fixed-point) + #define THERM_FLOOR (THERM_CEIL - (THERM_WINDOW_SIZE<<2)) + + // rotate measurements and add a new one + for (i=0; i THERM_FLOOR) { + underheat_lowpass = 0; // we're definitely not too cold + } else if (projected_temperature < THERM_CEIL) { + overheat_lowpass = 0; // we're definitely not too hot + } + + if (temperature_timer) { + temperature_timer --; + } else { // it has been long enough since the last warning + + // Too hot? + if (projected_temperature > THERM_CEIL) { + if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) { + overheat_lowpass ++; + } else { + // how far above the ceiling? + int16_t howmuch = (projected_temperature - THERM_CEIL) >> THERM_DIFF_ATTENUATION; + if (howmuch < 1) howmuch = 1; + // try to send out a warning + emit(EV_temperature_high, howmuch); + // reset counters + temperature_timer = TEMPERATURE_TIMER_START; + overheat_lowpass = 0; + } + } + + // Too cold? + else if (projected_temperature < THERM_FLOOR) { + if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) { + underheat_lowpass ++; + } else { + // how far below the floor? + int16_t howmuch = (THERM_FLOOR - projected_temperature) >> THERM_DIFF_ATTENUATION; + if (howmuch < 1) howmuch = 1; + // try to send out a warning + emit(EV_temperature_low, howmuch); + // reset counters + temperature_timer = TEMPERATURE_TIMER_START; + underheat_lowpass = 0; + } + } + } + } + #endif // ifdef USE_THERMAL_REGULATION + // start another measurement for next time #ifdef USE_THERMAL_REGULATION diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index ac42333..43d52a6 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -20,21 +20,54 @@ #ifndef FSM_ADC_H #define FSM_ADC_H + #ifdef USE_LVP -// volts * 10 -#define VOLTAGE_LOW 30 +// default 5 seconds between low-voltage warning events +#ifndef VOLTAGE_WARNING_SECONDS +#define VOLTAGE_WARNING_SECONDS 5 +#endif +// low-battery threshold in volts * 10 +#ifndef VOLTAGE_LOW +#define VOLTAGE_LOW 29 +#endif // MCU sees voltage 0.X volts lower than actual, add X to readings +#ifndef VOLTAGE_FUDGE_FACTOR #define VOLTAGE_FUDGE_FACTOR 2 +#endif volatile uint8_t voltage; void low_voltage(); #endif + + #ifdef USE_THERMAL_REGULATION +// default 5 seconds between thermal regulation events +#ifndef THERMAL_WARNING_SECONDS +#define THERMAL_WARNING_SECONDS 5 +#endif +// try to keep temperature below 45 C +#ifndef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 45 +#endif +// don't allow user to set ceiling above 70 C +#ifndef MAX_THERM_CEIL +#define MAX_THERM_CEIL 70 +#endif +// Local per-MCU calibration value +#ifndef THERM_CAL_OFFSET +#define THERM_CAL_OFFSET 0 +#endif +// temperature now, in C (ish) volatile int16_t temperature; -void low_temperature(); -void high_temperature(); +// temperature in a few seconds, in C (ish) * 4 (13.2 fixed-point) +volatile int16_t projected_temperature; // Fight the future! +uint8_t therm_ceil = DEFAULT_THERM_CEIL; +//void low_temperature(); +//void high_temperature(); #endif + inline void ADC_on(); inline void ADC_off(); + #endif diff --git a/spaghetti-monster/fsm-states.c b/spaghetti-monster/fsm-states.c index 652a9f2..ec24dc8 100644 --- a/spaghetti-monster/fsm-states.c +++ b/spaghetti-monster/fsm-states.c @@ -89,6 +89,7 @@ uint8_t default_state(EventPtr event, uint16_t arg) { } #endif + #if 0 #ifdef USE_THERMAL_REGULATION else if (event == EV_temperature_high) { high_temperature(); @@ -100,6 +101,7 @@ uint8_t default_state(EventPtr event, uint16_t arg) { return 0; } #endif + #endif // event not handled return 1; -- cgit v1.2.3