diff options
| author | Selene ToyKeeper | 2018-07-03 21:26:09 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2018-07-03 21:26:09 -0600 |
| commit | df0601f6ad03fb1ebbbe06ff99d1589ef0aeda25 (patch) | |
| tree | 365cfe00cb3e1fde102ca5a16b98fb0730b00cbd /spaghetti-monster | |
| parent | Added hardware profiles and build targets for Emisar D1 and D1S. (diff) | |
| download | anduril-df0601f6ad03fb1ebbbe06ff99d1589ef0aeda25.tar.gz anduril-df0601f6ad03fb1ebbbe06ff99d1589ef0aeda25.tar.bz2 anduril-df0601f6ad03fb1ebbbe06ff99d1589ef0aeda25.zip | |
Added a momentary + Werner-style UI for FSM, because why not.
Based on Anduril, but with most of the features removed.
Diffstat (limited to '')
| l--------- | spaghetti-monster/werner/cfg-blf-gt.h | 1 | ||||
| l--------- | spaghetti-monster/werner/cfg-blf-q8.h | 1 | ||||
| l--------- | spaghetti-monster/werner/cfg-emisar-d1.h | 1 | ||||
| l--------- | spaghetti-monster/werner/cfg-emisar-d1s.h | 1 | ||||
| l--------- | spaghetti-monster/werner/cfg-emisar-d4.h | 1 | ||||
| l--------- | spaghetti-monster/werner/cfg-fw3a.h | 1 | ||||
| -rw-r--r-- | spaghetti-monster/werner/werner.c | 724 |
7 files changed, 730 insertions, 0 deletions
diff --git a/spaghetti-monster/werner/cfg-blf-gt.h b/spaghetti-monster/werner/cfg-blf-gt.h new file mode 120000 index 0000000..5e10228 --- /dev/null +++ b/spaghetti-monster/werner/cfg-blf-gt.h @@ -0,0 +1 @@ +../anduril/cfg-blf-gt.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/cfg-blf-q8.h b/spaghetti-monster/werner/cfg-blf-q8.h new file mode 120000 index 0000000..fe84054 --- /dev/null +++ b/spaghetti-monster/werner/cfg-blf-q8.h @@ -0,0 +1 @@ +../anduril/cfg-blf-q8.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/cfg-emisar-d1.h b/spaghetti-monster/werner/cfg-emisar-d1.h new file mode 120000 index 0000000..f8b4c9b --- /dev/null +++ b/spaghetti-monster/werner/cfg-emisar-d1.h @@ -0,0 +1 @@ +../anduril/cfg-emisar-d1.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/cfg-emisar-d1s.h b/spaghetti-monster/werner/cfg-emisar-d1s.h new file mode 120000 index 0000000..b6cfba6 --- /dev/null +++ b/spaghetti-monster/werner/cfg-emisar-d1s.h @@ -0,0 +1 @@ +../anduril/cfg-emisar-d1s.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/cfg-emisar-d4.h b/spaghetti-monster/werner/cfg-emisar-d4.h new file mode 120000 index 0000000..fb74f2c --- /dev/null +++ b/spaghetti-monster/werner/cfg-emisar-d4.h @@ -0,0 +1 @@ +../anduril/cfg-emisar-d4.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/cfg-fw3a.h b/spaghetti-monster/werner/cfg-fw3a.h new file mode 120000 index 0000000..5bc8b21 --- /dev/null +++ b/spaghetti-monster/werner/cfg-fw3a.h @@ -0,0 +1 @@ +../anduril/cfg-fw3a.h
\ No newline at end of file diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c new file mode 100644 index 0000000..3c55d98 --- /dev/null +++ b/spaghetti-monster/werner/werner.c @@ -0,0 +1,724 @@ +/* + * Werner: Werner-style dual-switch UI for SpaghettiMonster. + * Side click to go up, side hold to go down, tail click for on/off. + * + * Copyright (C) 2018 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/>. + */ + +/********* User-configurable options *********/ +// Physical driver type (uncomment one of the following or define it at the gcc command line) +//#define FSM_EMISAR_D4_DRIVER +//#define FSM_BLF_Q8_DRIVER +//#define FSM_FW3A_DRIVER +//#define FSM_BLF_GT_DRIVER + +#define USE_LVP // FIXME: won't build when this option is turned off + +// parameters for this defined below or per-driver +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this + +// battery readout style (pick one) +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build + +/***** specific settings for known driver types *****/ +#if defined(FSM_BLF_GT_DRIVER) +#include "cfg-blf-gt.h" + +#elif defined(FSM_BLF_Q8_DRIVER) +#include "cfg-blf-q8.h" + +#elif defined(FSM_EMISAR_D4_DRIVER) +#include "cfg-emisar-d4.h" + +#elif defined(FSM_FW3A_DRIVER) +#include "cfg-fw3a.h" + +#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 + + +/********* Configure SpaghettiMonster *********/ +#define USE_DELAY_ZERO +#define USE_RAMPING +#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#define USE_BATTCHECK +#define MAX_CLICKS 4 +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending +#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency + +// auto-detect how many eeprom bytes +#define USE_EEPROM +#ifdef USE_THERMAL_REGULATION +#define EEPROM_BYTES 5 +#else +#define EEPROM_BYTES 3 +#endif +// for mode memory on tail switch +#define USE_EEPROM_WL +#define EEPROM_WL_BYTES 1 + +#include "spaghetti-monster.h" + + +// FSM states +uint8_t off_state(EventPtr event, uint16_t arg); +// simple numeric entry config menu +uint8_t config_state_base(EventPtr 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(EventPtr event, uint16_t arg); +uint8_t ramp_config_state(EventPtr event, uint16_t arg); +#ifdef USE_BATTCHECK +uint8_t battcheck_state(EventPtr event, uint16_t arg); +#endif +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(EventPtr event, uint16_t arg); +uint8_t thermal_config_state(EventPtr event, uint16_t arg); +#endif + +// general helper function for config modes +uint8_t number_entry_state(EventPtr event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + +void blink_confirm(uint8_t num); + +// remember stuff even after battery was changed +void load_config(); +void save_config(); +void save_config_wl(); + +// default ramp options if not overridden earlier per-driver +#ifndef RAMP_DISCRETE_FLOOR + #define RAMP_DISCRETE_FLOOR 1 +#endif +#ifndef RAMP_DISCRETE_CEIL + #define RAMP_DISCRETE_CEIL RAMP_SIZE +#endif +#ifndef RAMP_DISCRETE_STEPS + #define RAMP_DISCRETE_STEPS 7 +#endif + +// brightness control +uint8_t memorized_level = MAX_1x7135; +// smooth vs discrete ramping +volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; +volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; +volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; +uint8_t ramp_discrete_step_size; // don't set this + +// 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); + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + + +uint8_t off_state(EventPtr event, uint16_t arg) { + // turn emitter off when entering state + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + // let the user know the power is connected + blink_confirm(1); + // but otherwise stay off + set_level(0); + // sleep while off (lower power use) + go_to_standby = 1; + return MISCHIEF_MANAGED; + } + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + } + return MISCHIEF_MANAGED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_level(nearest_level(1)); + return MISCHIEF_MANAGED; + } + // hold: go to lowest level + else if (event == EV_click1_hold) { + // don't start ramping immediately; + // give the user time to release at moon level + if (arg >= HOLD_TIMEOUT) { + set_state(steady_state, 1); + } + return MISCHIEF_MANAGED; + } + // hold, release quickly: go to lowest level + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return MISCHIEF_MANAGED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(nearest_level(memorized_level)); + return MISCHIEF_MANAGED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return MISCHIEF_MANAGED; + } + // 2 clicks (initial press): off, to prep for later events + else if (event == EV_click2_press) { + set_level(0); + return MISCHIEF_MANAGED; + } + // click, hold: go to highest level (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return MISCHIEF_MANAGED; + } + // 2 clicks: highest mode + else if (event == EV_2clicks) { + set_state(steady_state, nearest_level(MAX_LEVEL)); + return MISCHIEF_MANAGED; + } + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif + // 4 clicks: configure ramp + else if (event == EV_4clicks) { + push_state(ramp_config_state, 0); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + + +uint8_t steady_state(EventPtr event, uint16_t arg) { + uint8_t mode_min = ramp_discrete_floor; + uint8_t mode_max = ramp_discrete_ceil; + uint8_t ramp_step_size = ramp_discrete_step_size; + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + // 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 + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(nearest_level(arg)); + return MISCHIEF_MANAGED; + } + // click: brighter + else if (event == EV_click1_release) { + memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + // make sure next click will respond quickly + empty_event_sequence(); + // remember mode for later + save_config_wl(); + return MISCHIEF_MANAGED; + } + // hold: dimmer + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (arg % HOLD_TIMEOUT != 0) { + return MISCHIEF_MANAGED; + } + memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + return MISCHIEF_MANAGED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + save_config_wl(); + return MISCHIEF_MANAGED; + } + #if defined(USE_SET_LEVEL_GRADUALLY) + // gradual thermal regulation + else if (event == EV_tick) { + #ifdef USE_SET_LEVEL_GRADUALLY + // make thermal adjustment speed scale with magnitude + if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { + return MISCHIEF_MANAGED; // adjust slower when not a high mode + } + #ifdef THERM_HARD_TURBO_DROP + else if ((! (actual_level < THERM_FASTER_LEVEL)) + && (actual_level > gradual_target)) { + gradual_tick(); + } + else { + #endif + // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] + uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; + uint8_t diff; + static uint8_t ticks_since_adjust = 0; + ticks_since_adjust ++; + if (gradual_target > actual_level) diff = gradual_target - actual_level; + else { + diff = actual_level - gradual_target; + } + uint8_t magnitude = 0; + #ifndef THERM_HARD_TURBO_DROP + // if we're on a really high mode, drop faster + if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } + #endif + while (diff) { + magnitude ++; + diff >>= 1; + } + uint8_t ticks_per_adjust = intervals[magnitude]; + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + //if (!(arg % ticks_per_adjust)) gradual_tick(); + #ifdef THERM_HARD_TURBO_DROP + } + #endif + #endif + 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) { + #ifdef THERM_HARD_TURBO_DROP + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + #else + set_level(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 (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; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(EventPtr event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + blink_confirm(2); + set_state(tempcheck_state, 0); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} +#endif + +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(EventPtr event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: battcheck mode + else if (event == EV_2clicks) { + blink_confirm(1); + set_state(battcheck_state, 0); + return MISCHIEF_MANAGED; + } + // 4 clicks: thermal config mode + else if (event == EV_4clicks) { + push_state(thermal_config_state, 0); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +// ask the user for a sequence of numbers, then save them and return to caller +uint8_t config_state_base(EventPtr 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; +} + +void ramp_config_save() { + // parse values + uint8_t val; + + val = config_state_values[0]; + if (val) { ramp_discrete_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } + + val = config_state_values[2]; + if (val) ramp_discrete_steps = val; +} + +uint8_t ramp_config_state(EventPtr event, uint16_t arg) { + uint8_t num_config_steps; + num_config_steps = 3; + return config_state_base(event, arg, + num_config_steps, ramp_config_save); +} + + +#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 >> 1) - therm_cal_offset; + therm_cal_offset = val - rawtemp; + } + + val = config_state_values[1]; + if (val) { + // set maximum heat limit + therm_ceil = 30 + val; + } + if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(EventPtr event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} +#endif + + +uint8_t number_entry_state(EventPtr 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) { + // 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_discrete_floor; + uint8_t mode_max = ramp_discrete_ceil; + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + + uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; + ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); + uint8_t this_level = ramp_discrete_floor; + + for(uint8_t i=0; i<ramp_discrete_steps; i++) { + this_level = ramp_discrete_floor + (i * (uint16_t)ramp_range / (ramp_discrete_steps-1)); + int8_t diff = target - this_level; + if (diff < 0) diff = -diff; + if (diff <= (ramp_discrete_step_size>>1)) + return this_level; + } + return this_level; +} + + +void blink_confirm(uint8_t num) { + for (; num>0; num--) { + set_level(MAX_LEVEL/4); + delay_4ms(10/4); + set_level(0); + delay_4ms(100/4); + } +} + + +void load_config() { + if (load_eeprom()) { + ramp_discrete_floor = eeprom[0]; + ramp_discrete_ceil = eeprom[1]; + ramp_discrete_steps = eeprom[2]; + #ifdef USE_THERMAL_REGULATION + therm_ceil = eeprom[3]; + therm_cal_offset = eeprom[4]; + #endif + } + if (load_eeprom_wl()) { + memorized_level = eeprom_wl[0]; + } +} + + +void save_config() { + eeprom[0] = ramp_discrete_floor; + eeprom[1] = ramp_discrete_ceil; + eeprom[2] = ramp_discrete_steps; + #ifdef USE_THERMAL_REGULATION + eeprom[3] = therm_ceil; + eeprom[4] = therm_cal_offset; + #endif + + save_eeprom(); +} + + +void save_config_wl() { + eeprom_wl[0] = memorized_level; + save_eeprom_wl(); +} + + +void low_voltage() { + StatePtr state = current_state; + + // in normal mode, step down or turn off + if (state == steady_state) { + if (actual_level > 1) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + set_level(lvl); + #ifdef USE_THERMAL_REGULATION + target_level = lvl; + #endif + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } +} + + +void setup() { + // dual switch: e-switch + power clicky + // power clicky acts as a momentary mode + load_config(); + + if (button_is_pressed()) + // hold button to go to moon + push_state(off_state, 0); + else + // otherwise use memory + push_state(steady_state, memorized_level); +} + + +void loop() { + + StatePtr state = current_state; + + #ifdef USE_DYNAMIC_UNDERCLOCKING + auto_clock_speed(); + #endif + if (0) {} + + #ifdef USE_BATTCHECK + else if (state == battcheck_state) { + battcheck(); + } + #endif + #ifdef USE_THERMAL_REGULATION + // TODO: blink out therm_ceil during thermal_config_state + else if (state == tempcheck_state) { + blink_num(temperature>>1); + nice_delay_ms(1000); + } + #endif + + #ifdef USE_IDLE_MODE + else { + // doze until next clock tick + idle_mode(); + } + #endif + +} |
