aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-04-13 20:38:25 -0600
committerSelene ToyKeeper2023-04-13 20:38:25 -0600
commit55541be4a505da3df7d1a2b8bf3b5295b0af58f7 (patch)
treefc1e7f22ffe1c51087117b28766ff266895228e3 /spaghetti-monster
parentmerging gchart's changes, part 1... (diff)
downloadanduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.tar.gz
anduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.tar.bz2
anduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.zip
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
with the new channel mode system ... but there's a lot more left to do
Diffstat (limited to '')
-rw-r--r--spaghetti-monster/anduril/anduril.c39
-rwxr-xr-xspaghetti-monster/anduril/build-all.sh4
-rw-r--r--spaghetti-monster/anduril/candle-mode.c2
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-219.h13
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h23
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h21
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h41
-rw-r--r--spaghetti-monster/anduril/channel-modes.c146
-rw-r--r--spaghetti-monster/anduril/channel-modes.h20
-rw-r--r--spaghetti-monster/anduril/factory-reset.c50
-rw-r--r--spaghetti-monster/anduril/factory-reset.h26
-rw-r--r--spaghetti-monster/anduril/hank-cfg.h7
-rw-r--r--spaghetti-monster/anduril/load-save-config-fsm.h30
-rw-r--r--spaghetti-monster/anduril/load-save-config.c44
-rw-r--r--spaghetti-monster/anduril/off-mode.c10
-rw-r--r--spaghetti-monster/anduril/ramp-mode.c55
-rw-r--r--spaghetti-monster/anduril/ramp-mode.h15
-rw-r--r--spaghetti-monster/anduril/strobe-modes.c30
-rw-r--r--spaghetti-monster/anduril/strobe-modes.h7
-rw-r--r--spaghetti-monster/anduril/tint-ramping.c23
-rw-r--r--spaghetti-monster/anduril/tint-ramping.h22
-rw-r--r--spaghetti-monster/fsm-main.c1
-rw-r--r--spaghetti-monster/fsm-misc.h5
-rw-r--r--spaghetti-monster/fsm-ramping.c422
-rw-r--r--spaghetti-monster/fsm-ramping.h196
-rw-r--r--spaghetti-monster/fsm-states.h2
26 files changed, 811 insertions, 443 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index 47cd00f..07560bc 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -54,10 +54,10 @@
/********* specific settings for known driver types *********/
// Anduril config file name (set it here or define it at the gcc command line)
-//#define CONFIGFILE cfg-blf-q8.h
+//#define CFG_H cfg-blf-q8.h
#include "tk.h"
-#include incfile(CONFIGFILE)
+#include incfile(CFG_H)
/********* Include headers which need to be before FSM *********/
@@ -92,8 +92,11 @@
#include "spaghetti-monster.h"
/********* does this build target have special code to include? *********/
-#ifdef OVERRIDES_FILE
-#include incfile(OVERRIDES_FILE)
+#ifdef HWDEF_C_FILE
+#include incfile(HWDEF_C_FILE)
+#endif
+#ifdef CFG_C_FILE
+#include incfile(CFG_C_FILE)
#endif
@@ -138,8 +141,10 @@
#include "tactical-mode.h"
#endif
-#ifdef USE_TINT_RAMPING
-#include "tint-ramping.h"
+// allow the channel mode handler even when only 1 mode
+// (so a tint ramp light could still use 3H even if there's no other mode)
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.h"
#endif
#ifdef USE_FACTORY_RESET
@@ -197,8 +202,8 @@
#include "tactical-mode.c"
#endif
-#ifdef USE_TINT_RAMPING
-#include "tint-ramping.c"
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.c"
#endif
#ifdef USE_FACTORY_RESET
@@ -234,13 +239,13 @@ void setup() {
#if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER)
// without this, initial boot-up brightness is wrong
// when manual mem is enabled with a non-zero timer
- if (manual_memory) memorized_level = manual_memory;
+ if (manual_memory) manual_memory_restore();
#endif
- #ifdef USE_TINT_RAMPING
- // add tint ramping underneath every other state
- push_state(tint_ramping_state, 0);
- #endif // ifdef USE_TINT_RAMPING
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
push_state(off_state, 1);
@@ -250,10 +255,10 @@ void setup() {
// power clicky acts as a momentary mode
load_config();
- #ifdef USE_TINT_RAMPING
- // add tint ramping underneath every other state
- push_state(tint_ramping_state, 0);
- #endif // ifdef USE_TINT_RAMPING
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
if (button_is_pressed())
// hold button to go to moon
diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh
index b9f6d15..768559c 100755
--- a/spaghetti-monster/anduril/build-all.sh
+++ b/spaghetti-monster/anduril/build-all.sh
@@ -33,8 +33,8 @@ for TARGET in cfg-*.h ; do
if [ -z "$ATTINY" ]; then ATTINY=85 ; fi
# try to compile
- echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
- ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ echo ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}"
+ ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}"
# track result, and rename compiled files
if [ 0 = $? ] ; then
diff --git a/spaghetti-monster/anduril/candle-mode.c b/spaghetti-monster/anduril/candle-mode.c
index d15195e..12ffa84 100644
--- a/spaghetti-monster/anduril/candle-mode.c
+++ b/spaghetti-monster/anduril/candle-mode.c
@@ -28,7 +28,7 @@
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)
+ #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15)
static uint8_t candle_wave1 = 0;
static uint8_t candle_wave2 = 0;
static uint8_t candle_wave3 = 0;
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
index 414971a..dad84f0 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
@@ -4,9 +4,10 @@
#define MODEL_NUMBER "0114"
// ATTINY: 1634
-// don't turn off first channel at turbo level
-#undef PWM1_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
-// 65% FET power
-#undef PWM2_LEVELS
-#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,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166
+// don't turn off the low channel at turbo level
+#undef LOW_PWM_LEVELS
+#define LOW_PWM_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
+// 65% DDFET power
+#undef HIGH_PWM_LEVELS
+#define HIGH_PWM_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,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
index 717afcf..5e33a05 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
@@ -4,11 +4,23 @@
#define MODEL_NUMBER "0115"
// ATTINY: 1634
+// switch to 1-channel support functions
+#undef USE_SET_LEVEL_2CH_STACKED
+#undef USE_GRADUAL_TICK_2CH_STACKED
+#define USE_SET_LEVEL_1CH
+#define USE_GRADUAL_TICK_1CH
+#undef SET_LEVEL_MODES
+#undef GRADUAL_TICK_MODES
+#define SET_LEVEL_MODES set_level_1ch
+#define GRADUAL_TICK_MODES gradual_tick_1ch
+
#undef PWM_CHANNELS
#define PWM_CHANNELS 1
-#undef PWM1_LEVELS
-#undef PWM2_LEVELS
-#define PWM1_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255
+
+#undef LOW_PWM_LEVELS
+#undef HIGH_PWM_LEVELS
+#define LOW_PWM_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255
+
#undef MAX_1x7135
#define MAX_1x7135 150
#undef QUARTERSPEED_LEVEL
@@ -39,4 +51,7 @@
#undef THERM_FASTER_LEVEL
#define THERM_FASTER_LEVEL 150
-#undef USE_THERMAL_REGULATION
+// maybe keep this, in case someone uses a higher power channel?
+//#undef USE_THERMAL_REGULATION
+//#undef USE_SET_LEVEL_GRADUALLY
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
index 4121465..54de297 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -1,28 +1,19 @@
// Emisar D4 config options for Anduril
+// Copyright (C) 2019 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#define MODEL_NUMBER "0113"
#include "hwdef-Emisar_D4v2.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// it also has an independent LED in the button
-#define USE_BUTTON_LED
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-// TODO: the whole "indicator LED" thing needs to be refactored into
-// "aux LED(s)" and "button LED(s)" since they work a bit differently
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
-
// 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 RAMP_SIZE 150
+#define LOW_PWM_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 HIGH_PWM_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
diff --git a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
index 4e5993b..e009e02 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
@@ -1,19 +1,9 @@
// Sofirn LT1S Pro
+
#define MODEL_NUMBER "0623"
#include "hwdef-Sofirn_LT1S-Pro.h"
// ATTINY: 1616
-// this model requires some special code
-#define OVERRIDES_FILE cfg-sofirn-lt1s-pro.c
-#define OVERRIDE_SET_LEVEL
-inline void set_level_override(uint8_t level);
-
-// uses 4C action while On to switch between white and red channels (overrides lockout action)
-#define USE_OUTPUT_MUX
-// the button lights up
-#define USE_INDICATOR_LED
-// the button is visible while main LEDs are on
-#define USE_INDICATOR_LED_WHILE_RAMPING
// off mode: high (1)
// lockout: blinking (3)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
@@ -21,19 +11,23 @@ inline void set_level_override(uint8_t level);
// the lantern has two PWM channels, but they drive different sets of emitters
// (one channel for warm emitters, one channel for cold)
// so enable a special ramping mode which changes tint instead of brightness
-#define USE_TINT_RAMPING
+//#define USE_TINT_RAMPING
// how much to increase total brightness at middle tint
// (0 = 100% brightness, 64 = 200% brightness)
#define TINT_RAMPING_CORRECTION 10 // 115%
-#ifdef RAMP_LENGTH
-#undef RAMP_LENGTH
-#endif
-
// level_calc.py 1 150 7135 1 30 800
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255
+#define RAMP_SIZE 150
+// TODO: use dynamic PWM instead of plain 8-bit
+// (so we can get lower lows and a smoother ramp)
+// TODO: 200% power at top of ramp on white blend mode
+#define PWM_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255
+#define BLEND_PWM_LEVELS PWM_LEVELS
+#define RED_PWM_LEVELS PWM_LEVELS
#define MAX_1x7135 65
+// FIXME: clock at 5 MHz w/ full+half+quarter speeds,
+// instead of 10 MHz with w/ only half+quarter
+// (10 MHz is just wasting power)
#define HALFSPEED_LEVEL 256 // red LEDs use a QX7138 chip which has max PWM speed of 10 kHz, so never run faster than halfspeed
#define QUARTERSPEED_LEVEL 5
@@ -68,8 +62,11 @@ inline void set_level_override(uint8_t level);
#define USE_SOS_MODE
#define USE_SOS_MODE_IN_BLINKY_GROUP
-// the sensor (attiny) is nowhere near the emitters
-// so thermal regulation can't work
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// FIXME: thermal regulation should actually work fine on this light
#ifdef USE_THERMAL_REGULATION
#undef USE_THERMAL_REGULATION
#endif
@@ -84,7 +81,7 @@ inline void set_level_override(uint8_t level);
#ifdef BLINK_AT_RAMP_CEIL
#undef BLINK_AT_RAMP_CEIL
#endif
+// without this, it's really hard to tell when ramping up stops
+#define BLINK_AT_RAMP_CEIL
-#ifndef USE_SOFT_FACTORY_RESET
#define USE_SOFT_FACTORY_RESET
-#endif \ No newline at end of file
diff --git a/spaghetti-monster/anduril/channel-modes.c b/spaghetti-monster/anduril/channel-modes.c
new file mode 100644
index 0000000..958b375
--- /dev/null
+++ b/spaghetti-monster/anduril/channel-modes.c
@@ -0,0 +1,146 @@
+/*
+ * channel-modes.c: Multi-channel functions for Anduril.
+ * Copyright (C) 2017-2023 Selene ToyKeeper
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "channel-modes.h"
+
+
+uint8_t channel_mode_state(Event event, uint16_t arg) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ 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;
+ uint8_t tint = channel_mode_args[channel_mode];
+ #endif
+
+ // it's possible that a light may need 3H but not 3C,
+ // so try to detect if 3C is needed
+ #if NUM_CHANNEL_MODES > 1
+ // 3 clicks: next channel mode
+ if (event == EV_3clicks) {
+ uint8_t next = channel_mode;
+ // go to next channel mode until we find one which is enabled
+ // (and don't do any infinite loops if the user disabled them all)
+ uint8_t count = 0;
+ do {
+ count ++;
+ next = (next + 1) % NUM_CHANNEL_MODES;
+ } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES);
+ //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES);
+
+ // undo change if infinite loop detected (redundant?)
+ //if (NUM_CHANNEL_MODES == count) next = channel_mode;
+
+ // if mode hasn't changed, abort
+ if (channel_mode == next)
+ return EVENT_NOT_HANDLED;
+
+ set_channel_mode(next);
+
+ // remember after battery changes
+ save_config();
+ return EVENT_HANDLED;
+ } else
+ #endif // if NUM_CHANNEL_MODES > 1
+
+ #ifdef USE_CUSTOM_CHANNEL_3H_MODES
+ // defer to mode-specific function if defined
+ if (tint_ramp_modes[channel_mode]) {
+ StatePtr tint_func = channel_3H_modes[channel_mode];
+ return tint_func(channel_mode);
+ } else
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ // click, click, hold: change the current channel's arg (like tint)
+ if (event == EV_click3_hold) {
+ ///// adjust value from 0 to 255
+ // 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_NOT_HANDLED;
+
+ // change normal tints
+ if ((tint_ramp_direction > 0) && (tint < 255)) {
+ tint += 1;
+ }
+ else if ((tint_ramp_direction < 0) && (tint > 0)) {
+ tint -= 1;
+ }
+ // if tint change stalled, let user know we hit the edge
+ else if (prev_tint == tint) {
+ if (past_edge_counter == 0) blip();
+ past_edge_counter = 1;
+ }
+ prev_tint = tint;
+ channel_mode_args[channel_mode] = 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 (0 == tint) tint_ramp_direction = 1;
+ else if (255 == tint) tint_ramp_direction = -1;
+ // remember tint after battery change
+ channel_mode_args[channel_mode] = tint;
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+ #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ #endif // ifdef USE_CHANNEL_MODE_ARGS
+
+ #if defined(USE_SIMPLE_UI)
+ // remaining mappings aren't "simple", so stop here
+ if (simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ #if NUM_CHANNEL_MODES > 1
+ // channel toggle menu on ... 9H?
+ else if (event == EV_click9_hold) {
+ push_state(channel_mode_config_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+#if NUM_CHANNEL_MODES > 1
+void channel_mode_config_save(uint8_t step, uint8_t value) {
+ // 1 menu item per channel mode, to enable or disable that mode
+ step --; // step is 1-based, channel modes are 0-based
+ if (value) channel_mode_enable(step);
+ else channel_mode_disable(step);
+}
+
+uint8_t channel_mode_config_state(Event event, uint16_t arg) {
+ // 1 menu item per channel mode, to enable or disable that mode
+ return config_state_base(
+ event, arg,
+ NUM_CHANNEL_MODES,
+ channel_mode_config_save
+ );
+}
+#endif
diff --git a/spaghetti-monster/anduril/channel-modes.h b/spaghetti-monster/anduril/channel-modes.h
new file mode 100644
index 0000000..f536d58
--- /dev/null
+++ b/spaghetti-monster/anduril/channel-modes.h
@@ -0,0 +1,20 @@
+/*
+ * channel-modes.h: Multi-channel functions for Anduril.
+ * Copyright (C) 2017-2023 Selene ToyKeeper
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if defined(USE_MANUAL_MEMORY) && defined(USE_CHANNEL_MODE_ARGS)
+// TODO: save to eeprom
+// remember and reset 1 extra parameter per channel mode (like tint)
+uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS };
+#endif
+
+// not actually a mode, more of a fallback under other modes
+uint8_t channel_mode_state(Event event, uint16_t arg);
+
+#if NUM_CHANNEL_MODES > 1
+uint8_t channel_mode_config_state(Event event, uint16_t arg);
+#endif
diff --git a/spaghetti-monster/anduril/factory-reset.c b/spaghetti-monster/anduril/factory-reset.c
index ecb4cc2..a14b5b9 100644
--- a/spaghetti-monster/anduril/factory-reset.c
+++ b/spaghetti-monster/anduril/factory-reset.c
@@ -1,27 +1,15 @@
-/*
- * factory-reset.c: Factory reset functions for Anduril.
- *
- * Copyright (C) 2017 Selene ToyKeeper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// factory-reset.c: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FACTORY_RESET_C
-#define FACTORY_RESET_C
+#pragma once
#include "factory-reset.h"
+// allows setting channel mode per animation stage,
+// so it can ramp up in red then explode in white (as one example)
+// TODO: maybe also do the same in menus?
+
void factory_reset() {
// display a warning for a few seconds before doing the actual reset,
// so the user has time to abort if they want
@@ -31,6 +19,9 @@ void factory_reset() {
uint8_t bright;
uint8_t reset = 1;
// wind up to an explosion
+ #ifdef FACTORY_RESET_WARN_CHANNEL
+ set_channel_mode(FACTORY_RESET_WARN_CHANNEL);
+ #endif
for (bright=0; bright<SPLODEY_STEPS; bright++) {
set_level(bright);
nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
@@ -43,17 +34,29 @@ void factory_reset() {
}
// explode, if button pressed long enough
if (reset) {
+ // auto-calibrate temperature
// AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset
#if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3)
- thermal_config_save(1,temperature - therm_cal_offset); // this will cancel out the offset
+ // this will cancel out the offset
+ thermal_config_save(1, temperature - therm_cal_offset);
#elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE)
- // auto-calibrate temperature... assume current temperature is 21 C
+ // assume current temperature is 21 C
thermal_config_save(1, 21);
#endif
+
+ #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE) && (FACTORY_RESET_WARN_CHANNEL != DEFAULT_CHANNEL_MODE)
+ // return to default channel before saving
+ set_channel_mode(DEFAULT_CHANNEL_MODE);
+ #endif
+
// save all settings to eeprom
// (assuming they're all at default because we haven't loaded them yet)
save_config();
+ // explosion animation
+ #ifdef FACTORY_RESET_SUCCESS_CHANNEL
+ set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL);
+ #endif
bright = MAX_LEVEL;
for (; bright > 0; bright--) {
set_level(bright);
@@ -69,6 +72,3 @@ void factory_reset() {
}
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/factory-reset.h b/spaghetti-monster/anduril/factory-reset.h
index 9f0af38..63c25cd 100644
--- a/spaghetti-monster/anduril/factory-reset.h
+++ b/spaghetti-monster/anduril/factory-reset.h
@@ -1,26 +1,8 @@
-/*
- * factory-reset.h: Factory reset functions for Anduril.
- *
- * Copyright (C) 2017 Selene ToyKeeper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// factory-reset.h: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FACTORY_RESET_H
-#define FACTORY_RESET_H
+#pragma once
void factory_reset();
-
-#endif
diff --git a/spaghetti-monster/anduril/hank-cfg.h b/spaghetti-monster/anduril/hank-cfg.h
index 11eb0a1..f24ea67 100644
--- a/spaghetti-monster/anduril/hank-cfg.h
+++ b/spaghetti-monster/anduril/hank-cfg.h
@@ -1,5 +1,7 @@
-#ifndef HANK_CFG
-#define HANK_CFG
+// Intl-Outdoor (Hank)'s config options for Anduril
+// Copyright (C) 2021 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
// config preferences for Hank Wang of Intl-Outdoor (Emisar, Noctigon)
@@ -18,4 +20,3 @@
// double click while on goes to full-power turbo, not ramp ceiling
#define DEFAULT_2C_STYLE 1
-#endif // ifndef HANK_CFG
diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h
index f0de161..9004f1f 100644
--- a/spaghetti-monster/anduril/load-save-config-fsm.h
+++ b/spaghetti-monster/anduril/load-save-config-fsm.h
@@ -54,13 +54,6 @@ typedef enum {
#ifdef USE_MANUAL_MEMORY_TIMER
manual_memory_timer_e,
#endif
- #ifdef USE_TINT_RAMPING
- manual_memory_tint_e,
- #endif
- #endif
- #ifdef USE_TINT_RAMPING
- tint_e,
- tint_style_e,
#endif
#ifdef USE_JUMP_START
jump_start_level_e,
@@ -100,7 +93,28 @@ typedef enum {
tactical_lvl_2_e,
tactical_lvl_3_e,
#endif
- eeprom_indexes_e_END
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode_e,
+ channel_modes_enabled_e,
+ #if defined(USE_MANUAL_MEMORY)
+ manual_memory_channel_mode_e,
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // this is an array, needs a few bytes
+ channel_mode_args_e,
+ #if defined(USE_MANUAL_MEMORY)
+ // this is an array, needs a few bytes
+ manual_memory_channel_args_e = channel_mode_args_e + NUM_CHANNEL_MODES,
+ // and this is an ugly ugly kludge
+ // FIXME: use a struct for eeprom, not an array
+ eeprom_indexes_e_END = manual_memory_channel_args_e + NUM_CHANNEL_MODES
+ #else
+ eeprom_indexes_e_END = channel_mode_args_e + NUM_CHANNEL_MODES
+ #endif
+ #else
+ eeprom_indexes_e_END
+ #endif
} eeprom_indexes_e;
#define EEPROM_BYTES eeprom_indexes_e_END
diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c
index 4101e7a..ad84450 100644
--- a/spaghetti-monster/anduril/load-save-config.c
+++ b/spaghetti-monster/anduril/load-save-config.c
@@ -56,13 +56,6 @@ void load_config() {
#ifdef USE_MANUAL_MEMORY_TIMER
manual_memory_timer = eeprom[manual_memory_timer_e];
#endif
- #ifdef USE_TINT_RAMPING
- manual_memory_tint = eeprom[manual_memory_tint_e];
- #endif
- #endif
- #ifdef USE_TINT_RAMPING
- tint = eeprom[tint_e];
- tint_style = eeprom[tint_style_e];
#endif
#ifdef USE_JUMP_START
jump_start_level = eeprom[jump_start_level_e],
@@ -100,6 +93,21 @@ void load_config() {
tactical_levels[1] = eeprom[tactical_lvl_2_e];
tactical_levels[2] = eeprom[tactical_lvl_3_e];
#endif
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = eeprom[channel_mode_e];
+ channel_modes_enabled = eeprom[channel_modes_enabled_e];
+ #if defined(USE_MANUAL_MEMORY)
+ manual_memory_channel_mode = eeprom[manual_memory_channel_mode_e];
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ channel_mode_args[i] = eeprom[i + channel_mode_args_e];
+ #if defined(USE_MANUAL_MEMORY)
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ manual_memory_channel_args[i] = eeprom[i + manual_memory_channel_args_e];
+ #endif
+ #endif
}
#ifdef START_AT_MEMORIZED_LEVEL
if (load_eeprom_wl()) {
@@ -140,13 +148,6 @@ void save_config() {
#ifdef USE_MANUAL_MEMORY_TIMER
eeprom[manual_memory_timer_e] = manual_memory_timer;
#endif
- #ifdef USE_TINT_RAMPING
- eeprom[manual_memory_tint_e] = manual_memory_tint;
- #endif
- #endif
- #ifdef USE_TINT_RAMPING
- eeprom[tint_e] = tint;
- eeprom[tint_style_e] = tint_style;
#endif
#ifdef USE_JUMP_START
eeprom[jump_start_level_e] = jump_start_level,
@@ -184,6 +185,21 @@ void save_config() {
eeprom[tactical_lvl_2_e] = tactical_levels[1];
eeprom[tactical_lvl_3_e] = tactical_levels[2];
#endif
+ #if NUM_CHANNEL_MODES > 1
+ eeprom[channel_mode_e] = channel_mode;
+ eeprom[channel_modes_enabled_e] = channel_modes_enabled;
+ #if defined(USE_MANUAL_MEMORY)
+ eeprom[manual_memory_channel_mode_e] = manual_memory_channel_mode;
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ eeprom[i + channel_mode_args_e] = channel_mode_args[i];
+ #if defined(USE_MANUAL_MEMORY)
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ eeprom[i + manual_memory_channel_args_e] = manual_memory_channel_args[i];
+ #endif
+ #endif
save_eeprom();
}
diff --git a/spaghetti-monster/anduril/off-mode.c b/spaghetti-monster/anduril/off-mode.c
index 6781ade..68ab234 100644
--- a/spaghetti-monster/anduril/off-mode.c
+++ b/spaghetti-monster/anduril/off-mode.c
@@ -65,10 +65,7 @@ uint8_t off_state(Event event, uint16_t arg) {
// reset to manual memory level when timer expires
if (manual_memory &&
(arg >= (manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
- memorized_level = manual_memory;
- #ifdef USE_TINT_RAMPING
- tint = manual_memory_tint;
- #endif
+ manual_memory_restore();
}
#endif
#ifdef USE_INDICATOR_LED
@@ -131,10 +128,7 @@ uint8_t off_state(Event event, uint16_t arg) {
// this clause probably isn't used by any configs any more
// but is included just in case someone configures it this way
if (manual_memory) {
- memorized_level = manual_memory;
- #ifdef USE_TINT_RAMPING
- tint = manual_memory_tint;
- #endif
+ manual_memory_restore();
}
#endif
set_level(nearest_level(memorized_level));
diff --git a/spaghetti-monster/anduril/ramp-mode.c b/spaghetti-monster/anduril/ramp-mode.c
index 6e8ba88..e57b68f 100644
--- a/spaghetti-monster/anduril/ramp-mode.c
+++ b/spaghetti-monster/anduril/ramp-mode.c
@@ -26,6 +26,7 @@
#include "sunset-timer.h"
#endif
+
uint8_t steady_state(Event event, uint16_t arg) {
static int8_t ramp_direction = 1;
#if (B_TIMING_OFF == B_RELEASE_T)
@@ -378,7 +379,26 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
// 3 clicks: toggle smooth vs discrete ramping
- else if (event == EV_3clicks) {
+ // (and/or 6 clicks when there are multiple channel modes)
+ // (handle 3C here anyway, when all but 1 mode is disabled)
+ else if ((event == EV_3clicks)
+ #if NUM_CHANNEL_MODES > 1
+ || (event == EV_6clicks)
+ ) {
+ // detect if > 1 channel mode is enabled,
+ // and if so, fall through so channel mode code can handle it
+ // otherwise, change the ramp style
+ if (event == EV_3clicks) {
+ uint8_t enabled = 0;
+ for (uint8_t m=0; m<NUM_CHANNEL_MODES; m++)
+ enabled += channel_mode_enabled(m);
+ if (enabled > 1)
+ return EVENT_NOT_HANDLED;
+ }
+ #else
+ ) {
+ #endif
+
ramp_style = !ramp_style;
save_config();
#ifdef START_AT_MEMORIZED_LEVEL
@@ -400,9 +420,9 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
- #ifndef USE_TINT_RAMPING
// 3H: momentary turbo (on lights with no tint ramping)
- else if (event == EV_click3_hold) {
+ // (or 4H on lights with tint ramping)
+ else if (event == EV_MOMENTARY_TURBO) {
if (! arg) { // first frame only, to allow thermal regulation to work
#ifdef USE_2C_STYLE_CONFIG
uint8_t tl = style_2c ? MAX_LEVEL : turbo_level;
@@ -413,11 +433,10 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
return MISCHIEF_MANAGED;
}
- else if (event == EV_click3_hold_release) {
+ else if (event == EV_MOMENTARY_TURBO_RELEASE) {
set_level_and_therm_target(memorized_level);
return MISCHIEF_MANAGED;
}
- #endif // ifndef USE_TINT_RAMPING
#ifdef USE_MOMENTARY_MODE
// 5 clicks: shortcut to momentary mode
@@ -439,10 +458,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_MANUAL_MEMORY
else if (event == EV_10clicks) {
// turn on manual memory and save current brightness
- manual_memory = actual_level;
- #ifdef USE_TINT_RAMPING
- manual_memory_tint = tint; // remember tint too
- #endif
+ manual_memory_save();
save_config();
blink_once();
return MISCHIEF_MANAGED;
@@ -643,6 +659,27 @@ void set_level_and_therm_target(uint8_t level) {
#define set_level_and_therm_target(level) set_level(level)
#endif
+void manual_memory_restore() {
+ memorized_level = manual_memory;
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = manual_memory_channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ channel_mode_args[i] = manual_memory_channel_args[i];
+ #endif
+}
+
+void manual_memory_save() {
+ manual_memory = actual_level;
+ #if NUM_CHANNEL_MODES > 1
+ manual_memory_channel_mode = channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ manual_memory_channel_args[i] = channel_mode_args[i];
+ #endif
+}
#endif
diff --git a/spaghetti-monster/anduril/ramp-mode.h b/spaghetti-monster/anduril/ramp-mode.h
index ed76ed5..969cb6b 100644
--- a/spaghetti-monster/anduril/ramp-mode.h
+++ b/spaghetti-monster/anduril/ramp-mode.h
@@ -72,6 +72,17 @@
#define B_TIMING_OFF B_TIMEOUT_T
#endif
+
+// move a couple actions depending on whether there are channel modes
+#ifdef USE_CHANNEL_MODE_ARGS
+ #define EV_MOMENTARY_TURBO EV_click4_hold
+ #define EV_MOMENTARY_TURBO_RELEASE EV_click4_hold_release
+#else
+ #define EV_MOMENTARY_TURBO EV_click3_hold
+ #define EV_MOMENTARY_TURBO_RELEASE EV_click3_hold_release
+#endif
+
+
// default ramp options if not overridden earlier per-driver
#ifndef RAMP_STYLE
#define RAMP_STYLE 0 // smooth default
@@ -102,6 +113,7 @@
// mile marker(s) partway up the ramp
// default: blink only at border between regulated and FET
#ifdef BLINK_AT_RAMP_MIDDLE
+ // FIXME: remove PWM_CHANNELS, use some other abstraction
#if PWM_CHANNELS >= 3
#ifndef BLINK_AT_RAMP_MIDDLE_1
#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
@@ -166,6 +178,9 @@ uint8_t manual_memory = DEFAULT_MANUAL_MEMORY;
uint8_t manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER;
#endif
#endif
+void manual_memory_restore();
+void manual_memory_save();
+
#ifdef USE_SIMPLE_UI
// whether to enable the simplified interface or not
uint8_t simple_ui_active = SIMPLE_UI_ACTIVE;
diff --git a/spaghetti-monster/anduril/strobe-modes.c b/spaghetti-monster/anduril/strobe-modes.c
index 78fe240..5ee2386 100644
--- a/spaghetti-monster/anduril/strobe-modes.c
+++ b/spaghetti-monster/anduril/strobe-modes.c
@@ -184,6 +184,12 @@ inline void strobe_state_iter() {
break;
#endif
+ #ifdef USE_POLICE_COLOR_STROBE_MODE
+ case police_color_strobe_e:
+ police_color_strobe_iter();
+ break;
+ #endif
+
#ifdef USE_LIGHTNING_MODE
case lightning_storm_e:
lightning_storm_iter();
@@ -205,7 +211,7 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) {
uint8_t del = strobe_delays[st];
// TODO: make tac strobe brightness configurable?
set_level(STROBE_BRIGHTNESS);
- if (0) {} // placeholde0
+ if (0) {} // placeholder
#ifdef USE_PARTY_STROBE_MODE
else if (st == party_strobe_e) { // party strobe
#ifdef PARTY_STROBE_ONTIME
@@ -226,6 +232,28 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) {
}
#endif
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter() {
+ // one iteration of main loop()
+ uint8_t del = 66;
+ // TODO: make police strobe brightness configurable
+ uint8_t bright = memorized_level;
+ uint8_t channel = channel_mode;
+
+ for (uint8_t i=0; i<10; i++) {
+ if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1);
+ else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2);
+ set_level(bright);
+ nice_delay_ms(del >> 1);
+ set_level(STROBE_OFF_LEVEL);
+ nice_delay_ms(del);
+ }
+
+ // restore this when done
+ set_channel_mode(channel);
+}
+#endif
+
#ifdef USE_LIGHTNING_MODE
inline void lightning_storm_iter() {
// one iteration of main loop()
diff --git a/spaghetti-monster/anduril/strobe-modes.h b/spaghetti-monster/anduril/strobe-modes.h
index c6cfb53..685c249 100644
--- a/spaghetti-monster/anduril/strobe-modes.h
+++ b/spaghetti-monster/anduril/strobe-modes.h
@@ -29,6 +29,9 @@ typedef enum {
#ifdef USE_TACTICAL_STROBE_MODE
tactical_strobe_e,
#endif
+ #ifdef USE_POLICE_COLOR_STROBE_MODE
+ police_color_strobe_e,
+ #endif
#ifdef USE_LIGHTNING_MODE
lightning_storm_e,
#endif
@@ -81,6 +84,10 @@ uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10
inline void party_tactical_strobe_mode_iter(uint8_t st);
#endif
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter();
+#endif
+
#ifdef USE_LIGHTNING_MODE
inline void lightning_storm_iter();
#endif
diff --git a/spaghetti-monster/anduril/tint-ramping.c b/spaghetti-monster/anduril/tint-ramping.c
index d270d9d..13f5d29 100644
--- a/spaghetti-monster/anduril/tint-ramping.c
+++ b/spaghetti-monster/anduril/tint-ramping.c
@@ -1,24 +1,10 @@
/*
* tint-ramping.c: Tint ramping functions for Anduril.
- *
- * Copyright (C) 2017 Selene ToyKeeper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2017-2023 Selene ToyKeeper
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
-#ifndef TINT_RAMPING_C
-#define TINT_RAMPING_C
+#pragma once
#include "tint-ramping.h"
@@ -100,6 +86,3 @@ uint8_t tint_ramping_state(Event event, uint16_t arg) {
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/tint-ramping.h b/spaghetti-monster/anduril/tint-ramping.h
index 1c5e22a..9b7f9a8 100644
--- a/spaghetti-monster/anduril/tint-ramping.h
+++ b/spaghetti-monster/anduril/tint-ramping.h
@@ -1,24 +1,10 @@
/*
* tint-ramping.h: Tint ramping functions for Anduril.
- *
- * Copyright (C) 2017 Selene ToyKeeper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2017-2023 Selene ToyKeeper
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
-#ifndef TINT_RAMPING_H
-#define TINT_RAMPING_H
+#pragma once
// 0: smooth tint ramp
// 1: toggle tint only between two extremes
@@ -35,5 +21,3 @@ uint8_t manual_memory_tint;
// not actually a mode, more of a fallback under other modes
uint8_t tint_ramping_state(Event event, uint16_t arg);
-
-#endif
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index 30b8a67..fca1e83 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -39,6 +39,7 @@ ISR(TIMER1_COMPA_vect) {
}
#endif
+// FIXME: hw_setup() shouldn't be here ... move it entirely to hwdef files
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
static inline void hw_setup() {
// configure PWM channels
diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h
index 66d31ba..17ed66f 100644
--- a/spaghetti-monster/fsm-misc.h
+++ b/spaghetti-monster/fsm-misc.h
@@ -53,6 +53,11 @@ void indicator_led(uint8_t lvl);
void button_led_set(uint8_t lvl);
#endif
+// if any type of aux LEDs exist, define a shorthand flag for it
+#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) || defined(USE_BUTTON_LED)
+#define HAS_AUX_LEDS
+#endif
+
#ifdef USE_AUX_RGB_LEDS
// value: 0b00BBGGRR
// each pair of bits: 0=off, 1=low, 2=high
diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c
index 63692c8..5096dfd 100644
--- a/spaghetti-monster/fsm-ramping.c
+++ b/spaghetti-monster/fsm-ramping.c
@@ -2,46 +2,28 @@
* fsm-ramping.c: Ramping functions for SpaghettiMonster.
* Handles 1- to 4-channel smooth ramping on a single LED.
*
- * Copyright (C) 2017 Selene ToyKeeper
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2017-2023 Selene ToyKeeper
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
-#ifndef FSM_RAMPING_C
-#define FSM_RAMPING_C
+#pragma once
#ifdef USE_RAMPING
-void set_level(uint8_t level) {
- #ifdef USE_JUMP_START
- // maybe "jump start" the engine, if it's prone to slow starts
- // (pulse the output high for a moment to wake up the power regulator)
- // (only do this when starting from off and going to a low level)
- if ((! actual_level)
- && level
- && (level < jump_start_level)) {
- set_level(jump_start_level);
- delay_4ms(JUMP_START_TIME/4);
- }
- #endif // ifdef USE_JUMP_START
+void set_channel_mode(uint8_t mode) {
+ uint8_t cur_level = actual_level;
+ // turn off old LEDs before changing channel
+ set_level(0);
- actual_level = level;
+ // change the channel
+ channel_mode = mode;
- #ifdef USE_SET_LEVEL_GRADUALLY
- gradual_target = level;
- #endif
+ // update the LEDs
+ set_level(cur_level);
+}
+#ifdef HAS_AUX_LEDS
+inline void set_level_aux_leds(uint8_t level) {
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
// use side-facing aux LEDs while main LEDs are on
if (! go_to_standby) {
@@ -52,9 +34,6 @@ void set_level(uint8_t level) {
button_led_set((level > 0) + (level > DEFAULT_LEVEL));
#endif
}
- //if (level > MAX_1x7135) indicator_led(2);
- //else if (level > 0) indicator_led(1);
- //else if (! go_to_standby) indicator_led(0);
#else // turn off front-facing aux LEDs while main LEDs are on
#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
if (! go_to_standby) {
@@ -70,33 +49,173 @@ void set_level(uint8_t level) {
}
#endif
#endif
+}
+#endif // ifdef HAS_AUX_LEDS
- #ifdef OVERRIDE_SET_LEVEL
- set_level_override(level);
- #else
- #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC)
- static uint8_t prev_level = 0;
- uint8_t api_level = level;
+void set_level(uint8_t level) {
+ #ifdef USE_JUMP_START
+ // maybe "jump start" the engine, if it's prone to slow starts
+ // (pulse the output high for a moment to wake up the power regulator)
+ // (only do this when starting from off and going to a low level)
+ // TODO: allow different jump start behavior per channel mode
+ if ((! actual_level)
+ && level
+ && (level < jump_start_level)) {
+ set_level(jump_start_level);
+ delay_4ms(JUMP_START_TIME/4);
+ }
+ #endif
+
+ #ifdef HAS_AUX_LEDS
+ set_level_aux_leds(level);
+ #endif
+
+ // call the relevant hardware-specific set_level_*()
+ SetLevelFuncPtr set_level_func = channel_modes[channel_mode];
+ set_level_func(level);
+
+ actual_level = level;
+
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ gradual_target = level;
+ #endif
+
+ #ifdef USE_DYNAMIC_UNDERCLOCKING
+ auto_clock_speed();
+ #endif
+}
+
+///// Common set_level_*() functions shared by multiple lights /////
+// (unique lights should use their own,
+// but these common versions cover most of the common hardware designs)
+
+#ifdef USE_SET_LEVEL_1CH
+// single set of LEDs with 1 power channel
+void set_level_1ch(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ }
+}
+#endif
+
+#ifdef USE_SET_LEVEL_2CH_STACKED
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_2ch_stacked(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ HIGH_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
+ }
+}
+#endif
+
+#ifdef USE_SET_LEVEL_3CH_STACKED
+// single set of LEDs with 3 stacked power channels, like DDFET+N+1
+void set_level_3ch_stacked(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ MED_PWM_LVL = 0;
+ HIGH_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ MED_PWM_LVL = PWM_GET(med_pwm_levels, level);
+ HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
+ }
+}
+#endif
+
+// TODO: upgrade some older lights to dynamic PWM
+// TODO: 1ch w/ dynamic PWM
+// TODO: 1ch w/ dynamic PWM and opamp enable pins?
+// TODO: 2ch stacked w/ dynamic PWM
+// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins?
+
+#ifdef USE_SET_LEVEL_2CH_BLEND
+// warm + cool blend w/ middle sag correction
+void set_level_2ch_blend(uint8_t level) {
+ #ifndef TINT_RAMPING_CORRECTION
+ #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
+ #endif
+
+ BLEND_PWM_DATATYPE vpwm;
+
+ if (level == 0) {
+ vpwm = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ vpwm = PWM_GET(blend_pwm_levels, level);
+ }
+
+ // calculate actual PWM levels based on a single-channel ramp
+ // and a global tint value
+ uint16_t brightness = vpwm;
+ uint16_t warm_PWM, cool_PWM;
+ const uint16_t top = PWM_TOP;
+
+ // auto-tint modes
+ uint8_t mytint = channel_mode_args[channel_mode];
+
+ PWM_DATATYPE2 base_PWM = brightness;
+ #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
+ // middle tints sag, so correct for that effect
+ // by adding extra power which peaks at the middle tint
+ // (correction is only necessary when PWM is fast)
+ if (level > HALFSPEED_LEVEL) {
+ base_PWM = brightness
+ + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
+ * triangle_wave(mytint) / 255);
+ }
+ // fade the triangle wave out when above 100% power,
+ // so it won't go over 200%
+ if (brightness > top) {
+ base_PWM -= 2 * (
+ ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
+ * triangle_wave(mytint) / 255
+ );
+ }
+ // guarantee no more than 200% power
+ if (base_PWM > (top << 1)) { base_PWM = top << 1; }
#endif
- //TCCR0A = PHASE;
+ cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
+ warm_PWM = base_PWM - cool_PWM;
+ // when running at > 100% power, spill extra over to other channel
+ if (cool_PWM > top) {
+ warm_PWM += (cool_PWM - top);
+ cool_PWM = top;
+ } else if (warm_PWM > top) {
+ cool_PWM += (warm_PWM - top);
+ warm_PWM = top;
+ }
+
+ WARM_PWM_LVL = warm_PWM;
+ COOL_PWM_LVL = cool_PWM;
+}
+#endif // ifdef USE_TINT_RAMPING
+
+#ifdef USE_LEGACY_SET_LEVEL
+// (this is mostly just here for reference, temporarily)
+// single set of LEDs with 1 to 3 stacked power channels,
+// like linear, FET+1, and FET+N+1
+// (default set_level_*() function for most lights)
+void set_level_legacy(uint8_t level) {
if (level == 0) {
#if PWM_CHANNELS >= 1
- PWM1_LVL = 0;
+ PWM1_LVL = 0;
#endif
#if PWM_CHANNELS >= 2
- PWM2_LVL = 0;
+ PWM2_LVL = 0;
#endif
#if PWM_CHANNELS >= 3
- PWM3_LVL = 0;
- #endif
- #if PWM_CHANNELS >= 4
- PWM4_LVL = 0;
- #endif
- #ifdef USE_TINT_RAMPING
- TINT1_LVL = 0;
- TINT2_LVL = 0;
+ PWM3_LVL = 0;
#endif
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF)
PWM1_CNT = 0;
@@ -121,7 +240,6 @@ void set_level(uint8_t level) {
#endif
} else {
// enable the power channel, if relevant
- #ifndef USE_TINT_RAMPING // update_tint handles this better
#ifdef LED_ENABLE_PIN
#ifdef LED_ON_DELAY
uint8_t led_enable_port_save = LED_ENABLE_PORT;
@@ -161,7 +279,6 @@ void set_level(uint8_t level) {
delay_4ms(LED2_ON_DELAY/4);
#endif
#endif
- #endif // ifndef USE_TINT_RAMPING
// PWM array index = level - 1
level --;
@@ -175,9 +292,6 @@ void set_level(uint8_t level) {
#if PWM_CHANNELS >= 3
PWM3_LVL = PWM_GET(pwm3_levels, level);
#endif
- #if PWM_CHANNELS >= 4
- PWM4_LVL = PWM_GET(pwm4_levels, level);
- #endif
#ifdef USE_DYN_PWM
uint16_t top = PWM_GET(pwm_tops, level);
@@ -190,29 +304,15 @@ void set_level(uint8_t level) {
// (but don't wait when turning on from zero, because
// it'll reset the phase below anyway)
// to be safe, allow at least 32 cycles to update TOP
- while(prev_level && (PWM1_CNT > (top - 32))) {}
+ while(actual_level && (PWM1_CNT > (top - 32))) {}
#endif
// pulse frequency modulation, a.k.a. dynamic PWM
PWM1_TOP = top;
-
- // repeat for other channels if necessary
- #ifdef PMW2_TOP
- #if defined(PWM2_CNT) && defined(PWM2_PHASE_SYNC)
- while(prev_level && (PWM2_CNT > (top - 32))) {}
- #endif
- PWM2_TOP = top;
- #endif
- #ifdef PMW3_TOP
- #if defined(PWM3_CNT) && defined(PWM3_PHASE_SYNC)
- while(prev_level && (PWM3_CNT > (top - 32))) {}
- #endif
- PWM3_TOP = top;
- #endif
#endif // ifdef USE_DYN_PWM
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON)
// force reset phase when turning on from zero
// (because otherwise the initial response is inconsistent)
- if (! prev_level) {
+ if (! actual_level) {
PWM1_CNT = 0;
#if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON)
PWM2_CNT = 0;
@@ -223,114 +323,117 @@ void set_level(uint8_t level) {
}
#endif
}
- #ifdef USE_TINT_RAMPING
- update_tint();
- #endif
-
- #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC)
- prev_level = api_level;
- #endif
- #endif // ifdef OVERRIDE_SET_LEVEL
#ifdef USE_DYNAMIC_UNDERCLOCKING
auto_clock_speed();
#endif
}
+#endif
+
#ifdef USE_SET_LEVEL_GRADUALLY
inline void set_level_gradually(uint8_t lvl) {
gradual_target = lvl;
}
-#ifndef OVERRIDE_GRADUAL_TICK
+
// call this every frame or every few frames to change brightness very smoothly
void gradual_tick() {
- // go by only one ramp level at a time instead of directly to the target
- uint8_t gt = gradual_target;
- if (gt < actual_level) gt = actual_level - 1;
- else if (gt > actual_level) gt = actual_level + 1;
-
- /*
- #ifdef LED_ENABLE_PIN_LEVEL_MIN
- // only enable during part of the ramp
- if ((gt >= LED_ENABLE_PIN_LEVEL_MIN)
- && (gt <= LED_ENABLE_PIN_LEVEL_MAX))
- LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
- else // disable during other parts of the ramp
- LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
- #endif
- */
+ // call the relevant hardware-specific function
+ GradualTickFuncPtr gradual_tick_func = gradual_tick_modes[channel_mode];
+ gradual_tick_func();
+}
- gt --; // convert 1-based number to 0-based
+// reduce repetition with macros
+// common code at the beginning of every gradual tick handler
+#define GRADUAL_TICK_SETUP() \
+ uint8_t gt = gradual_target; \
+ if (gt < actual_level) gt = actual_level - 1; \
+ else if (gt > actual_level) gt = actual_level + 1; \
+ gt --; \
PWM_DATATYPE target;
- #if PWM_CHANNELS >= 1
- target = PWM_GET(pwm1_levels, gt);
- #if PWM_CHANNELS > 1
- if ((gt < actual_level) // special case for FET-only turbo
- && (PWM1_LVL == 0) // (bypass adjustment period for first step)
- && (target == PWM_TOP)) PWM1_LVL = PWM_TOP;
- else
- #endif
- if (PWM1_LVL < target) PWM1_LVL ++;
- else if (PWM1_LVL > target) PWM1_LVL --;
- #endif
- #if PWM_CHANNELS >= 2
- target = PWM_GET(pwm2_levels, gt);
- #if PWM_CHANNELS > 2
- if ((gt < actual_level) // special case for FET-only turbo
- && (PWM2_LVL == 0) // (bypass adjustment period for first step)
- && (target == PWM_TOP)) PWM2_LVL = PWM_TOP;
- else
- #endif
- if (PWM2_LVL < target) PWM2_LVL ++;
- else if (PWM2_LVL > target) PWM2_LVL --;
- #endif
- #if PWM_CHANNELS >= 3
- target = PWM_GET(pwm3_levels, gt);
- if (PWM3_LVL < target) PWM3_LVL ++;
- else if (PWM3_LVL > target) PWM3_LVL --;
- #endif
- #if PWM_CHANNELS >= 4
- target = PWM_GET(pwm4_levels, gt);
- if (PWM4_LVL < target) PWM4_LVL ++;
- else if (PWM4_LVL > target) PWM4_LVL --;
- #endif
+// tick the top layer of the stack
+#define GRADUAL_ADJUST_1CH(TABLE,PWM) \
+ target = PWM_GET(TABLE, gt); \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+// tick a base level of the stack
+// (with support for special DD FET behavior
+// like "low=0, high=255" --> "low=255, high=254")
+#define GRADUAL_ADJUST(TABLE,PWM,TOP) \
+ target = PWM_GET(TABLE, gt); \
+ if ((gt < actual_level) \
+ && (PWM == 0) \
+ && (target == TOP)) PWM = TOP; \
+ else \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+// do this when output exactly matches a ramp level
+#define GRADUAL_IS_ACTUAL() \
+ uint8_t orig = gradual_target; \
+ set_level(gt + 1); \
+ gradual_target = orig;
+
+#ifdef USE_GRADUAL_TICK_1CH
+void gradual_tick_1ch() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL);
// did we go far enough to hit the next defined ramp level?
// if so, update the main ramp level tracking var
- if ((PWM1_LVL == PWM_GET(pwm1_levels, gt))
- #if PWM_CHANNELS >= 2
- && (PWM2_LVL == PWM_GET(pwm2_levels, gt))
- #endif
- #if PWM_CHANNELS >= 3
- && (PWM3_LVL == PWM_GET(pwm3_levels, gt))
- #endif
- #if PWM_CHANNELS >= 4
- && (PWM4_LVL == PWM_GET(pwm4_levels, gt))
- #endif
+ if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)))
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+#endif
+
+#ifdef USE_GRADUAL_TICK_2CH_STACKED
+void gradual_tick_2ch_stacked() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
+
+ // did we go far enough to hit the next defined ramp level?
+ // if so, update the main ramp level tracking var
+ if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
+ && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
+ )
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+#endif
+
+#ifdef USE_GRADUAL_TICK_3CH_STACKED
+void gradual_tick_3ch_stacked() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
+
+ // did we go far enough to hit the next defined ramp level?
+ // if so, update the main ramp level tracking var
+ if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
+ && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt))
+ && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
)
{
- //actual_level = gt + 1;
- uint8_t orig = gradual_target;
- set_level(gt + 1);
- gradual_target = orig;
+ GRADUAL_IS_ACTUAL();
}
- // is handled in set_level()
- //#ifdef USE_TINT_RAMPING
- //update_tint();
- //#endif
- // is handled in set_level()
- //#ifdef USE_DYNAMIC_UNDERCLOCKING
- //auto_clock_speed();
- //#endif
}
-#endif // ifdef OVERRIDE_GRADUAL_TICK
+#endif
#endif // ifdef USE_SET_LEVEL_GRADUALLY
#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
-void update_tint() {
+void set_level_2ch_blend() {
#ifndef TINT_RAMPING_CORRECTION
#define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
#endif
@@ -340,7 +443,7 @@ void update_tint() {
//PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
uint16_t brightness = PWM1_LVL;
uint16_t warm_PWM, cool_PWM;
- #ifdef USE_DYN_PWM
+ #ifdef USE_STACKED_DYN_PWM
uint16_t top = PWM1_TOP;
//uint16_t top = PWM_GET(pwm_tops, actual_level-1);
#else
@@ -414,5 +517,12 @@ void update_tint() {
#endif // ifdef USE_TINT_RAMPING
-#endif // ifdef USE_RAMPING
+// define the channel mode lists
+// TODO: move to progmem
+SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES] = { SET_LEVEL_MODES };
+#ifdef USE_SET_LEVEL_GRADUALLY
+GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES] = { GRADUAL_TICK_MODES };
#endif
+
+
+#endif // ifdef USE_RAMPING
diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h
index de090c2..3021ff2 100644
--- a/spaghetti-monster/fsm-ramping.h
+++ b/spaghetti-monster/fsm-ramping.h
@@ -18,23 +18,80 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef FSM_RAMPING_H
-#define FSM_RAMPING_H
+#pragma once
#ifdef USE_RAMPING
// actual_level: last ramp level set by set_level()
uint8_t actual_level = 0;
-#ifdef USE_TINT_RAMPING
-#ifdef TINT_RAMP_TOGGLE_ONLY
-uint8_t tint = 0;
-#else
-uint8_t tint = 128;
+// TODO: size-optimize the case with only 1 channel mode
+// (the arrays and stuff shouldn't be needed)
+
+// current multi-channel mode
+uint8_t channel_mode = DEFAULT_CHANNEL_MODE;
+#ifdef USE_MANUAL_MEMORY
+// reset w/ manual memory
+uint8_t manual_memory_channel_mode = DEFAULT_CHANNEL_MODE;
#endif
-#define USE_TRIANGLE_WAVE
+
+#if NUM_CHANNEL_MODES > 1
+#define USE_CHANNEL_MODES
#endif
+// one function per channel mode
+typedef void SetLevelFunc(uint8_t level);
+typedef SetLevelFunc * SetLevelFuncPtr;
+// TODO: move to progmem
+SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES];
+
+#ifdef USE_SET_LEVEL_GRADUALLY
+// the gradual tick mechanism may be different per channel
+typedef void GradualTickFunc();
+typedef GradualTickFunc * GradualTickFuncPtr;
+// TODO: move to progmem
+GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES];
+#endif
+
+#ifdef USE_CUSTOM_CHANNEL_3H_MODES
+// different 3H behavior per channel?
+// TODO: move to progmem
+StatePtr channel_3H_modes[NUM_CHANNEL_MODES];
+#endif
+
+//#ifdef USE_CHANNEL_MODE_TOGGLES
+#if NUM_CHANNEL_MODES > 1
+// user can take unwanted modes out of the rotation
+// TODO: save to eeprom
+// array
+//uint8_t channel_modes_enabled[NUM_CHANNEL_MODES] = { CHANNEL_MODES_ENABLED };
+// bitmask
+uint8_t channel_modes_enabled = CHANNEL_MODES_ENABLED;
+#define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1)
+#define channel_mode_enable(n) channel_modes_enabled |= (1 << n)
+#define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff)
+#endif
+
+#ifdef USE_CHANNEL_MODE_ARGS
+// one byte of extra data per channel mode, like for tint value
+uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS };
+#endif
+
+// TODO: remove this after implementing channel modes
+//#ifdef USE_TINT_RAMPING
+//#ifdef TINT_RAMP_TOGGLE_ONLY
+//uint8_t tint = 0;
+//#else
+//uint8_t tint = 128;
+//#endif
+//#define USE_TRIANGLE_WAVE
+//#endif
+
+void set_channel_mode(uint8_t mode);
+
+void set_level(uint8_t level);
+//void set_level_smooth(uint8_t level);
+
#ifdef USE_SET_LEVEL_GRADUALLY
// adjust brightness very smoothly
uint8_t gradual_target;
@@ -42,21 +99,24 @@ inline void set_level_gradually(uint8_t lvl);
void gradual_tick();
#endif
-#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
-void update_tint();
-#endif
-
// auto-detect the data type for PWM tables
-#ifndef PWM_BITS
- #define PWM_BITS 8
- #define PWM_TOP 255
+// FIXME: PWM bits and data type should be per PWM table
+#ifndef PWM1_BITS
+ #define PWM1_BITS 8
+ #define PWM1_TOP 255
+ #define STACKED_PWM_TOP 255
#endif
#if PWM_BITS <= 8
+ #define STACKED_PWM_DATATYPE uint8_t
#define PWM_DATATYPE uint8_t
#define PWM_DATATYPE2 uint16_t
#define PWM_TOP 255
+ #define STACKED_PWM_TOP 255
+ #ifndef PWM_GET
#define PWM_GET(x,y) pgm_read_byte(x+y)
+ #endif
#else
+ #define STACKED_PWM_DATATYPE uint16_t
#define PWM_DATATYPE uint16_t
#ifndef PWM_DATATYPE2
#define PWM_DATATYPE2 uint32_t
@@ -64,32 +124,55 @@ void update_tint();
#ifndef PWM_TOP
#define PWM_TOP 1023 // 10 bits by default
#endif
+ #ifndef STACKED_PWM_TOP
+ #define STACKED_PWM_TOP 1023
+ #endif
// pointer plus 2*y bytes
//#define PWM_GET(x,y) pgm_read_word(x+(2*y))
// nope, the compiler was already doing the math correctly
+ #ifndef PWM_GET
#define PWM_GET(x,y) pgm_read_word(x+y)
+ #endif
#endif
+#define PWM_GET8(x,y) pgm_read_byte(x+y)
+#define PWM_GET16(x,y) pgm_read_word(x+y)
// use UI-defined ramp tables if they exist
#ifdef PWM1_LEVELS
-PROGMEM const PWM_DATATYPE pwm1_levels[] = { PWM1_LEVELS };
+PROGMEM const PWM1_DATATYPE pwm1_levels[] = { PWM1_LEVELS };
#endif
#ifdef PWM2_LEVELS
-PROGMEM const PWM_DATATYPE pwm2_levels[] = { PWM2_LEVELS };
+PROGMEM const PWM2_DATATYPE pwm2_levels[] = { PWM2_LEVELS };
#endif
#ifdef PWM3_LEVELS
-PROGMEM const PWM_DATATYPE pwm3_levels[] = { PWM3_LEVELS };
+PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS };
+#endif
+
+// convenience defs for 1 LED with stacked channels
+#ifdef LOW_PWM_LEVELS
+PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS };
+#endif
+#ifdef MED_PWM_LEVELS
+PROGMEM const PWM_DATATYPE med_pwm_levels[] = { MED_PWM_LEVELS };
+#endif
+#ifdef HIGH_PWM_LEVELS
+PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS };
#endif
-#ifdef PWM4_LEVELS
-PROGMEM const PWM_DATATYPE pwm4_levels[] = { PWM4_LEVELS };
+
+// 2 channel CCT blending ramp
+#ifdef BLEND_PWM_LEVELS
+PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS };
#endif
+
// pulse frequency modulation, a.k.a. dynamic PWM
// (different ceiling / frequency at each ramp level)
+// FIXME: dynamic PWM should be a per-channel option, not global
#ifdef USE_DYN_PWM
PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS };
#endif
+// FIXME: jump start should be per channel / channel mode
#ifdef USE_JUMP_START
#ifndef JUMP_START_TIME
#define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12
@@ -100,78 +183,11 @@ PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS };
uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL;
#endif
-// default / example ramps
-#ifndef PWM1_LEVELS
-#if PWM_CHANNELS == 1
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 1 50 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,4,4,4,5,5,6,7,8,9,11,12,14,16,18,20,23,25,28,32,35,39,43,47,52,57,62,68,74,80,87,94,102,110,118,127,136,146,156,167,178,189,201,214,227,241,255 };
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 1 75 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,4,4,4,4,5,5,5,6,6,7,8,8,9,10,11,12,13,14,15,17,18,20,21,23,25,27,29,31,33,36,38,41,44,47,50,53,56,59,63,67,71,75,79,83,88,93,98,103,108,113,119,125,131,137,143,150,157,164,171,178,186,194,202,210,219,227,236,246,255 };
- #elif RAMP_LENGTH == 150
- // ../../bin/level_calc.py 1 150 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,12,12,13,13,14,15,15,16,17,17,18,19,19,20,21,22,23,24,24,25,26,27,28,29,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,57,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,89,91,93,96,98,101,103,106,109,111,114,117,120,123,125,128,131,134,138,141,144,147,151,154,157,161,164,168,171,175,179,183,186,190,194,198,202,206,210,215,219,223,228,232,236,241,246,250,255 };
- #endif
-#elif PWM_CHANNELS == 2
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 2 50 7135 4 0.33 150 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,10,13,17,22,28,35,44,54,65,78,93,109,128,149,171,197,224,255,255,255,255,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 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,20,26,31,37,44,51,58,65,73,82,91,100,110,121,132,143,155,168,181,194,209,224,239,255 };
- #define MAX_1x7135 22
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 2 75 7135 4 0.33 150 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,8,10,12,14,17,20,24,28,32,37,43,49,56,64,72,82,91,102,114,126,139,153,168,184,202,220,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 };
- PROGMEM const PWM_DATATYPE 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,2,5,7,10,13,16,19,23,26,30,34,38,42,47,51,56,61,66,72,77,83,89,95,101,108,115,122,129,136,144,152,160,168,177,186,195,204,214,224,234,244,255 };
- #define MAX_1x7135 33
- #elif RAMP_LENGTH == 150
- // ../../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
- PROGMEM const PWM_DATATYPE 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 };
- PROGMEM const PWM_DATATYPE 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
- #endif
-#elif PWM_CHANNELS == 3
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 3 50 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,11,15,20,26,34,43,54,67,82,99,118,140,165,192,221,254,255,255,255,255,255,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 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,25,33,42,52,62,73,85,97,111,125,140,157,174,192,210,230,251,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,34,54,76,98,122,146,172,198,226,255 };
- #define MAX_1x7135 20
- #define MAX_Nx7135 39
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 3 75 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,9,11,14,16,20,24,28,34,40,46,54,62,71,81,92,104,117,130,146,162,179,198,218,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 };
- PROGMEM const PWM_DATATYPE 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,5,9,14,18,23,29,34,40,47,53,60,67,75,83,91,99,108,117,127,137,148,158,170,181,193,206,219,232,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,15,28,42,55,70,84,99,115,131,147,164,181,199,217,236,255 };
- #define MAX_1x7135 30
- #define MAX_Nx7135 59
- #elif RAMP_LENGTH == 150
- // ../../bin/level_calc.py 1 65 7135 1 0.8 150
- // ... mixed with this:
- // ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500
- PROGMEM const PWM_DATATYPE 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 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 };
- #define MAX_1x7135 65
- #define MAX_Nx7135 130
- #define HALFSPEED_LEVEL 14
- #define QUARTERSPEED_LEVEL 5
- #endif
-#elif PWM_CHANNELS == 4
- 4-channel PWM not really supported yet, sorry.
-#endif
-#endif
-
// RAMP_SIZE / MAX_LVL
-#define RAMP_SIZE (sizeof(pwm1_levels)/sizeof(PWM_DATATYPE))
+// cfg-*.h should define RAMP_SIZE
+//#define RAMP_SIZE (sizeof(stacked_pwm1_levels)/sizeof(STACKED_PWM_DATATYPE))
#define MAX_LEVEL RAMP_SIZE
-void set_level(uint8_t level);
-//void set_level_smooth(uint8_t level);
#endif // ifdef USE_RAMPING
-#endif
+
diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h
index 9964bc1..2c51d0a 100644
--- a/spaghetti-monster/fsm-states.h
+++ b/spaghetti-monster/fsm-states.h
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
+
#ifndef FSM_STATES_H
#define FSM_STATES_H