aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--spaghetti-monster/anduril/anduril.c282
-rwxr-xr-xspaghetti-monster/anduril/build-all.sh6
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4.h14
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h6
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-219.h8
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h51
-rw-r--r--spaghetti-monster/fsm-adc.c137
-rw-r--r--spaghetti-monster/fsm-adc.h3
-rw-r--r--spaghetti-monster/fsm-events.c24
-rw-r--r--spaghetti-monster/fsm-main.c56
-rw-r--r--spaghetti-monster/fsm-misc.c35
-rw-r--r--spaghetti-monster/fsm-misc.h7
-rw-r--r--spaghetti-monster/fsm-pcint.c42
-rw-r--r--spaghetti-monster/fsm-ramping.c26
-rw-r--r--spaghetti-monster/fsm-standby.c2
-rw-r--r--spaghetti-monster/fsm-wdt.c82
-rw-r--r--spaghetti-monster/fsm-wdt.h5
-rw-r--r--spaghetti-monster/rampingios/rampingiosv3.c43
-rw-r--r--spaghetti-monster/spaghetti-monster.h1
19 files changed, 645 insertions, 185 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index c2a4b77..8ab66f5 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -132,11 +132,13 @@
// full FET strobe can be a bit much... use max regulated level instead,
// if there's a bright enough regulated level
+#ifndef STROBE_BRIGHTNESS
#ifdef MAX_Nx7135
#define STROBE_BRIGHTNESS MAX_Nx7135
#else
#define STROBE_BRIGHTNESS MAX_LEVEL
#endif
+#endif
#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE)
#define USE_STROBE_STATE
@@ -186,6 +188,10 @@ typedef enum {
#ifdef USE_INDICATOR_LED
indicator_led_mode_e,
#endif
+ #ifdef USE_AUX_RGB_LEDS
+ rgb_led_off_mode_e,
+ rgb_led_lockout_mode_e,
+ #endif
eeprom_indexes_e_END
} eeprom_indexes_e;
#define EEPROM_BYTES eeprom_indexes_e_END
@@ -299,6 +305,31 @@ void blip();
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
void indicator_blink(uint8_t arg);
#endif
+#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
+void rgb_led_update(uint8_t mode, uint8_t arg);
+/*
+ * 0: R
+ * 1: RG
+ * 2: G
+ * 3: GB
+ * 4: B
+ * 5: R B
+ * 6: RGB
+ * 7: rainbow
+ * 8: voltage
+ */
+#define RGB_LED_NUM_COLORS 10
+#define RGB_LED_NUM_PATTERNS 4
+#ifndef RGB_LED_OFF_DEFAULT
+//#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage
+#define RGB_LED_OFF_DEFAULT 0x17 // low, rainbow
+#endif
+#ifndef RGB_LED_LOCKOUT_DEFAULT
+#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
+#endif
+uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT;
+uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
+#endif
#ifdef USE_FACTORY_RESET
void factory_reset();
@@ -459,27 +490,36 @@ uint8_t off_state(Event event, uint16_t arg) {
set_level(0);
#ifdef USE_INDICATOR_LED
indicator_led(indicator_led_mode & 0x03);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(rgb_led_off_mode, 0);
#endif
// sleep while off (lower power use)
- go_to_standby = 1;
+ // (unless delay requested; give the ADC some time to catch up)
+ if (! arg) { go_to_standby = 1; }
return MISCHIEF_MANAGED;
}
// go back to sleep eventually if we got bumped but didn't leave "off" state
else if (event == EV_tick) {
- if (arg > TICKS_PER_SECOND*2) {
+ if (arg > HOLD_TIMEOUT) {
go_to_standby = 1;
#ifdef USE_INDICATOR_LED
indicator_led(indicator_led_mode & 0x03);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(rgb_led_off_mode, arg);
#endif
}
return MISCHIEF_MANAGED;
}
- #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
// blink the indicator LED, maybe
else if (event == EV_sleep_tick) {
+ #ifdef USE_INDICATOR_LED
if ((indicator_led_mode & 0b00000011) == 0b00000011) {
indicator_blink(arg);
}
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(rgb_led_off_mode, arg);
+ #endif
return MISCHIEF_MANAGED;
}
#endif
@@ -608,7 +648,33 @@ uint8_t off_state(Event event, uint16_t arg) {
save_config();
return MISCHIEF_MANAGED;
}
- #endif
+ #elif defined(USE_AUX_RGB_LEDS)
+ // 7 clicks: change RGB aux LED pattern
+ else if (event == EV_7clicks) {
+ uint8_t mode = (rgb_led_off_mode >> 4) + 1;
+ mode = mode % RGB_LED_NUM_PATTERNS;
+ rgb_led_off_mode = (mode << 4) | (rgb_led_off_mode & 0x0f);
+ rgb_led_update(rgb_led_off_mode, 0);
+ save_config();
+ blink_confirm(1);
+ return MISCHIEF_MANAGED;
+ }
+ // 7 clicks (hold last): change RGB aux LED color
+ else if (event == EV_click7_hold) {
+ if (0 == (arg & 0x3f)) {
+ uint8_t mode = (rgb_led_off_mode & 0x0f) + 1;
+ mode = mode % RGB_LED_NUM_COLORS;
+ rgb_led_off_mode = mode | (rgb_led_off_mode & 0xf0);
+ //save_config();
+ }
+ rgb_led_update(rgb_led_off_mode, arg);
+ return MISCHIEF_MANAGED;
+ }
+ else if (event == EV_click7_hold_release) {
+ save_config();
+ return MISCHIEF_MANAGED;
+ }
+ #endif // end 7 clicks
#ifdef USE_TENCLICK_THERMAL_CONFIG
// 10 clicks: thermal config mode
else if (event == EV_10clicks) {
@@ -854,7 +920,10 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
#ifdef USE_SET_LEVEL_GRADUALLY
// make thermal adjustment speed scale with magnitude
- if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
+ // also, adjust slower when going up
+ if ((arg & 1) &&
+ ((actual_level < THERM_FASTER_LEVEL) ||
+ (actual_level < gradual_target))) {
return MISCHIEF_MANAGED; // adjust slower when not a high mode
}
#ifdef THERM_HARD_TURBO_DROP
@@ -872,17 +941,21 @@ uint8_t steady_state(Event event, uint16_t arg) {
uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2};
uint8_t diff;
static uint8_t ticks_since_adjust = 0;
- ticks_since_adjust ++;
- if (gradual_target > actual_level) diff = gradual_target - actual_level;
- else {
+ if (gradual_target > actual_level) {
+ // rise at half speed (skip half the frames)
+ if (arg & 2) return MISCHIEF_MANAGED;
+ diff = gradual_target - actual_level;
+ } else {
diff = actual_level - gradual_target;
}
+ ticks_since_adjust ++;
// 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 ++; }
+ if ((actual_level >= THERM_FASTER_LEVEL)
+ && (actual_level > gradual_target)) { magnitude ++; }
#endif
while (diff) {
magnitude ++;
@@ -910,7 +983,8 @@ uint8_t steady_state(Event event, uint16_t arg) {
blip();
#endif
#ifdef THERM_HARD_TURBO_DROP
- if (actual_level > THERM_FASTER_LEVEL) {
+ //if (actual_level > THERM_FASTER_LEVEL) {
+ if (actual_level == MAX_LEVEL) {
#ifdef USE_SET_LEVEL_GRADUALLY
set_level_gradually(THERM_FASTER_LEVEL);
target_level = THERM_FASTER_LEVEL;
@@ -1553,6 +1627,10 @@ uint8_t lockout_state(Event event, uint16_t arg) {
#ifdef MOON_DURING_LOCKOUT_MODE
// momentary(ish) moon mode during lockout
// button is being held
+ #ifdef USE_AUX_RGB_LEDS
+ // don't turn on during RGB aux LED configuration
+ if (event == EV_click3_hold) { set_level(0); } else
+ #endif
if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
#ifdef LOCKOUT_MOON_LOWEST
// Use lowest moon configured
@@ -1586,75 +1664,82 @@ uint8_t lockout_state(Event event, uint16_t arg) {
if (event == EV_enter_state) {
indicator_led(indicator_led_mode >> 2);
} else
+ #elif defined(USE_AUX_RGB_LEDS)
+ if (event == EV_enter_state) {
+ rgb_led_update(rgb_led_lockout_mode, 0);
+ } else
#endif
if (event == EV_tick) {
- if (arg > TICKS_PER_SECOND*2) {
+ if (arg > HOLD_TIMEOUT) {
go_to_standby = 1;
#ifdef USE_INDICATOR_LED
indicator_led(indicator_led_mode >> 2);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(rgb_led_lockout_mode, arg);
#endif
}
return MISCHIEF_MANAGED;
}
- #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
else if (event == EV_sleep_tick) {
+ #if defined(USE_INDICATOR_LED)
if ((indicator_led_mode & 0b00001100) == 0b00001100) {
indicator_blink(arg);
}
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(rgb_led_lockout_mode, arg);
+ #endif
return MISCHIEF_MANAGED;
}
#endif
- #ifdef USE_INDICATOR_LED
+ #if defined(USE_INDICATOR_LED)
// 3 clicks: rotate through indicator LED modes (lockout mode)
else if (event == EV_3clicks) {
- uint8_t mode = indicator_led_mode >> 2;
- #ifdef TICK_DURING_STANDBY
- mode = (mode + 1) & 3;
- #else
- mode = (mode + 1) % 3;
- #endif
- #ifdef INDICATOR_LED_SKIP_LOW
- if (mode == 1) { mode ++; }
+ #if defined(USE_INDICATOR_LED)
+ uint8_t mode = indicator_led_mode >> 2;
+ #ifdef TICK_DURING_STANDBY
+ mode = (mode + 1) & 3;
+ #else
+ mode = (mode + 1) % 3;
+ #endif
+ #ifdef INDICATOR_LED_SKIP_LOW
+ if (mode == 1) { mode ++; }
+ #endif
+ indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
+ indicator_led(mode);
+ #elif defined(USE_AUX_RGB_LEDS)
#endif
- indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
- indicator_led(mode);
save_config();
return MISCHIEF_MANAGED;
}
- #if 0 // old method, deprecated in favor of "7 clicks from off"
- // click, click, hold: rotate through indicator LED modes (off mode)
+ #elif defined(USE_AUX_RGB_LEDS)
+ // 3 clicks: change RGB aux LED pattern
+ else if (event == EV_3clicks) {
+ uint8_t mode = (rgb_led_lockout_mode >> 4) + 1;
+ mode = mode % RGB_LED_NUM_PATTERNS;
+ rgb_led_lockout_mode = (mode << 4) | (rgb_led_lockout_mode & 0x0f);
+ rgb_led_update(rgb_led_lockout_mode, 0);
+ save_config();
+ blink_confirm(1);
+ return MISCHIEF_MANAGED;
+ }
+ // click, click, hold: change RGB aux LED color
else if (event == EV_click3_hold) {
- #ifndef USE_INDICATOR_LED_WHILE_RAMPING
- // if main LED obscures aux LEDs, turn it off
- set_level(0);
- #endif
- #ifdef TICK_DURING_STANDBY
- uint8_t mode = (arg >> 5) & 3;
- #else
- uint8_t mode = (arg >> 5) % 3;
- #endif
- #ifdef INDICATOR_LED_SKIP_LOW
- if (mode == 1) { mode ++; }
- #endif
- indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
- #ifdef TICK_DURING_STANDBY
- if (mode == 3)
- indicator_led(mode & (arg&3));
- else
- indicator_led(mode);
- #else
- indicator_led(mode);
- #endif
- //save_config();
+ if (0 == (arg & 0x3f)) {
+ uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1;
+ mode = mode % RGB_LED_NUM_COLORS;
+ rgb_led_lockout_mode = mode | (rgb_led_lockout_mode & 0xf0);
+ //save_config();
+ }
+ rgb_led_update(rgb_led_lockout_mode, arg);
return MISCHIEF_MANAGED;
}
- // click, click, hold, release: save indicator LED mode (off mode)
+ // click, click, hold, release: save new color
else if (event == EV_click3_hold_release) {
save_config();
return MISCHIEF_MANAGED;
}
#endif
- #endif
// 4 clicks: exit
else if (event == EV_4clicks) {
blink_confirm(1);
@@ -1845,10 +1930,13 @@ uint8_t muggle_state(Event event, uint16_t arg) {
#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);
+ // ignore warnings while off
+ if (! muggle_off_mode) {
+ // step down proportional to the amount of overheating
+ int16_t new = actual_level - arg;
+ if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; }
+ set_level(new);
+ }
return MISCHIEF_MANAGED;
}
#endif
@@ -2135,6 +2223,9 @@ void blip() {
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
// beacon-like mode for the indicator LED
void indicator_blink(uint8_t arg) {
+ // turn off aux LEDs when battery is empty
+ if (voltage < VOLTAGE_LOW) { indicator_led(0); return; }
+
#ifdef USE_FANCIER_BLINKING_INDICATOR
// fancy blink, set off/low/high levels here:
@@ -2155,6 +2246,81 @@ void indicator_blink(uint8_t arg) {
}
#endif
+#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
+// do fancy stuff with the RGB aux LEDs
+// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
+// arg: time slice number
+void rgb_led_update(uint8_t mode, uint8_t arg) {
+ static uint8_t rainbow = 0; // track state of rainbow mode
+ static uint8_t frame = 0; // track state of animation mode
+
+ // turn off aux LEDs when battery is empty
+ // (but if voltage==0, that means we just booted and don't know yet)
+ uint8_t volts = voltage; // save a few bytes by caching volatile value
+ if ((volts) && (volts < VOLTAGE_LOW)) { rgb_led_set(0); return; }
+
+ uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more?
+ uint8_t color = mode & 0x0f;
+
+ // preview in blinking mode is awkward... use high instead
+ if ((! go_to_standby) && (pattern > 2)) { pattern = 2; }
+
+
+ uint8_t colors[] = {
+ 0b00000001, // 0: red
+ 0b00000101, // 1: yellow
+ 0b00000100, // 2: green
+ 0b00010100, // 3: cyan
+ 0b00010000, // 4: blue
+ 0b00010001, // 5: purple
+ 0b00010101, // 6: white
+ };
+ uint8_t actual_color = 0;
+ if (color < 7) { // normal color
+ actual_color = colors[color];
+ }
+ else if (color == 7) { // rainbow
+ if (0 == (arg & 0x03)) {
+ rainbow = (rainbow + 1) % 6;
+ }
+ actual_color = colors[rainbow];
+ }
+ else { // voltage
+ // show actual voltage while asleep...
+ if (go_to_standby) {
+ // choose a color based on battery voltage
+ if (volts >= 38) actual_color = colors[4];
+ else if (volts >= 33) actual_color = colors[2];
+ else actual_color = colors[0];
+ }
+ // ... but during preview, cycle colors quickly
+ else {
+ actual_color = colors[((arg>>1) % 3) << 1];
+ }
+ }
+
+ // pick a brightness from the animation sequence
+ if (pattern == 3) {
+ // uses an odd length to avoid lining up with rainbow loop
+ uint8_t animation[] = {2, 1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ frame = (frame + 1) % sizeof(animation);
+ pattern = animation[frame];
+ }
+ switch (pattern) {
+ case 0: // off
+ rgb_led_set(0);
+ break;
+ case 1: // low
+ rgb_led_set(actual_color);
+ break;
+ case 2: // high
+ rgb_led_set(actual_color << 1);
+ break;
+ }
+}
+#endif
+
#ifdef USE_FACTORY_RESET
void factory_reset() {
@@ -2242,6 +2408,10 @@ void load_config() {
#ifdef USE_INDICATOR_LED
indicator_led_mode = eeprom[indicator_led_mode_e];
#endif
+ #ifdef USE_AUX_RGB_LEDS
+ rgb_led_off_mode = eeprom[rgb_led_off_mode_e];
+ rgb_led_lockout_mode = eeprom[rgb_led_lockout_mode_e];
+ #endif
}
#ifdef START_AT_MEMORIZED_LEVEL
if (load_eeprom_wl()) {
@@ -2286,6 +2456,10 @@ void save_config() {
#ifdef USE_INDICATOR_LED
eeprom[indicator_led_mode_e] = indicator_led_mode;
#endif
+ #ifdef USE_AUX_RGB_LEDS
+ eeprom[rgb_led_off_mode_e] = rgb_led_off_mode;
+ eeprom[rgb_led_lockout_mode_e] = rgb_led_lockout_mode;
+ #endif
save_eeprom();
}
@@ -2372,7 +2546,7 @@ void setup() {
push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2);
else
#endif
- push_state(off_state, 0);
+ push_state(off_state, 1);
#endif // ifdef START_AT_MEMORIZED_LEVEL
}
diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh
index c355f1e..56a88bf 100755
--- a/spaghetti-monster/anduril/build-all.sh
+++ b/spaghetti-monster/anduril/build-all.sh
@@ -5,7 +5,9 @@ UI=anduril
for TARGET in cfg-*.h ; do
NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
echo "===== $NAME ====="
- echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
- ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }')
+ if [ -z "$ATTINY" ]; then ATTINY=85 ; fi
+ echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
mv -f "$UI".hex "$UI".$NAME.hex
done
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h
index de8f796..c86a534 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4.h
@@ -9,7 +9,14 @@
#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,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
#define MAX_1x7135 65
#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 5
+#define QUARTERSPEED_LEVEL 6
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 120
+// 10, 28, 46, [65], 83, 101, 120
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 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
@@ -17,3 +24,8 @@
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
+// respond to thermal changes faster
+#define THERMAL_WARNING_SECONDS 3
+#define THERMAL_UPDATE_SPEED 1
+#define THERM_PREDICTION_STRENGTH 4
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h
index d19a514..230ac7c 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h
@@ -36,9 +36,13 @@
#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-1) // don't throttle back faster when high
+#define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high
// no need to be extra-careful on this light
#ifdef THERM_HARD_TURBO_DROP
#undef THERM_HARD_TURBO_DROP
#endif
+
+#define THERMAL_WARNING_SECONDS 3
+#define THERMAL_UPDATE_SPEED 2
+#define THERM_PREDICTION_STRENGTH 4
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
new file mode 100644
index 0000000..0770935
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
@@ -0,0 +1,8 @@
+// Emisar D4v2-219 config options for Anduril
+#include "cfg-emisar-d4v2.h"
+// ATTINY: 1634
+
+#undef PWM1_LEVELS
+#undef PWM2_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,0,0,2,3,3,5,6,6,8,9,10,11,12,14,15,16,18,18,20,21,23,24,26,27,29,30,32,33,36,37,39,41,43,44,46,48,50,52,54,56,58,61,63,65,67,70,72,74,77,79,82,84,86,89,92,95,97,100,103,106,108,111,114,117,120,124,127,130,133,137,140,144,147,151,154,157,161,165,169,172,176,180,184,188,192
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
new file mode 100644
index 0000000..b83c65c
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -0,0 +1,51 @@
+// Emisar D4 config options for Anduril
+#include "hwdef-Emisar_D4v2.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+//#define STANDBY_TICK_SPEED 4 // every 0.256 s
+//#define STANDBY_TICK_SPEED 5 // every 0.512 s
+
+
+// copied from original D4, since it's also a FET+1 and has the same host
+// ../../bin/level_calc.py 1 65 7135 1 0.8 150
+// ... mixed with this:
+// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
+#define RAMP_LENGTH 150
+#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,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,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
+#define MAX_1x7135 65
+#define HALFSPEED_LEVEL 14
+#define QUARTERSPEED_LEVEL 6
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 120
+// 10, 28, 46, [65], 83, 101, 120
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 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
+
+// stop panicking at ~30% power or ~1200 lm
+#define THERM_FASTER_LEVEL 105
+// respond to thermal changes faster
+#define THERMAL_WARNING_SECONDS 3
+#define THERMAL_UPDATE_SPEED 1
+#define THERM_PREDICTION_STRENGTH 4
+//#define THERM_RESPONSE_MAGNITUDE 128
+
+// easier access to thermal config mode, for Emisar
+#define USE_TENCLICK_THERMAL_CONFIG
+
+#define THERM_CAL_OFFSET 5
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
index bcf49ed..6832e32 100644
--- a/spaghetti-monster/fsm-adc.c
+++ b/spaghetti-monster/fsm-adc.c
@@ -20,27 +20,76 @@
#ifndef FSM_ADC_C
#define FSM_ADC_C
-#ifdef USE_VOLTAGE_DIVIDER
-// 1.1V / pin7
-#define ADMUX_VOLTAGE ADMUX_VOLTAGE_DIVIDER
-#else
-// VCC / 1.1V reference
-#define ADMUX_VOLTAGE ADMUX_VCC
-#endif
+inline void set_admux_therm() {
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
+ ADMUX = ADMUX_THERM;
+ #elif (ATTINY == 841)
+ ADMUXA = ADMUXA_THERM;
+ ADMUXB = ADMUXB_THERM;
+ #else
+ #error Unrecognized MCU type
+ #endif
+}
+
+inline void set_admux_voltage() {
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
+ #ifdef USE_VOLTAGE_DIVIDER
+ // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER;
+ #else
+ // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC;
+ #endif
+ #elif (ATTINY == 841)
+ #ifdef USE_VOLTAGE_DIVIDER
+ ADMUXA = ADMUXA_VOLTAGE_DIVIDER;
+ ADMUXB = ADMUXB_VOLTAGE_DIVIDER;
+ #else
+ ADMUXA = ADMUXA_VCC;
+ ADMUXB = ADMUXB_VCC;
+ #endif
+ #else
+ #error Unrecognized MCU type
+ #endif
+}
+
+inline void ADC_start_measurement() {
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634)
+ ADCSRA |= (1 << ADSC) | (1 << ADIE);
+ #else
+ #error unrecognized MCU type
+ #endif
+}
+
+// set up ADC for reading battery voltage
inline void ADC_on()
{
- // read voltage on VCC by default
- ADMUX = ADMUX_VOLTAGE;
- #ifdef USE_VOLTAGE_DIVIDER
- // disable digital input on divider pin to reduce power consumption
- DIDR0 |= (1 << VOLTAGE_ADC_DIDR);
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
+ set_admux_voltage();
+ #ifdef USE_VOLTAGE_DIVIDER
+ // disable digital input on divider pin to reduce power consumption
+ DIDR0 |= (1 << VOLTAGE_ADC_DIDR);
+ #else
+ // disable digital input on VCC pin to reduce power consumption
+ //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin
+ #endif
+ #if (ATTINY == 1634)
+ ACSRA |= (1 << ACD); // turn off analog comparator to save power
+ #endif
+ // enable, start, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ // end tiny25/45/85
+ #elif (ATTINY == 841)
+ ADCSRB = 0; // Right adjusted, auto trigger bits cleared.
+ //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8.
+ set_admux_voltage();
+ // enable, start, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ //ADCSRA |= (1 << ADSC); // start measuring
#else
- // disable digital input on VCC pin to reduce power consumption
- //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin
+ #error Unrecognized MCU type
#endif
- // enable, start, prescale
- ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
}
inline void ADC_off() {
@@ -89,6 +138,9 @@ ISR(ADC_vect) {
// thermal declarations
#ifdef USE_THERMAL_REGULATION
+ #ifndef THERMAL_UPDATE_SPEED
+ #define THERMAL_UPDATE_SPEED 2
+ #endif
#define NUM_THERMAL_VALUES_HISTORY 8
#define ADC_STEPS 4
static uint8_t history_step = 0; // don't update history as often
@@ -96,7 +148,7 @@ ISR(ADC_vect) {
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 TEMPERATURE_TIMER_START ((THERMAL_WARNING_SECONDS-2)*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events
#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
@@ -110,6 +162,12 @@ ISR(ADC_vect) {
pseudo_rand_seed += measurement;
#endif
+ #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
+ // only measure battery voltage while asleep
+ if (go_to_standby) adc_step = 1;
+ else
+ #endif
+
adc_step = (adc_step + 1) & (ADC_STEPS-1);
#ifdef USE_LVP
@@ -120,7 +178,7 @@ ISR(ADC_vect) {
if (voltage == 0) {
for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++)
voltage_values[i] = measurement;
- voltage = 42; // Life, the Universe, and Everything (*)
+ voltage = 42; // the answer to life, the universe, and the voltage of a full li-ion cell
} else {
uint16_t total = 0;
uint8_t i;
@@ -181,7 +239,7 @@ ISR(ADC_vect) {
// temperature
else if (adc_step == 3) {
// Convert ADC units to Celsius (ish)
- int16_t temp = measurement - 275 + THERM_CAL_OFFSET + therm_cal_offset;
+ int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
// prime on first execution
if (reset_thermal_history) {
@@ -202,14 +260,18 @@ ISR(ADC_vect) {
// guess what the temperature will be in a few seconds
int16_t pt;
{
- uint8_t i;
int16_t diff;
int16_t t = temperature;
// algorithm tweaking; not really intended to be modified
// how far ahead should we predict?
+ #ifndef THERM_PREDICTION_STRENGTH
#define THERM_PREDICTION_STRENGTH 4
- // how proportional should the adjustments be?
+ #endif
+ // how proportional should the adjustments be? (not used yet)
+ #ifndef THERM_RESPONSE_MAGNITUDE
+ #define THERM_RESPONSE_MAGNITUDE 128
+ #endif
// acceptable temperature window size in C
#define THERM_WINDOW_SIZE 5
// highest temperature allowed
@@ -219,13 +281,22 @@ ISR(ADC_vect) {
// if it's time to rotate the thermal history, do it
history_step ++;
- if (0 == (history_step & 7)) {
+ #if (THERMAL_UPDATE_SPEED == 4) // new value every 4s
+ #define THERM_HISTORY_STEP_MAX 15
+ #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s
+ #define THERM_HISTORY_STEP_MAX 7
+ #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s
+ #define THERM_HISTORY_STEP_MAX 3
+ #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s
+ #define THERM_HISTORY_STEP_MAX 1
+ #endif
+ if (0 == (history_step & THERM_HISTORY_STEP_MAX)) {
// rotate measurements and add a new one
- for (i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) {
+ for (uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) {
temperature_history[i] = temperature_history[i+1];
}
+ temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t;
}
- temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t;
// guess what the temp will be several seconds in the future
// diff = rate of temperature change
@@ -244,7 +315,8 @@ ISR(ADC_vect) {
// cancel counters if appropriate
if (pt > THERM_FLOOR) {
underheat_lowpass = 0; // we're probably not too cold
- } else if (pt < THERM_CEIL) {
+ }
+ if (pt < THERM_CEIL) {
overheat_lowpass = 0; // we're probably not too hot
}
@@ -261,6 +333,7 @@ ISR(ADC_vect) {
overheat_lowpass = 0;
temperature_timer = TEMPERATURE_TIMER_START;
// how far above the ceiling?
+ //int16_t howmuch = (pt - THERM_CEIL) * THERM_RESPONSE_MAGNITUDE / 128;
int16_t howmuch = pt - THERM_CEIL;
// try to send out a warning
emit(EV_temperature_high, howmuch);
@@ -276,6 +349,7 @@ ISR(ADC_vect) {
underheat_lowpass = 0;
temperature_timer = TEMPERATURE_TIMER_START;
// how far below the floor?
+ //int16_t howmuch = (THERM_FLOOR - pt) * THERM_RESPONSE_MAGNITUDE / 128;
int16_t howmuch = THERM_FLOOR - pt;
// try to send out a warning (unless voltage is low)
// (LVP and underheat warnings fight each other)
@@ -291,16 +365,21 @@ ISR(ADC_vect) {
// set the correct type of measurement for next time
#ifdef USE_THERMAL_REGULATION
#ifdef USE_LVP
- if (adc_step < 2) ADMUX = ADMUX_VOLTAGE;
- else ADMUX = ADMUX_THERM;
+ if (adc_step < 2) set_admux_voltage();
+ else set_admux_therm();
#else
- ADMUX = ADMUX_THERM;
+ set_admux_therm();
#endif
#else
#ifdef USE_LVP
- ADMUX = ADMUX_VOLTAGE;
+ set_admux_voltage();
#endif
#endif
+
+ #ifdef TICK_DURING_STANDBY
+ // if we were asleep, go back to sleep
+ if (go_to_standby) ADC_off();
+ #endif
}
#ifdef USE_BATTCHECK
diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h
index 6256e2c..274fb4d 100644
--- a/spaghetti-monster/fsm-adc.h
+++ b/spaghetti-monster/fsm-adc.h
@@ -38,7 +38,7 @@
#define VOLTAGE_FUDGE_FACTOR 5
#endif
#endif
-volatile uint8_t voltage;
+volatile uint8_t voltage = 0;
volatile uint8_t adcint_enable; // kludge, because adc auto-retrigger won't turn off
void low_voltage();
#ifdef USE_BATTCHECK
@@ -84,6 +84,7 @@ volatile uint8_t reset_thermal_history = 1;
inline void ADC_on();
inline void ADC_off();
+inline void ADC_start_measurement();
#endif
diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c
index 72216ae..362a5cc 100644
--- a/spaghetti-monster/fsm-events.c
+++ b/spaghetti-monster/fsm-events.c
@@ -20,6 +20,7 @@
#ifndef FSM_EVENTS_C
#define FSM_EVENTS_C
+
void empty_event_sequence() {
current_event = EV_none;
// when the user completes an input sequence, interrupt any running timers
@@ -116,26 +117,26 @@ uint8_t nice_delay_ms(uint16_t ms) {
#ifdef USE_RAMPING
uint8_t level = actual_level; // volatile, avoid repeat access
if (level < QUARTERSPEED_LEVEL) {
- CLKPR = 1<<CLKPCE; CLKPR = 2;
+ clock_prescale_set(clock_div_4);
_delay_loop_2(BOGOMIPS*98/100/4);
}
//else if (level < HALFSPEED_LEVEL) {
- // CLKPR = 1<<CLKPCE; CLKPR = 1;
+ // clock_prescale_set(clock_div_2);
// _delay_loop_2(BOGOMIPS*98/100/2);
//}
else {
- CLKPR = 1<<CLKPCE; CLKPR = 0;
+ clock_prescale_set(clock_div_1);
_delay_loop_2(BOGOMIPS*98/100);
}
// restore regular clock speed
- CLKPR = 1<<CLKPCE; CLKPR = 0;
+ clock_prescale_set(clock_div_1);
#else
// underclock MCU to save power
- CLKPR = 1<<CLKPCE; CLKPR = 2;
+ clock_prescale_set(clock_div_4);
// wait
_delay_loop_2(BOGOMIPS*98/100/4);
// restore regular clock speed
- CLKPR = 1<<CLKPCE; CLKPR = 0;
+ clock_prescale_set(clock_div_1);
#endif // ifdef USE_RAMPING
#else
// wait
@@ -159,11 +160,18 @@ uint8_t nice_delay_ms(uint16_t ms) {
void delay_4ms(uint8_t ms) {
while(ms-- > 0) {
// underclock MCU to save power
- CLKPR = 1<<CLKPCE; CLKPR = 2;
+ clock_prescale_set(clock_div_4);
// wait
_delay_loop_2(BOGOMIPS*98/100);
// restore regular clock speed
- CLKPR = 1<<CLKPCE; CLKPR = 0;
+ clock_prescale_set(clock_div_1);
+ }
+}
+#else
+void delay_4ms(uint8_t ms) {
+ while(ms-- > 0) {
+ // wait
+ _delay_loop_2(BOGOMIPS*398/100);
}
}
#endif
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index 1c28f5f..e537a9e 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -36,21 +36,8 @@ ISR(TIMER1_COMPA_vect) {
}
#endif
-int main() {
- // Don't allow interrupts while booting
- cli();
-
- #ifdef USE_REBOOT // prevent reboot loops
- MCUSR &= ~(1<<WDRF); // reset status flag
- wdt_disable();
- #endif
-
- #ifdef HALFSPEED
- // run at half speed
- CLKPR = 1<<CLKPCE;
- CLKPR = 1;
- #endif
-
+#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+inline void hw_setup() {
// configure PWM channels
#if PWM_CHANNELS >= 1
DDRB |= (1 << PWM1_PIN);
@@ -80,6 +67,45 @@ int main() {
// configure e-switch
PORTB = (1 << SWITCH_PIN); // e-switch is the only input
PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+#elif (ATTINY == 1634)
+inline void hw_setup() {
+ // this gets tricky with so many pins...
+ // ... so punt it to the hwdef file
+ hwdef_setup();
+}
+#else
+ #error Unrecognized MCU type
+#endif
+
+
+#ifdef USE_REBOOT
+void prevent_reboot_loop() {
+ // prevent WDT from rebooting MCU again
+ MCUSR &= ~(1<<WDRF); // reset status flag
+ wdt_disable();
+}
+#endif
+
+
+int main() {
+ // Don't allow interrupts while booting
+ cli();
+
+ #ifdef USE_REBOOT
+ prevent_reboot_loop();
+ #endif
+
+ hw_setup();
+
+ #if 0
+ #ifdef HALFSPEED
+ // run at half speed
+ // FIXME: not portable (also not needed)
+ CLKPR = 1<<CLKPCE;
+ CLKPR = 1;
+ #endif
+ #endif
#ifdef USE_DEBUG_BLINK
//debug_blink(1);
diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c
index eced482..8e88cbd 100644
--- a/spaghetti-monster/fsm-misc.c
+++ b/spaghetti-monster/fsm-misc.c
@@ -20,6 +20,7 @@
#ifndef FSM_MISC_C
#define FSM_MISC_C
+
#ifdef USE_DYNAMIC_UNDERCLOCKING
void auto_clock_speed() {
uint8_t level = actual_level; // volatile, avoid repeat access
@@ -27,14 +28,14 @@ void auto_clock_speed() {
// run at quarter speed
// note: this only works when executed as two consecutive instructions
// (don't try to combine them or put other stuff between)
- CLKPR = 1<<CLKPCE; CLKPR = 2;
+ clock_prescale_set(clock_div_4);
}
else if (level < HALFSPEED_LEVEL) {
// run at half speed
- CLKPR = 1<<CLKPCE; CLKPR = 1;
+ clock_prescale_set(clock_div_2);
} else {
// run at full speed
- CLKPR = 1<<CLKPCE; CLKPR = 0;
+ clock_prescale_set(clock_div_1);
}
}
#endif
@@ -146,6 +147,34 @@ void indicator_led_auto() {
*/
#endif // USE_INDICATOR_LED
+#ifdef USE_AUX_RGB_LEDS
+void rgb_led_set(uint8_t value) {
+ // value: 0b00BBGGRR
+ uint8_t pins[] = { AUXLED_R_PIN, AUXLED_G_PIN, AUXLED_B_PIN };
+ for (uint8_t i=0; i<3; i++) {
+ uint8_t lvl = (value >> (i<<1)) & 0x03;
+ uint8_t pin = pins[i];
+ switch (lvl) {
+ case 0: // LED off
+ AUXLED_RGB_DDR &= 0xff ^ (1 << pin);
+ AUXLED_RGB_PUE &= 0xff ^ (1 << pin);
+ AUXLED_RGB_PORT &= 0xff ^ (1 << pin);
+ break;
+ case 1: // LED low
+ AUXLED_RGB_DDR &= 0xff ^ (1 << pin);
+ AUXLED_RGB_PUE |= (1 << pin);
+ AUXLED_RGB_PORT |= (1 << pin);
+ break;
+ default: // LED high
+ AUXLED_RGB_DDR |= (1 << pin);
+ AUXLED_RGB_PUE |= (1 << pin);
+ AUXLED_RGB_PORT |= (1 << pin);
+ break;
+ }
+ }
+}
+#endif // ifdef USE_AUX_RGB_LEDS
+
#ifdef USE_TRIANGLE_WAVE
uint8_t triangle_wave(uint8_t phase) {
uint8_t result = phase << 1;
diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h
index 1381ca2..a39d31a 100644
--- a/spaghetti-monster/fsm-misc.h
+++ b/spaghetti-monster/fsm-misc.h
@@ -43,9 +43,16 @@ uint8_t blink(uint8_t num, uint8_t speed);
*/
#ifdef USE_INDICATOR_LED
+// lvl: 0=off, 1=low, 2=high
void indicator_led(uint8_t lvl);
#endif
+#ifdef USE_AUX_RGB_LEDS
+// value: 0b00BBGGRR
+// each pair of bits: 0=off, 1=low, 2=high
+void rgb_led_set(uint8_t value);
+#endif
+
#ifdef USE_TRIANGLE_WAVE
uint8_t triangle_wave(uint8_t phase);
#endif
diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c
index acb627d..4928980 100644
--- a/spaghetti-monster/fsm-pcint.c
+++ b/spaghetti-monster/fsm-pcint.c
@@ -30,7 +30,7 @@ uint8_t button_is_pressed() {
// and wait for measurements to settle to all zeroes or all ones
do {
// shift past readings and add current value
- readings = (readings << 1) | ((PINB & (1<<SWITCH_PIN)) == 0);
+ readings = (readings << 1) | ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0);
// wait a moment
_delay_loop_2(BOGOMIPS/16); // up to 2ms to stabilize
}
@@ -40,21 +40,45 @@ uint8_t button_is_pressed() {
}
inline void PCINT_on() {
- // enable pin change interrupt for pin N
- GIMSK |= (1 << PCIE);
- // only pay attention to the e-switch pin
- //PCMSK = (1 << SWITCH_PCINT);
- // set bits 1:0 to 0b01 (interrupt on rising *and* falling edge) (default)
- // MCUCR &= 0b11111101; MCUCR |= 0b00000001;
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ // enable pin change interrupt
+ GIMSK |= (1 << PCIE);
+ // only pay attention to the e-switch pin
+ #if 0 // this is redundant; was already done in main()
+ PCMSK = (1 << SWITCH_PCINT);
+ #endif
+ // set bits 1:0 to 0b01 (interrupt on rising *and* falling edge) (default)
+ // MCUCR &= 0b11111101; MCUCR |= 0b00000001;
+ #elif (ATTINY == 1634)
+ // enable pin change interrupt
+ #ifdef SWITCH2_PCIE
+ GIMSK |= ((1 << SWITCH_PCIE) | (1 << SWITCH2_PCIE));
+ #else
+ GIMSK |= (1 << SWITCH_PCIE);
+ #endif
+ #else
+ #error Unrecognized MCU type
+ #endif
}
inline void PCINT_off() {
- // disable all pin-change interrupts
- GIMSK &= ~(1 << PCIE);
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ // disable all pin-change interrupts
+ GIMSK &= ~(1 << PCIE);
+ #elif (ATTINY == 1634)
+ // disable all pin-change interrupts
+ GIMSK &= ~(1 << SWITCH_PCIE);
+ #else
+ #error Unrecognized MCU type
+ #endif
}
//void button_change_interrupt() {
+#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
EMPTY_INTERRUPT(PCINT0_vect);
+#else
+ #error Unrecognized MCU type
+#endif
/*
ISR(PCINT0_vect) {
diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c
index 082f8c9..efa07e4 100644
--- a/spaghetti-monster/fsm-ramping.c
+++ b/spaghetti-monster/fsm-ramping.c
@@ -30,17 +30,25 @@ void set_level(uint8_t level) {
gradual_target = level;
#endif
- #ifdef USE_INDICATOR_LED
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
- if (! go_to_standby)
- indicator_led((level > 0) + (level > MAX_1x7135));
- //if (level > MAX_1x7135) indicator_led(2);
- //else if (level > 0) indicator_led(1);
- //else if (! go_to_standby) indicator_led(0);
+ #ifdef USE_INDICATOR_LED
+ if (! go_to_standby)
+ indicator_led((level > 0) + (level > MAX_1x7135));
+ #endif
+ //if (level > MAX_1x7135) indicator_led(2);
+ //else if (level > 0) indicator_led(1);
+ //else if (! go_to_standby) indicator_led(0);
#else
- if (! go_to_standby)
- indicator_led(0);
- #endif
+ #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
+ if (! go_to_standby) {
+ #ifdef USE_INDICATOR_LED
+ indicator_led(0);
+ #endif
+ #ifdef USE_AUX_RGB_LEDS
+ rgb_led_set(0);
+ #endif
+ }
+ #endif
#endif
//TCCR0A = PHASE;
diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c
index 7d60c1d..44b047a 100644
--- a/spaghetti-monster/fsm-standby.c
+++ b/spaghetti-monster/fsm-standby.c
@@ -56,7 +56,9 @@ void sleep_until_eswitch_pressed()
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
+ #ifdef BODCR // only do this on MCUs which support it
sleep_bod_disable();
+ #endif
sleep_cpu(); // wait here
// something happened; wake up
diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c
index d5bbdb9..6e61e87 100644
--- a/spaghetti-monster/fsm-wdt.c
+++ b/spaghetti-monster/fsm-wdt.c
@@ -25,38 +25,65 @@
void WDT_on()
{
- // interrupt every 16ms
- //cli(); // Disable interrupts
- wdt_reset(); // Reset the WDT
- WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
- WDTCR = (1<<WDIE); // Enable interrupt every 16ms
- //sei(); // Enable interrupts
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ // interrupt every 16ms
+ //cli(); // Disable interrupts
+ wdt_reset(); // Reset the WDT
+ WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
+ WDTCR = (1<<WDIE); // Enable interrupt every 16ms
+ //sei(); // Enable interrupts
+ #elif (ATTINY == 1634)
+ wdt_reset(); // Reset the WDT
+ WDTCSR = (1<<WDIE); // Enable interrupt every 16ms
+ #else
+ #error Unrecognized MCU type
+ #endif
}
#ifdef TICK_DURING_STANDBY
inline void WDT_slow()
{
- // interrupt slower
- //cli(); // Disable interrupts
- wdt_reset(); // Reset the WDT
- WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
- WDTCR = (1<<WDIE) | STANDBY_TICK_SPEED; // Enable interrupt every so often
- //sei(); // Enable interrupts
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ // interrupt slower
+ //cli(); // Disable interrupts
+ wdt_reset(); // Reset the WDT
+ WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
+ WDTCR = (1<<WDIE) | STANDBY_TICK_SPEED; // Enable interrupt every so often
+ //sei(); // Enable interrupts
+ #elif (ATTINY == 1634)
+ wdt_reset(); // Reset the WDT
+ WDTCSR = (1<<WDIE) | STANDBY_TICK_SPEED;
+ #else
+ #error Unrecognized MCU type
+ #endif
}
#endif
inline void WDT_off()
{
- //cli(); // Disable interrupts
- wdt_reset(); // Reset the WDT
- MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
- WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
- WDTCR = 0x00; // Disable WDT
- //sei(); // Enable interrupts
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ //cli(); // Disable interrupts
+ wdt_reset(); // Reset the WDT
+ MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
+ WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
+ WDTCR = 0x00; // Disable WDT
+ //sei(); // Enable interrupts
+ #elif (ATTINY == 1634)
+ cli(); // needed because CCP, below
+ wdt_reset(); // Reset the WDT
+ MCUSR &= ~(1<<WDRF); // clear watchdog reset flag
+ CCP = 0xD8; // enable config changes
+ WDTCSR = 0; // disable and clear all WDT settings
+ sei();
+ #else
+ #error Unrecognized MCU type
+ #endif
}
// clock tick -- this runs every 16ms (62.5 fps)
ISR(WDT_vect) {
+ static uint8_t adc_trigger = 0;
+
#ifdef TICK_DURING_STANDBY
f_wdt = 1; // WDT event happened
@@ -68,9 +95,16 @@ ISR(WDT_vect) {
// wrap around from 65535 to 32768, not 0
sleep_counter = (sleep_counter + 1) | (sleep_counter & 0x8000);
process_emissions();
- return;
+
+ #if defined(USE_SLEEP_LVP)
+ // stop here, usually... but proceed often enough for sleep LVP to work
+ if (0 != (sleep_counter & 0x7f)) return;
+ adc_trigger = 255; // make sure a measurement will happen
+ #else
+ return; // no sleep LVP needed if nothing drains power while off
+ #endif
}
- sleep_counter = 0;
+ else { sleep_counter = 0; }
#endif
// detect and emit button change events
@@ -136,10 +170,14 @@ ISR(WDT_vect) {
#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
// start a new ADC measurement every 4 ticks
- static uint8_t adc_trigger = 0;
adc_trigger ++;
if (0 == (adc_trigger & 3)) {
- ADCSRA |= (1 << ADSC) | (1 << ADIE);
+ #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
+ // we shouldn't be here unless it woke up for a LVP check...
+ // so enable ADC voltage measurement functions temporarily
+ if (go_to_standby) ADC_on();
+ #endif
+ ADC_start_measurement();
adcint_enable = 1;
}
#endif
diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h
index 3c395c1..78fe791 100644
--- a/spaghetti-monster/fsm-wdt.h
+++ b/spaghetti-monster/fsm-wdt.h
@@ -27,6 +27,11 @@ inline void WDT_off();
#ifdef TICK_DURING_STANDBY
volatile uint8_t f_wdt = 0;
+
+ #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
+ // measure battery charge while asleep
+ #define USE_SLEEP_LVP
+ #endif
#endif
#endif
diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c
index e4eb2fa..d72e971 100644
--- a/spaghetti-monster/rampingios/rampingiosv3.c
+++ b/spaghetti-monster/rampingios/rampingiosv3.c
@@ -148,9 +148,6 @@ void blink_confirm(uint8_t num);
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
void indicator_blink(uint8_t arg);
#endif
-#ifdef USE_INDICATOR_LED
-uint8_t auxled_next_state(Event event, uint16_t arg);
-#endif
// remember stuff even after battery was changed
void load_config();
@@ -347,7 +344,18 @@ uint8_t off_state(Event event, uint16_t arg) {
// 7 clicks: next aux LED mode
else if (event == EV_7clicks) {
blink_confirm(1);
- set_state(auxled_next_state, 0);
+ 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
@@ -811,33 +819,6 @@ uint8_t lockout_state(Event event, uint16_t arg) {
}
-#ifdef USE_INDICATOR_LED
-uint8_t auxled_next_state(Event event, uint16_t arg) {
- if (event == EV_enter_state) {
- uint8_t mode = indicator_led_mode & 3;
- #ifdef TICK_DURING_STANDBY
- mode = (mode + 1) & 3;
- #else
- mode = (mode + 1) % 3;
- #endif
- #ifdef INDICATOR_LED_SKIP_LOW
- if (mode == 1) { mode ++; }
- #endif
- indicator_led_mode = mode + (indicator_led_mode & 0b00001100);
- indicator_led(mode);
- save_config();
- return MISCHIEF_MANAGED;
- }
- else if (event == EV_tick) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
-
- return EVENT_NOT_HANDLED;
-}
-#endif
-
-
uint8_t momentary_state(Event event, uint16_t arg) {
// TODO: momentary strobe here? (for light painting)
diff --git a/spaghetti-monster/spaghetti-monster.h b/spaghetti-monster/spaghetti-monster.h
index 08690b4..853eac3 100644
--- a/spaghetti-monster/spaghetti-monster.h
+++ b/spaghetti-monster/spaghetti-monster.h
@@ -25,6 +25,7 @@
#include "tk-attiny.h"
#include <avr/eeprom.h>
+#include <avr/power.h>
// include project definitions to help with recognizing symbols
#include "fsm-events.h"