aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--spaghetti-monster/anduril/anduril.c1419
-rw-r--r--spaghetti-monster/anduril/battcheck-mode-fsm.h26
-rw-r--r--spaghetti-monster/anduril/battcheck-mode.c62
-rw-r--r--spaghetti-monster/anduril/battcheck-mode.h26
-rw-r--r--spaghetti-monster/anduril/cfg-default.h5
-rw-r--r--spaghetti-monster/anduril/config-mode.h2
-rw-r--r--spaghetti-monster/anduril/goodnight-mode.c72
-rw-r--r--spaghetti-monster/anduril/goodnight-mode.h27
-rw-r--r--spaghetti-monster/anduril/load-save-config-fsm.h80
-rw-r--r--spaghetti-monster/anduril/load-save-config.c134
-rw-r--r--spaghetti-monster/anduril/load-save-config.h31
-rw-r--r--spaghetti-monster/anduril/misc.c45
-rw-r--r--spaghetti-monster/anduril/misc.h27
-rw-r--r--spaghetti-monster/anduril/momentary-mode.c86
-rw-r--r--spaghetti-monster/anduril/momentary-mode.h29
-rw-r--r--spaghetti-monster/anduril/off-state.c261
-rw-r--r--spaghetti-monster/anduril/off-state.h27
-rw-r--r--spaghetti-monster/anduril/ramping-fsm.h37
-rw-r--r--spaghetti-monster/anduril/ramping.c460
-rw-r--r--spaghetti-monster/anduril/ramping.h179
-rw-r--r--spaghetti-monster/anduril/sos-mode.c75
-rw-r--r--spaghetti-monster/anduril/sos-mode.h29
-rw-r--r--spaghetti-monster/anduril/strobes-fsm.h11
-rw-r--r--spaghetti-monster/anduril/strobes.c36
-rw-r--r--spaghetti-monster/anduril/strobes.h5
-rw-r--r--spaghetti-monster/anduril/tempcheck-mode.c73
-rw-r--r--spaghetti-monster/anduril/tempcheck-mode.h30
-rw-r--r--spaghetti-monster/anduril/version-check-mode.c47
-rw-r--r--spaghetti-monster/anduril/version-check-mode.h31
-rw-r--r--spaghetti-monster/fsm-misc.c1
30 files changed, 2016 insertions, 1357 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index d69be54..23f14d5 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -54,126 +54,68 @@
#include incfile(CONFIGFILE)
-////////// Include headers which need to be before FSM //////////
+/********* Include headers which need to be before FSM *********/
+
+// enable FSM features needed by strobe modes
+#include "ramping-fsm.h"
+
#ifdef USE_FACTORY_RESET
#include "factory-reset-fsm.h"
#endif
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode-fsm.h"
+#endif
+
// enable FSM features needed by strobe modes
#include "strobes-fsm.h"
+// figure out how many bytes of eeprom are needed,
+// based on which UI features are enabled
+#include "load-save-config-fsm.h"
-// brightness to use when no memory is set
-#ifndef DEFAULT_LEVEL
-#define DEFAULT_LEVEL MAX_1x7135
-#endif
-// thermal properties, if not defined per-driver
-#ifndef MIN_THERM_STEPDOWN
-#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to
-#endif
-#ifndef THERM_FASTER_LEVEL
- #ifdef MAX_Nx7135
- #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high
- #else
- #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high
- #endif
-#endif
-#ifdef USE_THERMAL_REGULATION
-#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments
-#endif
+/********* bring in FSM / SpaghettiMonster *********/
+#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
+#include "spaghetti-monster.h"
-/********* Configure SpaghettiMonster *********/
-#define USE_DELAY_ZERO
-#define USE_RAMPING
-#ifndef RAMP_LENGTH
-#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
-#endif
-#define MAX_BIKING_LEVEL 120 // should be 127 or less
-#define USE_BATTCHECK
-#if defined(USE_SIMPLE_UI)
-// start in the simple UI after each factory reset?
-#ifndef DEFAULT_SIMPLE_UI_ACTIVE
-#define DEFAULT_SIMPLE_UI_ACTIVE 1
-#endif
-#ifndef DEFAULT_SIMPLE_UI_FLOOR
-#define DEFAULT_SIMPLE_UI_FLOOR 22
-#endif
-#ifndef DEFAULT_SIMPLE_UI_CEIL
-#define DEFAULT_SIMPLE_UI_CEIL (MAX_1x7135+20)
-#endif
-#ifndef DEFAULT_SIMPLE_UI_STEPS
-#define DEFAULT_SIMPLE_UI_STEPS 3
-#endif
-#endif
+/********* Include all the regular app headers *********/
-#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
+#include "off-state.h"
+#include "ramping.h"
+#include "load-save-config.h"
+#include "config-mode.h"
+#include "misc.h"
-// auto-detect how many eeprom bytes
-#define USE_EEPROM
-typedef enum {
- ramp_style_e,
- #ifdef USE_RAMP_CONFIG
- ramp_smooth_floor_e,
- ramp_smooth_ceil_e,
- ramp_discrete_floor_e,
- ramp_discrete_ceil_e,
- ramp_discrete_steps_e,
- #endif
- #ifdef USE_MANUAL_MEMORY
- manual_memory_e,
- #endif
- #ifdef USE_TINT_RAMPING
- tint_e,
- #endif
- #ifdef USE_STROBE_STATE
- strobe_type_e,
- #endif
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- strobe_delays_0_e,
- strobe_delays_1_e,
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- bike_flasher_brightness_e,
- #endif
- #ifdef USE_BEACON_MODE
- beacon_seconds_e,
- #endif
- #ifdef USE_SIMPLE_UI
- simple_ui_active_e,
- simple_ui_floor_e,
- simple_ui_ceil_e,
- simple_ui_steps_e,
- #endif
- #ifdef USE_THERMAL_REGULATION
- therm_ceil_e,
- therm_cal_offset_e,
- #endif
- #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
+#ifdef USE_VERSION_CHECK
+#include "version-check-mode.h"
+#endif
-#ifdef START_AT_MEMORIZED_LEVEL
-#define USE_EEPROM_WL
-#define EEPROM_WL_BYTES 1
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode.h"
#endif
-#include "spaghetti-monster.h"
+#ifdef USE_GOODNIGHT_MODE
+#include "goodnight-mode.h"
+#endif
+#ifdef USE_BEACON_MODE
+#include "beacon.h"
+#endif
-////////// Include all the extra headers //////////
+#ifdef USE_THERMAL_REGULATION
+#include "tempcheck-mode.h"
+#endif
-#include "config-mode.h"
+#ifdef USE_LOCKOUT_MODE
#include "lockout.h"
+#endif
+
+#ifdef USE_MOMENTARY_MODE
+#include "momentary-mode.h"
+#endif
#ifdef USE_TINT_RAMPING
#include "tint-ramping.h"
@@ -183,64 +125,15 @@ typedef enum {
#include "factory-reset.h"
#endif
-#ifdef USE_BEACON_MODE
-#include "beacon.h"
-#endif
-
// this one detects its own enable/disable settings
#include "strobes.h"
-
-// configure the timing of turning on/off in regular ramp mode
-// press: react as soon as the button is pressed
-#define B_PRESS_T 0
-// release: react as soon as the button is released
-#define B_RELEASE_T 1
-// timeout: react as soon as we're sure the user isn't doing a double-click
-#define B_TIMEOUT_T 2
-// defaults are release on, timeout off
-#ifndef B_TIMING_ON
-//#define B_TIMING_ON B_PRESS_T
-#define B_TIMING_ON B_RELEASE_T
-#endif
-#ifndef B_TIMING_OFF
-//#define B_TIMING_OFF B_RELEASE_T
-#define B_TIMING_OFF B_TIMEOUT_T
+#ifdef USE_SOS_MODE
+#include "sos-mode.h"
#endif
-
// FSM states
-uint8_t off_state(Event event, uint16_t arg);
-// ramping mode and its related config mode
-uint8_t steady_state(Event event, uint16_t arg);
-#ifdef USE_RAMP_CONFIG
-uint8_t ramp_config_state(Event event, uint16_t arg);
-#endif
-#ifdef USE_BATTCHECK
-uint8_t battcheck_state(Event event, uint16_t arg);
-#endif
-#ifdef USE_THERMAL_REGULATION
-#define USE_BLINK_NUM
-uint8_t tempcheck_state(Event event, uint16_t arg);
-uint8_t thermal_config_state(Event event, uint16_t arg);
-#endif
-#ifdef USE_GOODNIGHT_MODE
-// 1-hour ramp down from low, then automatic off
-uint8_t goodnight_state(Event event, uint16_t arg);
-#endif
-#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
-// automatic SOS emergency signal
-uint8_t sos_state(Event event, uint16_t arg);
-#endif
-#ifdef USE_MOMENTARY_MODE
-// momentary / signalling mode
-uint8_t momentary_state(Event event, uint16_t arg);
-uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
-uint8_t momentary_active = 0; // boolean, true if active *right now*
-#endif
-void blink_confirm(uint8_t num);
-void blip();
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
void indicator_blink(uint8_t arg);
#endif
@@ -284,91 +177,6 @@ uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT;
uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
#endif
-// remember stuff even after battery was changed
-void load_config();
-void save_config();
-#ifdef START_AT_MEMORIZED_LEVEL
-void save_config_wl();
-#endif
-
-// default ramp options if not overridden earlier per-driver
-#ifndef RAMP_STYLE
-#define RAMP_STYLE 0 // smooth default
-#endif
-#ifndef RAMP_SMOOTH_FLOOR
- #define RAMP_SMOOTH_FLOOR 1
-#endif
-#ifndef RAMP_SMOOTH_CEIL
- #if PWM_CHANNELS == 3
- #define RAMP_SMOOTH_CEIL MAX_Nx7135
- #else
- #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
- #endif
-#endif
-#ifndef RAMP_DISCRETE_FLOOR
- #define RAMP_DISCRETE_FLOOR 20
-#endif
-#ifndef RAMP_DISCRETE_CEIL
- #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#endif
-#ifndef RAMP_DISCRETE_STEPS
- #define RAMP_DISCRETE_STEPS 7
-#endif
-
-// mile marker(s) partway up the ramp
-// default: blink only at border between regulated and FET
-#ifdef BLINK_AT_RAMP_MIDDLE
- #if PWM_CHANNELS >= 3
- #ifndef BLINK_AT_RAMP_MIDDLE_1
- #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
- #ifndef BLINK_AT_RAMP_MIDDLE_2
- #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
- #endif
- #endif
- #else
- #ifndef BLINK_AT_RAMP_MIDDLE_1
- #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
- #endif
- #endif
-#endif
-
-// brightness control
-uint8_t memorized_level = DEFAULT_LEVEL;
-#ifdef USE_MANUAL_MEMORY
-uint8_t manual_memory = 0;
-#endif
-#ifdef USE_SIMPLE_UI
-// whether to enable the simplified interface or not
-uint8_t simple_ui_active = DEFAULT_SIMPLE_UI_ACTIVE;
-#endif
-// smooth vs discrete ramping
-uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete
-// current values, regardless of style
-uint8_t ramp_floor = RAMP_SMOOTH_FLOOR;
-uint8_t ramp_ceil = RAMP_SMOOTH_CEIL;
-// per style
-uint8_t ramp_floors[] = {
- RAMP_SMOOTH_FLOOR,
- RAMP_DISCRETE_FLOOR,
- #ifdef USE_SIMPLE_UI
- DEFAULT_SIMPLE_UI_FLOOR,
- #endif
- };
-uint8_t ramp_ceils[] = {
- RAMP_SMOOTH_CEIL,
- RAMP_DISCRETE_CEIL,
- #ifdef USE_SIMPLE_UI
- DEFAULT_SIMPLE_UI_CEIL,
- #endif
- };
-uint8_t ramp_stepss[] = {
- 0,
- RAMP_DISCRETE_STEPS,
- #ifdef USE_SIMPLE_UI
- DEFAULT_SIMPLE_UI_STEPS,
- #endif
- };
-uint8_t ramp_discrete_step_size; // don't set this
#ifdef USE_INDICATOR_LED
// bits 2-3 control lockout mode
@@ -386,998 +194,60 @@ uint8_t ramp_discrete_step_size; // don't set this
#endif
#endif
-// calculate the nearest ramp level which would be valid at the moment
-// (is a no-op for smooth ramp, but limits discrete ramp to only the
-// correct levels for the user's config)
-uint8_t nearest_level(int16_t target);
-// ensure ramp globals are correct
-void ramp_update_config();
-#ifdef USE_THERMAL_REGULATION
-// brightness before thermal step-down
-uint8_t target_level = 0;
-void set_level_and_therm_target(uint8_t level);
-#else
-#define set_level_and_therm_target(level) set_level(level)
-#endif
-
-#ifdef USE_VERSION_CHECK
-#define USE_BLINK_DIGIT
-#include "version.h"
-const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
-uint8_t version_check_state(Event event, uint16_t arg);
-#endif
-
-#ifdef USE_RAMP_CONFIG
-void ramp_config_save();
-#endif
-#ifdef USE_THERMAL_REGULATION
-void thermal_config_save();
-#endif
-
-////////// Include all the extra sources //////////
+/********* Include all the app logic source files *********/
// (is a bit weird to do things this way,
// but it saves a lot of space by letting use use the -fwhole-program flag)
+#include "off-state.c"
+#include "ramping.c"
+#include "load-save-config.c"
#include "config-mode.c"
-#include "lockout.c"
+#include "misc.c"
-#ifdef USE_TINT_RAMPING
-#include "tint-ramping.c"
-#endif
-
-#ifdef USE_FACTORY_RESET
-#include "factory-reset.c"
-#endif
-
-#ifdef USE_BEACON_MODE
-#include "beacon.c"
+#ifdef USE_VERSION_CHECK
+#include "version-check-mode.c"
#endif
-#ifdef USE_STROBE_STATE
-#include "strobes.c"
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode.c"
#endif
-
-uint8_t off_state(Event event, uint16_t arg) {
- // turn emitter off when entering state
- if (event == EV_enter_state) {
- 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)
- // (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 > 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) || 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
- #if (B_TIMING_ON == B_PRESS_T)
- // hold (initially): go to lowest level (floor), but allow abort for regular click
- else if (event == EV_click1_press) {
- set_level(nearest_level(1));
- return MISCHIEF_MANAGED;
- }
- #endif // B_TIMING_ON == B_PRESS_T
- // hold: go to lowest level
- else if (event == EV_click1_hold) {
- #if (B_TIMING_ON == B_PRESS_T)
- #ifdef MOON_TIMING_HINT
- if (arg == 0) {
- // let the user know they can let go now to stay at moon
- blip();
- } else
- #endif
- #else // B_RELEASE_T or B_TIMEOUT_T
- set_level(nearest_level(1));
- #endif
- // don't start ramping immediately;
- // give the user time to release at moon level
- //if (arg >= HOLD_TIMEOUT) { // smaller
- if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
- set_state(steady_state, 1);
- }
- return MISCHIEF_MANAGED;
- }
- // hold, release quickly: go to lowest level (floor)
- else if (event == EV_click1_hold_release) {
- set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
- }
- #if (B_TIMING_ON != B_TIMEOUT_T)
- // 1 click (before timeout): go to memorized level, but allow abort for double click
- else if (event == EV_click1_release) {
- #ifdef USE_MANUAL_MEMORY
- if (manual_memory)
- set_level(nearest_level(manual_memory));
- else
- #endif
- set_level(nearest_level(memorized_level));
- return MISCHIEF_MANAGED;
- }
- #endif // if (B_TIMING_ON != B_TIMEOUT_T)
- // 1 click: regular mode
- else if (event == EV_1click) {
- #ifdef USE_MANUAL_MEMORY
- if (manual_memory)
- set_state(steady_state, manual_memory);
- else
- #endif
- set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
- }
- // click, hold: go to highest level (ceiling) (for ramping down)
- else if (event == EV_click2_hold) {
- set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
- }
- // 2 clicks: highest mode (ceiling)
- else if (event == EV_2clicks) {
- set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
- }
- // 3 clicks (initial press): off, to prep for later events
- else if (event == EV_click3_press) {
- set_level(0);
- return MISCHIEF_MANAGED;
- }
- #ifdef USE_BATTCHECK
- // 3 clicks: battcheck mode / blinky mode group 1
- else if (event == EV_3clicks) {
- set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- // 4 clicks: soft lockout
- else if (event == EV_4clicks) {
- blink_confirm(2);
- set_state(lockout_state, 0);
- return MISCHIEF_MANAGED;
- }
- #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
- // 13 clicks and hold the last click: invoke factory reset (reboot)
- else if (event == EV_click13_hold) {
- reboot();
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_VERSION_CHECK
- // 15+ clicks: show the version number
- else if (event == EV_15clicks) {
- set_state(version_check_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
-
- #ifdef USE_SIMPLE_UI
- // 8 clicks, but hold last click: turn simple UI off
- else if ((event == EV_click8_hold) && (!arg)) {
- blink_confirm(1);
- simple_ui_active = 0;
- return MISCHIEF_MANAGED;
- }
-
- ////////// Every action below here is blocked in the simple UI //////////
- if (simple_ui_active) {
- return EVENT_NOT_HANDLED;
- }
- // 8 clicks: enable simple UI
- else if (event == EV_8clicks) {
- blink_confirm(1);
- simple_ui_active = 1;
- return MISCHIEF_MANAGED;
- }
- #endif
-
- // click, click, long-click: strobe mode
- #ifdef USE_STROBE_STATE
- else if (event == EV_click3_hold) {
- set_state(strobe_state, 0);
- return MISCHIEF_MANAGED;
- }
- #elif defined(USE_BORING_STROBE_STATE)
- else if (event == EV_click3_hold) {
- set_state(boring_strobe_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_MOMENTARY_MODE
- // 5 clicks: momentary mode
- else if (event == EV_5clicks) {
- blink_confirm(1);
- set_state(momentary_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_INDICATOR_LED
- // 7 clicks: change indicator LED mode
- else if (event == EV_7clicks) {
- 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;
- }
- #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) {
- setting_rgb_mode_now = 1;
- 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) {
- setting_rgb_mode_now = 0;
- save_config();
- return MISCHIEF_MANAGED;
- }
- #endif // end 7 clicks
- #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION)
- // 10 clicks: thermal config mode
- else if (event == EV_10clicks) {
- push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- return EVENT_NOT_HANDLED;
-}
-
-
-uint8_t steady_state(Event event, uint16_t arg) {
- #ifdef USE_REVERSING
- static int8_t ramp_direction = 1;
- #endif
- #if (B_TIMING_OFF == B_RELEASE_T)
- // if the user double clicks, we need to abort turning off,
- // and this stores the level to return to
- static uint8_t level_before_off = 0;
- #endif
-
- // make sure ramp globals are correct...
- // ... but they already are; no need to do it here
- //ramp_update_config();
- //nearest_level(1); // same effect, takes less space
-
- uint8_t mode_min = ramp_floor;
- uint8_t mode_max = ramp_ceil;
- uint8_t step_size;
- if (ramp_style) { step_size = ramp_discrete_step_size; }
- else { step_size = 1; }
-
- // turn LED on when we first enter the mode
- if ((event == EV_enter_state) || (event == EV_reenter_state)) {
- #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
- momentary_mode = 0; // 0 = ramping, 1 = strobes
- #endif
- // if we just got back from config mode, go back to memorized level
- if (event == EV_reenter_state) {
- arg = memorized_level;
- }
- // remember this level, unless it's moon or turbo
- if ((arg > mode_min) && (arg < mode_max))
- memorized_level = arg;
- // use the requested level even if not memorized
- arg = nearest_level(arg);
- set_level_and_therm_target(arg);
- #ifdef USE_REVERSING
- ramp_direction = 1;
- #endif
- return MISCHIEF_MANAGED;
- }
- #if (B_TIMING_OFF == B_RELEASE_T)
- // 1 click (early): off, if configured for early response
- else if (event == EV_click1_release) {
- level_before_off = actual_level;
- set_level_and_therm_target(0);
- return MISCHIEF_MANAGED;
- }
- // 2 clicks (early): abort turning off, if configured for early response
- else if (event == EV_click2_press) {
- set_level_and_therm_target(level_before_off);
- return MISCHIEF_MANAGED;
- }
- #endif // if (B_TIMING_OFF == B_RELEASE_T)
- // 1 click: off
- else if (event == EV_1click) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
- // 2 clicks: go to/from highest level
- else if (event == EV_2clicks) {
- uint8_t turbo_level;
- #ifdef USE_SIMPLE_UI
- if (simple_ui_active) { turbo_level = mode_max; }
- else
- #endif
- turbo_level = MAX_LEVEL;
-
- if (actual_level < turbo_level) {
- // true turbo, not the mode-specific ceiling
- set_level_and_therm_target(turbo_level);
- }
- else {
- set_level_and_therm_target(memorized_level);
- }
- return MISCHIEF_MANAGED;
- }
- // hold: change brightness (brighter)
- else if (event == EV_click1_hold) {
- // ramp slower in discrete mode
- if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
- }
- #ifdef USE_REVERSING
- // fix ramp direction on first frame if necessary
- if (!arg) {
- // make it ramp down instead, if already at max
- if (actual_level >= mode_max) { ramp_direction = -1; }
- // make it ramp up if already at min
- // (off->hold->stepped_min->release causes this state)
- else if (actual_level <= mode_min) { ramp_direction = 1; }
- }
- // if the button is stuck, err on the side of safety and ramp down
- else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) {
- ramp_direction = -1;
- }
- // if the button is still stuck, lock the light
- else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) {
- blip();
- set_state(lockout_state, 0);
- }
- memorized_level = nearest_level((int16_t)actual_level \
- + (step_size * ramp_direction));
- #else
- memorized_level = nearest_level((int16_t)actual_level + step_size);
- #endif
- #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE)
- // only blink once for each threshold
- if ((memorized_level != actual_level) && (
- 0 // for easier syntax below
- #ifdef BLINK_AT_RAMP_MIDDLE_1
- || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
- #endif
- #ifdef BLINK_AT_RAMP_MIDDLE_2
- || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
- #endif
- #ifdef BLINK_AT_RAMP_CEIL
- || (memorized_level == mode_max)
- #endif
- #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR)
- || (memorized_level == mode_min)
- #endif
- )) {
- blip();
- }
- #endif
- #if defined(BLINK_AT_STEPS)
- uint8_t foo = ramp_style;
- ramp_style = 1;
- uint8_t nearest = nearest_level((int16_t)actual_level);
- ramp_style = foo;
- // only blink once for each threshold
- if ((memorized_level != actual_level) &&
- (ramp_style == 0) &&
- (memorized_level == nearest)
- )
- {
- blip();
- }
- #endif
- set_level_and_therm_target(memorized_level);
- return MISCHIEF_MANAGED;
- }
- #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL)
- // reverse ramp direction on hold release
- else if (event == EV_click1_hold_release) {
- #ifdef USE_REVERSING
- ramp_direction = -ramp_direction;
- #endif
- #ifdef START_AT_MEMORIZED_LEVEL
- save_config_wl();
- #endif
- return MISCHIEF_MANAGED;
- }
- #endif
- // click, hold: change brightness (dimmer)
- else if (event == EV_click2_hold) {
- #ifdef USE_REVERSING
- ramp_direction = 1;
- #endif
- // ramp slower in discrete mode
- if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
- }
- // TODO? make it ramp up instead, if already at min?
- memorized_level = nearest_level((int16_t)actual_level - step_size);
- #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE)
- // only blink once for each threshold
- if ((memorized_level != actual_level) && (
- 0 // for easier syntax below
- #ifdef BLINK_AT_RAMP_MIDDLE_1
- || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
- #endif
- #ifdef BLINK_AT_RAMP_MIDDLE_2
- || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
- #endif
- #ifdef BLINK_AT_RAMP_FLOOR
- || (memorized_level == mode_min)
- #endif
- )) {
- blip();
- }
- #endif
- #if defined(BLINK_AT_STEPS)
- uint8_t foo = ramp_style;
- ramp_style = 1;
- uint8_t nearest = nearest_level((int16_t)actual_level);
- ramp_style = foo;
- // only blink once for each threshold
- if ((memorized_level != actual_level) &&
- (ramp_style == 0) &&
- (memorized_level == nearest)
- )
- {
- blip();
- }
- #endif
- set_level_and_therm_target(memorized_level);
- return MISCHIEF_MANAGED;
- }
- #ifdef START_AT_MEMORIZED_LEVEL
- // click, release, hold, release: save new ramp level (if necessary)
- else if (event == EV_click2_hold_release) {
- save_config_wl();
- return MISCHIEF_MANAGED;
- }
- #endif
-
- #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
- else if (event == EV_tick) {
- #ifdef USE_REVERSING
- // un-reverse after 1 second
- if (arg == TICKS_PER_SECOND) ramp_direction = 1;
- #endif
- #ifdef USE_SET_LEVEL_GRADUALLY
- int16_t diff = gradual_target - actual_level;
- static uint16_t ticks_since_adjust = 0;
- ticks_since_adjust++;
- if (diff) {
- uint16_t ticks_per_adjust = 256;
- if (diff < 0) {
- //diff = -diff;
- if (actual_level > THERM_FASTER_LEVEL) {
- #ifdef THERM_HARD_TURBO_DROP
- ticks_per_adjust >>= 2;
- #endif
- ticks_per_adjust >>= 2;
- }
- } else {
- // rise at half speed
- ticks_per_adjust <<= 1;
- }
- while (diff) {
- ticks_per_adjust >>= 1;
- //diff >>= 1;
- diff /= 2; // because shifting produces weird behavior
- }
- if (ticks_since_adjust > ticks_per_adjust)
- {
- gradual_tick();
- ticks_since_adjust = 0;
- }
- }
- #endif // ifdef USE_SET_LEVEL_GRADUALLY
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_THERMAL_REGULATION
- // overheating: drop by an amount proportional to how far we are above the ceiling
- else if (event == EV_temperature_high) {
- #if 0
- blip();
- #endif
- #ifdef THERM_HARD_TURBO_DROP
- //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;
- #else
- set_level_and_therm_target(THERM_FASTER_LEVEL);
- #endif
- } else
- #endif
- if (actual_level > MIN_THERM_STEPDOWN) {
- int16_t stepdown = actual_level - arg;
- if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN;
- else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL;
- #ifdef USE_SET_LEVEL_GRADUALLY
- set_level_gradually(stepdown);
- #else
- set_level(stepdown);
- #endif
- }
- return MISCHIEF_MANAGED;
- }
- // underheating: increase slowly if we're lower than the target
- // (proportional to how low we are)
- else if (event == EV_temperature_low) {
- #if 0
- blip();
- #endif
- if (actual_level < target_level) {
- //int16_t stepup = actual_level + (arg>>1);
- int16_t stepup = actual_level + arg;
- if (stepup > target_level) stepup = target_level;
- else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
- #ifdef USE_SET_LEVEL_GRADUALLY
- set_level_gradually(stepup);
- #else
- set_level(stepup);
- #endif
- }
- return MISCHIEF_MANAGED;
- }
- #ifdef USE_SET_LEVEL_GRADUALLY
- // temperature is within target window
- // (so stop trying to adjust output)
- else if (event == EV_temperature_okay) {
- // if we're still adjusting output... stop after the current step
- if (gradual_target > actual_level)
- gradual_target = actual_level + 1;
- else if (gradual_target < actual_level)
- gradual_target = actual_level - 1;
- return MISCHIEF_MANAGED;
- }
- #endif // ifdef USE_SET_LEVEL_GRADUALLY
- #endif // ifdef USE_THERMAL_REGULATION
-
- ////////// Every action below here is blocked in the simple UI //////////
- #ifdef USE_SIMPLE_UI
- if (simple_ui_active) {
- return EVENT_NOT_HANDLED;
- }
- #endif
-
- // 3 clicks: toggle smooth vs discrete ramping
- else if (event == EV_3clicks) {
- ramp_style = !ramp_style;
- save_config();
- #ifdef START_AT_MEMORIZED_LEVEL
- save_config_wl();
- #endif
- blip();
- memorized_level = nearest_level(actual_level);
- set_level_and_therm_target(memorized_level);
- return MISCHIEF_MANAGED;
- }
-
- #ifdef USE_MANUAL_MEMORY
- else if (event == EV_5clicks) {
- manual_memory = actual_level;
- save_config();
- blip();
- return MISCHIEF_MANAGED;
- }
- else if (event == EV_click5_hold) {
- if (0 == arg) {
- manual_memory = 0;
- save_config();
- blip();
- }
- return MISCHIEF_MANAGED;
- }
- #endif
-
- #ifdef USE_RAMP_CONFIG
- // 7 clicks: configure this ramp mode
- else if (event == EV_7clicks) {
- push_state(ramp_config_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- return EVENT_NOT_HANDLED;
-}
-
-
-#ifdef USE_SOS_MODE
-#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
-uint8_t sos_state(Event event, uint16_t arg) {
- // 1 click: off
- if (event == EV_1click) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
- // 2 clicks: next mode
- else if (event == EV_2clicks) {
- #ifdef USE_THERMAL_REGULATION
- set_state(tempcheck_state, 0);
- #else
- set_state(battcheck_state, 0);
- #endif
- return MISCHIEF_MANAGED;
- }
- return EVENT_NOT_HANDLED;
-}
+#ifdef USE_GOODNIGHT_MODE
+#include "goodnight-mode.c"
#endif
-void sos_blink(uint8_t num, uint8_t dah) {
- #define DIT_LENGTH 200
- for (; num > 0; num--) {
- set_level(memorized_level);
- nice_delay_ms(DIT_LENGTH);
- if (dah) { // dah is 3X as long as a dit
- nice_delay_ms(DIT_LENGTH*2);
- }
- set_level(0);
- // one "off" dit between blinks
- nice_delay_ms(DIT_LENGTH);
- }
- // three "off" dits (or one "dah") between letters
- // (except for SOS, which is collectively treated as a single "letter")
- //nice_delay_ms(DIT_LENGTH*2);
-}
-
-inline void sos_mode_iter() {
- // one iteration of main loop()
- //nice_delay_ms(1000);
- sos_blink(3, 0); // S
- sos_blink(3, 1); // O
- sos_blink(3, 0); // S
- nice_delay_ms(2000);
-}
-#endif // #ifdef USE_SOS_MODE
-
-
-#ifdef USE_BATTCHECK
-uint8_t battcheck_state(Event event, uint16_t arg) {
- ////////// Every action below here is blocked in the simple UI //////////
- #ifdef USE_SIMPLE_UI
- if (simple_ui_active) {
- return EVENT_NOT_HANDLED;
- }
- #endif
-
- // 1 click: off
- if (event == EV_1click) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
- #ifdef USE_GOODNIGHT_MODE
- // 2 clicks: goodnight mode
- else if (event == EV_2clicks) {
- set_state(goodnight_state, 0);
- return MISCHIEF_MANAGED;
- }
- #elif defined(USE_BEACON_MODE)
- // 2 clicks: beacon mode
- else if (event == EV_2clicks) {
- set_state(beacon_state, 0);
- return MISCHIEF_MANAGED;
- }
- #elif defined(USE_THERMAL_REGULATION)
- // 2 clicks: tempcheck mode
- else if (event == EV_2clicks) {
- set_state(tempcheck_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- return EVENT_NOT_HANDLED;
-}
+#ifdef USE_BEACON_MODE
+#include "beacon.c"
#endif
-
#ifdef USE_THERMAL_REGULATION
-uint8_t tempcheck_state(Event event, uint16_t arg) {
- // 1 click: off
- if (event == EV_1click) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
- #ifdef USE_BATTCHECK
- // 2 clicks: battcheck mode
- else if (event == EV_2clicks) {
- set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- // 7 clicks: thermal config mode
- else if (event == EV_7clicks) {
- push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
- }
- return EVENT_NOT_HANDLED;
-}
+#include "tempcheck-mode.c"
#endif
-
-#ifdef USE_GOODNIGHT_MODE
-#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL)
-uint8_t goodnight_state(Event event, uint16_t arg) {
- static uint16_t ticks_since_stepdown = 0;
- // blink on start
- if (event == EV_enter_state) {
- ticks_since_stepdown = 0;
- blink_confirm(2);
- set_level(GOODNIGHT_LEVEL);
- return MISCHIEF_MANAGED;
- }
- // 1 click: off
- else if (event == EV_1click) {
- set_state(off_state, 0);
- return MISCHIEF_MANAGED;
- }
- // 2 clicks: next mode
- else if (event == EV_2clicks) {
- #ifdef USE_BEACON_MODE
- set_state(beacon_state, 0);
- #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP)
- set_state(sos_state, 0);
- #elif defined(USE_THERMAL_REGULATION)
- set_state(tempcheck_state, 0);
- #endif
- return MISCHIEF_MANAGED;
- }
- // tick: step down (maybe) or off (maybe)
- else if (event == EV_tick) {
- if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) {
- ticks_since_stepdown = 0;
- set_level(actual_level-1);
- if (! actual_level) {
- #if 0 // test blink, to help measure timing
- set_level(MAX_LEVEL>>2);
- delay_4ms(8/2);
- set_level(0);
- #endif
- set_state(off_state, 0);
- }
- }
- return MISCHIEF_MANAGED;
- }
- return EVENT_NOT_HANDLED;
-}
+#ifdef USE_LOCKOUT_MODE
+#include "lockout.c"
#endif
-
#ifdef USE_MOMENTARY_MODE
-uint8_t momentary_state(Event event, uint16_t arg) {
- // TODO: momentary strobe here? (for light painting)
-
- // init strobe mode, if relevant
- #ifdef USE_STROBE_STATE
- if ((event == EV_enter_state) && (momentary_mode == 1)) {
- strobe_state(event, arg);
- }
- #endif
-
- // light up when the button is pressed; go dark otherwise
- // button is being held
- if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
- momentary_active = 1;
- // 0 = ramping, 1 = strobes
- if (momentary_mode == 0) {
- set_level(memorized_level);
- }
- return MISCHIEF_MANAGED;
- }
- // button was released
- else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
- momentary_active = 0;
- set_level(0);
- //go_to_standby = 1; // sleep while light is off
- return MISCHIEF_MANAGED;
- }
-
- // Sleep, dammit! (but wait a few seconds first)
- // (because standby mode uses such little power that it can interfere
- // with exiting via tailcap loosen+tighten unless you leave power
- // disconnected for several seconds, so we want to be awake when that
- // happens to speed up the process)
- else if (event == EV_tick) {
- #ifdef USE_STROBE_STATE
- if (momentary_active) {
- // 0 = ramping, 1 = strobes
- if (momentary_mode == 1) {
- return strobe_state(event, arg);
- }
- }
- else {
- #endif
- if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds
- go_to_standby = 1; // sleep while light is off
- // turn off lighted button
- #ifdef USE_INDICATOR_LED
- indicator_led(0);
- #elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(0, 0);
- #endif
- }
- #ifdef USE_STROBE_STATE
- }
- #endif
- return MISCHIEF_MANAGED;
- }
-
- return EVENT_NOT_HANDLED;
-}
+#include "momentary-mode.c"
#endif
-
-#ifdef USE_VERSION_CHECK
-uint8_t version_check_state(Event event, uint16_t arg) {
- return EVENT_NOT_HANDLED;
-}
+#ifdef USE_TINT_RAMPING
+#include "tint-ramping.c"
#endif
-
-#ifdef USE_RAMP_CONFIG
-void ramp_config_save() {
- // parse values
- uint8_t val;
- uint8_t style = ramp_style;
- // TODO: detect if we're configuring the simple UI
-
- val = config_state_values[0];
- if (val) { ramp_floors[style] = val; }
-
- val = config_state_values[1];
- if (val) { ramp_ceils[style] = MAX_LEVEL + 1 - val; }
-
- if (ramp_style) { // discrete / stepped ramp
- val = config_state_values[2];
- if (val) ramp_stepss[style] = val;
- }
-}
-
-uint8_t ramp_config_state(Event event, uint16_t arg) {
- uint8_t num_config_steps;
- num_config_steps = 2 + ramp_style;
- return config_state_base(event, arg,
- num_config_steps, ramp_config_save);
-}
-#endif // #ifdef USE_RAMP_CONFIG
-
-
-#ifdef USE_THERMAL_REGULATION
-void thermal_config_save() {
- // parse values
- uint8_t val;
-
- // calibrate room temperature
- val = config_state_values[0];
- if (val) {
- int8_t rawtemp = temperature - therm_cal_offset;
- therm_cal_offset = val - rawtemp;
- reset_thermal_history = 1; // invalidate all recent temperature data
- }
-
- val = config_state_values[1];
- if (val) {
- // set maximum heat limit
- therm_ceil = 30 + val - 1;
- }
- if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
-}
-
-uint8_t thermal_config_state(Event event, uint16_t arg) {
- return config_state_base(event, arg,
- 2, thermal_config_save);
-}
-#endif // #ifdef USE_THERMAL_REGULATION
-
-
-// find the ramp level closest to the target,
-// using only the levels which are allowed in the current state
-uint8_t nearest_level(int16_t target) {
- ramp_update_config();
-
- // bounds check
- // using int16_t here saves us a bunch of logic elsewhere,
- // by allowing us to correct for numbers < 0 or > 255 in one central place
- uint8_t mode_min = ramp_floor;
- uint8_t mode_max = ramp_ceil;
- if (target < mode_min) return mode_min;
- if (target > mode_max) return mode_max;
- // the rest isn't relevant for smooth ramping
- if (! ramp_style) return target;
-
- uint8_t ramp_range = mode_max - mode_min;
- uint8_t num_steps = ramp_stepss[1 + simple_ui_active];
- ramp_discrete_step_size = ramp_range / (num_steps-1);
- uint8_t this_level = mode_min;
-
- for(uint8_t i=0; i<num_steps; i++) {
- this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1));
- int16_t diff = target - this_level;
- if (diff < 0) diff = -diff;
- if (diff <= (ramp_discrete_step_size>>1))
- return this_level;
- }
- return this_level;
-}
-
-// ensure ramp globals are correct
-void ramp_update_config() {
- uint8_t which = ramp_style;
- if (simple_ui_active) { which = 2; }
-
- ramp_floor = ramp_floors[which];
- ramp_ceil = ramp_ceils[which];
-}
-
-#ifdef USE_THERMAL_REGULATION
-void set_level_and_therm_target(uint8_t level) {
- target_level = level;
- set_level(level);
-}
+#ifdef USE_FACTORY_RESET
+#include "factory-reset.c"
#endif
-void blink_confirm(uint8_t num) {
- for (; num>0; num--) {
- set_level(MAX_LEVEL/4);
- delay_4ms(10/4);
- set_level(0);
- // TODO: only do this delay if num > 1
- delay_4ms(100/4);
- }
-}
+#ifdef USE_STROBE_STATE
+#include "strobes.c"
+#endif
-// Just go dark for a moment to indicate to user that something happened
-void blip() {
- uint8_t temp = actual_level;
- set_level(0);
- delay_4ms(3);
- set_level(temp);
-}
+#ifdef USE_SOS_MODE
+#include "sos-mode.c"
+#endif
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
@@ -1527,113 +397,6 @@ void rgb_led_voltage_readout(uint8_t bright) {
#endif
-void load_config() {
- if (load_eeprom()) {
- ramp_style = eeprom[ramp_style_e];
- #ifdef USE_RAMP_CONFIG
- ramp_floors[0] = eeprom[ramp_smooth_floor_e];
- ramp_ceils[0] = eeprom[ramp_smooth_ceil_e];
- ramp_floors[1] = eeprom[ramp_discrete_floor_e];
- ramp_ceils[1] = eeprom[ramp_discrete_ceil_e];
- ramp_stepss[1] = eeprom[ramp_discrete_steps_e];
- #endif
- #ifdef USE_MANUAL_MEMORY
- manual_memory = eeprom[manual_memory_e];
- #endif
- #ifdef USE_TINT_RAMPING
- tint = eeprom[tint_e];
- #endif
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl?
- strobe_delays[0] = eeprom[strobe_delays_0_e];
- strobe_delays[1] = eeprom[strobe_delays_1_e];
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- bike_flasher_brightness = eeprom[bike_flasher_brightness_e];
- #endif
- #ifdef USE_BEACON_MODE
- beacon_seconds = eeprom[beacon_seconds_e];
- #endif
- #ifdef USE_SIMPLE_UI
- simple_ui_active = eeprom[simple_ui_active_e];
- ramp_floors[2] = eeprom[simple_ui_floor_e];
- ramp_ceils[2] = eeprom[simple_ui_ceil_e];
- ramp_stepss[2] = eeprom[simple_ui_steps_e];
- #endif
- #ifdef USE_THERMAL_REGULATION
- therm_ceil = eeprom[therm_ceil_e];
- therm_cal_offset = eeprom[therm_cal_offset_e];
- #endif
- #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()) {
- memorized_level = eeprom_wl[0];
- }
- #endif
-}
-
-void save_config() {
- eeprom[ramp_style_e] = ramp_style;
- #ifdef USE_RAMP_CONFIG
- eeprom[ramp_smooth_floor_e] = ramp_floors[0];
- eeprom[ramp_smooth_ceil_e] = ramp_ceils[0];
- eeprom[ramp_discrete_floor_e] = ramp_floors[1];
- eeprom[ramp_discrete_ceil_e] = ramp_ceils[1];
- eeprom[ramp_discrete_steps_e] = ramp_stepss[1];
- #endif
- #ifdef USE_MANUAL_MEMORY
- eeprom[manual_memory_e] = manual_memory;
- #endif
- #ifdef USE_TINT_RAMPING
- eeprom[tint_e] = tint;
- #endif
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl?
- eeprom[strobe_delays_0_e] = strobe_delays[0];
- eeprom[strobe_delays_1_e] = strobe_delays[1];
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- eeprom[bike_flasher_brightness_e] = bike_flasher_brightness;
- #endif
- #ifdef USE_BEACON_MODE
- eeprom[beacon_seconds_e] = beacon_seconds;
- #endif
- #ifdef USE_SIMPLE_UI
- eeprom[simple_ui_active_e] = simple_ui_active;
- eeprom[simple_ui_floor_e] = ramp_floors[2];
- eeprom[simple_ui_ceil_e] = ramp_ceils[2];
- eeprom[simple_ui_steps_e] = ramp_stepss[2];
- #endif
- #ifdef USE_THERMAL_REGULATION
- eeprom[therm_ceil_e] = therm_ceil;
- eeprom[therm_cal_offset_e] = therm_cal_offset;
- #endif
- #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();
-}
-
-#ifdef START_AT_MEMORIZED_LEVEL
-void save_config_wl() {
- eeprom_wl[0] = memorized_level;
- save_eeprom_wl();
-}
-#endif
-
-
void low_voltage() {
StatePtr state = current_state;
@@ -1715,19 +478,9 @@ void loop() {
#ifdef USE_VERSION_CHECK
else if (state == version_check_state) {
- for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
- blink_digit(pgm_read_byte(version_number + i) - '0');
- nice_delay_ms(300);
- }
- // FIXME: when user interrupts with button, "off" takes an extra click
- // before it'll turn back on, because the click to cancel gets sent
- // to the "off" state instead of version_check_state
- //while (button_is_pressed()) {}
- //empty_event_sequence();
-
- set_state(off_state, 0);
+ version_check_iter();
}
- #endif // #ifdef USE_VERSION_CHECK
+ #endif
#ifdef USE_STROBE_STATE
else if ((state == strobe_state)
@@ -1736,33 +489,7 @@ void loop() {
|| ((state == momentary_state) && (momentary_mode == 1) && (momentary_active))
#endif
) {
- uint8_t st = strobe_type;
-
- switch(st) {
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- #ifdef USE_PARTY_STROBE_MODE
- case party_strobe_e:
- #endif
- #ifdef USE_TACTICAL_STROBE_MODE
- case tactical_strobe_e:
- #endif
- party_tactical_strobe_mode_iter(st);
- break;
- #endif
-
- #ifdef USE_LIGHTNING_MODE
- case lightning_storm_e:
- lightning_storm_iter();
- break;
- #endif
-
- #ifdef USE_BIKE_FLASHER_MODE
- case bike_flasher_e:
- bike_flasher_iter();
- break;
- #endif
- }
-
+ strobe_state_iter();
}
#endif // #ifdef USE_STROBE_STATE
diff --git a/spaghetti-monster/anduril/battcheck-mode-fsm.h b/spaghetti-monster/anduril/battcheck-mode-fsm.h
new file mode 100644
index 0000000..8f19e12
--- /dev/null
+++ b/spaghetti-monster/anduril/battcheck-mode-fsm.h
@@ -0,0 +1,26 @@
+/*
+ * battcheck-mode-fsm.h: FSM config for battery check mode in 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/>.
+ */
+
+#ifndef BATTCHECK_MODE_FSM_H
+#define BATTCHECK_MODE_FSM_H
+
+#define USE_BATTCHECK
+
+
+#endif
diff --git a/spaghetti-monster/anduril/battcheck-mode.c b/spaghetti-monster/anduril/battcheck-mode.c
new file mode 100644
index 0000000..3755249
--- /dev/null
+++ b/spaghetti-monster/anduril/battcheck-mode.c
@@ -0,0 +1,62 @@
+/*
+ * battcheck-mode.c: Battery check mode 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/>.
+ */
+
+#ifndef BATTCHECK_MODE_C
+#define BATTCHECK_MODE_C
+
+#include "battcheck-mode.h"
+
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ ////////// Every action below here is blocked in the simple UI //////////
+ #ifdef USE_SIMPLE_UI
+ if (simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef USE_GOODNIGHT_MODE
+ // 2 clicks: goodnight mode
+ else if (event == EV_2clicks) {
+ set_state(goodnight_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #elif defined(USE_BEACON_MODE)
+ // 2 clicks: beacon mode
+ else if (event == EV_2clicks) {
+ set_state(beacon_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #elif defined(USE_THERMAL_REGULATION)
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ set_state(tempcheck_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/battcheck-mode.h b/spaghetti-monster/anduril/battcheck-mode.h
new file mode 100644
index 0000000..f57fa74
--- /dev/null
+++ b/spaghetti-monster/anduril/battcheck-mode.h
@@ -0,0 +1,26 @@
+/*
+ * battcheck-mode.h: Battery check mode 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/>.
+ */
+
+#ifndef BATTCHECK_MODE_H
+#define BATTCHECK_MODE_H
+
+uint8_t battcheck_state(Event event, uint16_t arg);
+
+
+#endif
diff --git a/spaghetti-monster/anduril/cfg-default.h b/spaghetti-monster/anduril/cfg-default.h
index 1405422..ee76c6e 100644
--- a/spaghetti-monster/anduril/cfg-default.h
+++ b/spaghetti-monster/anduril/cfg-default.h
@@ -58,11 +58,16 @@
// or the user can go back to automatic with click-click-click-click-hold)
#define USE_MANUAL_MEMORY
+// enable the battery check mode (shows remaining charge)
+#define USE_BATTCHECK_MODE
// battery readout style (pick one)
#define BATTCHECK_VpT
//#define BATTCHECK_8bars // FIXME: breaks build
//#define BATTCHECK_4bars // FIXME: breaks build
+// enable a mode for locking the light for safe carry
+#define USE_LOCKOUT_MODE
+
// enable/disable various strobe modes
#define USE_BIKE_FLASHER_MODE
#define USE_PARTY_STROBE_MODE
diff --git a/spaghetti-monster/anduril/config-mode.h b/spaghetti-monster/anduril/config-mode.h
index 7bf51fc..81abf2c 100644
--- a/spaghetti-monster/anduril/config-mode.h
+++ b/spaghetti-monster/anduril/config-mode.h
@@ -20,8 +20,6 @@
#ifndef CONFIG_MODE_H
#define CONFIG_MODE_H
-#include "fsm-events.h"
-
#define MAX_CONFIG_VALUES 3
uint8_t config_state_values[MAX_CONFIG_VALUES];
diff --git a/spaghetti-monster/anduril/goodnight-mode.c b/spaghetti-monster/anduril/goodnight-mode.c
new file mode 100644
index 0000000..2fe90e9
--- /dev/null
+++ b/spaghetti-monster/anduril/goodnight-mode.c
@@ -0,0 +1,72 @@
+/*
+ * goodnight-mode.c: Goodnight / sunset mode 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/>.
+ */
+
+#ifndef GOODNIGHT_MODE_C
+#define GOODNIGHT_MODE_C
+
+#include "goodnight-mode.h"
+
+#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL)
+uint8_t goodnight_state(Event event, uint16_t arg) {
+ static uint16_t ticks_since_stepdown = 0;
+ // blink on start
+ if (event == EV_enter_state) {
+ ticks_since_stepdown = 0;
+ blink_confirm(2);
+ set_level(GOODNIGHT_LEVEL);
+ return MISCHIEF_MANAGED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks: next mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_BEACON_MODE
+ set_state(beacon_state, 0);
+ #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ set_state(sos_state, 0);
+ #elif defined(USE_THERMAL_REGULATION)
+ set_state(tempcheck_state, 0);
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+ // tick: step down (maybe) or off (maybe)
+ else if (event == EV_tick) {
+ if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) {
+ ticks_since_stepdown = 0;
+ set_level(actual_level-1);
+ if (! actual_level) {
+ #if 0 // test blink, to help measure timing
+ set_level(MAX_LEVEL>>2);
+ delay_4ms(8/2);
+ set_level(0);
+ #endif
+ set_state(off_state, 0);
+ }
+ }
+ return MISCHIEF_MANAGED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/goodnight-mode.h b/spaghetti-monster/anduril/goodnight-mode.h
new file mode 100644
index 0000000..3bb5272
--- /dev/null
+++ b/spaghetti-monster/anduril/goodnight-mode.h
@@ -0,0 +1,27 @@
+/*
+ * goodnight-mode.h: Goodnight / sunset 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/>.
+ */
+
+#ifndef GOODNIGHT_MODE_H
+#define GOODNIGHT_MODE_H
+
+// 1-hour ramp down from low, then automatic off
+uint8_t goodnight_state(Event event, uint16_t arg);
+
+
+#endif
diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h
new file mode 100644
index 0000000..7760048
--- /dev/null
+++ b/spaghetti-monster/anduril/load-save-config-fsm.h
@@ -0,0 +1,80 @@
+/*
+ * load-save-config-fsm.h: FSM config for eeprom configuration in 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/>.
+ */
+
+#ifndef LOAD_SAVE_CONFIG_FSM_H
+#define LOAD_SAVE_CONFIG_FSM_H
+
+// auto-detect how many eeprom bytes
+#define USE_EEPROM
+typedef enum {
+ ramp_style_e,
+ #ifdef USE_RAMP_CONFIG
+ ramp_smooth_floor_e,
+ ramp_smooth_ceil_e,
+ ramp_discrete_floor_e,
+ ramp_discrete_ceil_e,
+ ramp_discrete_steps_e,
+ #endif
+ #ifdef USE_MANUAL_MEMORY
+ manual_memory_e,
+ #endif
+ #ifdef USE_TINT_RAMPING
+ tint_e,
+ #endif
+ #ifdef USE_STROBE_STATE
+ strobe_type_e,
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ strobe_delays_0_e,
+ strobe_delays_1_e,
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ bike_flasher_brightness_e,
+ #endif
+ #ifdef USE_BEACON_MODE
+ beacon_seconds_e,
+ #endif
+ #ifdef USE_SIMPLE_UI
+ simple_ui_active_e,
+ simple_ui_floor_e,
+ simple_ui_ceil_e,
+ simple_ui_steps_e,
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ therm_ceil_e,
+ therm_cal_offset_e,
+ #endif
+ #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
+
+#ifdef START_AT_MEMORIZED_LEVEL
+#define USE_EEPROM_WL
+#define EEPROM_WL_BYTES 1
+#endif
+
+
+#endif
diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c
new file mode 100644
index 0000000..8cfb69d
--- /dev/null
+++ b/spaghetti-monster/anduril/load-save-config.c
@@ -0,0 +1,134 @@
+/*
+ * load-save-config.c: Load/save/eeprom 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/>.
+ */
+
+#ifndef LOAD_SAVE_CONFIG_C
+#define LOAD_SAVE_CONFIG_C
+
+#include "load-save-config-fsm.h"
+#include "load-save-config.h"
+
+void load_config() {
+ if (load_eeprom()) {
+ ramp_style = eeprom[ramp_style_e];
+ #ifdef USE_RAMP_CONFIG
+ ramp_floors[0] = eeprom[ramp_smooth_floor_e];
+ ramp_ceils[0] = eeprom[ramp_smooth_ceil_e];
+ ramp_floors[1] = eeprom[ramp_discrete_floor_e];
+ ramp_ceils[1] = eeprom[ramp_discrete_ceil_e];
+ ramp_stepss[1] = eeprom[ramp_discrete_steps_e];
+ #endif
+ #ifdef USE_MANUAL_MEMORY
+ manual_memory = eeprom[manual_memory_e];
+ #endif
+ #ifdef USE_TINT_RAMPING
+ tint = eeprom[tint_e];
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl?
+ strobe_delays[0] = eeprom[strobe_delays_0_e];
+ strobe_delays[1] = eeprom[strobe_delays_1_e];
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ bike_flasher_brightness = eeprom[bike_flasher_brightness_e];
+ #endif
+ #ifdef USE_BEACON_MODE
+ beacon_seconds = eeprom[beacon_seconds_e];
+ #endif
+ #ifdef USE_SIMPLE_UI
+ simple_ui_active = eeprom[simple_ui_active_e];
+ ramp_floors[2] = eeprom[simple_ui_floor_e];
+ ramp_ceils[2] = eeprom[simple_ui_ceil_e];
+ ramp_stepss[2] = eeprom[simple_ui_steps_e];
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ therm_ceil = eeprom[therm_ceil_e];
+ therm_cal_offset = eeprom[therm_cal_offset_e];
+ #endif
+ #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()) {
+ memorized_level = eeprom_wl[0];
+ }
+ #endif
+}
+
+void save_config() {
+ eeprom[ramp_style_e] = ramp_style;
+ #ifdef USE_RAMP_CONFIG
+ eeprom[ramp_smooth_floor_e] = ramp_floors[0];
+ eeprom[ramp_smooth_ceil_e] = ramp_ceils[0];
+ eeprom[ramp_discrete_floor_e] = ramp_floors[1];
+ eeprom[ramp_discrete_ceil_e] = ramp_ceils[1];
+ eeprom[ramp_discrete_steps_e] = ramp_stepss[1];
+ #endif
+ #ifdef USE_MANUAL_MEMORY
+ eeprom[manual_memory_e] = manual_memory;
+ #endif
+ #ifdef USE_TINT_RAMPING
+ eeprom[tint_e] = tint;
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl?
+ eeprom[strobe_delays_0_e] = strobe_delays[0];
+ eeprom[strobe_delays_1_e] = strobe_delays[1];
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ eeprom[bike_flasher_brightness_e] = bike_flasher_brightness;
+ #endif
+ #ifdef USE_BEACON_MODE
+ eeprom[beacon_seconds_e] = beacon_seconds;
+ #endif
+ #ifdef USE_SIMPLE_UI
+ eeprom[simple_ui_active_e] = simple_ui_active;
+ eeprom[simple_ui_floor_e] = ramp_floors[2];
+ eeprom[simple_ui_ceil_e] = ramp_ceils[2];
+ eeprom[simple_ui_steps_e] = ramp_stepss[2];
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ eeprom[therm_ceil_e] = therm_ceil;
+ eeprom[therm_cal_offset_e] = therm_cal_offset;
+ #endif
+ #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();
+}
+
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl() {
+ eeprom_wl[0] = memorized_level;
+ save_eeprom_wl();
+}
+#endif
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/load-save-config.h b/spaghetti-monster/anduril/load-save-config.h
new file mode 100644
index 0000000..29c1a69
--- /dev/null
+++ b/spaghetti-monster/anduril/load-save-config.h
@@ -0,0 +1,31 @@
+/*
+ * load-save-config.h: Load/save/eeprom 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/>.
+ */
+
+#ifndef LOAD_SAVE_CONFIG_H
+#define LOAD_SAVE_CONFIG_H
+
+// remember stuff even after battery was changed
+void load_config();
+void save_config();
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl();
+#endif
+
+
+#endif
diff --git a/spaghetti-monster/anduril/misc.c b/spaghetti-monster/anduril/misc.c
new file mode 100644
index 0000000..a03225c
--- /dev/null
+++ b/spaghetti-monster/anduril/misc.c
@@ -0,0 +1,45 @@
+/*
+ * misc.c: Misc extra 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/>.
+ */
+
+#ifndef MISC_C
+#define MISC_C
+
+#include "misc.h"
+
+void blink_confirm(uint8_t num) {
+ for (; num>0; num--) {
+ set_level(MAX_LEVEL/4);
+ delay_4ms(10/4);
+ set_level(0);
+ // TODO: only do this delay if num > 1
+ delay_4ms(100/4);
+ }
+}
+
+// Just go dark for a moment to indicate to user that something happened
+void blip() {
+ uint8_t temp = actual_level;
+ set_level(0);
+ delay_4ms(3);
+ set_level(temp);
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/misc.h b/spaghetti-monster/anduril/misc.h
new file mode 100644
index 0000000..e582a71
--- /dev/null
+++ b/spaghetti-monster/anduril/misc.h
@@ -0,0 +1,27 @@
+/*
+ * misc.h: Misc extra 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/>.
+ */
+
+#ifndef MISC_H
+#define MISC_H
+
+void blink_confirm(uint8_t num);
+void blip();
+
+
+#endif
diff --git a/spaghetti-monster/anduril/momentary-mode.c b/spaghetti-monster/anduril/momentary-mode.c
new file mode 100644
index 0000000..8d111c7
--- /dev/null
+++ b/spaghetti-monster/anduril/momentary-mode.c
@@ -0,0 +1,86 @@
+/*
+ * momentary-mode.c: Momentary mode 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/>.
+ */
+
+#ifndef MOMENTARY_MODE_C
+#define MOMENTARY_MODE_C
+
+#include "momentary-mode.h"
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+ // init strobe mode, if relevant
+ #ifdef USE_STROBE_STATE
+ if ((event == EV_enter_state) && (momentary_mode == 1)) {
+ strobe_state(event, arg);
+ }
+ #endif
+
+ // light up when the button is pressed; go dark otherwise
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ momentary_active = 1;
+ // 0 = ramping, 1 = strobes
+ if (momentary_mode == 0) {
+ set_level(memorized_level);
+ }
+ return MISCHIEF_MANAGED;
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ momentary_active = 0;
+ set_level(0);
+ //go_to_standby = 1; // sleep while light is off
+ return MISCHIEF_MANAGED;
+ }
+
+ // Sleep, dammit! (but wait a few seconds first)
+ // (because standby mode uses such little power that it can interfere
+ // with exiting via tailcap loosen+tighten unless you leave power
+ // disconnected for several seconds, so we want to be awake when that
+ // happens to speed up the process)
+ else if (event == EV_tick) {
+ #ifdef USE_STROBE_STATE
+ if (momentary_active) {
+ // 0 = ramping, 1 = strobes
+ if (momentary_mode == 1) {
+ return strobe_state(event, arg);
+ }
+ }
+ else {
+ #endif
+ if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds
+ go_to_standby = 1; // sleep while light is off
+ // turn off lighted button
+ #ifdef USE_INDICATOR_LED
+ indicator_led(0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(0, 0);
+ #endif
+ }
+ #ifdef USE_STROBE_STATE
+ }
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/momentary-mode.h b/spaghetti-monster/anduril/momentary-mode.h
new file mode 100644
index 0000000..c5ccf0f
--- /dev/null
+++ b/spaghetti-monster/anduril/momentary-mode.h
@@ -0,0 +1,29 @@
+/*
+ * momentary-mode.h: Momentary mode 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/>.
+ */
+
+#ifndef MOMENTARY_MODE_H
+#define MOMENTARY_MODE_H
+
+// momentary / signalling mode
+uint8_t momentary_state(Event event, uint16_t arg);
+uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
+uint8_t momentary_active = 0; // boolean, true if active *right now*
+
+
+#endif
diff --git a/spaghetti-monster/anduril/off-state.c b/spaghetti-monster/anduril/off-state.c
new file mode 100644
index 0000000..9bbdc81
--- /dev/null
+++ b/spaghetti-monster/anduril/off-state.c
@@ -0,0 +1,261 @@
+/*
+ * off-state.c: "Off" state 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/>.
+ */
+
+#ifndef OFF_STATE_C
+#define OFF_STATE_C
+
+#include "off-state.h"
+
+uint8_t off_state(Event event, uint16_t arg) {
+ // turn emitter off when entering state
+ if (event == EV_enter_state) {
+ 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)
+ // (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 > 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) || 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
+ #if (B_TIMING_ON == B_PRESS_T)
+ // hold (initially): go to lowest level (floor), but allow abort for regular click
+ else if (event == EV_click1_press) {
+ set_level(nearest_level(1));
+ return MISCHIEF_MANAGED;
+ }
+ #endif // B_TIMING_ON == B_PRESS_T
+ // hold: go to lowest level
+ else if (event == EV_click1_hold) {
+ #if (B_TIMING_ON == B_PRESS_T)
+ #ifdef MOON_TIMING_HINT
+ if (arg == 0) {
+ // let the user know they can let go now to stay at moon
+ blip();
+ } else
+ #endif
+ #else // B_RELEASE_T or B_TIMEOUT_T
+ set_level(nearest_level(1));
+ #endif
+ // don't start ramping immediately;
+ // give the user time to release at moon level
+ //if (arg >= HOLD_TIMEOUT) { // smaller
+ if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
+ set_state(steady_state, 1);
+ }
+ return MISCHIEF_MANAGED;
+ }
+ // hold, release quickly: go to lowest level (floor)
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return MISCHIEF_MANAGED;
+ }
+ #if (B_TIMING_ON != B_TIMEOUT_T)
+ // 1 click (before timeout): go to memorized level, but allow abort for double click
+ else if (event == EV_click1_release) {
+ #ifdef USE_MANUAL_MEMORY
+ if (manual_memory)
+ set_level(nearest_level(manual_memory));
+ else
+ #endif
+ set_level(nearest_level(memorized_level));
+ return MISCHIEF_MANAGED;
+ }
+ #endif // if (B_TIMING_ON != B_TIMEOUT_T)
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ #ifdef USE_MANUAL_MEMORY
+ if (manual_memory)
+ set_state(steady_state, manual_memory);
+ else
+ #endif
+ set_state(steady_state, memorized_level);
+ return MISCHIEF_MANAGED;
+ }
+ // click, hold: go to highest level (ceiling) (for ramping down)
+ else if (event == EV_click2_hold) {
+ set_state(steady_state, MAX_LEVEL);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks: highest mode (ceiling)
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return MISCHIEF_MANAGED;
+ }
+ // 3 clicks (initial press): off, to prep for later events
+ else if (event == EV_click3_press) {
+ set_level(0);
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef USE_BATTCHECK
+ // 3 clicks: battcheck mode / blinky mode group 1
+ else if (event == EV_3clicks) {
+ set_state(battcheck_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #ifdef USE_LOCKOUT_MODE
+ // 4 clicks: soft lockout
+ else if (event == EV_4clicks) {
+ blink_confirm(2);
+ set_state(lockout_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
+ // 13 clicks and hold the last click: invoke factory reset (reboot)
+ else if (event == EV_click13_hold) {
+ reboot();
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #ifdef USE_VERSION_CHECK
+ // 15+ clicks: show the version number
+ else if (event == EV_15clicks) {
+ set_state(version_check_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+
+ #ifdef USE_SIMPLE_UI
+ // 8 clicks, but hold last click: turn simple UI off
+ else if ((event == EV_click8_hold) && (!arg)) {
+ blink_confirm(1);
+ simple_ui_active = 0;
+ return MISCHIEF_MANAGED;
+ }
+
+ ////////// Every action below here is blocked in the simple UI //////////
+ if (simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ // 8 clicks: enable simple UI
+ else if (event == EV_8clicks) {
+ blink_confirm(1);
+ simple_ui_active = 1;
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+
+ // click, click, long-click: strobe mode
+ #ifdef USE_STROBE_STATE
+ else if (event == EV_click3_hold) {
+ set_state(strobe_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #elif defined(USE_BORING_STROBE_STATE)
+ else if (event == EV_click3_hold) {
+ set_state(boring_strobe_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #ifdef USE_MOMENTARY_MODE
+ // 5 clicks: momentary mode
+ else if (event == EV_5clicks) {
+ blink_confirm(1);
+ set_state(momentary_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #ifdef USE_INDICATOR_LED
+ // 7 clicks: change indicator LED mode
+ else if (event == EV_7clicks) {
+ 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;
+ }
+ #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) {
+ setting_rgb_mode_now = 1;
+ 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) {
+ setting_rgb_mode_now = 0;
+ save_config();
+ return MISCHIEF_MANAGED;
+ }
+ #endif // end 7 clicks
+ #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION)
+ // 10 clicks: thermal config mode
+ else if (event == EV_10clicks) {
+ push_state(thermal_config_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/off-state.h b/spaghetti-monster/anduril/off-state.h
new file mode 100644
index 0000000..2bd7508
--- /dev/null
+++ b/spaghetti-monster/anduril/off-state.h
@@ -0,0 +1,27 @@
+/*
+ * off-state.h: "Off" state 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/>.
+ */
+
+#ifndef OFF_STATE_H
+#define OFF_STATE_H
+
+// handles when the light is "off" or in standby
+uint8_t off_state(Event event, uint16_t arg);
+
+
+#endif
diff --git a/spaghetti-monster/anduril/ramping-fsm.h b/spaghetti-monster/anduril/ramping-fsm.h
new file mode 100644
index 0000000..97b4321
--- /dev/null
+++ b/spaghetti-monster/anduril/ramping-fsm.h
@@ -0,0 +1,37 @@
+/*
+ * ramping-fsm.h: FSM config for ramping functions in 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/>.
+ */
+
+#ifndef RAMPING_FSM_H
+#define RAMPING_FSM_H
+
+#define USE_RAMPING
+
+#ifdef USE_THERMAL_REGULATION
+#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments
+#endif
+
+// brightness to use when no memory is set
+// FIXME: this is only here to stop an error in fsm-ramping.c,
+// which should be fixed by using a different symbol instead
+#ifndef DEFAULT_LEVEL
+#define DEFAULT_LEVEL MAX_1x7135
+#endif
+
+
+#endif
diff --git a/spaghetti-monster/anduril/ramping.c b/spaghetti-monster/anduril/ramping.c
new file mode 100644
index 0000000..f2a23c8
--- /dev/null
+++ b/spaghetti-monster/anduril/ramping.c
@@ -0,0 +1,460 @@
+/*
+ * ramping.c: 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/>.
+ */
+
+#ifndef RAMPING_C
+#define RAMPING_C
+
+#include "ramping.h"
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ #ifdef USE_REVERSING
+ static int8_t ramp_direction = 1;
+ #endif
+ #if (B_TIMING_OFF == B_RELEASE_T)
+ // if the user double clicks, we need to abort turning off,
+ // and this stores the level to return to
+ static uint8_t level_before_off = 0;
+ #endif
+
+ // make sure ramp globals are correct...
+ // ... but they already are; no need to do it here
+ //ramp_update_config();
+ //nearest_level(1); // same effect, takes less space
+
+ uint8_t mode_min = ramp_floor;
+ uint8_t mode_max = ramp_ceil;
+ uint8_t step_size;
+ if (ramp_style) { step_size = ramp_discrete_step_size; }
+ else { step_size = 1; }
+
+ // turn LED on when we first enter the mode
+ if ((event == EV_enter_state) || (event == EV_reenter_state)) {
+ #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
+ momentary_mode = 0; // 0 = ramping, 1 = strobes
+ #endif
+ // if we just got back from config mode, go back to memorized level
+ if (event == EV_reenter_state) {
+ arg = memorized_level;
+ }
+ // remember this level, unless it's moon or turbo
+ if ((arg > mode_min) && (arg < mode_max))
+ memorized_level = arg;
+ // use the requested level even if not memorized
+ arg = nearest_level(arg);
+ set_level_and_therm_target(arg);
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+ #if (B_TIMING_OFF == B_RELEASE_T)
+ // 1 click (early): off, if configured for early response
+ else if (event == EV_click1_release) {
+ level_before_off = actual_level;
+ set_level_and_therm_target(0);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks (early): abort turning off, if configured for early response
+ else if (event == EV_click2_press) {
+ set_level_and_therm_target(level_before_off);
+ return MISCHIEF_MANAGED;
+ }
+ #endif // if (B_TIMING_OFF == B_RELEASE_T)
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ uint8_t turbo_level;
+ #ifdef USE_SIMPLE_UI
+ if (simple_ui_active) { turbo_level = mode_max; }
+ else
+ #endif
+ turbo_level = MAX_LEVEL;
+
+ if (actual_level < turbo_level) {
+ // true turbo, not the mode-specific ceiling
+ set_level_and_therm_target(turbo_level);
+ }
+ else {
+ set_level_and_therm_target(memorized_level);
+ }
+ return MISCHIEF_MANAGED;
+ }
+ // hold: change brightness (brighter)
+ else if (event == EV_click1_hold) {
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef USE_REVERSING
+ // fix ramp direction on first frame if necessary
+ if (!arg) {
+ // make it ramp down instead, if already at max
+ if (actual_level >= mode_max) { ramp_direction = -1; }
+ // make it ramp up if already at min
+ // (off->hold->stepped_min->release causes this state)
+ else if (actual_level <= mode_min) { ramp_direction = 1; }
+ }
+ // if the button is stuck, err on the side of safety and ramp down
+ else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) {
+ ramp_direction = -1;
+ }
+ #ifdef USE_LOCKOUT_MODE
+ // if the button is still stuck, lock the light
+ else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) {
+ blip();
+ set_state(lockout_state, 0);
+ }
+ #endif
+ memorized_level = nearest_level((int16_t)actual_level \
+ + (step_size * ramp_direction));
+ #else
+ memorized_level = nearest_level((int16_t)actual_level + step_size);
+ #endif
+ #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_CEIL
+ || (memorized_level == mode_max)
+ #endif
+ #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR)
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ blip();
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ blip();
+ }
+ #endif
+ set_level_and_therm_target(memorized_level);
+ return MISCHIEF_MANAGED;
+ }
+ #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL)
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ #ifdef USE_REVERSING
+ ramp_direction = -ramp_direction;
+ #endif
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return MISCHIEF_MANAGED;
+ }
+ // TODO? make it ramp up instead, if already at min?
+ memorized_level = nearest_level((int16_t)actual_level - step_size);
+ #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_FLOOR
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ blip();
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ blip();
+ }
+ #endif
+ set_level_and_therm_target(memorized_level);
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef START_AT_MEMORIZED_LEVEL
+ // click, release, hold, release: save new ramp level (if necessary)
+ else if (event == EV_click2_hold_release) {
+ save_config_wl();
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+
+ #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
+ else if (event == EV_tick) {
+ #ifdef USE_REVERSING
+ // un-reverse after 1 second
+ if (arg == TICKS_PER_SECOND) ramp_direction = 1;
+ #endif
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ int16_t diff = gradual_target - actual_level;
+ static uint16_t ticks_since_adjust = 0;
+ ticks_since_adjust++;
+ if (diff) {
+ uint16_t ticks_per_adjust = 256;
+ if (diff < 0) {
+ //diff = -diff;
+ if (actual_level > THERM_FASTER_LEVEL) {
+ #ifdef THERM_HARD_TURBO_DROP
+ ticks_per_adjust >>= 2;
+ #endif
+ ticks_per_adjust >>= 2;
+ }
+ } else {
+ // rise at half speed
+ ticks_per_adjust <<= 1;
+ }
+ while (diff) {
+ ticks_per_adjust >>= 1;
+ //diff >>= 1;
+ diff /= 2; // because shifting produces weird behavior
+ }
+ if (ticks_since_adjust > ticks_per_adjust)
+ {
+ gradual_tick();
+ ticks_since_adjust = 0;
+ }
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by an amount proportional to how far we are above the ceiling
+ else if (event == EV_temperature_high) {
+ #if 0
+ blip();
+ #endif
+ #ifdef THERM_HARD_TURBO_DROP
+ //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;
+ #else
+ set_level_and_therm_target(THERM_FASTER_LEVEL);
+ #endif
+ } else
+ #endif
+ if (actual_level > MIN_THERM_STEPDOWN) {
+ int16_t stepdown = actual_level - arg;
+ if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN;
+ else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepdown);
+ #else
+ set_level(stepdown);
+ #endif
+ }
+ return MISCHIEF_MANAGED;
+ }
+ // underheating: increase slowly if we're lower than the target
+ // (proportional to how low we are)
+ else if (event == EV_temperature_low) {
+ #if 0
+ blip();
+ #endif
+ if (actual_level < target_level) {
+ //int16_t stepup = actual_level + (arg>>1);
+ int16_t stepup = actual_level + arg;
+ if (stepup > target_level) stepup = target_level;
+ else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepup);
+ #else
+ set_level(stepup);
+ #endif
+ }
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // temperature is within target window
+ // (so stop trying to adjust output)
+ else if (event == EV_temperature_okay) {
+ // if we're still adjusting output... stop after the current step
+ if (gradual_target > actual_level)
+ gradual_target = actual_level + 1;
+ else if (gradual_target < actual_level)
+ gradual_target = actual_level - 1;
+ return MISCHIEF_MANAGED;
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
+ #endif // ifdef USE_THERMAL_REGULATION
+
+ ////////// Every action below here is blocked in the simple UI //////////
+ #ifdef USE_SIMPLE_UI
+ if (simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ // 3 clicks: toggle smooth vs discrete ramping
+ else if (event == EV_3clicks) {
+ ramp_style = !ramp_style;
+ save_config();
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ blip();
+ memorized_level = nearest_level(actual_level);
+ set_level_and_therm_target(memorized_level);
+ return MISCHIEF_MANAGED;
+ }
+
+ #ifdef USE_MANUAL_MEMORY
+ else if (event == EV_5clicks) {
+ manual_memory = actual_level;
+ save_config();
+ blip();
+ return MISCHIEF_MANAGED;
+ }
+ else if (event == EV_click5_hold) {
+ if (0 == arg) {
+ manual_memory = 0;
+ save_config();
+ blip();
+ }
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+
+ #ifdef USE_RAMP_CONFIG
+ // 7 clicks: configure this ramp mode
+ else if (event == EV_7clicks) {
+ push_state(ramp_config_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_RAMP_CONFIG
+void ramp_config_save() {
+ // parse values
+ uint8_t val;
+ uint8_t style = ramp_style;
+ // TODO: detect if we're configuring the simple UI
+
+ val = config_state_values[0];
+ if (val) { ramp_floors[style] = val; }
+
+ val = config_state_values[1];
+ if (val) { ramp_ceils[style] = MAX_LEVEL + 1 - val; }
+
+ if (ramp_style) { // discrete / stepped ramp
+ val = config_state_values[2];
+ if (val) ramp_stepss[style] = val;
+ }
+}
+
+uint8_t ramp_config_state(Event event, uint16_t arg) {
+ uint8_t num_config_steps;
+ num_config_steps = 2 + ramp_style;
+ return config_state_base(event, arg,
+ num_config_steps, ramp_config_save);
+}
+#endif // #ifdef USE_RAMP_CONFIG
+
+
+// find the ramp level closest to the target,
+// using only the levels which are allowed in the current state
+uint8_t nearest_level(int16_t target) {
+ ramp_update_config();
+
+ // bounds check
+ // using int16_t here saves us a bunch of logic elsewhere,
+ // by allowing us to correct for numbers < 0 or > 255 in one central place
+ uint8_t mode_min = ramp_floor;
+ uint8_t mode_max = ramp_ceil;
+ if (target < mode_min) return mode_min;
+ if (target > mode_max) return mode_max;
+ // the rest isn't relevant for smooth ramping
+ if (! ramp_style) return target;
+
+ uint8_t ramp_range = mode_max - mode_min;
+ uint8_t num_steps = ramp_stepss[1 + simple_ui_active];
+ ramp_discrete_step_size = ramp_range / (num_steps-1);
+ uint8_t this_level = mode_min;
+
+ for(uint8_t i=0; i<num_steps; i++) {
+ this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1));
+ int16_t diff = target - this_level;
+ if (diff < 0) diff = -diff;
+ if (diff <= (ramp_discrete_step_size>>1))
+ return this_level;
+ }
+ return this_level;
+}
+
+// ensure ramp globals are correct
+void ramp_update_config() {
+ uint8_t which = ramp_style;
+ if (simple_ui_active) { which = 2; }
+
+ ramp_floor = ramp_floors[which];
+ ramp_ceil = ramp_ceils[which];
+}
+
+#ifdef USE_THERMAL_REGULATION
+void set_level_and_therm_target(uint8_t level) {
+ target_level = level;
+ set_level(level);
+}
+#endif
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/ramping.h b/spaghetti-monster/anduril/ramping.h
new file mode 100644
index 0000000..b47297d
--- /dev/null
+++ b/spaghetti-monster/anduril/ramping.h
@@ -0,0 +1,179 @@
+/*
+ * ramping.h: 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/>.
+ */
+
+#ifndef RAMPING_H
+#define RAMPING_H
+
+#ifndef RAMP_LENGTH
+#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
+#endif
+
+// thermal properties, if not defined per-driver
+#ifndef MIN_THERM_STEPDOWN
+#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to
+#endif
+#ifndef THERM_FASTER_LEVEL
+ #ifdef MAX_Nx7135
+ #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high
+ #else
+ #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high
+ #endif
+#endif
+
+#if defined(USE_SIMPLE_UI)
+// start in the simple UI after each factory reset?
+#ifndef DEFAULT_SIMPLE_UI_ACTIVE
+#define DEFAULT_SIMPLE_UI_ACTIVE 1
+#endif
+#ifndef DEFAULT_SIMPLE_UI_FLOOR
+#define DEFAULT_SIMPLE_UI_FLOOR 22
+#endif
+#ifndef DEFAULT_SIMPLE_UI_CEIL
+#define DEFAULT_SIMPLE_UI_CEIL (MAX_1x7135+20)
+#endif
+#ifndef DEFAULT_SIMPLE_UI_STEPS
+#define DEFAULT_SIMPLE_UI_STEPS 3
+#endif
+#endif
+
+
+// configure the timing of turning on/off in regular ramp mode
+// press: react as soon as the button is pressed
+#define B_PRESS_T 0
+// release: react as soon as the button is released
+#define B_RELEASE_T 1
+// timeout: react as soon as we're sure the user isn't doing a double-click
+#define B_TIMEOUT_T 2
+// defaults are release on, timeout off
+#ifndef B_TIMING_ON
+//#define B_TIMING_ON B_PRESS_T
+#define B_TIMING_ON B_RELEASE_T
+#endif
+#ifndef B_TIMING_OFF
+//#define B_TIMING_OFF B_RELEASE_T
+#define B_TIMING_OFF B_TIMEOUT_T
+#endif
+
+// default ramp options if not overridden earlier per-driver
+#ifndef RAMP_STYLE
+#define RAMP_STYLE 0 // smooth default
+#endif
+#ifndef RAMP_SMOOTH_FLOOR
+ #define RAMP_SMOOTH_FLOOR 1
+#endif
+#ifndef RAMP_SMOOTH_CEIL
+ #if PWM_CHANNELS == 3
+ #define RAMP_SMOOTH_CEIL MAX_Nx7135
+ #else
+ #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
+ #endif
+#endif
+#ifndef RAMP_DISCRETE_FLOOR
+ #define RAMP_DISCRETE_FLOOR 20
+#endif
+#ifndef RAMP_DISCRETE_CEIL
+ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#endif
+#ifndef RAMP_DISCRETE_STEPS
+ #define RAMP_DISCRETE_STEPS 7
+#endif
+
+// mile marker(s) partway up the ramp
+// default: blink only at border between regulated and FET
+#ifdef BLINK_AT_RAMP_MIDDLE
+ #if PWM_CHANNELS >= 3
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
+ #ifndef BLINK_AT_RAMP_MIDDLE_2
+ #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
+ #endif
+ #endif
+ #else
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
+ #endif
+ #endif
+#endif
+
+
+// ramping mode and its related config mode
+uint8_t steady_state(Event event, uint16_t arg);
+
+#ifdef USE_RAMP_CONFIG
+uint8_t ramp_config_state(Event event, uint16_t arg);
+void ramp_config_save();
+#endif
+
+// calculate the nearest ramp level which would be valid at the moment
+// (is a no-op for smooth ramp, but limits discrete ramp to only the
+// correct levels for the user's config)
+uint8_t nearest_level(int16_t target);
+
+// ensure ramp globals are correct
+void ramp_update_config();
+
+#ifdef USE_THERMAL_REGULATION
+// brightness before thermal step-down
+uint8_t target_level = 0;
+void set_level_and_therm_target(uint8_t level);
+#else
+#define set_level_and_therm_target(level) set_level(level)
+#endif
+
+
+// brightness control
+uint8_t memorized_level = DEFAULT_LEVEL;
+#ifdef USE_MANUAL_MEMORY
+uint8_t manual_memory = 0;
+#endif
+#ifdef USE_SIMPLE_UI
+// whether to enable the simplified interface or not
+uint8_t simple_ui_active = DEFAULT_SIMPLE_UI_ACTIVE;
+#endif
+// smooth vs discrete ramping
+uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete
+// current values, regardless of style
+uint8_t ramp_floor = RAMP_SMOOTH_FLOOR;
+uint8_t ramp_ceil = RAMP_SMOOTH_CEIL;
+// per style
+uint8_t ramp_floors[] = {
+ RAMP_SMOOTH_FLOOR,
+ RAMP_DISCRETE_FLOOR,
+ #ifdef USE_SIMPLE_UI
+ DEFAULT_SIMPLE_UI_FLOOR,
+ #endif
+ };
+uint8_t ramp_ceils[] = {
+ RAMP_SMOOTH_CEIL,
+ RAMP_DISCRETE_CEIL,
+ #ifdef USE_SIMPLE_UI
+ DEFAULT_SIMPLE_UI_CEIL,
+ #endif
+ };
+uint8_t ramp_stepss[] = {
+ 0,
+ RAMP_DISCRETE_STEPS,
+ #ifdef USE_SIMPLE_UI
+ DEFAULT_SIMPLE_UI_STEPS,
+ #endif
+ };
+uint8_t ramp_discrete_step_size; // don't set this
+
+
+#endif
diff --git a/spaghetti-monster/anduril/sos-mode.c b/spaghetti-monster/anduril/sos-mode.c
new file mode 100644
index 0000000..9f87818
--- /dev/null
+++ b/spaghetti-monster/anduril/sos-mode.c
@@ -0,0 +1,75 @@
+/*
+ * sos-mode.c: SOS mode 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/>.
+ */
+
+#ifndef SOS_MODE_C
+#define SOS_MODE_C
+
+#include "sos-mode.h"
+
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+uint8_t sos_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks: next mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_THERMAL_REGULATION
+ set_state(tempcheck_state, 0);
+ #else
+ #ifdef USE_BATTCHECK_MODE
+ set_state(battcheck_state, 0);
+ #endif
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+void sos_blink(uint8_t num, uint8_t dah) {
+ #define DIT_LENGTH 200
+ for (; num > 0; num--) {
+ set_level(memorized_level);
+ nice_delay_ms(DIT_LENGTH);
+ if (dah) { // dah is 3X as long as a dit
+ nice_delay_ms(DIT_LENGTH*2);
+ }
+ set_level(0);
+ // one "off" dit between blinks
+ nice_delay_ms(DIT_LENGTH);
+ }
+ // three "off" dits (or one "dah") between letters
+ // (except for SOS, which is collectively treated as a single "letter")
+ //nice_delay_ms(DIT_LENGTH*2);
+}
+
+inline void sos_mode_iter() {
+ // one iteration of main loop()
+ //nice_delay_ms(1000);
+ sos_blink(3, 0); // S
+ sos_blink(3, 1); // O
+ sos_blink(3, 0); // S
+ nice_delay_ms(2000);
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/sos-mode.h b/spaghetti-monster/anduril/sos-mode.h
new file mode 100644
index 0000000..397aa3f
--- /dev/null
+++ b/spaghetti-monster/anduril/sos-mode.h
@@ -0,0 +1,29 @@
+/*
+ * sos-mode.h: SOS mode 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/>.
+ */
+
+#ifndef SOS_MODE_H
+#define SOS_MODE_H
+
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+// automatic SOS emergency signal
+uint8_t sos_state(Event event, uint16_t arg);
+#endif
+
+
+#endif
diff --git a/spaghetti-monster/anduril/strobes-fsm.h b/spaghetti-monster/anduril/strobes-fsm.h
index 6514d5f..2eb2d79 100644
--- a/spaghetti-monster/anduril/strobes-fsm.h
+++ b/spaghetti-monster/anduril/strobes-fsm.h
@@ -25,11 +25,20 @@
#define USE_PSEUDO_RAND
#endif
+// party strobe uses really short pulses
+#ifdef USE_PARTY_STROBE_MODE
+#define USE_DELAY_ZERO
+#endif
+
+// candle mode is basically a bunch of stacked random triangle waves
+#if defined(USE_CANDLE_MODE)
+#define USE_TRIANGLE_WAVE
+#endif
+
// the presence of strobe mode(s) affects how many eeprom bytes we need,
// so it's relevant for FSM configuration
#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
#endif
-
#endif
diff --git a/spaghetti-monster/anduril/strobes.c b/spaghetti-monster/anduril/strobes.c
index 42d085d..1037c0e 100644
--- a/spaghetti-monster/anduril/strobes.c
+++ b/spaghetti-monster/anduril/strobes.c
@@ -162,6 +162,36 @@ uint8_t strobe_state(Event event, uint16_t arg) {
#endif
return EVENT_NOT_HANDLED;
}
+
+// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe
+inline void strobe_state_iter() {
+ uint8_t st = strobe_type;
+
+ switch(st) {
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ #ifdef USE_PARTY_STROBE_MODE
+ case party_strobe_e:
+ #endif
+ #ifdef USE_TACTICAL_STROBE_MODE
+ case tactical_strobe_e:
+ #endif
+ party_tactical_strobe_mode_iter(st);
+ break;
+ #endif
+
+ #ifdef USE_LIGHTNING_MODE
+ case lightning_storm_e:
+ lightning_storm_iter();
+ break;
+ #endif
+
+ #ifdef USE_BIKE_FLASHER_MODE
+ case bike_flasher_e:
+ bike_flasher_iter();
+ break;
+ #endif
+ }
+}
#endif // ifdef USE_STROBE_STATE
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
@@ -385,12 +415,6 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
}
return EVENT_NOT_HANDLED;
}
-
-uint8_t triangle_wave(uint8_t phase) {
- uint8_t result = phase << 1;
- if (phase > 127) result = 255 - result;
- return result;
-}
#endif // #ifdef USE_CANDLE_MODE
diff --git a/spaghetti-monster/anduril/strobes.h b/spaghetti-monster/anduril/strobes.h
index 8cfc4a7..c74b4a0 100644
--- a/spaghetti-monster/anduril/strobes.h
+++ b/spaghetti-monster/anduril/strobes.h
@@ -65,6 +65,7 @@ volatile strobe_mode_te strobe_type = 0;
// party and tactical strobes
#ifdef USE_STROBE_STATE
uint8_t strobe_state(Event event, uint16_t arg);
+inline void strobe_state_iter();
#endif
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
@@ -79,13 +80,15 @@ inline void lightning_storm_iter();
// bike mode config options
#ifdef USE_BIKE_FLASHER_MODE
+#define MAX_BIKING_LEVEL 120 // should be 127 or less
volatile uint8_t bike_flasher_brightness = MAX_1x7135;
inline void bike_flasher_iter();
#endif
#ifdef USE_CANDLE_MODE
uint8_t candle_mode_state(Event event, uint16_t arg);
-uint8_t triangle_wave(uint8_t phase);
+// moved to fsm-misc.c because tint ramping power correction
+//uint8_t triangle_wave(uint8_t phase);
#ifndef CANDLE_AMPLITUDE
#define CANDLE_AMPLITUDE 25
#endif
diff --git a/spaghetti-monster/anduril/tempcheck-mode.c b/spaghetti-monster/anduril/tempcheck-mode.c
new file mode 100644
index 0000000..b4e95ff
--- /dev/null
+++ b/spaghetti-monster/anduril/tempcheck-mode.c
@@ -0,0 +1,73 @@
+/*
+ * tempcheck-mode.c: Temperature check mode 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/>.
+ */
+
+#ifndef TEMPCHECK_MODE_C
+#define TEMPCHECK_MODE_C
+
+#include "tempcheck-mode.h"
+
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #ifdef USE_BATTCHECK
+ // 2 clicks: battcheck mode
+ else if (event == EV_2clicks) {
+ set_state(battcheck_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
+ // 7 clicks: thermal config mode
+ else if (event == EV_7clicks) {
+ push_state(thermal_config_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+void thermal_config_save() {
+ // parse values
+ uint8_t val;
+
+ // calibrate room temperature
+ val = config_state_values[0];
+ if (val) {
+ int8_t rawtemp = temperature - therm_cal_offset;
+ therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
+ }
+
+ val = config_state_values[1];
+ if (val) {
+ // set maximum heat limit
+ therm_ceil = 30 + val - 1;
+ }
+ if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
+}
+
+uint8_t thermal_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 2, thermal_config_save);
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/tempcheck-mode.h b/spaghetti-monster/anduril/tempcheck-mode.h
new file mode 100644
index 0000000..36c41c1
--- /dev/null
+++ b/spaghetti-monster/anduril/tempcheck-mode.h
@@ -0,0 +1,30 @@
+/*
+ * tempcheck-mode.h: Temperature check mode 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/>.
+ */
+
+#ifndef TEMPCHECK_MODE_H
+#define TEMPCHECK_MODE_H
+
+#define USE_BLINK_NUM // FIXME: this only matters in an earlier header
+
+uint8_t tempcheck_state(Event event, uint16_t arg);
+uint8_t thermal_config_state(Event event, uint16_t arg);
+void thermal_config_save();
+
+
+#endif
diff --git a/spaghetti-monster/anduril/version-check-mode.c b/spaghetti-monster/anduril/version-check-mode.c
new file mode 100644
index 0000000..76efde3
--- /dev/null
+++ b/spaghetti-monster/anduril/version-check-mode.c
@@ -0,0 +1,47 @@
+/*
+ * version-check-mode.c: Version check mode 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/>.
+ */
+
+#ifndef VERSION_CHECK_MODE_C
+#define VERSION_CHECK_MODE_C
+
+#include "version-check-mode.h"
+
+// empty state; logic is handled in FSM loop() instead
+uint8_t version_check_state(Event event, uint16_t arg) {
+ return EVENT_NOT_HANDLED;
+}
+
+// this happens in FSM loop()
+inline void version_check_iter() {
+ for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
+ blink_digit(pgm_read_byte(version_number + i) - '0');
+ nice_delay_ms(300);
+ }
+ // FIXME: when user interrupts with button, "off" takes an extra click
+ // before it'll turn back on, because the click to cancel gets sent
+ // to the "off" state instead of version_check_state
+ //while (button_is_pressed()) {}
+ //empty_event_sequence();
+
+ set_state(off_state, 0);
+}
+
+
+#endif
+
diff --git a/spaghetti-monster/anduril/version-check-mode.h b/spaghetti-monster/anduril/version-check-mode.h
new file mode 100644
index 0000000..7e89d73
--- /dev/null
+++ b/spaghetti-monster/anduril/version-check-mode.h
@@ -0,0 +1,31 @@
+/*
+ * version-check-mode.h: Version check mode 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/>.
+ */
+
+#ifndef VERSION_CHECK_MODE_H
+#define VERSION_CHECK_MODE_H
+
+#define USE_BLINK_DIGIT // FIXME: does nothing unless defined earlier
+
+#include "version.h"
+const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
+uint8_t version_check_state(Event event, uint16_t arg);
+inline void version_check_iter();
+
+
+#endif
diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c
index eaa6fb3..82be745 100644
--- a/spaghetti-monster/fsm-misc.c
+++ b/spaghetti-monster/fsm-misc.c
@@ -222,7 +222,6 @@ void rgb_led_set(uint8_t value) {
#endif // ifdef USE_AUX_RGB_LEDS
#ifdef USE_TRIANGLE_WAVE
-// TODO: remove this, it's in the wrong file
uint8_t triangle_wave(uint8_t phase) {
uint8_t result = phase << 1;
if (phase > 127) result = 255 - result;