From af011b08919e42736f514ae6ffe045571dbefda9 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 30 Aug 2017 23:15:39 -0600 Subject: Reorganized FSM files, one dir per UI. --- spaghetti-monster/darkhorse/darkhorse.c | 378 ++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 spaghetti-monster/darkhorse/darkhorse.c (limited to 'spaghetti-monster/darkhorse') diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c new file mode 100644 index 0000000..2aa3e90 --- /dev/null +++ b/spaghetti-monster/darkhorse/darkhorse.c @@ -0,0 +1,378 @@ +/* + * 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 . + */ + +#define FSM_EMISAR_D4_DRIVER +#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 +#define DONT_DELAY_AFTER_BATTCHECK +#define USE_EEPROM +#define EEPROM_BYTES 5 +#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_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); + +void load_config(); +void save_config(); + +// 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 +// strobe/beacon modes: +// 0: 0.2 Hz beacon at L1 +// 1: 0.2 Hz beacon at H1 +// 2: 4 Hz strobe at H1 +// 3: 19 Hz strobe at H1 +uint8_t strobe_beacon_mode = 0; + +// deferred "off" so we won't suspend in a weird state +volatile uint8_t go_to_standby = 0; + +#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]); + } + #ifdef USE_THERMAL_REGULATION + target_level = actual_level; + #endif +} + +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) + go_to_standby = 1; + return EVENT_HANDLED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_low_mode(); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to high level, but allow abort for double click + else if (event == EV_click1_release) { + set_hi_mode(); + return EVENT_HANDLED; + } + // 1 click: high mode + else if (event == EV_1click) { + set_state(hi_mode_state, 0); + return EVENT_HANDLED; + } + // click, press (initially): go to medium mode, but allow abort + else if (event == EV_click2_press) { + set_med_mode(); + return EVENT_HANDLED; + } + // 2 clicks: medium mode + else if (event == EV_2clicks) { + set_state(med_mode_state, 0); + return EVENT_HANDLED; + } + // click, click, press (initially): light off, prep for blinkies + else if (event == EV_click3_press) { + set_level(0); + return EVENT_HANDLED; + } + // 3 clicks: strobe mode + else if (event == EV_3clicks) { + set_state(strobe_beacon_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 4 clicks: battcheck mode + else if (event == EV_4clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #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 EVENT_HANDLED; + } + // hold, release quickly: go to low mode + else if (event == EV_click1_hold_release) { + set_state(low_mode_state, 0); + return EVENT_HANDLED; + } + /* TODO: implement + // click-release-hold: discrete ramp through all levels + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + */ + return EVENT_NOT_HANDLED; +} + + +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; + } + // hold: change brightness (low, med, hi, always starting at low) + else if (event == EV_click1_hold) { + uint8_t which = arg % (HOLD_TIMEOUT * 3) / HOLD_TIMEOUT; + switch(which) { + case 0: + set_state(low_mode_state, 0); + break; + case 1: + set_state(med_mode_state, 0); + break; + case 2: + set_state(hi_mode_state, 0); + break; + } + return MISCHIEF_MANAGED; + } + // 2 clicks: toggle primary/secondary level + else if (event == EV_2clicks) { + *primary ^= 1; + set_any_mode(*primary, *secondary, modes); + save_config(); + return MISCHIEF_MANAGED; + } + // click-release-hold: change secondary level + else 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; + } + // click, hold, release: save secondary level + else if (event == EV_click2_hold_release) { + save_config(); + } + #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 EVENT_HANDLED; + } + // 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 EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +uint8_t low_mode_state(EventPtr event, uint16_t arg) { + return any_mode_state(event, arg, &L1, &L2, low_modes); +} + +uint8_t med_mode_state(EventPtr event, uint16_t arg) { + return any_mode_state(event, arg, &M1, &M2, med_modes); +} + +uint8_t hi_mode_state(EventPtr event, uint16_t arg) { + return any_mode_state(event, arg, &H1, &H2, hi_modes); +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(EventPtr event, uint16_t arg) { + return EVENT_NOT_HANDLED; +} +#endif + + +uint8_t strobe_beacon_state(EventPtr event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 1 click (initially): cancel current blink + if (event == EV_click1_release) { + interrupt_nice_delays(); + return MISCHIEF_MANAGED; + } + // 2 clicks: rotate through blinky modes + else if (event == EV_2clicks) { + strobe_beacon_mode = (strobe_beacon_mode + 1) & 3; + save_config(); + interrupt_nice_delays(); + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} + + +void low_voltage() { + 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); + } + // "step down" from blinkies to low + else if (current_state == strobe_beacon_state) { + set_state(low_mode_state, 0); + } +} + +void strobe(uint8_t level, uint16_t ontime, uint16_t offtime) { + set_level(level); + if (! nice_delay_ms(ontime)) return; + set_level(0); + nice_delay_ms(offtime); +} + +void load_config() { + if (load_eeprom()) { + H1 = !(!(eeprom[0] & 0b00000100)); + M1 = !(!(eeprom[0] & 0b00000010)); + L1 = !(!(eeprom[0] & 0b00000001)); + H2 = eeprom[1]; + M2 = eeprom[2]; + L2 = eeprom[3]; + strobe_beacon_mode = eeprom[4]; + } +} + +void save_config() { + eeprom[0] = (H1<<2) | (M1<<1) | (L1); + eeprom[1] = H2; + eeprom[2] = M2; + eeprom[3] = L2; + eeprom[4] = strobe_beacon_mode; + + save_eeprom(); +} + +void setup() { + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + load_config(); + + push_state(off_state, 0); +} + +void loop() { + // deferred "off" so we won't suspend in a weird state + // (like... during the middle of a strobe pulse) + if (go_to_standby) { + go_to_standby = 0; + set_level(0); + standby_mode(); + } + + if (current_state == strobe_beacon_state) { + switch(strobe_beacon_mode) { + // 0.2 Hz beacon at L1 + case 0: + strobe(low_modes[0], 500, 4500); + break; + // 0.2 Hz beacon at H1 + case 1: + strobe(hi_modes[0], 500, 4500); + break; + // 4 Hz tactical strobe at H1 + case 2: + strobe(hi_modes[0], 83, 167); + break; + // 19 Hz tactical strobe at H1 + case 3: + strobe(hi_modes[0], 17, 35); + break; + } + } + + #ifdef USE_BATTCHECK + else if (current_state == battcheck_state) { + nice_delay_ms(500); // wait a moment to measure voltage + battcheck(); + set_state(off_state, 0); + } + #endif +} + + -- cgit v1.2.3 From a419850e536f00549120255a627137faffded47a Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 3 Sep 2017 14:58:22 -0600 Subject: Got the 4th PWM channel to work, ish. (channel 4 is inverted though) Moved go_to_suspend thing into main() instead of making each UI handle that during loop(). Made default_state() optional. Fixed bug where battcheck and other number readouts could interfere with the state which interrupted them. (they would sometimes turn the LED off after the new state had already started) Updated darkhorse's moon levels to match new ramp on D4 hardware. --- spaghetti-monster/darkhorse/darkhorse.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'spaghetti-monster/darkhorse') diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c index 2aa3e90..97053b5 100644 --- a/spaghetti-monster/darkhorse/darkhorse.c +++ b/spaghetti-monster/darkhorse/darkhorse.c @@ -55,7 +55,7 @@ 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 low_modes[] = {12, 3, 5, 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 // strobe/beacon modes: @@ -65,9 +65,6 @@ uint8_t hi_modes[] = {MAX_LEVEL, 81, 96, 113}; // 1500 lm, 678 lm, 430 lm, 270 // 3: 19 Hz strobe at H1 uint8_t strobe_beacon_mode = 0; -// deferred "off" so we won't suspend in a weird state -volatile uint8_t go_to_standby = 0; - #ifdef USE_THERMAL_REGULATION // brightness before thermal step-down uint8_t target_level = 0; @@ -337,14 +334,6 @@ void setup() { } void loop() { - // deferred "off" so we won't suspend in a weird state - // (like... during the middle of a strobe pulse) - if (go_to_standby) { - go_to_standby = 0; - set_level(0); - standby_mode(); - } - if (current_state == strobe_beacon_state) { switch(strobe_beacon_mode) { // 0.2 Hz beacon at L1 -- cgit v1.2.3