diff options
| author | Selene ToyKeeper | 2017-08-26 02:37:33 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2017-08-26 02:37:33 -0600 |
| commit | 5bd3a4edd1e0b324118c9d875b33994bfc85c0dc (patch) | |
| tree | f522cdb9768a9b88421775f71e098901e75ad27a | |
| parent | Added a temperature check mode to ramping-ui, mostly for testing purposes. (diff) | |
| download | anduril-5bd3a4edd1e0b324118c9d875b33994bfc85c0dc.tar.gz anduril-5bd3a4edd1e0b324118c9d875b33994bfc85c0dc.tar.bz2 anduril-5bd3a4edd1e0b324118c9d875b33994bfc85c0dc.zip | |
Added DarkHorse, a clone of the ZebraLight UI.
Still needs strobe, beacon, and memory which lasts across battery changes.
| -rw-r--r-- | spaghetti-monster/darkhorse.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/spaghetti-monster/darkhorse.c b/spaghetti-monster/darkhorse.c new file mode 100644 index 0000000..b77f541 --- /dev/null +++ b/spaghetti-monster/darkhorse.c @@ -0,0 +1,290 @@ +/* + * DarkHorse: Improved ZebraLight clone UI for SpaghettiMonster. + * + * 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/>. + */ + +#define FSM_EMISAR_D4_LAYOUT +#define USE_LVP +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 +#define USE_DELAY_4MS +#define USE_RAMPING +#define RAMP_LENGTH 150 +#define USE_BATTCHECK +#define BATTCHECK_4bars +#include "spaghetti-monster.h" + +// FSM states +uint8_t off_state(EventPtr event, uint16_t arg); +uint8_t low_mode_state(EventPtr event, uint16_t arg); +uint8_t med_mode_state(EventPtr event, uint16_t arg); +uint8_t hi_mode_state(EventPtr event, uint16_t arg); +//uint8_t strobe_state(EventPtr event, uint16_t arg); +//uint8_t beacon_state(EventPtr event, uint16_t arg); +#ifdef USE_BATTCHECK +uint8_t battcheck_state(EventPtr event, uint16_t arg); +#endif +// Not a FSM state, just handles stuff common to all low/med/hi states +uint8_t any_mode_state(EventPtr event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes); + +// toggle between L1/L2, M1/M2, H1/H2 +uint8_t L1 = 1; +uint8_t M1 = 1; +uint8_t H1 = 1; +// brightness for L2, M2, H2 (valid range 1 to 3 inclusive) +uint8_t L2 = 1; +uint8_t M2 = 1; +uint8_t H2 = 1; +// mode groups, ish +uint8_t low_modes[] = {12, 1, 4, 9}; // 3.3 lm, 2.0 lm, 0.8 lm, 0.3 lm +uint8_t med_modes[] = {56, 21, 29, 37}; // 101 lm, 35 lm, 20 lm, 10 lm +uint8_t hi_modes[] = {MAX_LEVEL, 81, 96, 113}; // 1500 lm, 678 lm, 430 lm, 270 lm + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +void set_any_mode(uint8_t primary, uint8_t secondary, uint8_t *modes) { + // primary (H1/M1/L1) + if (primary) { + set_level(modes[0]); + } + // secondary (H2/M2/L2) + else { + set_level(modes[secondary]); + } + target_level = actual_level; +} + +inline void set_low_mode() { set_any_mode(L1, L2, low_modes); } +inline void set_med_mode() { set_any_mode(M1, M2, med_modes); } +inline void set_hi_mode() { set_any_mode(H1, H2, hi_modes); } + + +uint8_t off_state(EventPtr event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + // sleep while off (lower power use) + standby_mode(); + return 0; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_low_mode(); + return 0; + } + // 1 click (before timeout): go to high level, but allow abort for double click + else if (event == EV_click1_release) { + set_hi_mode(); + return 0; + } + // 1 click: high mode + else if (event == EV_1click) { + set_state(hi_mode_state, 0); + return 0; + } + // click, hold (initially): go to medium mode, but allow abort + else if (event == EV_click2_press) { + set_med_mode(); + return 0; + } + // 2 clicks: medium mode + else if (event == EV_2clicks) { + set_state(med_mode_state, 0); + return 0; + } + // click, click, hold (initially): light off, prep for blinkies + else if (event == EV_click3_press) { + set_level(0); + return 0; + } + // 3 clicks: strobe mode + /* TODO: implement + else if (event == EV_3clicks) { + set_state(party_strobe_state, 255); + return 0; + } + */ + #ifdef USE_BATTCHECK + // 4 clicks: battcheck mode + else if (event == EV_4clicks) { + set_state(battcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif + // hold: go to low mode, but allow ramping up + else if (event == EV_click1_hold) { + // don't start ramping immediately; + // give the user time to release at low mode + if (arg >= HOLD_TIMEOUT) + set_state(low_mode_state, 0); + return 0; + } + // hold, release quickly: go to low mode + else if (event == EV_click1_hold_release) { + set_state(low_mode_state, 0); + return 0; + } + /* TODO: implement + // click-release-hold: discrete ramp through all levels + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return 0; + } + */ + return 1; +} + + +uint8_t any_mode_state(EventPtr event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes) { + // turn on LED when entering the mode + if (event == EV_enter_state) { + set_any_mode(*primary, *secondary, modes); + return MISCHIEF_MANAGED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: toggle primary/secondary level + else if (event == EV_2clicks) { + *primary ^= 1; + set_any_mode(*primary, *secondary, modes); + return MISCHIEF_MANAGED; + } + // click-release-hold: change secondary level + if (event == EV_click2_hold) { + if (arg % HOLD_TIMEOUT == 0) { + *secondary = (*secondary + 1) & 3; + if (! *secondary) *secondary = 1; + *primary = 0; + set_any_mode(*primary, *secondary, modes); + } + return MISCHIEF_MANAGED; + } + #ifdef USE_THERMAL_REGULATION + // TODO: test this on a real light + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + if (actual_level > MAX_LEVEL/4) { + uint8_t stepdown = actual_level - arg; + if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; + set_level(stepdown); + } + return 0; + } + // 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) { + uint8_t stepup = actual_level + (arg>>1); + if (stepup > target_level) stepup = target_level; + set_level(stepup); + } + return 0; + } + #endif + return EVENT_NOT_HANDLED; +} + +uint8_t low_mode_state(EventPtr event, uint16_t arg) { + // hold: change brightness (brighter) + if (event == EV_click1_hold) { + if (arg % HOLD_TIMEOUT == 0) { + set_state(med_mode_state, 0); + } + return MISCHIEF_MANAGED; + } + else return any_mode_state(event, arg, &L1, &L2, low_modes); +} + +uint8_t med_mode_state(EventPtr event, uint16_t arg) { + // hold: change brightness (brighter) + if (event == EV_click1_hold) { + if (arg % HOLD_TIMEOUT == 0) { + set_state(hi_mode_state, 0); + } + return MISCHIEF_MANAGED; + } + else return any_mode_state(event, arg, &M1, &M2, med_modes); +} + +uint8_t hi_mode_state(EventPtr event, uint16_t arg) { + // hold: change brightness (brighter) + if (event == EV_click1_hold) { + if (arg % HOLD_TIMEOUT == 0) { + set_state(low_mode_state, 0); + } + return MISCHIEF_MANAGED; + } + else return any_mode_state(event, arg, &H1, &H2, hi_modes); +} + + +#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; + } + return EVENT_NOT_HANDLED; +} +#endif + + +void low_voltage() { + // TODO: rewrite this + /* + // "step down" from strobe to something low + if (current_state == party_strobe_state) { + set_state(steady_state, RAMP_SIZE/6); + } + */ + if (current_state == hi_mode_state) { + set_state(med_mode_state, 0); + } + else if (current_state == med_mode_state) { + set_state(low_mode_state, 0); + } + else if (current_state == low_mode_state) { + set_state(off_state, 0); + } +} + +void setup() { + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + push_state(off_state, 0); +} + +void loop() { + #ifdef USE_BATTCHECK + if (current_state == battcheck_state) { + nice_delay_ms(500); // wait a moment to measure voltage + battcheck(); + set_state(off_state, 0); + } + #endif +} + + |
