diff options
| author | Selene ToyKeeper | 2020-07-21 20:56:34 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2020-07-21 20:56:34 -0600 |
| commit | 9c9135640db4a71528765cd04ee0fb0fb7cd7938 (patch) | |
| tree | 2673060fe7cc0f46b07d86dc772c9a51d37b8962 | |
| parent | merged fsm branch (mostly because D4v2.5 build targets were merged into KR4, (diff) | |
| download | anduril-9c9135640db4a71528765cd04ee0fb0fb7cd7938.tar.gz anduril-9c9135640db4a71528765cd04ee0fb0fb7cd7938.tar.bz2 anduril-9c9135640db4a71528765cd04ee0fb0fb7cd7938.zip | |
started refactoring anduril into individual files...
... messy and very incomplete, but it's a start, and at least it still compiles.
(the overall structure is ... weird. mostly because of compiling the entire
program plus libraries in a single step, as a single file, to make -fwhole-program work)
Diffstat (limited to '')
| -rw-r--r-- | spaghetti-monster/anduril/anduril.c | 1105 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/beacon.c | 72 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/beacon.h | 31 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-default.h | 103 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/config-mode.c | 153 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/config-mode.h | 34 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/factory-reset-fsm.h | 28 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/factory-reset.c | 73 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/factory-reset.h | 26 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/ff-strobes.c | 66 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/ff-strobes.h | 32 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/lockout.c | 169 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/lockout.h | 31 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/strobes-fsm.h | 35 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/strobes.c | 403 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/strobes.h | 100 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/tint-ramping.c | 87 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/tint-ramping.h | 27 | ||||
| -rw-r--r-- | spaghetti-monster/fsm-misc.c | 1 |
19 files changed, 1548 insertions, 1028 deletions
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 7ab7aee..d69be54 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -18,93 +18,51 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +// Usually a program would be structured like this... +// - Library headers +// - App headers +// - App code +// +// ... in each source file. +// ... and each library and part of the program would be linked together. +// +// But this doesn't follow that pattern, because it's using the +// -fwhole-program +// flag to reduce the compiled size. It lets us fit more features +// in a tiny MCU chip's ROM. +// +// So the structure is like this instead... +// - App-level configuration headers +// - Default config +// - Per build target config +// - Library-level configuration headers +// - Library code (FSM itself) +// - App headers +// - App code (all of it) +// +// Don't do this in regular programs. It's weird and kind of gross. +// But in this case it gives us a bunch of much-needed space, so... + /********* User-configurable options *********/ +#include "cfg-default.h" + +/***** specific settings for known driver types *****/ // Anduril config file name (set it here or define it at the gcc command line) //#define CONFIGFILE cfg-blf-q8.h -#define USE_LVP - -// parameters for this defined below or per-driver -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this - -#define USE_FACTORY_RESET -//#define USE_SOFT_FACTORY_RESET // only needed on models which can't use hold-button-at-boot - -// dual-switch support (second switch is a tail clicky) -// (currently incompatible with factory reset) -//#define START_AT_MEMORIZED_LEVEL - -// include a function to blink out the firmware version -#define USE_VERSION_CHECK - -// short blip when crossing from "click" to "hold" from off -// (helps the user hit moon mode exactly, instead of holding too long -// or too short) -#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T -// short blips while ramping -#define BLINK_AT_RAMP_MIDDLE -//#define BLINK_AT_RAMP_FLOOR -#define BLINK_AT_RAMP_CEIL -//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode - -// ramp down via regular button hold if a ramp-up ended <1s ago -// ("hold, release, hold" ramps down instead of up) -#define USE_REVERSING - -// add a runtime option to switch between automatic memory (default) -// and manual memory (only available if compiled in) -// (manual memory makes 1-click-from-off start at the same level each time) -// (the level can be set explicitly with 5 clicks from on, -// or the user can go back to automatic with click-click-click-click-hold) -#define USE_MANUAL_MEMORY - -// battery readout style (pick one) -#define BATTCHECK_VpT -//#define BATTCHECK_8bars // FIXME: breaks build -//#define BATTCHECK_4bars // FIXME: breaks build - -// enable/disable various strobe modes -#define USE_BIKE_FLASHER_MODE -#define USE_PARTY_STROBE_MODE -#define USE_TACTICAL_STROBE_MODE -#define USE_LIGHTNING_MODE -#define USE_CANDLE_MODE - -// enable sunset (goodnight) mode -#define USE_GOODNIGHT_MODE -#define GOODNIGHT_TIME 60 // minutes (approximately) -#define GOODNIGHT_LEVEL 24 // ~11 lm - -// enable beacon mode -#define USE_BEACON_MODE - -// enable momentary mode -#define USE_MOMENTARY_MODE - -// Include a simplified UI for non-enthusiasts? -#define USE_SIMPLE_UI - -// make the ramps configurable by the user -#define USE_RAMP_CONFIG - -// boring strobes nobody really likes, but sometimes flashlight companies want -// (these replace the fun strobe group, -// so don't enable them at the same time as any of the above strobes) -//#define USE_POLICE_STROBE_MODE -//#define USE_SOS_MODE -//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode -//#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group - -// cut clock speed at very low modes for better efficiency -// (defined here so config files can override it) -#define USE_DYNAMIC_UNDERCLOCKING - -/***** specific settings for known driver types *****/ #include "tk.h" #include incfile(CONFIGFILE) +////////// Include headers which need to be before FSM ////////// +#ifdef USE_FACTORY_RESET +#include "factory-reset-fsm.h" +#endif + +// enable FSM features needed by strobe modes +#include "strobes-fsm.h" + + // brightness to use when no memory is set #ifndef DEFAULT_LEVEL #define DEFAULT_LEVEL MAX_1x7135 @@ -153,24 +111,6 @@ #define USE_IDLE_MODE // reduce power use while awake and no tasks are pending -// full FET strobe can be a bit much... use max regulated level instead, -// if there's a bright enough regulated level -#ifndef STROBE_BRIGHTNESS -#ifdef MAX_Nx7135 -#define STROBE_BRIGHTNESS MAX_Nx7135 -#else -#define STROBE_BRIGHTNESS MAX_LEVEL -#endif -#endif - -#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) -#define USE_STROBE_STATE -#endif - -#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP) -#define USE_BORING_STROBE_STATE -#endif - // auto-detect how many eeprom bytes #define USE_EEPROM typedef enum { @@ -227,22 +167,28 @@ typedef enum { #define EEPROM_WL_BYTES 1 #endif -// auto-configure other stuff... -#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) -#define USE_PSEUDO_RAND -#endif +#include "spaghetti-monster.h" + -#if defined(USE_CANDLE_MODE) -#ifndef USE_TRIANGLE_WAVE -#define USE_TRIANGLE_WAVE +////////// Include all the extra headers ////////// + +#include "config-mode.h" +#include "lockout.h" + +#ifdef USE_TINT_RAMPING +#include "tint-ramping.h" #endif + +#ifdef USE_FACTORY_RESET +#include "factory-reset.h" #endif -#ifdef USE_SOFT_FACTORY_RESET -#define USE_REBOOT +#ifdef USE_BEACON_MODE +#include "beacon.h" #endif -#include "spaghetti-monster.h" +// this one detects its own enable/disable settings +#include "strobes.h" // configure the timing of turning on/off in regular ramp mode @@ -265,31 +211,11 @@ typedef enum { // FSM states uint8_t off_state(Event event, uint16_t arg); -// simple numeric entry config menu -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()); -#define MAX_CONFIG_VALUES 3 -uint8_t config_state_values[MAX_CONFIG_VALUES]; // 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_TINT_RAMPING -// not actually a mode, more of a fallback under other modes -uint8_t tint_ramping_state(Event event, uint16_t arg); -#endif -// party and tactical strobes -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg); -#endif -#ifdef USE_BORING_STROBE_STATE -uint8_t boring_strobe_state(Event event, uint16_t arg); -volatile uint8_t boring_strobe_type = 0; -void sos_blink(uint8_t num, uint8_t dah); -#define NUM_BORING_STROBES 2 -#endif #ifdef USE_BATTCHECK uint8_t battcheck_state(Event event, uint16_t arg); #endif @@ -302,19 +228,10 @@ uint8_t thermal_config_state(Event event, uint16_t arg); // 1-hour ramp down from low, then automatic off uint8_t goodnight_state(Event event, uint16_t arg); #endif -#ifdef USE_BEACON_MODE -// beacon mode and its related config mode -uint8_t beacon_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 -// soft lockout -#define MOON_DURING_LOCKOUT_MODE -// if enabled, 2nd lockout click goes to the other ramp's floor level -#define LOCKOUT_MOON_FANCY -uint8_t lockout_state(Event event, uint16_t arg); #ifdef USE_MOMENTARY_MODE // momentary / signalling mode uint8_t momentary_state(Event event, uint16_t arg); @@ -322,11 +239,6 @@ uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe uint8_t momentary_active = 0; // boolean, true if active *right now* #endif -// general helper function for config modes -uint8_t number_entry_state(Event event, uint16_t arg); -// return value from number_entry_state() -volatile uint8_t number_entry_value; - void blink_confirm(uint8_t num); void blip(); #if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) @@ -372,10 +284,6 @@ uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT; uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT; #endif -#ifdef USE_FACTORY_RESET -void factory_reset(); -#endif - // remember stuff even after battery was changed void load_config(); void save_config(); @@ -493,67 +401,44 @@ void set_level_and_therm_target(uint8_t level); #define set_level_and_therm_target(level) set_level(level) #endif -// internal numbering for strobe modes -#ifdef USE_STROBE_STATE -typedef enum { - #ifdef USE_PARTY_STROBE_MODE - party_strobe_e, - #endif - #ifdef USE_TACTICAL_STROBE_MODE - tactical_strobe_e, - #endif - #ifdef USE_LIGHTNING_MODE - lightning_storm_e, - #endif - #ifdef USE_CANDLE_MODE - candle_mode_e, - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_e, - #endif - strobe_mode_END -} strobe_mode_te; - -const int NUM_STROBES = strobe_mode_END; +#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 -// which strobe mode is active? -#ifdef USE_CANDLE_MODE -volatile strobe_mode_te strobe_type = candle_mode_e; -#else -volatile strobe_mode_te strobe_type = 0; +#ifdef USE_RAMP_CONFIG +void ramp_config_save(); #endif +#ifdef USE_THERMAL_REGULATION +void thermal_config_save(); #endif -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -// party / tactical strobe timing -volatile uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz -#endif +////////// Include all the extra sources ////////// +// (is a bit weird to do things this way, +// but it saves a lot of space by letting use use the -fwhole-program flag) -// bike mode config options -#ifdef USE_BIKE_FLASHER_MODE -volatile uint8_t bike_flasher_brightness = MAX_1x7135; -#endif +#include "config-mode.c" +#include "lockout.c" -#ifdef USE_CANDLE_MODE -uint8_t candle_mode_state(Event event, uint16_t arg); -uint8_t triangle_wave(uint8_t phase); -#ifndef CANDLE_AMPLITUDE -#define CANDLE_AMPLITUDE 25 +#ifdef USE_TINT_RAMPING +#include "tint-ramping.c" #endif + +#ifdef USE_FACTORY_RESET +#include "factory-reset.c" #endif #ifdef USE_BEACON_MODE -// beacon timing -volatile uint8_t beacon_seconds = 2; +#include "beacon.c" #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); +#ifdef USE_STROBE_STATE +#include "strobes.c" #endif + uint8_t off_state(Event event, uint16_t arg) { // turn emitter off when entering state if (event == EV_enter_state) { @@ -1144,491 +1029,6 @@ uint8_t steady_state(Event event, uint16_t arg) { } -#ifdef USE_TINT_RAMPING -uint8_t tint_ramping_state(Event event, uint16_t arg) { - static int8_t tint_ramp_direction = 1; - static uint8_t prev_tint = 0; - // don't activate auto-tint modes unless the user hits the edge - // and keeps pressing for a while - static uint8_t past_edge_counter = 0; - // bugfix: click-click-hold from off to strobes would invoke tint ramping - // in addition to changing state... so ignore any tint-ramp events which - // don't look like they were meant to be here - static uint8_t active = 0; - - // click, click, hold: change the tint - if (event == EV_click3_hold) { - // reset at beginning of movement - if (! arg) { - active = 1; // first frame means this is for us - past_edge_counter = 0; // doesn't start until user hits the edge - } - // ignore event if we weren't the ones who handled the first frame - if (! active) return EVENT_HANDLED; - - // change normal tints - if ((tint_ramp_direction > 0) && (tint < 254)) { - tint += 1; - } - else if ((tint_ramp_direction < 0) && (tint > 1)) { - tint -= 1; - } - // if the user kept pressing long enough, go the final step - if (past_edge_counter == 64) { - past_edge_counter ++; - tint ^= 1; // 0 -> 1, 254 -> 255 - blip(); - } - // if tint change stalled, let user know we hit the edge - else if (prev_tint == tint) { - if (past_edge_counter == 0) blip(); - // count up but don't wrap back to zero - if (past_edge_counter < 255) past_edge_counter ++; - } - prev_tint = tint; - set_level(actual_level); - return EVENT_HANDLED; - } - - // click, click, hold, release: reverse direction for next ramp - else if (event == EV_click3_hold_release) { - active = 0; // ignore next hold if it wasn't meant for us - // reverse - tint_ramp_direction = -tint_ramp_direction; - if (tint == 0) tint_ramp_direction = 1; - else if (tint == 255) tint_ramp_direction = -1; - // remember tint after battery change - save_config(); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} -#endif // ifdef USE_TINT_RAMPING - - -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - - // 'st' reduces ROM size by avoiding access to a volatile var - // (maybe I should just make it nonvolatile?) - strobe_mode_te st = strobe_type; - - #ifdef USE_MOMENTARY_MODE - momentary_mode = 1; // 0 = ramping, 1 = strobes - #endif - - #ifdef USE_CANDLE_MODE - // pass all events to candle mode, when it's active - // (the code is in its own pseudo-state to keep things cleaner) - if (st == candle_mode_e) { - candle_mode_state(event, arg); - } - #endif - - if (0) {} // placeholder - // init anything which needs to be initialized - else if (event == EV_enter_state) { - ramp_direction = 1; - return MISCHIEF_MANAGED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - strobe_type = (st + 1) % NUM_STROBES; - save_config(); - return MISCHIEF_MANAGED; - } - // hold: change speed (go faster) - // or change brightness (brighter) - else if (event == EV_click1_hold) { - if (0) {} // placeholder - - // party / tactical strobe faster - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - uint8_t d = strobe_delays[st]; - d -= ramp_direction; - if (d < 8) d = 8; - else if (d > 254) d = 254; - strobe_delays[st] = d; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode brighter - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - bike_flasher_brightness += ramp_direction; - if (bike_flasher_brightness < 2) bike_flasher_brightness = 2; - else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL; - set_level(bike_flasher_brightness); - } - #endif - - return MISCHIEF_MANAGED; - } - // reverse ramp direction on hold release - // ... and save new strobe settings - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - save_config(); - return MISCHIEF_MANAGED; - } - // click, hold: change speed (go slower) - // or change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - - if (0) {} // placeholder - - // party / tactical strobe slower - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - if (strobe_delays[st] < 255) strobe_delays[st] ++; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode dimmer - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - if (bike_flasher_brightness > 2) - bike_flasher_brightness --; - set_level(bike_flasher_brightness); - } - #endif - - return MISCHIEF_MANAGED; - } - // release hold: save new strobe settings - else if (event == EV_click2_hold_release) { - save_config(); - return MISCHIEF_MANAGED; - } - #ifdef USE_MOMENTARY_MODE - // 5 clicks: go to momentary mode (momentary strobe) - else if (event == EV_5clicks) { - set_state(momentary_state, 0); - set_level(0); - return MISCHIEF_MANAGED; - } - #endif - #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) - // clock tick: bump the random seed - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - - pseudo_rand_seed += arg; - return MISCHIEF_MANAGED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_BEACON_MODE -inline void beacon_mode_iter() { - // one iteration of main loop() - if (! button_last_state) { - set_level(memorized_level); - nice_delay_ms(100); - set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 100); - } -} -#endif // #ifdef USE_BEACON_MODE - - -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -inline void party_tactical_strobe_mode_iter(uint8_t st) { - // one iteration of main loop() - uint8_t del = strobe_delays[st]; - // TODO: make tac strobe brightness configurable? - set_level(STROBE_BRIGHTNESS); - if (0) {} // placeholde0 - #ifdef USE_PARTY_STROBE_MODE - else if (st == party_strobe_e) { // party strobe - #ifdef PARTY_STROBE_ONTIME - nice_delay_ms(PARTY_STROBE_ONTIME); - #else - if (del < 42) delay_zero(); - else nice_delay_ms(1); - #endif - } - #endif - #ifdef USE_TACTICAL_STROBE_MODE - else { //tactical strobe - nice_delay_ms(del >> 1); - } - #endif - set_level(0); - nice_delay_ms(del); // no return check necessary on final delay -} -#endif - -#ifdef USE_LIGHTNING_MODE -inline void lightning_storm_iter() { - // one iteration of main loop() - int16_t brightness; - uint16_t rand_time; - - // turn the emitter on at a random level, - // for a random amount of time between 1ms and 32ms - //rand_time = 1 << (pseudo_rand() % 7); - rand_time = pseudo_rand() & 63; - brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 - brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now - brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) - if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; - set_level(brightness); - nice_delay_ms(rand_time); - - // decrease the brightness somewhat more gradually, like lightning - uint8_t stepdown = brightness >> 3; - if (stepdown < 1) stepdown = 1; - while(brightness > 1) { - nice_delay_ms(rand_time); - brightness -= stepdown; - if (brightness < 0) brightness = 0; - set_level(brightness); - /* - if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { - brightness <<= 1; - set_level(brightness); - } - */ - if (! (pseudo_rand() & 3)) { - nice_delay_ms(rand_time); - set_level(brightness>>1); - } - } - - // turn the emitter off, - // for a random amount of time between 1ms and 8192ms - // (with a low bias) - rand_time = 1 << (pseudo_rand() % 13); - rand_time += pseudo_rand() % rand_time; - set_level(0); - nice_delay_ms(rand_time); // no return check necessary on final delay -} -#endif - -#ifdef USE_BIKE_FLASHER_MODE -inline void bike_flasher_iter() { - // one iteration of main loop() - uint8_t burst = bike_flasher_brightness << 1; - if (burst > MAX_LEVEL) burst = MAX_LEVEL; - for(uint8_t i=0; i<4; i++) { - set_level(burst); - nice_delay_ms(5); - set_level(bike_flasher_brightness); - nice_delay_ms(65); - } - nice_delay_ms(720); // no return check necessary on final delay -} -#endif - -#endif // ifdef USE_STROBE_STATE - -#ifdef USE_CANDLE_MODE -uint8_t candle_mode_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) - static uint8_t candle_wave1 = 0; - static uint8_t candle_wave2 = 0; - static uint8_t candle_wave3 = 0; - static uint8_t candle_wave2_speed = 0; - // these should add up to 100 - #define CANDLE_WAVE1_MAXDEPTH 30 - #define CANDLE_WAVE2_MAXDEPTH 45 - #define CANDLE_WAVE3_MAXDEPTH 25 - static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_mode_brightness = 24; - static uint8_t candle_mode_timer = 0; - #define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds - #define MINUTES_PER_CANDLE_HALFHOUR 27 // ish - - if (event == EV_enter_state) { - candle_mode_timer = 0; // in case any time was left over from earlier - ramp_direction = 1; - return MISCHIEF_MANAGED; - } - // 2 clicks: cancel timer - else if (event == EV_2clicks) { - // parent state just rotated through strobe/flasher modes, - // so cancel timer... in case any time was left over from earlier - candle_mode_timer = 0; - return MISCHIEF_MANAGED; - } - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp away from extremes - if (! arg) { - if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } - else if (candle_mode_brightness <= 1) { ramp_direction = 1; } - } - // change brightness, but not too far - candle_mode_brightness += ramp_direction; - if (candle_mode_brightness < 1) candle_mode_brightness = 1; - else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; - return MISCHIEF_MANAGED; - } - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - return MISCHIEF_MANAGED; - } - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - if (candle_mode_brightness > 1) - candle_mode_brightness --; - return MISCHIEF_MANAGED; - } - // 3 clicks: add 30m to candle timer - else if (event == EV_3clicks) { - if (candle_mode_timer < (255 - MINUTES_PER_CANDLE_HALFHOUR)) { - // add 30m to the timer - candle_mode_timer += MINUTES_PER_CANDLE_HALFHOUR; - // blink to confirm - set_level(actual_level + 32); - delay_4ms(2); - } - return MISCHIEF_MANAGED; - } - // clock tick: animate candle brightness - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - - // self-timer dims the light during the final minute - uint8_t subtract = 0; - if (candle_mode_timer == 1) { - subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE) - * ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4)) - >> 8; - } - // we passed a minute mark, decrease timer if it's running - if ((arg & (TICKS_PER_CANDLE_MINUTE-1)) == (TICKS_PER_CANDLE_MINUTE - 1)) { - if (candle_mode_timer > 0) { - candle_mode_timer --; - //set_level(0); delay_4ms(2); - // if the timer ran out, shut off - if (! candle_mode_timer) { - set_state(off_state, 0); - } - } - } - // 3-oscillator synth for a relatively organic pattern - uint8_t add; - add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) - + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) - + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); - int8_t brightness = candle_mode_brightness + add - subtract; - if (brightness < 0) { brightness = 0; } - set_level(brightness); - - // wave1: slow random LFO - // TODO: make wave slower and more erratic? - if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; - // wave2: medium-speed erratic LFO - candle_wave2 += candle_wave2_speed; - // wave3: erratic fast wave - candle_wave3 += pseudo_rand() % 37; - // S&H on wave2 frequency to make it more erratic - if ((pseudo_rand() & 0b00111111) == 0) - candle_wave2_speed = pseudo_rand() % 13; - // downward sawtooth on wave2 depth to simulate stabilizing - if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) - candle_wave2_depth --; - // random sawtooth retrigger - if (pseudo_rand() == 0) { - // random amplitude - //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); - //candle_wave3_depth = 5; - candle_wave2 = 0; - } - // downward sawtooth on wave3 depth to simulate stabilizing - if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) - candle_wave3_depth --; - if ((pseudo_rand() & 0b01111111) == 0) - // random amplitude - //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} -#endif // #ifdef USE_CANDLE_MODE - - -#ifdef USE_BORING_STROBE_STATE -uint8_t boring_strobe_state(Event event, uint16_t arg) { - // police strobe and SOS, meh - // 'st' reduces ROM size by avoiding access to a volatile var - // (maybe I should just make it nonvolatile?) - uint8_t st = boring_strobe_type; - - if (event == EV_enter_state) { - return MISCHIEF_MANAGED; - } - // 1 click: off - else if (event == EV_1click) { - // reset to police strobe for next time - boring_strobe_type = 0; - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - boring_strobe_type = (st + 1) % NUM_BORING_STROBES; - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} - -#ifdef USE_POLICE_STROBE_MODE -inline void police_strobe_iter() { - // one iteration of main loop() - // flash at 16 Hz then 8 Hz, 8 times each - for (uint8_t del=41; del<100; del+=41) { - for (uint8_t f=0; f<8; f++) { - set_level(STROBE_BRIGHTNESS); - nice_delay_ms(del >> 1); - set_level(0); - nice_delay_ms(del); - } - } -} -#endif -#endif // #ifdef USE_BORING_STROBE_STATE - #ifdef USE_SOS_MODE #ifdef USE_SOS_MODE_IN_BLINKY_GROUP uint8_t sos_state(Event event, uint16_t arg) { @@ -1740,44 +1140,6 @@ uint8_t tempcheck_state(Event event, uint16_t arg) { #endif -#ifdef USE_BEACON_MODE -uint8_t beacon_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return MISCHIEF_MANAGED; - } - // TODO: use sleep ticks to measure time between pulses, - // to save power - // 2 clicks: next mode - else if (event == EV_2clicks) { - #ifdef USE_SOS_MODE_IN_BLINKY_GROUP - set_state(sos_state, 0); - #elif defined(USE_THERMAL_REGULATION) - set_state(tempcheck_state, 0); - #elif defined(USE_BATTCHECK) - set_state(battcheck_state, 0); - #endif - return MISCHIEF_MANAGED; - } - // hold: configure beacon timing - else if (event == EV_click1_hold) { - if (0 == (arg % TICKS_PER_SECOND)) { - blink_confirm(1); - } - return MISCHIEF_MANAGED; - } - // release hold: save new timing - else if (event == EV_click1_hold_release) { - beacon_seconds = 1 + (arg / TICKS_PER_SECOND); - save_config(); - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} -#endif // #ifdef USE_BEACON_MODE - - #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) { @@ -1826,149 +1188,6 @@ uint8_t goodnight_state(Event event, uint16_t arg) { #endif -uint8_t lockout_state(Event event, uint16_t arg) { - #ifdef MOON_DURING_LOCKOUT_MODE - // momentary(ish) moon mode during lockout - // button is being held - #ifdef USE_AUX_RGB_LEDS - // don't turn on during RGB aux LED configuration - if (event == EV_click3_hold) { set_level(0); } else - #endif - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - #ifdef LOCKOUT_MOON_LOWEST - // Use lowest moon configured - uint8_t lvl = ramp_floors[0]; - if (ramp_floors[1] < lvl) lvl = ramp_floors[1]; - set_level(lvl); - #elif defined(LOCKOUT_MOON_FANCY) - if ((event & 0x0f) == 2) { - set_level(ramp_floors[ramp_style^1]); - } else { - set_level(ramp_floors[ramp_style]); - } - #else - // Use moon from current ramp - set_level(nearest_level(1)); - #endif - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); - } - #endif - - // regular event handling - // conserve power while locked out - // (allow staying awake long enough to exit, but otherwise - // be persistent about going back to sleep every few seconds - // even if the user keeps pressing the button) - #ifdef USE_INDICATOR_LED - if (event == EV_enter_state) { - indicator_led(indicator_led_mode >> 2); - } else - #elif defined(USE_AUX_RGB_LEDS) - if (event == EV_enter_state) { - rgb_led_update(rgb_led_lockout_mode, 0); - } else - #endif - if (event == EV_tick) { - if (arg > HOLD_TIMEOUT) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode >> 2); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(rgb_led_lockout_mode, arg); - #endif - } - return MISCHIEF_MANAGED; - } - #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) - else if (event == EV_sleep_tick) { - #if defined(USE_INDICATOR_LED) - if ((indicator_led_mode & 0b00001100) == 0b00001100) { - indicator_blink(arg); - } - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(rgb_led_lockout_mode, arg); - #endif - return MISCHIEF_MANAGED; - } - #endif - // 4 clicks: exit and turn on - else if (event == EV_4clicks) { - blink_confirm(1); - set_state(steady_state, memorized_level); - return MISCHIEF_MANAGED; - } - // 4 clicks, but hold last: exit and start at floor - else if (event == EV_click4_hold) { - blink_confirm(1); - set_state(steady_state, 1); - return MISCHIEF_MANAGED; - } - - ////////// Every action below here is blocked in the simple UI ////////// - #ifdef USE_SIMPLE_UI - if (simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - #if defined(USE_INDICATOR_LED) - // 7 clicks: rotate through indicator LED modes (lockout mode) - else if (event == EV_7clicks) { - #if defined(USE_INDICATOR_LED) - uint8_t mode = indicator_led_mode >> 2; - #ifdef TICK_DURING_STANDBY - mode = (mode + 1) & 3; - #else - mode = (mode + 1) % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); - indicator_led(mode); - #elif defined(USE_AUX_RGB_LEDS) - #endif - 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_lockout_mode >> 4) + 1; - mode = mode % RGB_LED_NUM_PATTERNS; - rgb_led_lockout_mode = (mode << 4) | (rgb_led_lockout_mode & 0x0f); - rgb_led_update(rgb_led_lockout_mode, 0); - save_config(); - blink_confirm(1); - return MISCHIEF_MANAGED; - } - // 7H: 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_lockout_mode & 0x0f) + 1; - mode = mode % RGB_LED_NUM_COLORS; - rgb_led_lockout_mode = mode | (rgb_led_lockout_mode & 0xf0); - //save_config(); - } - rgb_led_update(rgb_led_lockout_mode, arg); - return MISCHIEF_MANAGED; - } - // 7H, release: save new color - else if (event == EV_click7_hold_release) { - setting_rgb_mode_now = 0; - save_config(); - return MISCHIEF_MANAGED; - } - #endif - - return EVENT_NOT_HANDLED; -} - - #ifdef USE_MOMENTARY_MODE uint8_t momentary_state(Event event, uint16_t arg) { // TODO: momentary strobe here? (for light painting) @@ -2040,41 +1259,6 @@ uint8_t version_check_state(Event event, uint16_t arg) { #endif -// ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()) { - static uint8_t config_step; - if (event == EV_enter_state) { - config_step = 0; - set_level(0); - return MISCHIEF_MANAGED; - } - // advance forward through config steps - else if (event == EV_tick) { - if (config_step < num_config_steps) { - push_state(number_entry_state, config_step + 1); - } - else { - // TODO: blink out some sort of success pattern - savefunc(); - save_config(); - //set_state(retstate, retval); - pop_state(); - } - return MISCHIEF_MANAGED; - } - // an option was set (return from number_entry_state) - else if (event == EV_reenter_state) { - config_state_values[config_step] = number_entry_value; - config_step ++; - return MISCHIEF_MANAGED; - } - //return EVENT_NOT_HANDLED; - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - #ifdef USE_RAMP_CONFIG void ramp_config_save() { // parse values @@ -2131,92 +1315,6 @@ uint8_t thermal_config_state(Event event, uint16_t arg) { #endif // #ifdef USE_THERMAL_REGULATION -uint8_t number_entry_state(Event event, uint16_t arg) { - static uint8_t value; - static uint8_t blinks_left; - static uint8_t entry_step; - static uint16_t wait_ticks; - if (event == EV_enter_state) { - value = 0; - blinks_left = arg; - entry_step = 0; - wait_ticks = 0; - return MISCHIEF_MANAGED; - } - // advance through the process: - // 0: wait a moment - // 1: blink out the 'arg' value - // 2: wait a moment - // 3: "buzz" while counting clicks - // 4: save and exit - else if (event == EV_tick) { - // wait a moment - if ((entry_step == 0) || (entry_step == 2)) { - if (wait_ticks < TICKS_PER_SECOND/2) - wait_ticks ++; - else { - entry_step ++; - wait_ticks = 0; - } - } - // blink out the option number - else if (entry_step == 1) { - if (blinks_left) { - if ((wait_ticks & 31) == 10) { - set_level(RAMP_SIZE/4); - } - else if ((wait_ticks & 31) == 20) { - set_level(0); - } - else if ((wait_ticks & 31) == 31) { - blinks_left --; - } - wait_ticks ++; - } - else { - entry_step ++; - wait_ticks = 0; - } - } - else if (entry_step == 3) { // buzz while waiting for a number to be entered - wait_ticks ++; - // buzz for N seconds after last event - if ((wait_ticks & 3) == 0) { - set_level(RAMP_SIZE/6); - } - else if ((wait_ticks & 3) == 2) { - set_level(RAMP_SIZE/8); - } - // time out after 3 seconds - if (wait_ticks > TICKS_PER_SECOND*3) { - //number_entry_value = value; - set_level(0); - entry_step ++; - } - } - else if (entry_step == 4) { - number_entry_value = value; - pop_state(); - } - return MISCHIEF_MANAGED; - } - // count clicks - else if (event == EV_click1_release) { - empty_event_sequence(); - if (entry_step == 3) { // only count during the "buzz" - value ++; - wait_ticks = 0; - // flash briefly - set_level(RAMP_SIZE/2); - delay_4ms(8/2); - set_level(0); - } - return MISCHIEF_MANAGED; - } - return EVENT_NOT_HANDLED; -} - - // 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) { @@ -2429,55 +1527,6 @@ void rgb_led_voltage_readout(uint8_t bright) { #endif -#ifdef USE_FACTORY_RESET -void factory_reset() { - // display a warning for a few seconds before doing the actual reset, - // so the user has time to abort if they want - #define SPLODEY_TIME 2500 - #define SPLODEY_STEPS 64 - #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS) - uint8_t bright; - uint8_t reset = 1; - // wind up to an explosion - for (bright=0; bright<SPLODEY_STEPS; bright++) { - set_level(bright); - nice_delay_ms(SPLODEY_TIME_PER_STEP/2); - set_level(bright>>1); - nice_delay_ms(SPLODEY_TIME_PER_STEP/2); - if (! button_is_pressed()) { - reset = 0; - break; - } - } - // explode, if button pressed long enough - if (reset) { - #ifdef USE_THERMAL_REGULATION - // auto-calibrate temperature... assume current temperature is 21 C - config_state_values[0] = 21; - config_state_values[1] = 0; - thermal_config_save(); - #endif - // save all settings to eeprom - // (assuming they're all at default because we haven't loaded them yet) - save_config(); - - bright = MAX_LEVEL; - for (; bright > 0; bright--) { - set_level(bright); - nice_delay_ms(SPLODEY_TIME_PER_STEP/8); - } - } - // explosion cancelled, fade away - else { - for (; bright > 0; bright--) { - set_level(bright); - nice_delay_ms(SPLODEY_TIME_PER_STEP/3); - } - } -} -#endif - - void load_config() { if (load_eeprom()) { ramp_style = eeprom[ramp_style_e]; diff --git a/spaghetti-monster/anduril/beacon.c b/spaghetti-monster/anduril/beacon.c new file mode 100644 index 0000000..ac56632 --- /dev/null +++ b/spaghetti-monster/anduril/beacon.c @@ -0,0 +1,72 @@ +/* + * beacon.c: Beacon 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 BEACON_C +#define BEACON_C + +#include "beacon.h" + +inline void beacon_mode_iter() { + // one iteration of main loop() + if (! button_last_state) { + set_level(memorized_level); + nice_delay_ms(100); + set_level(0); + nice_delay_ms(((beacon_seconds) * 1000) - 100); + } +} + +uint8_t beacon_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // TODO: use sleep ticks to measure time between pulses, + // to save power + // 2 clicks: next mode + else if (event == EV_2clicks) { + #ifdef USE_SOS_MODE_IN_BLINKY_GROUP + set_state(sos_state, 0); + #elif defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #elif defined(USE_BATTCHECK) + set_state(battcheck_state, 0); + #endif + return MISCHIEF_MANAGED; + } + // hold: configure beacon timing + else if (event == EV_click1_hold) { + if (0 == (arg % TICKS_PER_SECOND)) { + blink_confirm(1); + } + return MISCHIEF_MANAGED; + } + // release hold: save new timing + else if (event == EV_click1_hold_release) { + beacon_seconds = 1 + (arg / TICKS_PER_SECOND); + save_config(); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/beacon.h b/spaghetti-monster/anduril/beacon.h new file mode 100644 index 0000000..ba885c7 --- /dev/null +++ b/spaghetti-monster/anduril/beacon.h @@ -0,0 +1,31 @@ +/* + * beacon.h: Beacon 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 BEACON_H +#define BEACON_H + +// beacon timing +volatile uint8_t beacon_seconds = 2; + +// beacon mode +uint8_t beacon_state(Event event, uint16_t arg); +inline void beacon_mode_iter(); + + +#endif diff --git a/spaghetti-monster/anduril/cfg-default.h b/spaghetti-monster/anduril/cfg-default.h new file mode 100644 index 0000000..1405422 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-default.h @@ -0,0 +1,103 @@ +/* + * cfg-default.h: Default configuration 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 CFG_DEFAULT_H +#define CFG_DEFAULT_H + +/********* User-configurable options *********/ +#define USE_LVP + +// parameters for this defined below or per-driver +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this + +#define USE_FACTORY_RESET +//#define USE_SOFT_FACTORY_RESET // only needed on models which can't use hold-button-at-boot + +// dual-switch support (second switch is a tail clicky) +// (currently incompatible with factory reset) +//#define START_AT_MEMORIZED_LEVEL + +// include a function to blink out the firmware version +#define USE_VERSION_CHECK + +// short blip when crossing from "click" to "hold" from off +// (helps the user hit moon mode exactly, instead of holding too long +// or too short) +#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T +// short blips while ramping +#define BLINK_AT_RAMP_MIDDLE +//#define BLINK_AT_RAMP_FLOOR +#define BLINK_AT_RAMP_CEIL +//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode + +// ramp down via regular button hold if a ramp-up ended <1s ago +// ("hold, release, hold" ramps down instead of up) +#define USE_REVERSING + +// add a runtime option to switch between automatic memory (default) +// and manual memory (only available if compiled in) +// (manual memory makes 1-click-from-off start at the same level each time) +// (the level can be set explicitly with 5 clicks from on, +// or the user can go back to automatic with click-click-click-click-hold) +#define USE_MANUAL_MEMORY + +// battery readout style (pick one) +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build + +// enable/disable various strobe modes +#define USE_BIKE_FLASHER_MODE +#define USE_PARTY_STROBE_MODE +#define USE_TACTICAL_STROBE_MODE +#define USE_LIGHTNING_MODE +#define USE_CANDLE_MODE + +// enable sunset (goodnight) mode +#define USE_GOODNIGHT_MODE +#define GOODNIGHT_TIME 60 // minutes (approximately) +#define GOODNIGHT_LEVEL 24 // ~11 lm + +// enable beacon mode +#define USE_BEACON_MODE + +// enable momentary mode +#define USE_MOMENTARY_MODE + +// Include a simplified UI for non-enthusiasts? +#define USE_SIMPLE_UI + +// make the ramps configurable by the user +#define USE_RAMP_CONFIG + +// boring strobes nobody really likes, but sometimes flashlight companies want +// (these replace the fun strobe group, +// so don't enable them at the same time as any of the above strobes) +//#define USE_POLICE_STROBE_MODE +//#define USE_SOS_MODE +//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode +//#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group + +// cut clock speed at very low modes for better efficiency +// (defined here so config files can override it) +#define USE_DYNAMIC_UNDERCLOCKING + + +#endif diff --git a/spaghetti-monster/anduril/config-mode.c b/spaghetti-monster/anduril/config-mode.c new file mode 100644 index 0000000..a62cec0 --- /dev/null +++ b/spaghetti-monster/anduril/config-mode.c @@ -0,0 +1,153 @@ +/* + * config-mode.c: Config mode base 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 CONFIG_MODE_C +#define CONFIG_MODE_C + +#include "config-mode.h" + +// general helper function for config modes +uint8_t number_entry_state(Event event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + + +// ask the user for a sequence of numbers, then save them and return to caller +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()) { + static uint8_t config_step; + if (event == EV_enter_state) { + config_step = 0; + set_level(0); + return MISCHIEF_MANAGED; + } + // advance forward through config steps + else if (event == EV_tick) { + if (config_step < num_config_steps) { + push_state(number_entry_state, config_step + 1); + } + else { + // TODO: blink out some sort of success pattern + savefunc(); + save_config(); + //set_state(retstate, retval); + pop_state(); + } + return MISCHIEF_MANAGED; + } + // an option was set (return from number_entry_state) + else if (event == EV_reenter_state) { + config_state_values[config_step] = number_entry_value; + config_step ++; + return MISCHIEF_MANAGED; + } + //return EVENT_NOT_HANDLED; + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + +uint8_t number_entry_state(Event event, uint16_t arg) { + static uint8_t value; + static uint8_t blinks_left; + static uint8_t entry_step; + static uint16_t wait_ticks; + if (event == EV_enter_state) { + value = 0; + blinks_left = arg; + entry_step = 0; + wait_ticks = 0; + return MISCHIEF_MANAGED; + } + // advance through the process: + // 0: wait a moment + // 1: blink out the 'arg' value + // 2: wait a moment + // 3: "buzz" while counting clicks + // 4: save and exit + else if (event == EV_tick) { + // wait a moment + if ((entry_step == 0) || (entry_step == 2)) { + if (wait_ticks < TICKS_PER_SECOND/2) + wait_ticks ++; + else { + entry_step ++; + wait_ticks = 0; + } + } + // blink out the option number + else if (entry_step == 1) { + if (blinks_left) { + if ((wait_ticks & 31) == 10) { + set_level(RAMP_SIZE/4); + } + else if ((wait_ticks & 31) == 20) { + set_level(0); + } + else if ((wait_ticks & 31) == 31) { + blinks_left --; + } + wait_ticks ++; + } + else { + entry_step ++; + wait_ticks = 0; + } + } + else if (entry_step == 3) { // buzz while waiting for a number to be entered + wait_ticks ++; + // buzz for N seconds after last event + if ((wait_ticks & 3) == 0) { + set_level(RAMP_SIZE/6); + } + else if ((wait_ticks & 3) == 2) { + set_level(RAMP_SIZE/8); + } + // time out after 3 seconds + if (wait_ticks > TICKS_PER_SECOND*3) { + //number_entry_value = value; + set_level(0); + entry_step ++; + } + } + else if (entry_step == 4) { + number_entry_value = value; + pop_state(); + } + return MISCHIEF_MANAGED; + } + // count clicks + else if (event == EV_click1_release) { + empty_event_sequence(); + if (entry_step == 3) { // only count during the "buzz" + value ++; + wait_ticks = 0; + // flash briefly + set_level(RAMP_SIZE/2); + delay_4ms(8/2); + set_level(0); + } + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/config-mode.h b/spaghetti-monster/anduril/config-mode.h new file mode 100644 index 0000000..7bf51fc --- /dev/null +++ b/spaghetti-monster/anduril/config-mode.h @@ -0,0 +1,34 @@ +/* + * config-mode.h: Config mode base 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 CONFIG_MODE_H +#define CONFIG_MODE_H + +#include "fsm-events.h" + +#define MAX_CONFIG_VALUES 3 +uint8_t config_state_values[MAX_CONFIG_VALUES]; + +// simple numeric entry config menu +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()); + + +#endif diff --git a/spaghetti-monster/anduril/factory-reset-fsm.h b/spaghetti-monster/anduril/factory-reset-fsm.h new file mode 100644 index 0000000..a8fb0d9 --- /dev/null +++ b/spaghetti-monster/anduril/factory-reset-fsm.h @@ -0,0 +1,28 @@ +/* + * factory-reset-fsm.h: FSM config options to enable factory reset 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 FACTORY_RESET_FSM_H +#define FACTORY_RESET_FSM_H + +#ifdef USE_SOFT_FACTORY_RESET +#define USE_REBOOT +#endif + + +#endif diff --git a/spaghetti-monster/anduril/factory-reset.c b/spaghetti-monster/anduril/factory-reset.c new file mode 100644 index 0000000..fe961b1 --- /dev/null +++ b/spaghetti-monster/anduril/factory-reset.c @@ -0,0 +1,73 @@ +/* + * factory-reset.c: Factory reset functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef FACTORY_RESET_C +#define FACTORY_RESET_C + +#include "factory-reset.h" + +void factory_reset() { + // display a warning for a few seconds before doing the actual reset, + // so the user has time to abort if they want + #define SPLODEY_TIME 2500 + #define SPLODEY_STEPS 64 + #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS) + uint8_t bright; + uint8_t reset = 1; + // wind up to an explosion + for (bright=0; bright<SPLODEY_STEPS; bright++) { + set_level(bright); + nice_delay_ms(SPLODEY_TIME_PER_STEP/2); + set_level(bright>>1); + nice_delay_ms(SPLODEY_TIME_PER_STEP/2); + if (! button_is_pressed()) { + reset = 0; + break; + } + } + // explode, if button pressed long enough + if (reset) { + #ifdef USE_THERMAL_REGULATION + // auto-calibrate temperature... assume current temperature is 21 C + config_state_values[0] = 21; + config_state_values[1] = 0; + thermal_config_save(); + #endif + // save all settings to eeprom + // (assuming they're all at default because we haven't loaded them yet) + save_config(); + + bright = MAX_LEVEL; + for (; bright > 0; bright--) { + set_level(bright); + nice_delay_ms(SPLODEY_TIME_PER_STEP/8); + } + } + // explosion cancelled, fade away + else { + for (; bright > 0; bright--) { + set_level(bright); + nice_delay_ms(SPLODEY_TIME_PER_STEP/3); + } + } +} + + +#endif + diff --git a/spaghetti-monster/anduril/factory-reset.h b/spaghetti-monster/anduril/factory-reset.h new file mode 100644 index 0000000..9f0af38 --- /dev/null +++ b/spaghetti-monster/anduril/factory-reset.h @@ -0,0 +1,26 @@ +/* + * factory-reset.h: Factory reset functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef FACTORY_RESET_H +#define FACTORY_RESET_H + +void factory_reset(); + + +#endif diff --git a/spaghetti-monster/anduril/ff-strobes.c b/spaghetti-monster/anduril/ff-strobes.c new file mode 100644 index 0000000..c0b655e --- /dev/null +++ b/spaghetti-monster/anduril/ff-strobes.c @@ -0,0 +1,66 @@ +/* + * ff-strobes.c: Fireflies Flashlights strobe modes 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 FF_STROBES_C +#define FF_STROBES_C + +#include "ff-strobes.h" + +uint8_t boring_strobe_state(Event event, uint16_t arg) { + // police strobe and SOS, meh + // 'st' reduces ROM size by avoiding access to a volatile var + // (maybe I should just make it nonvolatile?) + uint8_t st = boring_strobe_type; + + if (event == EV_enter_state) { + return MISCHIEF_MANAGED; + } + // 1 click: off + else if (event == EV_1click) { + // reset to police strobe for next time + boring_strobe_type = 0; + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + boring_strobe_type = (st + 1) % NUM_BORING_STROBES; + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + +#ifdef USE_POLICE_STROBE_MODE +inline void police_strobe_iter() { + // one iteration of main loop() + // flash at 16 Hz then 8 Hz, 8 times each + for (uint8_t del=41; del<100; del+=41) { + for (uint8_t f=0; f<8; f++) { + set_level(STROBE_BRIGHTNESS); + nice_delay_ms(del >> 1); + set_level(0); + nice_delay_ms(del); + } + } +} +#endif + + +#endif + diff --git a/spaghetti-monster/anduril/ff-strobes.h b/spaghetti-monster/anduril/ff-strobes.h new file mode 100644 index 0000000..0c57e2c --- /dev/null +++ b/spaghetti-monster/anduril/ff-strobes.h @@ -0,0 +1,32 @@ +/* + * ff-strobes.h: Fireflies Flashlights strobe modes 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 FF_STROBES_H +#define FF_STROBES_H + +uint8_t boring_strobe_state(Event event, uint16_t arg); +volatile uint8_t boring_strobe_type = 0; +void sos_blink(uint8_t num, uint8_t dah); +#ifdef USE_POLICE_STROBE_MODE +inline void police_strobe_iter(); +#endif +#define NUM_BORING_STROBES 2 + + +#endif diff --git a/spaghetti-monster/anduril/lockout.c b/spaghetti-monster/anduril/lockout.c new file mode 100644 index 0000000..56b1051 --- /dev/null +++ b/spaghetti-monster/anduril/lockout.c @@ -0,0 +1,169 @@ +/* + * lockout.c: Lockout 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 LOCKOUT_C +#define LOCKOUT_C + +#include "lockout.h" + +uint8_t lockout_state(Event event, uint16_t arg) { + #ifdef MOON_DURING_LOCKOUT_MODE + // momentary(ish) moon mode during lockout + // button is being held + #ifdef USE_AUX_RGB_LEDS + // don't turn on during RGB aux LED configuration + if (event == EV_click3_hold) { set_level(0); } else + #endif + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + #ifdef LOCKOUT_MOON_LOWEST + // Use lowest moon configured + uint8_t lvl = ramp_floors[0]; + if (ramp_floors[1] < lvl) lvl = ramp_floors[1]; + set_level(lvl); + #elif defined(LOCKOUT_MOON_FANCY) + if ((event & 0x0f) == 2) { + set_level(ramp_floors[ramp_style^1]); + } else { + set_level(ramp_floors[ramp_style]); + } + #else + // Use moon from current ramp + set_level(nearest_level(1)); + #endif + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + set_level(0); + } + #endif + + // regular event handling + // conserve power while locked out + // (allow staying awake long enough to exit, but otherwise + // be persistent about going back to sleep every few seconds + // even if the user keeps pressing the button) + #ifdef USE_INDICATOR_LED + if (event == EV_enter_state) { + indicator_led(indicator_led_mode >> 2); + } else + #elif defined(USE_AUX_RGB_LEDS) + if (event == EV_enter_state) { + rgb_led_update(rgb_led_lockout_mode, 0); + } else + #endif + if (event == EV_tick) { + if (arg > HOLD_TIMEOUT) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode >> 2); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(rgb_led_lockout_mode, arg); + #endif + } + return MISCHIEF_MANAGED; + } + #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) + else if (event == EV_sleep_tick) { + #if defined(USE_INDICATOR_LED) + if ((indicator_led_mode & 0b00001100) == 0b00001100) { + indicator_blink(arg); + } + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(rgb_led_lockout_mode, arg); + #endif + return MISCHIEF_MANAGED; + } + #endif + // 4 clicks: exit and turn on + else if (event == EV_4clicks) { + blink_confirm(1); + set_state(steady_state, memorized_level); + return MISCHIEF_MANAGED; + } + // 4 clicks, but hold last: exit and start at floor + else if (event == EV_click4_hold) { + blink_confirm(1); + set_state(steady_state, 1); + return MISCHIEF_MANAGED; + } + + ////////// Every action below here is blocked in the simple UI ////////// + #ifdef USE_SIMPLE_UI + if (simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + #if defined(USE_INDICATOR_LED) + // 7 clicks: rotate through indicator LED modes (lockout mode) + else if (event == EV_7clicks) { + #if defined(USE_INDICATOR_LED) + uint8_t mode = indicator_led_mode >> 2; + #ifdef TICK_DURING_STANDBY + mode = (mode + 1) & 3; + #else + mode = (mode + 1) % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); + indicator_led(mode); + #elif defined(USE_AUX_RGB_LEDS) + #endif + 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_lockout_mode >> 4) + 1; + mode = mode % RGB_LED_NUM_PATTERNS; + rgb_led_lockout_mode = (mode << 4) | (rgb_led_lockout_mode & 0x0f); + rgb_led_update(rgb_led_lockout_mode, 0); + save_config(); + blink_confirm(1); + return MISCHIEF_MANAGED; + } + // 7H: 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_lockout_mode & 0x0f) + 1; + mode = mode % RGB_LED_NUM_COLORS; + rgb_led_lockout_mode = mode | (rgb_led_lockout_mode & 0xf0); + //save_config(); + } + rgb_led_update(rgb_led_lockout_mode, arg); + return MISCHIEF_MANAGED; + } + // 7H, release: save new color + else if (event == EV_click7_hold_release) { + setting_rgb_mode_now = 0; + save_config(); + return MISCHIEF_MANAGED; + } + #endif + + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/lockout.h b/spaghetti-monster/anduril/lockout.h new file mode 100644 index 0000000..4268595 --- /dev/null +++ b/spaghetti-monster/anduril/lockout.h @@ -0,0 +1,31 @@ +/* + * lockout.h: Lockout 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 LOCKOUT_H +#define LOCKOUT_H + +// should lockout mode function as a momentary moon mode? +#define MOON_DURING_LOCKOUT_MODE +// if enabled, 2nd lockout click goes to the other ramp's floor level +#define LOCKOUT_MOON_FANCY +// soft lockout +uint8_t lockout_state(Event event, uint16_t arg); + + +#endif diff --git a/spaghetti-monster/anduril/strobes-fsm.h b/spaghetti-monster/anduril/strobes-fsm.h new file mode 100644 index 0000000..6514d5f --- /dev/null +++ b/spaghetti-monster/anduril/strobes-fsm.h @@ -0,0 +1,35 @@ +/* + * strobes-fsm.h: FSM config for strobe modes 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 STROBES_FSM_H +#define STROBES_FSM_H + +// enable the random number generator if we need it +#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) +#define USE_PSEUDO_RAND +#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 new file mode 100644 index 0000000..42d085d --- /dev/null +++ b/spaghetti-monster/anduril/strobes.c @@ -0,0 +1,403 @@ +/* + * strobes.c: Strobe modes 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 STROBES_C +#define STROBES_C + +#include "strobes.h" + +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + + // 'st' reduces ROM size by avoiding access to a volatile var + // (maybe I should just make it nonvolatile?) + strobe_mode_te st = strobe_type; + + #ifdef USE_MOMENTARY_MODE + momentary_mode = 1; // 0 = ramping, 1 = strobes + #endif + + #ifdef USE_CANDLE_MODE + // pass all events to candle mode, when it's active + // (the code is in its own pseudo-state to keep things cleaner) + if (st == candle_mode_e) { + candle_mode_state(event, arg); + } + #endif + + if (0) {} // placeholder + // init anything which needs to be initialized + else if (event == EV_enter_state) { + ramp_direction = 1; + return MISCHIEF_MANAGED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + strobe_type = (st + 1) % NUM_STROBES; + save_config(); + return MISCHIEF_MANAGED; + } + // hold: change speed (go faster) + // or change brightness (brighter) + else if (event == EV_click1_hold) { + if (0) {} // placeholder + + // party / tactical strobe faster + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + uint8_t d = strobe_delays[st]; + d -= ramp_direction; + if (d < 8) d = 8; + else if (d > 254) d = 254; + strobe_delays[st] = d; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode brighter + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + bike_flasher_brightness += ramp_direction; + if (bike_flasher_brightness < 2) bike_flasher_brightness = 2; + else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL; + set_level(bike_flasher_brightness); + } + #endif + + return MISCHIEF_MANAGED; + } + // reverse ramp direction on hold release + // ... and save new strobe settings + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + save_config(); + return MISCHIEF_MANAGED; + } + // click, hold: change speed (go slower) + // or change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + + if (0) {} // placeholder + + // party / tactical strobe slower + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + if (strobe_delays[st] < 255) strobe_delays[st] ++; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode dimmer + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + if (bike_flasher_brightness > 2) + bike_flasher_brightness --; + set_level(bike_flasher_brightness); + } + #endif + + return MISCHIEF_MANAGED; + } + // release hold: save new strobe settings + else if (event == EV_click2_hold_release) { + save_config(); + return MISCHIEF_MANAGED; + } + #ifdef USE_MOMENTARY_MODE + // 5 clicks: go to momentary mode (momentary strobe) + else if (event == EV_5clicks) { + set_state(momentary_state, 0); + set_level(0); + return MISCHIEF_MANAGED; + } + #endif + #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) + // clock tick: bump the random seed + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + + pseudo_rand_seed += arg; + return MISCHIEF_MANAGED; + } + #endif + return EVENT_NOT_HANDLED; +} +#endif // ifdef USE_STROBE_STATE + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +inline void party_tactical_strobe_mode_iter(uint8_t st) { + // one iteration of main loop() + uint8_t del = strobe_delays[st]; + // TODO: make tac strobe brightness configurable? + set_level(STROBE_BRIGHTNESS); + if (0) {} // placeholde0 + #ifdef USE_PARTY_STROBE_MODE + else if (st == party_strobe_e) { // party strobe + #ifdef PARTY_STROBE_ONTIME + nice_delay_ms(PARTY_STROBE_ONTIME); + #else + if (del < 42) delay_zero(); + else nice_delay_ms(1); + #endif + } + #endif + #ifdef USE_TACTICAL_STROBE_MODE + else { //tactical strobe + nice_delay_ms(del >> 1); + } + #endif + set_level(0); + nice_delay_ms(del); // no return check necessary on final delay +} +#endif + +#ifdef USE_LIGHTNING_MODE +inline void lightning_storm_iter() { + // one iteration of main loop() + int16_t brightness; + uint16_t rand_time; + + // turn the emitter on at a random level, + // for a random amount of time between 1ms and 32ms + //rand_time = 1 << (pseudo_rand() % 7); + rand_time = pseudo_rand() & 63; + brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 + brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now + brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) + if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; + set_level(brightness); + nice_delay_ms(rand_time); + + // decrease the brightness somewhat more gradually, like lightning + uint8_t stepdown = brightness >> 3; + if (stepdown < 1) stepdown = 1; + while(brightness > 1) { + nice_delay_ms(rand_time); + brightness -= stepdown; + if (brightness < 0) brightness = 0; + set_level(brightness); + /* + if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { + brightness <<= 1; + set_level(brightness); + } + */ + if (! (pseudo_rand() & 3)) { + nice_delay_ms(rand_time); + set_level(brightness>>1); + } + } + + // turn the emitter off, + // for a random amount of time between 1ms and 8192ms + // (with a low bias) + rand_time = 1 << (pseudo_rand() % 13); + rand_time += pseudo_rand() % rand_time; + set_level(0); + nice_delay_ms(rand_time); // no return check necessary on final delay +} +#endif + +#ifdef USE_BIKE_FLASHER_MODE +inline void bike_flasher_iter() { + // one iteration of main loop() + uint8_t burst = bike_flasher_brightness << 1; + if (burst > MAX_LEVEL) burst = MAX_LEVEL; + for(uint8_t i=0; i<4; i++) { + set_level(burst); + nice_delay_ms(5); + set_level(bike_flasher_brightness); + nice_delay_ms(65); + } + nice_delay_ms(720); // no return check necessary on final delay +} +#endif + +#ifdef USE_CANDLE_MODE +uint8_t candle_mode_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) + static uint8_t candle_wave1 = 0; + static uint8_t candle_wave2 = 0; + static uint8_t candle_wave3 = 0; + static uint8_t candle_wave2_speed = 0; + // these should add up to 100 + #define CANDLE_WAVE1_MAXDEPTH 30 + #define CANDLE_WAVE2_MAXDEPTH 45 + #define CANDLE_WAVE3_MAXDEPTH 25 + static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_mode_brightness = 24; + static uint8_t candle_mode_timer = 0; + #define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds + #define MINUTES_PER_CANDLE_HALFHOUR 27 // ish + + if (event == EV_enter_state) { + candle_mode_timer = 0; // in case any time was left over from earlier + ramp_direction = 1; + return MISCHIEF_MANAGED; + } + // 2 clicks: cancel timer + else if (event == EV_2clicks) { + // parent state just rotated through strobe/flasher modes, + // so cancel timer... in case any time was left over from earlier + candle_mode_timer = 0; + return MISCHIEF_MANAGED; + } + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp away from extremes + if (! arg) { + if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } + else if (candle_mode_brightness <= 1) { ramp_direction = 1; } + } + // change brightness, but not too far + candle_mode_brightness += ramp_direction; + if (candle_mode_brightness < 1) candle_mode_brightness = 1; + else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; + return MISCHIEF_MANAGED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + return MISCHIEF_MANAGED; + } + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + if (candle_mode_brightness > 1) + candle_mode_brightness --; + return MISCHIEF_MANAGED; + } + // 3 clicks: add 30m to candle timer + else if (event == EV_3clicks) { + if (candle_mode_timer < (255 - MINUTES_PER_CANDLE_HALFHOUR)) { + // add 30m to the timer + candle_mode_timer += MINUTES_PER_CANDLE_HALFHOUR; + // blink to confirm + set_level(actual_level + 32); + delay_4ms(2); + } + return MISCHIEF_MANAGED; + } + // clock tick: animate candle brightness + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + + // self-timer dims the light during the final minute + uint8_t subtract = 0; + if (candle_mode_timer == 1) { + subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE) + * ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4)) + >> 8; + } + // we passed a minute mark, decrease timer if it's running + if ((arg & (TICKS_PER_CANDLE_MINUTE-1)) == (TICKS_PER_CANDLE_MINUTE - 1)) { + if (candle_mode_timer > 0) { + candle_mode_timer --; + //set_level(0); delay_4ms(2); + // if the timer ran out, shut off + if (! candle_mode_timer) { + set_state(off_state, 0); + } + } + } + // 3-oscillator synth for a relatively organic pattern + uint8_t add; + add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) + + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) + + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); + int8_t brightness = candle_mode_brightness + add - subtract; + if (brightness < 0) { brightness = 0; } + set_level(brightness); + + // wave1: slow random LFO + // TODO: make wave slower and more erratic? + if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; + // wave2: medium-speed erratic LFO + candle_wave2 += candle_wave2_speed; + // wave3: erratic fast wave + candle_wave3 += pseudo_rand() % 37; + // S&H on wave2 frequency to make it more erratic + if ((pseudo_rand() & 0b00111111) == 0) + candle_wave2_speed = pseudo_rand() % 13; + // downward sawtooth on wave2 depth to simulate stabilizing + if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) + candle_wave2_depth --; + // random sawtooth retrigger + if (pseudo_rand() == 0) { + // random amplitude + //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); + //candle_wave3_depth = 5; + candle_wave2 = 0; + } + // downward sawtooth on wave3 depth to simulate stabilizing + if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) + candle_wave3_depth --; + if ((pseudo_rand() & 0b01111111) == 0) + // random amplitude + //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); + return MISCHIEF_MANAGED; + } + 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 + + +#ifdef USE_BORING_STROBE_STATE +#include "ff-strobes.c" +#endif + + +#endif + diff --git a/spaghetti-monster/anduril/strobes.h b/spaghetti-monster/anduril/strobes.h new file mode 100644 index 0000000..8cfc4a7 --- /dev/null +++ b/spaghetti-monster/anduril/strobes.h @@ -0,0 +1,100 @@ +/* + * strobes.h: Strobe modes 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 STROBES_H +#define STROBES_H + +// internal numbering for strobe modes +#ifdef USE_STROBE_STATE +typedef enum { + #ifdef USE_PARTY_STROBE_MODE + party_strobe_e, + #endif + #ifdef USE_TACTICAL_STROBE_MODE + tactical_strobe_e, + #endif + #ifdef USE_LIGHTNING_MODE + lightning_storm_e, + #endif + #ifdef USE_CANDLE_MODE + candle_mode_e, + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_e, + #endif + strobe_mode_END +} strobe_mode_te; + +const int NUM_STROBES = strobe_mode_END; + +// which strobe mode is active? +#ifdef USE_CANDLE_MODE +volatile strobe_mode_te strobe_type = candle_mode_e; +#else +volatile strobe_mode_te strobe_type = 0; +#endif +#endif + + +// full FET strobe can be a bit much... use max regulated level instead, +// if there's a bright enough regulated level +#ifndef STROBE_BRIGHTNESS +#ifdef MAX_Nx7135 +#define STROBE_BRIGHTNESS MAX_Nx7135 +#else +#define STROBE_BRIGHTNESS MAX_LEVEL +#endif +#endif + +// party and tactical strobes +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg); +#endif + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +// party / tactical strobe timing +volatile uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz +inline void party_tactical_strobe_mode_iter(uint8_t st); +#endif + +#ifdef USE_LIGHTNING_MODE +inline void lightning_storm_iter(); +#endif + +// bike mode config options +#ifdef USE_BIKE_FLASHER_MODE +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); +#ifndef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 25 +#endif +#endif + + +#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP) +#define USE_BORING_STROBE_STATE +#include "ff-strobes.h" +#endif + +#endif diff --git a/spaghetti-monster/anduril/tint-ramping.c b/spaghetti-monster/anduril/tint-ramping.c new file mode 100644 index 0000000..0196bc7 --- /dev/null +++ b/spaghetti-monster/anduril/tint-ramping.c @@ -0,0 +1,87 @@ +/* + * tint-ramping.c: Tint ramping functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TINT_RAMPING_C +#define TINT_RAMPING_C + +#include "tint-ramping.h" + +uint8_t tint_ramping_state(Event event, uint16_t arg) { + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + + // click, click, hold: change the tint + if (event == EV_click3_hold) { + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_HANDLED; + + // change normal tints + if ((tint_ramp_direction > 0) && (tint < 254)) { + tint += 1; + } + else if ((tint_ramp_direction < 0) && (tint > 1)) { + tint -= 1; + } + // if the user kept pressing long enough, go the final step + if (past_edge_counter == 64) { + past_edge_counter ++; + tint ^= 1; // 0 -> 1, 254 -> 255 + blip(); + } + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + // count up but don't wrap back to zero + if (past_edge_counter < 255) past_edge_counter ++; + } + prev_tint = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (tint == 0) tint_ramp_direction = 1; + else if (tint == 255) tint_ramp_direction = -1; + // remember tint after battery change + save_config(); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + + +#endif + diff --git a/spaghetti-monster/anduril/tint-ramping.h b/spaghetti-monster/anduril/tint-ramping.h new file mode 100644 index 0000000..bd86659 --- /dev/null +++ b/spaghetti-monster/anduril/tint-ramping.h @@ -0,0 +1,27 @@ +/* + * tint-ramping.h: Tint ramping functions for Anduril. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TINT_RAMPING_H +#define TINT_RAMPING_H + +// not actually a mode, more of a fallback under other modes +uint8_t tint_ramping_state(Event event, uint16_t arg); + + +#endif diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c index 82be745..eaa6fb3 100644 --- a/spaghetti-monster/fsm-misc.c +++ b/spaghetti-monster/fsm-misc.c @@ -222,6 +222,7 @@ 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; |
