aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
authorSelene ToyKeeper2020-07-22 01:48:22 -0600
committerSelene ToyKeeper2020-07-22 01:48:22 -0600
commit4f8a7d66a6f98fc51d3cb75e9906c049faffe14a (patch)
treee503ac174d211e317d5dcd00035a6731dcf88b2e /spaghetti-monster
parentstarted refactoring anduril into individual files... (diff)
downloadanduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.tar.gz
anduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.tar.bz2
anduril-4f8a7d66a6f98fc51d3cb75e9906c049faffe14a.zip
more progress on refactoring Anduril into separate files... nearly done with the initial split
Diffstat (limited to 'spaghetti-monster')
-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;
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.