aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
authorSelene ToyKeeper2017-08-23 19:22:22 -0600
committerSelene ToyKeeper2017-08-23 19:22:22 -0600
commit5631564b329d0445fb282e5e387217ba4e4ff191 (patch)
tree008d8aec159c7ada2b2e650d71c4f092bc6679d5 /spaghetti-monster
parentMade Baton a little easier to read: (diff)
downloadanduril-5631564b329d0445fb282e5e387217ba4e4ff191.tar.gz
anduril-5631564b329d0445fb282e5e387217ba4e4ff191.tar.bz2
anduril-5631564b329d0445fb282e5e387217ba4e4ff191.zip
Added thermal regulation to SpaghettiMonster / Baton.
Made some LVP values configurable. Removed high_temperature() / low_temperature() shortcuts for now.
Diffstat (limited to 'spaghetti-monster')
-rw-r--r--spaghetti-monster/baton.c36
-rw-r--r--spaghetti-monster/fsm-adc.c136
-rw-r--r--spaghetti-monster/fsm-adc.h41
-rw-r--r--spaghetti-monster/fsm-states.c2
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<<ADEN); //ADC off
}
+// Each full cycle runs 7.8X per second with just voltage enabled,
+// or 3.9X per second with voltage and temperature.
+#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION)
+#define ADC_CYCLES_PER_SECOND 4
+#else
+#define ADC_CYCLES_PER_SECOND 8
+#endif
// TODO: is this better done in main() or WDT()?
ISR(ADC_vect) {
static uint8_t adc_step = 0;
+
+ // LVP declarations
#ifdef USE_LVP
#ifdef USE_LVP_AVG
#define NUM_VOLTAGE_VALUES 4
@@ -45,14 +54,24 @@ ISR(ADC_vect) {
#endif
static uint8_t lvp_timer = 0;
static uint8_t lvp_lowpass = 0;
- #define LVP_TIMER_START 50 // ticks between LVP warnings
- #define LVP_LOWPASS_STRENGTH 4
+ #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings
+ #define LVP_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second
#endif
+ // thermal declarations
#ifdef USE_THERMAL_REGULATION
#define NUM_THERMAL_VALUES 4
+ #define NUM_THERMAL_VALUES_HISTORY 16
#define ADC_STEPS 4
- static int16_t temperature_values[NUM_THERMAL_VALUES];
+ static uint8_t first_temp_reading = 1;
+ static int16_t temperature_values[NUM_THERMAL_VALUES]; // last few readings in C
+ static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; // 13.2 fixed-point
+ 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
#else
#define ADC_STEPS 2
#endif
@@ -116,7 +135,116 @@ ISR(ADC_vect) {
}
#endif // ifdef USE_LVP
- // TODO: temperature
+
+ #ifdef USE_THERMAL_REGULATION
+ // temperature
+ else if (adc_step == 3) {
+ // Convert ADC units to Celsius (ish)
+ int16_t temp = measurement - 275 + THERM_CAL_OFFSET;
+
+ // prime on first execution
+ if (first_temp_reading) {
+ first_temp_reading = 0;
+ for(uint8_t i=0; i<NUM_THERMAL_VALUES; i++)
+ temperature_values[i] = temp;
+ for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++)
+ temperature_history[i] = temp;
+ temperature = temp;
+ } else { // update our current temperature estimate
+ uint8_t i;
+ int16_t total=0;
+
+ // rotate array
+ for(i=0; i<NUM_THERMAL_VALUES-1; i++) {
+ temperature_values[i] = temperature_values[i+1];
+ total += temperature_values[i];
+ }
+ 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;
+ }
+
+ // 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<NUM_THERMAL_VALUES_HISTORY-1; i++) {
+ temperature_history[i] = temperature_history[i+1];
+ }
+ temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = temperature;
+
+ // guess what the temp will be several seconds in the future
+ diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0];
+ projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH);
+
+ }
+
+ // cancel counters if necessary
+ if (projected_temperature > 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;
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.