aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-11-02 17:16:25 -0600
committerSelene ToyKeeper2023-11-02 17:16:25 -0600
commit7cb4fe0944b839f28dfd96a88a772cd6a8b58019 (patch)
tree8d3b203f1650edc28b1f67e1589e3bc870b33fa6 /ui
parentadded LICENSE (GPLv3) (diff)
downloadanduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.tar.gz
anduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.tar.bz2
anduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.zip
reorganized project files (part 1)
(just moved files, didn't change the contents yet, and nothing will work without updating #includes and build scripts and stuff)
Diffstat (limited to 'ui')
-rw-r--r--ui/anduril/Makefile14
-rw-r--r--ui/anduril/anduril.c396
-rw-r--r--ui/anduril/aux-leds.c210
-rw-r--r--ui/anduril/aux-leds.h65
-rw-r--r--ui/anduril/battcheck-mode-fsm.h18
-rw-r--r--ui/anduril/battcheck-mode.c82
-rw-r--r--ui/anduril/battcheck-mode.h12
-rw-r--r--ui/anduril/beacon-mode.c53
-rw-r--r--ui/anduril/beacon-mode.h9
-rw-r--r--ui/anduril/candle-mode.c136
-rw-r--r--ui/anduril/candle-mode.h13
-rw-r--r--ui/anduril/channel-modes.c237
-rw-r--r--ui/anduril/channel-modes.h26
-rw-r--r--ui/anduril/config-default.h207
-rw-r--r--ui/anduril/config-mode.c196
-rw-r--r--ui/anduril/config-mode.h24
-rw-r--r--ui/anduril/factory-reset-fsm.h10
-rw-r--r--ui/anduril/factory-reset.c73
-rw-r--r--ui/anduril/factory-reset.h8
-rw-r--r--ui/anduril/ff-strobe-modes.c62
-rw-r--r--ui/anduril/ff-strobe-modes.h15
-rw-r--r--ui/anduril/load-save-config-fsm.h139
-rw-r--r--ui/anduril/load-save-config.c33
-rw-r--r--ui/anduril/load-save-config.h173
-rw-r--r--ui/anduril/lockout-mode-fsm.h11
-rw-r--r--ui/anduril/lockout-mode.c219
-rw-r--r--ui/anduril/lockout-mode.h16
-rw-r--r--ui/anduril/misc.c42
-rw-r--r--ui/anduril/misc.h10
-rw-r--r--ui/anduril/momentary-mode.c67
-rw-r--r--ui/anduril/momentary-mode.h11
-rw-r--r--ui/anduril/off-mode.c384
-rw-r--r--ui/anduril/off-mode.h12
-rw-r--r--ui/anduril/ramp-mode-fsm.h38
-rw-r--r--ui/anduril/ramp-mode.c741
-rw-r--r--ui/anduril/ramp-mode.h224
-rw-r--r--ui/anduril/smooth-steps.c47
-rw-r--r--ui/anduril/smooth-steps.h19
-rw-r--r--ui/anduril/sos-mode.c56
-rw-r--r--ui/anduril/sos-mode.h11
-rw-r--r--ui/anduril/strobe-modes-fsm.h55
-rw-r--r--ui/anduril/strobe-modes.c332
-rw-r--r--ui/anduril/strobe-modes.h71
-rw-r--r--ui/anduril/sunset-timer.c60
-rw-r--r--ui/anduril/sunset-timer.h17
-rw-r--r--ui/anduril/tactical-mode.c109
-rw-r--r--ui/anduril/tactical-mode.h22
-rw-r--r--ui/anduril/tempcheck-mode.c56
-rw-r--r--ui/anduril/tempcheck-mode.h12
-rw-r--r--ui/anduril/tint-ramping.c86
-rw-r--r--ui/anduril/tint-ramping.h21
-rw-r--r--ui/anduril/version-check-mode.c31
-rw-r--r--ui/anduril/version-check-mode.h19
-rw-r--r--ui/anduril/version.h4
-rw-r--r--ui/baton/baton.c188
-rw-r--r--ui/baton/baton.txt21
-rw-r--r--ui/darkhorse/darkhorse.c367
-rw-r--r--ui/fireflies-ui/Makefile7
-rwxr-xr-xui/fireflies-ui/build-all.sh13
-rw-r--r--ui/fireflies-ui/cfg-ff-e01.h44
-rw-r--r--ui/fireflies-ui/cfg-ff-e07-2.h27
-rw-r--r--ui/fireflies-ui/fireflies-ui.c2386
-rw-r--r--ui/meteor/meteor.c556
-rw-r--r--ui/momentary/momentary.c80
-rw-r--r--ui/ramping-ui/ramping-ui.c359
-rw-r--r--ui/rampingios/Makefile7
-rwxr-xr-xui/rampingios/build-all.sh13
-rw-r--r--ui/rampingios/rampingios-v3.html501
-rw-r--r--ui/rampingios/rampingios-v3.md262
-rw-r--r--ui/rampingios/rampingios-v3.txt324
-rw-r--r--ui/rampingios/rampingiosv3-ui.pngbin0 -> 240749 bytes
-rw-r--r--ui/rampingios/rampingiosv3.c1253
-rw-r--r--ui/rampingios/rampingiosv3.svg4113
-rw-r--r--ui/werner/Makefile7
-rw-r--r--ui/werner/README56
-rwxr-xr-xui/werner/build-all.sh15
-rw-r--r--ui/werner/werner.c715
77 files changed, 16328 insertions, 0 deletions
diff --git a/ui/anduril/Makefile b/ui/anduril/Makefile
new file mode 100644
index 0000000..332f0f3
--- /dev/null
+++ b/ui/anduril/Makefile
@@ -0,0 +1,14 @@
+all:
+ ./build-all.sh
+
+clean:
+ rm -f *.hex *~ *.elf *.o
+
+todo:
+ @egrep 'TODO:|FIXME:' *.[ch]
+
+models:
+ @./models.py > MODELS
+ @cat MODELS
+
+.phony: clean todo
diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c
new file mode 100644
index 0000000..e46eeaf
--- /dev/null
+++ b/ui/anduril/anduril.c
@@ -0,0 +1,396 @@
+// Anduril: Narsil-inspired UI for SpaghettiMonster.
+// (Anduril is Aragorn's sword, the blade Narsil reforged)
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/*
+ * 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, inline)
+ *
+ * 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... woot.
+ *
+ * Also, there are a ton of compile-time options because it needs to build
+ * a bunch of different versions and each one needs to be trimmed as small
+ * as possible. These are mostly "USE" flags.
+ */
+
+/********* User-configurable options *********/
+#include "config-default.h"
+
+/********* specific settings for known driver types *********/
+// Anduril config file name (set it here or define it at the gcc command line)
+//#define CFG_H cfg-blf-q8.h
+
+#include "tk.h"
+#include incfile(CFG_H)
+
+
+/********* Include headers which need to be before FSM *********/
+
+// enable FSM features needed by basic ramping functions
+#include "ramp-mode-fsm.h"
+
+#ifdef USE_FACTORY_RESET
+#include "factory-reset-fsm.h"
+#endif
+
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode-fsm.h"
+#endif
+
+#ifdef USE_LOCKOUT_MODE
+#include "lockout-mode-fsm.h"
+#endif
+
+// enable FSM features needed by strobe modes
+#include "strobe-modes-fsm.h"
+
+// figure out how many bytes of eeprom are needed,
+// based on which UI features are enabled
+// (include this one last)
+#include "load-save-config-fsm.h"
+
+
+/********* bring in FSM / SpaghettiMonster *********/
+#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
+
+#include "spaghetti-monster.h"
+
+/********* does this build target have special code to include? *********/
+#ifdef HWDEF_C_FILE
+#include incfile(HWDEF_C_FILE)
+#endif
+#ifdef CFG_C_FILE
+#include incfile(CFG_C_FILE)
+#endif
+
+
+/********* Include all the regular app headers *********/
+
+#include "off-mode.h"
+#include "ramp-mode.h"
+#include "config-mode.h"
+#include "aux-leds.h"
+#include "misc.h"
+
+#ifdef USE_SUNSET_TIMER
+#include "sunset-timer.h"
+#endif
+
+#ifdef USE_VERSION_CHECK
+#include "version-check-mode.h"
+#endif
+
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode.h"
+#endif
+
+#ifdef USE_BEACON_MODE
+#include "beacon-mode.h"
+#endif
+
+#ifdef USE_THERMAL_REGULATION
+#include "tempcheck-mode.h"
+#endif
+
+#ifdef USE_LOCKOUT_MODE
+#include "lockout-mode.h"
+#endif
+
+#ifdef USE_MOMENTARY_MODE
+#include "momentary-mode.h"
+#endif
+
+#ifdef USE_TACTICAL_MODE
+#include "tactical-mode.h"
+#endif
+
+// allow the channel mode handler even when only 1 mode
+// (so a tint ramp light could still use 3H even if there's no other mode)
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.h"
+#endif
+
+#ifdef USE_FACTORY_RESET
+#include "factory-reset.h"
+#endif
+
+// this one detects its own enable/disable settings
+#include "strobe-modes.h"
+
+#ifdef USE_SOS_MODE
+#include "sos-mode.h"
+#endif
+
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.h"
+#endif
+
+// this should be last, so other headers have a chance to declare values
+#include "load-save-config.h"
+
+
+/********* Include all the app logic source files *********/
+// (is a bit weird to do things this way,
+// but it saves a lot of space by letting us use the -fwhole-program flag)
+
+#include "off-mode.c"
+#include "ramp-mode.c"
+#include "load-save-config.c"
+#include "config-mode.c"
+#include "aux-leds.c"
+#include "misc.c"
+
+#ifdef USE_SUNSET_TIMER
+#include "sunset-timer.c"
+#endif
+
+#ifdef USE_VERSION_CHECK
+#include "version-check-mode.c"
+#endif
+
+#ifdef USE_BATTCHECK_MODE
+#include "battcheck-mode.c"
+#endif
+
+#ifdef USE_BEACON_MODE
+#include "beacon-mode.c"
+#endif
+
+#ifdef USE_THERMAL_REGULATION
+#include "tempcheck-mode.c"
+#endif
+
+#ifdef USE_LOCKOUT_MODE
+#include "lockout-mode.c"
+#endif
+
+#ifdef USE_MOMENTARY_MODE
+#include "momentary-mode.c"
+#endif
+
+#ifdef USE_TACTICAL_MODE
+#include "tactical-mode.c"
+#endif
+
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.c"
+#endif
+
+#ifdef USE_FACTORY_RESET
+#include "factory-reset.c"
+#endif
+
+#ifdef USE_STROBE_STATE
+#include "strobe-modes.c"
+#endif
+
+#ifdef USE_SOS_MODE
+#include "sos-mode.c"
+#endif
+
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.c"
+#endif
+
+
+// runs one time at boot, when power is connected
+void setup() {
+
+ #ifndef START_AT_MEMORIZED_LEVEL
+
+ // regular e-switch light, no hard clicky power button
+
+ // blink at power-on to let user know power is connected
+ blink_once();
+
+ #ifdef USE_FACTORY_RESET
+ if (button_is_pressed())
+ factory_reset();
+ #endif
+
+ load_config();
+
+ #if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER)
+ // without this, initial boot-up brightness is wrong
+ // when manual mem is enabled with a non-zero timer
+ if (cfg.manual_memory) manual_memory_restore();
+ #endif
+
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
+
+ push_state(off_state, 1);
+
+ #else // if START_AT_MEMORIZED_LEVEL
+
+ // dual switch: e-switch + power clicky
+ // power clicky acts as a momentary mode
+ load_config();
+
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
+
+ if (button_is_pressed())
+ // hold button to go to moon
+ push_state(steady_state, 1);
+ else
+ // otherwise use memory
+ push_state(steady_state, memorized_level);
+
+ #endif // ifdef START_AT_MEMORIZED_LEVEL
+
+}
+
+
+// runs repeatedly whenever light is "on" (not in standby)
+void loop() {
+
+ // "current_state" is volatile, so cache it to reduce code size
+ StatePtr state = current_state;
+
+ #ifdef USE_AUX_RGB_LEDS_WHILE_ON
+ // display battery charge on RGB button during use
+ if (state == steady_state)
+ rgb_led_voltage_readout(actual_level > USE_AUX_RGB_LEDS_WHILE_ON);
+ #endif
+
+ if (0) {} // placeholder
+
+ #ifdef USE_VERSION_CHECK
+ else if (state == version_check_state) {
+ version_check_iter();
+ }
+ #endif
+
+ #ifdef USE_STROBE_STATE
+ else if ((state == strobe_state)
+ #ifdef USE_MOMENTARY_MODE
+ // also handle momentary strobes
+ || ((
+ (state == momentary_state)
+ #ifdef USE_TACTICAL_MODE
+ || (state == tactical_state)
+ #endif
+ )
+ && (momentary_mode == 1) && (momentary_active))
+ #endif
+ ) {
+ strobe_state_iter();
+ }
+ #endif // #ifdef USE_STROBE_STATE
+
+ #ifdef USE_BORING_STROBE_STATE
+ else if (state == boring_strobe_state) {
+ boring_strobe_state_iter();
+ }
+ #endif
+
+ #ifdef USE_BATTCHECK
+ else if (state == battcheck_state) {
+ battcheck();
+ #ifdef USE_SIMPLE_UI
+ // in simple mode, turn off after one readout
+ // FIXME: can eat the next button press
+ // (state changes in loop() act weird)
+ if (cfg.simple_ui_active) set_state_deferred(off_state, 0);
+ else nice_delay_ms(1000);
+ #endif
+ }
+ #endif
+
+ #ifdef USE_THERMAL_REGULATION
+ // TODO: blink out therm_ceil during thermal_config_state?
+ else if (state == tempcheck_state) {
+ blink_num(temperature);
+ nice_delay_ms(1000);
+ }
+ #endif
+
+ #ifdef USE_BEACON_MODE
+ else if (state == beacon_state) {
+ beacon_mode_iter();
+ }
+ #endif
+
+ #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ else if (state == sos_state) {
+ sos_mode_iter();
+ }
+ #endif
+
+ #ifdef USE_SMOOTH_STEPS
+ else if (cfg.smooth_steps_style && smooth_steps_in_progress) {
+ smooth_steps_iter();
+ }
+ #endif
+
+ #ifdef USE_IDLE_MODE
+ else {
+ // doze until next clock tick
+ idle_mode();
+ }
+ #endif
+
+}
+
+
+// instead of handling EV_low_voltage in each mode,
+// it's handled globally here to make the code smaller and simpler
+void low_voltage() {
+
+ // "current_state" is volatile, so cache it to reduce code size
+ StatePtr state = current_state;
+
+ // TODO: turn off aux LED(s) when power is really low
+
+ if (0) {} // placeholder
+
+ #ifdef USE_STROBE_STATE
+ // "step down" from strobe to something low
+ else if (state == strobe_state) {
+ set_state(steady_state, RAMP_SIZE/6);
+ }
+ #endif
+
+ // in normal mode, step down or turn off
+ else if (state == steady_state) {
+ if (actual_level > 1) {
+ uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
+ set_level_and_therm_target(lvl);
+ }
+ else {
+ set_state(off_state, 0);
+ }
+ }
+ // all other modes, just turn off when voltage is low
+ else {
+ set_state(off_state, 0);
+ }
+
+}
+
diff --git a/ui/anduril/aux-leds.c b/ui/anduril/aux-leds.c
new file mode 100644
index 0000000..af59aa6
--- /dev/null
+++ b/ui/anduril/aux-leds.c
@@ -0,0 +1,210 @@
+// aux-leds.c: Aux LED functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "aux-leds.h"
+
+
+#if defined(USE_INDICATOR_LED)
+void indicator_led_update(uint8_t mode, uint8_t tick) {
+ //uint8_t volts = voltage; // save a few bytes by caching volatile value
+ // turn off when battery is too low
+ #ifdef DUAL_VOLTAGE_FLOOR
+ if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR))
+ || (voltage < DUAL_VOLTAGE_LOW_LOW)) {
+ #else
+ if (voltage < VOLTAGE_LOW) {
+ #endif
+ indicator_led(0);
+ }
+ //#ifdef USE_INDICATOR_LOW_BAT_WARNING
+ #ifndef DUAL_VOLTAGE_FLOOR // this isn't set up for dual-voltage lights like the Sofirn SP10 Pro
+ // fast blink a warning when battery is low but not critical
+ else if (voltage < VOLTAGE_RED) {
+ indicator_led(mode & (((tick & 0b0010)>>1) - 3));
+ }
+ #endif
+ //#endif
+ // normal steady output, 0/1/2 = off / low / high
+ else if ((mode & 0b00001111) < 3) {
+ indicator_led(mode);
+ }
+ // beacon-like blinky mode
+ else {
+ #ifdef USE_OLD_BLINKING_INDICATOR
+
+ // basic blink, 1/8th duty cycle
+ if (! (tick & 7)) {
+ indicator_led(2);
+ }
+ else {
+ indicator_led(0);
+ }
+
+ #else
+
+ // fancy blink, set off/low/high levels here:
+ static const uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0};
+ indicator_led(seq[tick & 15]);
+
+ #endif // ifdef USE_OLD_BLINKING_INDICATOR
+ }
+}
+#endif
+
+#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
+uint8_t voltage_to_rgb() {
+ static const uint8_t levels[] = {
+ // voltage, color
+ 0, 0, // black
+ #ifdef DUAL_VOLTAGE_FLOOR
+ // AA / NiMH voltages
+ 9, 1, // R
+ 10, 2, // R+G
+ 11, 3, // G
+ 12, 4, // G+B
+ 13, 5, // B
+ 14, 6, // R + B
+ 15, 7, // R+G+B
+ 20, 0, // black
+ #endif
+ // li-ion voltages
+ 29, 1, // R
+ 33, 2, // R+G
+ 35, 3, // G
+ 37, 4, // G+B
+ 39, 5, // B
+ 41, 6, // R + B
+ 44, 7, // R+G+B // skip; looks too similar to G+B
+ 255, 7, // R+G+B
+ };
+ uint8_t volts = voltage;
+ //if (volts < VOLTAGE_LOW) return 0;
+
+ uint8_t i;
+ for (i = 0; volts >= levels[i]; i += 2) {}
+ uint8_t color_num = levels[(i - 2) + 1];
+ return pgm_read_byte(rgb_led_colors + color_num);
+}
+
+// do fancy stuff with the RGB aux LEDs
+// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
+// arg: time slice number
+void rgb_led_update(uint8_t mode, uint16_t arg) {
+ static uint8_t rainbow = 0; // track state of rainbow mode
+ static uint8_t frame = 0; // track state of animation mode
+
+ // turn off aux LEDs when battery is empty
+ // (but if voltage==0, that means we just booted and don't know yet)
+ uint8_t volts = voltage; // save a few bytes by caching volatile value
+ #ifdef DUAL_VOLTAGE_FLOOR
+ if ((volts) && (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW))) {
+ #else
+ if ((volts) && (volts < VOLTAGE_LOW)) {
+ #endif
+ rgb_led_set(0);
+ #ifdef USE_BUTTON_LED
+ button_led_set(0);
+ #endif
+ return;
+ }
+
+ uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more?
+ uint8_t color = mode & 0x0f;
+
+ // always preview in high mode
+ if (setting_rgb_mode_now) { pattern = 2; }
+
+ #ifdef USE_POST_OFF_VOLTAGE
+ // use voltage high mode for a few seconds after initial poweroff
+ // (but not after changing aux LED settings and other similar actions)
+ else if ((arg < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND))
+ && (ticks_since_on < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND))
+ && (ticks_since_on > 0) // don't blink red on 1st frame
+ ) {
+ // use high mode if regular aux level is high or prev level was high
+ pattern = 1 + ((2 == pattern) | (prev_level >= POST_OFF_VOLTAGE_BRIGHTNESS));
+ // voltage mode
+ color = RGB_LED_NUM_COLORS - 1;
+ }
+ #endif
+
+ const uint8_t *colors = rgb_led_colors + 1;
+ uint8_t actual_color = 0;
+ if (color < 7) { // normal color
+ actual_color = pgm_read_byte(colors + color);
+ }
+ else if (color == 7) { // disco
+ rainbow = (rainbow + 1 + pseudo_rand() % 5) % 6;
+ actual_color = pgm_read_byte(colors + rainbow);
+ }
+ else if (color == 8) { // rainbow
+ uint8_t speed = 0x03; // awake speed
+ if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed
+ if (0 == (arg & speed)) {
+ rainbow = (rainbow + 1) % 6;
+ }
+ actual_color = pgm_read_byte(colors + rainbow);
+ }
+ else { // voltage
+ // show actual voltage while asleep...
+ if (go_to_standby) {
+ actual_color = voltage_to_rgb();
+ // choose a color based on battery voltage
+ //if (volts >= 38) actual_color = pgm_read_byte(colors + 4);
+ //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2);
+ //else actual_color = pgm_read_byte(colors + 0);
+ }
+ // ... but during preview, cycle colors quickly
+ else {
+ actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1));
+ }
+ }
+
+ // pick a brightness from the animation sequence
+ if (pattern == 3) {
+ // uses an odd length to avoid lining up with rainbow loop
+ static const uint8_t animation[] = {2, 1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ frame = (frame + 1) % sizeof(animation);
+ pattern = animation[frame];
+ }
+ uint8_t result;
+ #ifdef USE_BUTTON_LED
+ uint8_t button_led_result;
+ #endif
+ switch (pattern) {
+ case 0: // off
+ result = 0;
+ #ifdef USE_BUTTON_LED
+ button_led_result = 0;
+ #endif
+ break;
+ case 1: // low
+ result = actual_color;
+ #ifdef USE_BUTTON_LED
+ button_led_result = 1;
+ #endif
+ break;
+ default: // high
+ result = (actual_color << 1);
+ #ifdef USE_BUTTON_LED
+ button_led_result = 2;
+ #endif
+ break;
+ }
+ rgb_led_set(result);
+ #ifdef USE_BUTTON_LED
+ button_led_set(button_led_result);
+ #endif
+}
+
+void rgb_led_voltage_readout(uint8_t bright) {
+ uint8_t color = voltage_to_rgb();
+ if (bright) color = color << 1;
+ rgb_led_set(color);
+}
+#endif
+
diff --git a/ui/anduril/aux-leds.h b/ui/anduril/aux-leds.h
new file mode 100644
index 0000000..fa97e6b
--- /dev/null
+++ b/ui/anduril/aux-leds.h
@@ -0,0 +1,65 @@
+// aux-leds.h: Aux LED functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
+void indicator_led_update(uint8_t mode, uint8_t tick);
+#endif
+#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
+uint8_t setting_rgb_mode_now = 0;
+void rgb_led_update(uint8_t mode, uint16_t arg);
+void rgb_led_voltage_readout(uint8_t bright);
+/*
+ * 0: R
+ * 1: RG
+ * 2: G
+ * 3: GB
+ * 4: B
+ * 5: R B
+ * 6: RGB
+ * 7: rainbow
+ * 8: voltage
+ */
+const PROGMEM uint8_t rgb_led_colors[] = {
+ 0b00000000, // 0: black
+ 0b00000001, // 1: red
+ 0b00000101, // 2: yellow
+ 0b00000100, // 3: green
+ 0b00010100, // 4: cyan
+ 0b00010000, // 5: blue
+ 0b00010001, // 6: purple
+ 0b00010101, // 7: white
+};
+// intentionally 1 higher than total modes, to make "voltage" easier to reach
+// (at Hank's request)
+#define RGB_LED_NUM_COLORS 11
+#define RGB_LED_NUM_PATTERNS 4
+#ifndef RGB_LED_OFF_DEFAULT
+#define RGB_LED_OFF_DEFAULT 0x19 // low, voltage
+//#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow
+#endif
+#ifndef RGB_LED_LOCKOUT_DEFAULT
+#define RGB_LED_LOCKOUT_DEFAULT 0x39 // blinking, voltage
+//#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, disco
+#endif
+#ifndef RGB_RAINBOW_SPEED
+#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames
+#endif
+#endif
+
+//#define USE_OLD_BLINKING_INDICATOR
+//#define USE_FANCIER_BLINKING_INDICATOR
+#ifdef USE_INDICATOR_LED
+ // bits 2-3 control lockout mode
+ // bits 0-1 control "off" mode
+ // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
+ #ifndef INDICATOR_LED_DEFAULT_MODE
+ #ifdef USE_INDICATOR_LED_WHILE_RAMPING
+ #define INDICATOR_LED_DEFAULT_MODE ((2<<2) + 1)
+ #else
+ #define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+ #endif
+ #endif
+#endif
+
diff --git a/ui/anduril/battcheck-mode-fsm.h b/ui/anduril/battcheck-mode-fsm.h
new file mode 100644
index 0000000..5f8e8ec
--- /dev/null
+++ b/ui/anduril/battcheck-mode-fsm.h
@@ -0,0 +1,18 @@
+// battcheck-mode-fsm.h: FSM config for battery check mode in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define USE_BATTCHECK
+
+#ifdef USE_AUX_RGB_LEDS
+ // show voltage colors for a few seconds after going to standby
+ #define USE_POST_OFF_VOLTAGE
+ #ifndef DEFAULT_POST_OFF_VOLTAGE_SECONDS
+ #define DEFAULT_POST_OFF_VOLTAGE_SECONDS 4
+ #endif
+ #ifndef POST_OFF_VOLTAGE_BRIGHTNESS
+ // level at which to switch from low to high aux brightness
+ #define POST_OFF_VOLTAGE_BRIGHTNESS (RAMP_SIZE/10)
+ #endif
+#endif
diff --git a/ui/anduril/battcheck-mode.c b/ui/anduril/battcheck-mode.c
new file mode 100644
index 0000000..462540e
--- /dev/null
+++ b/ui/anduril/battcheck-mode.c
@@ -0,0 +1,82 @@
+// battcheck-mode.c: Battery check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "battcheck-mode.h"
+
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ ////////// Every action below here is blocked in the simple UI //////////
+ #ifdef USE_SIMPLE_UI
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ // 2 clicks: next blinky mode
+ else if (event == EV_2clicks) {
+ #if defined(USE_THERMAL_REGULATION)
+ set_state(tempcheck_state, 0);
+ #elif defined(USE_BEACON_MODE)
+ set_state(beacon_state, 0);
+ #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ set_state(sos_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ #ifdef DEFAULT_BLINK_CHANNEL
+ // 3 clicks: next channel mode (specific to number blinky modes)
+ else if (event == EV_3clicks) {
+ cfg.blink_channel = (cfg.blink_channel + 1) % NUM_CHANNEL_MODES;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef DEFAULT_BLINK_CHANNEL
+
+ #ifdef USE_VOLTAGE_CORRECTION
+ // 7H: voltage config mode
+ else if (event == EV_click7_hold) {
+ push_state(voltage_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+#ifdef USE_VOLTAGE_CORRECTION
+// the user can adjust the battery measurements... on a scale of 1 to 13
+// 1 = subtract 0.30V
+// 2 = subtract 0.25V
+// ...
+// 7 = no effect (add 0V)
+// 8 = add 0.05V
+// ...
+// 13 = add 0.30V
+void voltage_config_save(uint8_t step, uint8_t value) {
+ #ifdef USE_POST_OFF_VOLTAGE
+ if (2 == step) cfg.post_off_voltage = value;
+ else
+ #endif
+ if (value) cfg.voltage_correction = value;
+}
+
+uint8_t voltage_config_state(Event event, uint16_t arg) {
+ #ifdef USE_POST_OFF_VOLTAGE
+ #define VOLTAGE_CONFIG_STEPS 2
+ #else
+ #define VOLTAGE_CONFIG_STEPS 1
+ #endif
+ return config_state_base(event, arg,
+ VOLTAGE_CONFIG_STEPS,
+ voltage_config_save);
+}
+#endif // #ifdef USE_VOLTAGE_CORRECTION
+
diff --git a/ui/anduril/battcheck-mode.h b/ui/anduril/battcheck-mode.h
new file mode 100644
index 0000000..b505b68
--- /dev/null
+++ b/ui/anduril/battcheck-mode.h
@@ -0,0 +1,12 @@
+// battcheck-mode.h: Battery check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+uint8_t battcheck_state(Event event, uint16_t arg);
+
+#ifdef USE_VOLTAGE_CORRECTION
+void voltage_config_save(uint8_t step, uint8_t value);
+uint8_t voltage_config_state(Event event, uint16_t arg);
+#endif
+
diff --git a/ui/anduril/beacon-mode.c b/ui/anduril/beacon-mode.c
new file mode 100644
index 0000000..5aff508
--- /dev/null
+++ b/ui/anduril/beacon-mode.c
@@ -0,0 +1,53 @@
+// beacon-mode.c: Beacon mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "beacon-mode.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(((cfg.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 EVENT_HANDLED;
+ }
+ // TODO: use sleep ticks to measure time between pulses,
+ // to save power
+
+ // 2 clicks: next blinky mode
+ else if (event == EV_2clicks) {
+ #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ set_state(sos_state, 0);
+ #elif defined(USE_BATTCHECK)
+ set_state(battcheck_state, 0);
+ #elif defined(USE_THERMAL_REGULATION)
+ set_state(tempcheck_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ // hold: configure beacon timing
+ else if (event == EV_click1_hold) {
+ if (0 == (arg % TICKS_PER_SECOND)) {
+ blink_once();
+ }
+ return EVENT_HANDLED;
+ }
+ // release hold: save new timing
+ else if (event == EV_click1_hold_release) {
+ cfg.beacon_seconds = 1 + (arg / TICKS_PER_SECOND);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
diff --git a/ui/anduril/beacon-mode.h b/ui/anduril/beacon-mode.h
new file mode 100644
index 0000000..df047ad
--- /dev/null
+++ b/ui/anduril/beacon-mode.h
@@ -0,0 +1,9 @@
+// beacon-mode.h: Beacon mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// beacon mode
+uint8_t beacon_state(Event event, uint16_t arg);
+inline void beacon_mode_iter();
+
diff --git a/ui/anduril/candle-mode.c b/ui/anduril/candle-mode.c
new file mode 100644
index 0000000..ab47c34
--- /dev/null
+++ b/ui/anduril/candle-mode.c
@@ -0,0 +1,136 @@
+// candle-mode.c: Candle mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "candle-mode.h"
+
+#ifdef USE_SUNSET_TIMER
+#include "sunset-timer.h"
+#endif
+
+uint8_t candle_mode_state(Event event, uint16_t arg) {
+ static int8_t ramp_direction = 1;
+ #define MAX_CANDLE_LEVEL (MAX_LEVEL-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;
+
+ #ifdef USE_SUNSET_TIMER
+ // let the candle "burn out" and shut itself off
+ // if the user told it to
+ // cache this in case it changes when the timer is called
+ uint8_t sunset_active = sunset_timer;
+ // clock tick
+ sunset_timer_state(event, arg);
+ // if the timer just expired, shut off
+ if (sunset_active && (! sunset_timer)) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SUNSET_TIMER
+
+
+ if (event == EV_enter_state) {
+ ramp_direction = 1;
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_SUNSET_TIMER
+ // 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
+ sunset_timer = 0;
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SUNSET_TIMER
+ // 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 EVENT_HANDLED;
+ }
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ ramp_direction = -ramp_direction;
+ return EVENT_HANDLED;
+ }
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ ramp_direction = 1;
+ if (candle_mode_brightness > 1)
+ candle_mode_brightness --;
+ return EVENT_HANDLED;
+ }
+ // clock tick: animate candle brightness
+ else if (event == EV_tick) {
+ // un-reverse after 1 second
+ if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
+
+ // 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);
+ uint16_t brightness = candle_mode_brightness + add;
+
+ // self-timer dims the light during the final minute
+ #ifdef USE_SUNSET_TIMER
+ if (1 == sunset_timer) {
+ brightness = brightness
+ * ((TICKS_PER_MINUTE>>5) - (sunset_ticks>>5))
+ / (TICKS_PER_MINUTE>>5);
+ }
+ #endif // ifdef USE_SUNSET_TIMER
+
+ 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 EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
diff --git a/ui/anduril/candle-mode.h b/ui/anduril/candle-mode.h
new file mode 100644
index 0000000..aab237d
--- /dev/null
+++ b/ui/anduril/candle-mode.h
@@ -0,0 +1,13 @@
+// candle-mode.h: Candle mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#ifndef CANDLE_AMPLITUDE
+#define CANDLE_AMPLITUDE 25
+#endif
+
+uint8_t candle_mode_state(Event event, uint16_t arg);
+// moved to fsm-misc.c because it's also used for tint ramping power correction
+//uint8_t triangle_wave(uint8_t phase);
+
diff --git a/ui/anduril/channel-modes.c b/ui/anduril/channel-modes.c
new file mode 100644
index 0000000..b2fc8d1
--- /dev/null
+++ b/ui/anduril/channel-modes.c
@@ -0,0 +1,237 @@
+// channel-modes.c: Multi-channel functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "channel-modes.h"
+
+uint8_t channel_mode_state(Event event, uint16_t arg) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ 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;
+ uint8_t tint = cfg.channel_mode_args[channel_mode];
+ #endif
+
+ // it's possible that a light may need 3H but not 3C,
+ // so try to detect if 3C is needed
+ #if NUM_CHANNEL_MODES > 1
+ // 3 clicks: next channel mode
+ if (event == EV_3clicks) {
+ uint8_t next = channel_mode;
+ // go to next channel mode until we find one which is enabled
+ // (and don't do any infinite loops if the user disabled them all)
+ uint8_t count = 0;
+ do {
+ count ++;
+ next = (next + 1) % NUM_CHANNEL_MODES;
+ } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES);
+ //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES);
+
+ // undo change if infinite loop detected (redundant?)
+ //if (NUM_CHANNEL_MODES == count) next = channel_mode;
+
+ // if mode hasn't changed, abort
+ if (channel_mode == next)
+ return EVENT_NOT_HANDLED;
+
+ set_channel_mode(next);
+
+ // remember after battery changes
+ cfg.channel_mode = channel_mode;
+ save_config();
+ return EVENT_HANDLED;
+ } else
+ #endif // if NUM_CHANNEL_MODES > 1
+
+ #ifdef USE_CUSTOM_CHANNEL_3H_MODES
+ // defer to mode-specific function if defined
+ if (channel_3H_modes[channel_mode]) {
+ StatePtr tint_func = channel_3H_modes[channel_mode];
+ uint8_t err = tint_func(event, arg);
+ if (EVENT_HANDLED == err) return EVENT_HANDLED;
+ // else let the default handler run
+ }
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ // click, click, hold: change the current channel's arg (like tint)
+ if (event == EV_click3_hold) {
+ ///// adjust value from 0 to 255
+ // 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_NOT_HANDLED;
+
+ #ifdef USE_STEPPED_TINT_RAMPING
+ if ((tint_ramp_direction > 0 && tint < 255) ||
+ (tint_ramp_direction < 0 && tint > 0)) {
+ // ramp slower in stepped mode
+ if (cfg.tint_ramp_style && (arg % HOLD_TIMEOUT != 0))
+ return EVENT_HANDLED;
+
+ const uint8_t step_size = (cfg.tint_ramp_style < 2)
+ ? 1 : 254 / (cfg.tint_ramp_style-1);
+ tint = nearest_tint_value(
+ tint + ((int16_t)step_size * tint_ramp_direction)
+ );
+ }
+ #else // smooth tint ramping only
+ if ((tint_ramp_direction > 0) && (tint < 255)) { tint ++; }
+ else
+ if ((tint_ramp_direction < 0) && (tint > 0)) { tint --; }
+ #endif // ifdef USE_STEPPED_TINT_RAMPING
+
+ // if tint change stalled, let user know we hit the edge
+ else if (prev_tint == tint) {
+ if (past_edge_counter == 0) blip();
+ past_edge_counter = 1;
+ }
+ prev_tint = tint;
+ cfg.channel_mode_args[channel_mode] = 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 (0 == tint) tint_ramp_direction = 1;
+ else if (255 == tint) tint_ramp_direction = -1;
+ // remember tint after battery change
+ cfg.channel_mode_args[channel_mode] = tint;
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+ #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ #endif // ifdef USE_CHANNEL_MODE_ARGS
+
+ #if defined(USE_SIMPLE_UI)
+ // remaining mappings aren't "simple", so stop here
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ #if NUM_CHANNEL_MODES > 1
+ // channel toggle menu on ... 9H?
+ else if (event == EV_click9_hold) {
+ push_state(channel_mode_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+#if NUM_CHANNEL_MODES > 1
+void channel_mode_config_save(uint8_t step, uint8_t value) {
+ // 1 menu item per channel mode, to enable or disable that mode
+ step --; // step is 1-based, channel modes are 0-based
+ if (value) channel_mode_enable(step);
+ else channel_mode_disable(step);
+}
+
+uint8_t channel_mode_config_state(Event event, uint16_t arg) {
+ uint8_t ret;
+ // make config steps match channel modes
+ config_color_per_step = true;
+ // 1 menu item per channel mode, to enable or disable that mode
+ ret = config_state_base(
+ event, arg,
+ NUM_CHANNEL_MODES,
+ channel_mode_config_save
+ );
+ // no other menu needs this
+ config_color_per_step = false;
+ return ret;
+}
+#endif
+
+
+#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+uint8_t nearest_tint_value(const int16_t target) {
+ // const symbols for more readable code, will be removed by the compiler
+ const uint8_t tint_min = 0;
+ const uint8_t tint_max = 255;
+ const uint8_t tint_range = tint_max - tint_min;
+
+ // only equal mix of both channels
+ if (1 == cfg.tint_ramp_style) return (tint_min + tint_max) >> 1;
+
+ if (target < tint_min) return tint_min;
+ if (target > tint_max) return tint_max;
+ if (0 == cfg.tint_ramp_style) return target; // smooth ramping
+
+ const uint8_t step_size = tint_range / (cfg.tint_ramp_style-1);
+
+ uint8_t tint_result = tint_min;
+ for (uint8_t i=0; i<cfg.tint_ramp_style; i++) {
+ tint_result = tint_min
+ + (i * (uint16_t)tint_range / (cfg.tint_ramp_style-1));
+ int16_t diff = target - tint_result;
+ if (diff <= (step_size>>1)) return tint_result;
+ }
+ return tint_result;
+}
+#endif
+
+#ifdef USE_CIRCULAR_TINT_3H
+uint8_t circular_tint_3h(Event event, uint16_t arg) {
+ static int8_t tint_ramp_direction = 1;
+ // 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;
+ uint8_t tint = cfg.channel_mode_args[channel_mode];
+
+ // click, click, hold: change the current channel's arg (like tint)
+ if (event == EV_click3_hold) {
+ ///// adjust value from 0 to 255 in a circle
+ // reset at beginning of movement
+ if (! arg) {
+ active = 1; // first frame means this is for us
+ }
+ // ignore event if we weren't the ones who handled the first frame
+ if (! active) return EVENT_NOT_HANDLED;
+
+ // smooth tint ramping only
+ tint += tint_ramp_direction;
+
+ cfg.channel_mode_args[channel_mode] = 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;
+ // remember tint after battery change
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+#endif
diff --git a/ui/anduril/channel-modes.h b/ui/anduril/channel-modes.h
new file mode 100644
index 0000000..b51721d
--- /dev/null
+++ b/ui/anduril/channel-modes.h
@@ -0,0 +1,26 @@
+// channel-modes.h: Multi-channel functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// not actually a mode, more of a fallback under other modes
+uint8_t channel_mode_state(Event event, uint16_t arg);
+
+#if NUM_CHANNEL_MODES > 1
+uint8_t channel_mode_config_state(Event event, uint16_t arg);
+#endif
+
+#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+// calculate the nearest tint value which would be valid at the moment
+uint8_t nearest_tint_value(const int16_t target);
+#endif
+
+#ifdef USE_CUSTOM_CHANNEL_3H_MODES
+StatePtr channel_3H_modes[NUM_CHANNEL_MODES];
+#endif
+
+#ifdef USE_CIRCULAR_TINT_3H
+uint8_t circular_tint_3h(Event event, uint16_t arg);
+#endif
+
diff --git a/ui/anduril/config-default.h b/ui/anduril/config-default.h
new file mode 100644
index 0000000..899bc4a
--- /dev/null
+++ b/ui/anduril/config-default.h
@@ -0,0 +1,207 @@
+// config-default.h: Default configuration for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+/*
+ * This file specifies the default settings for Anduril.
+ *
+ * These settings can be overridden per build target, in cfg-*.h files...
+ * ... but most are not. So changing one here will make it change in
+ * almost every build target.
+ *
+ * Some configurable settings are also in other *.h files.
+ */
+
+/********* User-configurable options *********/
+// low voltage protection (also required for battery check mode)
+#define USE_LVP
+
+// overheat protection
+#define USE_THERMAL_REGULATION
+#if (ATTINY==85) || (ATTINY==1634)
+// sloppy temperature sensor needs bigger error margin
+#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this (in C)
+#else
+// more accurate temperature sensor can regulate higher safely
+#define DEFAULT_THERM_CEIL 50 // try not to get hotter than this (in C)
+#endif
+// Comment out to disable automatic calibration on factory reset
+// - If so, be sure to set THERM_CAL_OFFSET to the correct calibration offset
+// - Calibration can still be overridden in temperature check mode
+// Or uncomment to use the default auto-calibrate on factory reset
+//
+// To determine THERM_CAL_OFFSET, comment out USE_THERM_AUTOCALIBRATE to
+// disable auto-calibration, compile and flash, let flashlight rest at a known
+// temperature, then enter temp check mode (do NOT enter calibration mode).
+//
+// THERM_CAL_OFFSET = known_temperature - temp_check_blinks + THERM_CAL_OFFSET
+//
+// (include THERM_CAL_OFFSET in sum as it might already be a non-zero number)
+#define USE_THERM_AUTOCALIBRATE
+
+// Include a simplified UI for non-enthusiasts?
+#define USE_SIMPLE_UI
+
+
+///// Ramp mode options /////
+
+// button timing for turning light on/off:
+// B_PRESS_T: activate as soon as button is pressed
+// B_RELEASE_T: activate when user lets go of button
+// B_TIMEOUT_T: activate when we're sure the user won't double-click
+// defaults are release on, timeout off
+#define B_TIMING_ON B_RELEASE_T
+#define B_TIMING_OFF B_TIMEOUT_T
+
+// default ramp style: 0 = smooth, 1 = stepped
+#define RAMP_STYLE 0
+
+// smooth ramp speed: 1, 2, 3, 4, ... for 1X speed, 1/2, 1/3rd, 1/4th, ...
+#define USE_RAMP_SPEED_CONFIG
+
+// after ramping, how long until the direction resets to "up"?
+#define AUTO_REVERSE_TIME (TICKS_PER_SECOND * 2 / 3)
+
+// add runtime option for whether hold-from-off should ramp or stay at moon
+#define USE_RAMP_AFTER_MOON_CONFIG
+
+// 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
+
+// Uncomment for Anduril1 "Ramp 2C" behavior:
+// - Ramp 2C goes to turbo (advanced UI) or ceiling (simple UI), like in Anduril1
+// Or comment out to use Anduril2 behavior instead:
+// - Ramp 2C goes to ceiling, unless already at ceiling or in simple UI.
+// (Advanced UI ceiling 2C goes to turbo)
+//#define USE_2C_MAX_TURBO
+// Or uncomment to let the user decide which style they want:
+#define USE_2C_STYLE_CONFIG
+// 0 = no turbo
+// 1 = A1 style: Off 2C = ceil, On 2C = turbo
+// 2 = A2 style: Off 2C = ceil, On 2C = ceil, Ramped ceil 2C = turbo
+// All styles allow momentary turbo in advanced UI
+//#define DEFAULT_2C_STYLE 2 // default to Anduril 2 style
+//#define DEFAULT_2C_STYLE_SIMPLE 0 // no turbo at all
+
+// make the ramps configurable by the user
+#define USE_RAMP_CONFIG
+
+// adds 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 10 clicks from on,
+// or the user can go back to automatic with 10H)
+#define USE_MANUAL_MEMORY
+// if enabled, user can use "hybrid memory"
+// The light will use automatic or manual memory level, depending on how long
+// the light was off. Short off = automatic, long off = manual.
+// This also remaps 10C/10H:
+// - 10C toggles manual mem on/off at current level.
+// - 10H configures the timer value.
+#define USE_MANUAL_MEMORY_TIMER
+
+// enable sunset timer (ramp-down and automatic shutoff)
+// timer is available in regular ramp mode and candle mode
+#define USE_SUNSET_TIMER
+
+
+///// What to do when power is connected /////
+// factory reset function erases user's runtime configuration in eeprom
+#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
+
+
+///// extra modes (enable / disable / configure each mode) /////
+
+// include a function to blink out the firmware version
+#define USE_VERSION_CHECK
+
+// enable the battery check mode (shows remaining charge) (requires USE_LVP)
+#define USE_BATTCHECK_MODE
+// battery readout style (pick one)
+// TODO: allow VpT and 4-bar simultaneously,
+// so one can be in "simple mode" and the other in "advanced mode"
+#define BATTCHECK_VpT
+//#define BATTCHECK_8bars // FIXME: breaks build
+//#define BATTCHECK_4bars // FIXME: breaks build
+// allow the user to calibrate the voltage readings?
+// (adjust in 0.05V increments from -0.30V to +0.30V)
+// (1 = -0.30V, 2 = -0.25V, ... 7 = 0V, ... 13 = +0.30V)
+#define USE_VOLTAGE_CORRECTION
+
+// enable beacon mode
+#define USE_BEACON_MODE
+
+// 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
+
+// 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
+
+// enable a mode for locking the light for safe carry
+#define USE_LOCKOUT_MODE
+// should lockout mode function as a momentary moon mode?
+#define USE_MOON_DURING_LOCKOUT_MODE
+// add an optional setting to lock the light after being off for a while
+#define USE_AUTOLOCK
+
+// enable momentary mode
+#define USE_MOMENTARY_MODE
+
+// enable tactical mode
+#define USE_TACTICAL_MODE
+
+
+// enable a shortcut for +10 in number entry mode
+// (click for +1, hold for +10)
+#define USE_NUMBER_ENTRY_PLUS10
+
+// cut clock speed at very low modes for better efficiency
+// (defined here so config files can override it)
+#define USE_DYNAMIC_UNDERCLOCKING
+
+// if the aux LEDs oscillate between "full battery" and "empty battery"
+// while in "voltage" mode, enable this to reduce the amplitude of
+// those oscillations
+#if (ATTINY==1616) || (ATTINY==1634)
+#define USE_LOWPASS_WHILE_ASLEEP
+#endif
+
+// if there's tint ramping, allow user to set it smooth or stepped
+#define USE_STEPPED_TINT_RAMPING
+#define DEFAULT_TINT_RAMP_STYLE 0 // smooth
+
+// Use "smooth steps" to soften on/off and step changes
+// on MCUs with enough room for extra stuff like this
+#if (ATTINY==1616) || (ATTINY==1634)
+#define USE_SMOOTH_STEPS
+#endif
+// 0 = none, 1 = smooth, 2+ = undefined
+#define DEFAULT_SMOOTH_STEPS_STYLE 1
+
+// by default, allow user to set the channel for each strobe-group mode
+// (but allow disabling this feature per build)
+#define USE_CHANNEL_PER_STROBE
+
diff --git a/ui/anduril/config-mode.c b/ui/anduril/config-mode.c
new file mode 100644
index 0000000..71b0d69
--- /dev/null
+++ b/ui/anduril/config-mode.c
@@ -0,0 +1,196 @@
+// config-mode.c: Config mode base functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#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;
+
+
+#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+// TODO: promote this to fsm-channels.c ?
+void set_chan_if(bool cond, uint8_t chan) {
+ if ((cond) && (chan != channel_mode))
+ set_channel_mode(chan);
+}
+#endif
+
+// allow the user to set a new value for a config option
+// can be called two ways:
+// - with a "click" action: Configures first menu item only.
+// - with a "hold" action: Sets user select a menu item and then
+// choose a new value for it. User should hold button until light
+// blinks N times, to choose menu item N. Then let go, and light
+// should give a buzzing prompt to enter a number. Click N times
+// at the prompt to set the new value to N.
+// after completing this process, config state calls the savefunc callback
+// and then returns to caller/parent state
+uint8_t config_state_base(
+ Event event,
+ uint16_t arg,
+ uint8_t num_config_steps,
+ void (*savefunc)(uint8_t step, uint8_t value)) {
+
+ static uint8_t config_step;
+ #ifdef USE_CONFIG_COLORS
+ static uint8_t orig_channel;
+ #endif
+ if (event == EV_enter_state) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ orig_channel = channel_mode;
+ #endif
+ config_step = 0;
+ set_level(0);
+ // if button isn't held, configure first menu item
+ if (! button_last_state) {
+ config_step ++;
+ push_state(number_entry_state, 0);
+ }
+ }
+
+ // if initial "hold" event still active
+ // blink while holding to indicate option number
+ #define B_CLICK_FLAGS (B_CLICK|B_HOLD|B_PRESS|B_RELEASE|B_TIMEOUT)
+ #define B_ANY_HOLD (B_CLICK|B_HOLD|B_PRESS)
+ #define B_ANY_HOLD_RELEASE (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT)
+ else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD) {
+ if (config_step <= num_config_steps) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ uint8_t chan = config_step - 1;
+ if (chan < NUM_CHANNEL_MODES)
+ set_chan_if(config_color_per_step, chan);
+ #endif
+ if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) {
+ config_step ++;
+ // blink when config step advances
+ if (config_step <= num_config_steps) {
+ #ifdef CONFIG_BLINK_CHANNEL
+ set_chan_if(!config_color_per_step, CONFIG_BLINK_CHANNEL);
+ #endif
+ set_level(RAMP_SIZE * 3 / 8);
+ }
+ }
+ else {
+ // stay on at a low level to indicate menu is active
+ #ifdef CONFIG_WAITING_CHANNEL
+ set_chan_if(!config_color_per_step, CONFIG_WAITING_CHANNEL);
+ #endif
+ set_level(RAMP_SIZE * 1 / 8);
+ }
+ } else {
+ // turn off when end of menu is reached
+ set_level(0);
+ }
+ }
+
+ // button release: activate number entry for one menu item
+ else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD_RELEASE) {
+ // ask the user for a number, if they selected a menu item
+ if (config_step && config_step <= num_config_steps) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ // put the colors back how they were
+ set_channel_mode(orig_channel);
+ #endif
+ push_state(number_entry_state, 0);
+ }
+ // exit after falling out of end of menu
+ else {
+ pop_state();
+ }
+ }
+
+ // an option was set (return from number_entry_state)
+ else if (event == EV_reenter_state) {
+ // process value with parent's callback
+ savefunc(config_step, number_entry_value);
+ // make changes persist in eeprom
+ save_config();
+ pop_state();
+ }
+
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ else if (event == EV_leave_state) {
+ // put the colors back how they were
+ set_channel_mode(orig_channel);
+ }
+ #endif
+
+ // 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 entry_step;
+
+ if (event == EV_enter_state) {
+ number_entry_value = 0;
+ entry_step = 0;
+ set_level(0); // initial pause should be dark
+ }
+
+ // advance through the process:
+ // 0: wait a moment
+ // 1: "buzz" while counting clicks
+ // 2: save and exit
+ else if (event == EV_tick) {
+ // wait a moment
+ if (entry_step == 0) {
+ if (arg > TICKS_PER_SECOND/2) {
+ entry_step ++;
+ empty_event_sequence(); // reset tick counter to 0
+ }
+ }
+ // buzz while waiting for a number to be entered
+ else if (entry_step == 1) {
+ // time out and exit after 3 seconds
+ if (arg > TICKS_PER_SECOND*3) {
+ entry_step ++;
+ set_level(0);
+ }
+ // buzz for N seconds after last event
+ // (flicker every other frame,
+ // except first frame (so we can see flashes after each click))
+ else if (arg) {
+ #ifdef CONFIG_WAITING_CHANNEL
+ set_chan_if(1, CONFIG_WAITING_CHANNEL);
+ #endif
+ set_level( (RAMP_SIZE/8)
+ + ((arg&2)<<2) );
+ }
+ }
+ // all done, save result and return to parent state
+ else {
+ pop_state();
+ }
+ return EVENT_HANDLED;
+ }
+
+ // count clicks: click = +1, hold = +10
+ else if ((event == EV_click1_release)
+ #ifdef USE_NUMBER_ENTRY_PLUS10
+ || (event == EV_click1_hold_release)
+ #endif
+ ) {
+ entry_step = 1; // in case user clicked during initial delay
+ #ifdef USE_NUMBER_ENTRY_PLUS10
+ if (event == EV_click1_hold_release) number_entry_value += 10;
+ else
+ #endif
+ number_entry_value ++; // update the result
+ empty_event_sequence(); // reset FSM's click count
+ #ifdef CONFIG_BLINK_CHANNEL
+ set_channel_mode(CONFIG_BLINK_CHANNEL);
+ #endif
+ set_level(RAMP_SIZE/2); // flash briefly
+ return EVENT_HANDLED;
+ }
+
+ // eat all other events; don't pass any through to parent
+ return EVENT_HANDLED;
+}
+
diff --git a/ui/anduril/config-mode.h b/ui/anduril/config-mode.h
new file mode 100644
index 0000000..d4a7652
--- /dev/null
+++ b/ui/anduril/config-mode.h
@@ -0,0 +1,24 @@
+// config-mode.h: Config mode base functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// menus can use 2 colors
+#if defined (CONFIG_WAITING_CHANNEL) || defined(CONFIG_BLINK_CHANNEL)
+#define USE_CONFIG_COLORS
+#endif
+
+#if NUM_CHANNEL_MODES > 1
+// when true, changes the channel mode for each config step
+bool config_color_per_step = false;
+#endif
+
+// config menu
+uint8_t config_state_base(
+ Event event,
+ uint16_t arg,
+ uint8_t num_config_steps,
+ void (*savefunc)(uint8_t step, uint8_t value)
+ );
+
diff --git a/ui/anduril/factory-reset-fsm.h b/ui/anduril/factory-reset-fsm.h
new file mode 100644
index 0000000..3cb0875
--- /dev/null
+++ b/ui/anduril/factory-reset-fsm.h
@@ -0,0 +1,10 @@
+// factory-reset-fsm.h: FSM config options to enable factory reset in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifdef USE_SOFT_FACTORY_RESET
+#define USE_REBOOT
+#endif
+
diff --git a/ui/anduril/factory-reset.c b/ui/anduril/factory-reset.c
new file mode 100644
index 0000000..f9fb472
--- /dev/null
+++ b/ui/anduril/factory-reset.c
@@ -0,0 +1,73 @@
+// factory-reset.c: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "factory-reset.h"
+
+// allows setting channel mode per animation stage,
+// so it can ramp up in red then explode in white (as one example)
+
+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
+ #ifdef FACTORY_RESET_WARN_CHANNEL
+ set_channel_mode(FACTORY_RESET_WARN_CHANNEL);
+ #endif
+ 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) {
+ #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE)
+ // return to default channel before saving
+ set_channel_mode(DEFAULT_CHANNEL_MODE);
+ #endif
+
+ // auto-calibrate temperature
+ // AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset
+ #if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3)
+ // this will cancel out the offset
+ thermal_config_save(1, temperature - cfg.therm_cal_offset);
+ #elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE)
+ // assume current temperature is 21 C
+ thermal_config_save(1, 21);
+ #endif
+
+ // save all settings to eeprom
+ // (assuming they're all at default because we haven't loaded them yet)
+ save_config();
+
+ // explosion animation
+ #ifdef FACTORY_RESET_SUCCESS_CHANNEL
+ set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL);
+ #endif
+ 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);
+ }
+ }
+}
+
diff --git a/ui/anduril/factory-reset.h b/ui/anduril/factory-reset.h
new file mode 100644
index 0000000..63c25cd
--- /dev/null
+++ b/ui/anduril/factory-reset.h
@@ -0,0 +1,8 @@
+// factory-reset.h: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+void factory_reset();
+
diff --git a/ui/anduril/ff-strobe-modes.c b/ui/anduril/ff-strobe-modes.c
new file mode 100644
index 0000000..b7a7303
--- /dev/null
+++ b/ui/anduril/ff-strobe-modes.c
@@ -0,0 +1,62 @@
+// ff-strobe-modes.c: Fireflies Flashlights strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "ff-strobe-modes.h"
+
+uint8_t boring_strobe_state(Event event, uint16_t arg) {
+ // police strobe and SOS, meh
+ // 'st' reduces ROM size slightly
+ uint8_t st = boring_strobe_type;
+
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 2 clicks: rotate through strobe/flasher modes
+ else if (event == EV_2clicks) {
+ boring_strobe_type = (st + 1) % NUM_BORING_STROBES;
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+inline void boring_strobe_state_iter() {
+ switch(boring_strobe_type) {
+ #ifdef USE_POLICE_STROBE_MODE
+ case 0: // police strobe
+ police_strobe_iter();
+ break;
+ #endif
+
+ #ifdef USE_SOS_MODE_IN_FF_GROUP
+ default: // SOS
+ sos_mode_iter();
+ break;
+ #endif
+ }
+}
+
+#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
+
diff --git a/ui/anduril/ff-strobe-modes.h b/ui/anduril/ff-strobe-modes.h
new file mode 100644
index 0000000..d7adfec
--- /dev/null
+++ b/ui/anduril/ff-strobe-modes.h
@@ -0,0 +1,15 @@
+// ff-strobe-modes.h: Fireflies Flashlights strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+uint8_t boring_strobe_state(Event event, uint16_t arg);
+inline void boring_strobe_state_iter();
+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
+
diff --git a/ui/anduril/load-save-config-fsm.h b/ui/anduril/load-save-config-fsm.h
new file mode 100644
index 0000000..d189d3a
--- /dev/null
+++ b/ui/anduril/load-save-config-fsm.h
@@ -0,0 +1,139 @@
+// load-save-config-fsm.h: FSM config for eeprom configuration in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#define USE_EEPROM
+// load into a custom RAM location instead of FSM's default byte array
+#define EEPROM_OVERRIDE
+
+#ifdef USE_SIMPLE_UI
+#define NUM_RAMPS 3
+#else
+#define NUM_RAMPS 2
+#endif
+
+// let FSM know this config struct exists
+#define USE_CFG
+
+typedef struct Config {
+
+ ///// ramp vars
+ uint8_t ramp_style;
+ #ifdef USE_2C_STYLE_CONFIG
+ uint8_t ramp_2c_style;
+ #endif
+ #ifdef USE_RAMP_CONFIG
+ uint8_t ramp_floors[NUM_RAMPS];
+ uint8_t ramp_ceils [NUM_RAMPS];
+ uint8_t ramp_stepss[NUM_RAMPS];
+ #endif
+ #ifdef USE_SIMPLE_UI
+ uint8_t simple_ui_active;
+ #ifdef USE_2C_STYLE_CONFIG
+ uint8_t ramp_2c_style_simple;
+ #endif
+ #endif
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ uint8_t dont_ramp_after_moon;
+ #endif
+ #ifdef USE_MANUAL_MEMORY
+ uint8_t manual_memory;
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ uint8_t manual_memory_timer;
+ #endif
+ #endif
+
+ ///// channel modes / color modes
+ #if NUM_CHANNEL_MODES > 1
+ uint8_t channel_mode;
+ uint16_t channel_modes_enabled;
+ #ifdef USE_MANUAL_MEMORY
+ uint8_t manual_memory_channel_mode;
+ #endif
+ #ifdef DEFAULT_BLINK_CHANNEL
+ uint8_t blink_channel;
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // this is an array, needs a few bytes
+ uint8_t channel_mode_args[NUM_CHANNEL_MODES];
+ #ifdef USE_MANUAL_MEMORY
+ uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES];
+ #endif
+ #ifdef USE_STEPPED_TINT_RAMPING
+ uint8_t tint_ramp_style;
+ #endif
+ #endif
+
+ ///// Smooth animation between steps, and for on/off
+ #ifdef USE_SMOOTH_STEPS
+ uint8_t smooth_steps_style;
+ #endif
+
+ ///// strobe / blinky mode settings
+ #ifdef USE_STROBE_STATE
+ uint8_t strobe_type;
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ uint8_t strobe_channels[NUM_STROBES];
+ #endif
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ uint8_t strobe_delays[2];
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ uint8_t bike_flasher_brightness;
+ #endif
+ #ifdef USE_BEACON_MODE
+ uint8_t beacon_seconds;
+ #endif
+
+ ///// voltage and temperature
+ #ifdef USE_VOLTAGE_CORRECTION
+ uint8_t voltage_correction;
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ uint8_t therm_ceil;
+ int8_t therm_cal_offset;
+ #endif
+
+ ///// aux LEDs
+ #ifdef USE_INDICATOR_LED
+ uint8_t indicator_led_mode;
+ #endif
+ #ifdef USE_AUX_RGB_LEDS
+ uint8_t rgb_led_off_mode;
+ uint8_t rgb_led_lockout_mode;
+ #ifdef USE_POST_OFF_VOLTAGE
+ uint8_t post_off_voltage;
+ #endif
+ #endif
+
+ ///// misc other mode settings
+ #ifdef USE_AUTOLOCK
+ uint8_t autolock_time;
+ #endif
+ #ifdef USE_TACTICAL_MODE
+ uint8_t tactical_levels[3];
+ #endif
+
+ ///// hardware config / globals menu
+ #ifdef USE_JUMP_START
+ uint8_t jump_start_level;
+ #endif
+
+} Config;
+
+// auto-detect how many eeprom bytes
+#define EEPROM_BYTES sizeof(Config)
+
+// declare this so FSM can see it,
+// but define its values in a file which loads later
+Config cfg;
+
+#ifdef START_AT_MEMORIZED_LEVEL
+#define USE_EEPROM_WL
+#define EEPROM_WL_BYTES 1
+#endif
+
diff --git a/ui/anduril/load-save-config.c b/ui/anduril/load-save-config.c
new file mode 100644
index 0000000..aa772e1
--- /dev/null
+++ b/ui/anduril/load-save-config.c
@@ -0,0 +1,33 @@
+// load-save-config.c: Load/save/eeprom functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "load-save-config-fsm.h"
+#include "load-save-config.h"
+
+void load_config() {
+ eeprom = (uint8_t *)&cfg;
+
+ if (! load_eeprom()) return;
+
+ #ifdef START_AT_MEMORIZED_LEVEL
+ if (load_eeprom_wl()) {
+ memorized_level = eeprom_wl[0];
+ }
+ #endif
+}
+
+void save_config() {
+ eeprom = (uint8_t *)&cfg;
+ save_eeprom();
+}
+
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl() {
+ eeprom_wl[0] = memorized_level;
+ save_eeprom_wl();
+}
+#endif
+
diff --git a/ui/anduril/load-save-config.h b/ui/anduril/load-save-config.h
new file mode 100644
index 0000000..514fcbb
--- /dev/null
+++ b/ui/anduril/load-save-config.h
@@ -0,0 +1,173 @@
+// load-save-config.h: Load/save/eeprom functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// remember stuff even after battery was changed
+void load_config();
+void save_config();
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl();
+#endif
+
+// a struct to hold config values
+Config cfg = {
+
+ ///// ramp vars
+
+ // smooth vs discrete ramping
+ .ramp_style = RAMP_STYLE, // 0 = smooth, 1 = discrete
+ #ifdef USE_2C_STYLE_CONFIG
+ // 1 = A1 style, 2 = A2 style
+ .ramp_2c_style = DEFAULT_2C_STYLE,
+ #endif
+
+ // settings for each ramp (smooth, stepped, simple UI)
+ #ifdef USE_RAMP_CONFIG
+ .ramp_floors = {
+ RAMP_SMOOTH_FLOOR,
+ RAMP_DISCRETE_FLOOR,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_FLOOR,
+ #endif
+ },
+ .ramp_ceils = {
+ RAMP_SMOOTH_CEIL,
+ RAMP_DISCRETE_CEIL,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_CEIL,
+ #endif
+ },
+ .ramp_stepss = {
+ DEFAULT_RAMP_SPEED,
+ RAMP_DISCRETE_STEPS,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_STEPS,
+ #endif
+ },
+ #endif
+
+ #ifdef USE_SIMPLE_UI
+ // whether to enable the simplified interface or not
+ .simple_ui_active = SIMPLE_UI_ACTIVE,
+ #ifdef USE_2C_STYLE_CONFIG
+ // 0 = no turbo, 1 = A1 style, 2 = A2 style
+ .ramp_2c_style_simple = DEFAULT_2C_STYLE_SIMPLE,
+ #endif
+ #endif
+
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ .dont_ramp_after_moon = DEFAULT_DONT_RAMP_AFTER_MOON,
+ #endif
+
+ #ifdef USE_MANUAL_MEMORY
+ .manual_memory = DEFAULT_MANUAL_MEMORY,
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ .manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER,
+ #endif
+ #endif
+
+ ///// channel modes / color modes
+
+ #if NUM_CHANNEL_MODES > 1
+ // current multi-channel mode
+ .channel_mode = DEFAULT_CHANNEL_MODE,
+ // user can take unwanted modes out of the rotation (bitmask)
+ .channel_modes_enabled = CHANNEL_MODES_ENABLED,
+ #ifdef USE_MANUAL_MEMORY
+ // reset w/ manual memory
+ .manual_memory_channel_mode = DEFAULT_CHANNEL_MODE,
+ #endif
+ #ifdef DEFAULT_BLINK_CHANNEL
+ // blink numbers in a specific channel (user configurable)
+ .blink_channel = DEFAULT_BLINK_CHANNEL,
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // one byte of extra data per channel mode, like for tint value
+ .channel_mode_args = { CHANNEL_MODE_ARGS },
+ #ifdef USE_MANUAL_MEMORY
+ // remember and reset 1 extra parameter per channel mode (like tint)
+ .manual_memory_channel_args = { CHANNEL_MODE_ARGS },
+ #endif
+ #ifdef USE_STEPPED_TINT_RAMPING
+ .tint_ramp_style = DEFAULT_TINT_RAMP_STYLE,
+ #endif
+ #endif
+
+ ///// Smooth animation between steps, and for on/off
+ #ifdef USE_SMOOTH_STEPS
+ .smooth_steps_style = DEFAULT_SMOOTH_STEPS_STYLE,
+ #endif
+
+ ///// strobe / blinky mode settings
+
+ #ifdef USE_STROBE_STATE
+ .strobe_type = DEFAULT_STROBE,
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // channel mode saved per strobe-group mode
+ #ifdef DEFAULT_STROBE_CHANNELS
+ .strobe_channels = { DEFAULT_STROBE_CHANNELS },
+ #endif
+ #endif
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ // party / tactical strobe timing
+ // party strobe 24 Hz, tactical strobe 10 Hz
+ .strobe_delays = { 41, 67 },
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ .bike_flasher_brightness = MAX_1x7135,
+ #endif
+ #ifdef USE_BEACON_MODE
+ // beacon timing
+ .beacon_seconds = 2,
+ #endif
+
+ ///// voltage and temperature
+
+ #ifdef USE_VOLTAGE_CORRECTION
+ // same 0.05V units as fudge factor,
+ // but 7 is neutral, and the expected range is from 1 to 13
+ .voltage_correction = 7,
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ .therm_ceil = DEFAULT_THERM_CEIL,
+ .therm_cal_offset = 0,
+ #endif
+
+ ///// aux LEDs
+
+ #ifdef USE_INDICATOR_LED
+ // bits 2-3 control lockout mode
+ // bits 0-1 control "off" mode
+ // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
+ .indicator_led_mode = INDICATOR_LED_DEFAULT_MODE,
+ #endif
+ #ifdef USE_AUX_RGB_LEDS
+ .rgb_led_off_mode = RGB_LED_OFF_DEFAULT,
+ .rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT,
+ #ifdef USE_POST_OFF_VOLTAGE
+ // display voltage readout for a while after turning off?
+ .post_off_voltage = DEFAULT_POST_OFF_VOLTAGE_SECONDS,
+ #endif
+ #endif
+
+ ///// misc other mode settings
+
+ #ifdef USE_AUTOLOCK
+ .autolock_time = DEFAULT_AUTOLOCK_TIME,
+ #endif
+ #ifdef USE_TACTICAL_MODE
+ .tactical_levels = { TACTICAL_LEVELS },
+ #endif
+
+ ///// hardware config / globals menu
+
+ #ifdef USE_JUMP_START
+ .jump_start_level = DEFAULT_JUMP_START_LEVEL,
+ #endif
+
+};
+
diff --git a/ui/anduril/lockout-mode-fsm.h b/ui/anduril/lockout-mode-fsm.h
new file mode 100644
index 0000000..ede251c
--- /dev/null
+++ b/ui/anduril/lockout-mode-fsm.h
@@ -0,0 +1,11 @@
+// lockout-mode-fsm.h: FSM config for lockout mode in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// autolock function requires the ability to measure time while "off"
+#ifdef USE_AUTOLOCK
+#define TICK_DURING_STANDBY
+#endif
+
diff --git a/ui/anduril/lockout-mode.c b/ui/anduril/lockout-mode.c
new file mode 100644
index 0000000..422d081
--- /dev/null
+++ b/ui/anduril/lockout-mode.c
@@ -0,0 +1,219 @@
+// lockout-mode.c: Lockout mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "lockout-mode.h"
+
+uint8_t lockout_state(Event event, uint16_t arg) {
+ #ifdef USE_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_click7_hold) { set_level(0); } else
+ #endif
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ // hold: lowest floor
+ // click, hold: highest floor (or manual mem level)
+ uint8_t lvl = cfg.ramp_floors[0];
+ if (1 == (event & 0x0f)) { // first click
+ if (cfg.ramp_floors[1] < lvl) lvl = cfg.ramp_floors[1];
+ } else { // 2nd click or later
+ if (cfg.ramp_floors[1] > lvl) lvl = cfg.ramp_floors[1];
+ #ifdef USE_MANUAL_MEMORY
+ if (cfg.manual_memory) lvl = cfg.manual_memory;
+ #endif
+ }
+ set_level(lvl);
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ set_level(0);
+ }
+ #endif // ifdef USE_MOON_DURING_LOCKOUT_MODE
+
+ // 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)
+ if (event == EV_enter_state) {
+ ticks_since_on = 0;
+ #ifdef USE_INDICATOR_LED
+ // redundant, sleep tick does the same thing
+ // indicator_led_update(cfg.indicator_led_mode >> 2, 0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(cfg.rgb_led_lockout_mode, 0);
+ #endif
+ }
+
+ else if (event == EV_tick) {
+ if (arg > HOLD_TIMEOUT) {
+ go_to_standby = 1;
+ #ifdef USE_INDICATOR_LED
+ // redundant, sleep tick does the same thing
+ //indicator_led_update(cfg.indicator_led_mode >> 2, arg);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+
+ #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
+ else if (event == EV_sleep_tick) {
+ if (ticks_since_on < 255) ticks_since_on ++;
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ // reset to manual memory level when timer expires
+ if (cfg.manual_memory &&
+ (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
+ manual_memory_restore();
+ }
+ #endif
+ #if defined(USE_INDICATOR_LED)
+ indicator_led_update(cfg.indicator_led_mode >> 2, arg);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
+ #endif
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ // 3 clicks: exit and turn off
+ else if (event == EV_3clicks) {
+ blink_once();
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ // 4 clicks: exit and turn on
+ else if (event == EV_4clicks) {
+ #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
+ // this clause probably isn't used by any configs any more
+ // but is included just in case someone configures it this way
+ if (cfg.manual_memory)
+ set_state(steady_state, cfg.manual_memory);
+ else
+ #endif
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+
+ // 4 clicks, but hold last: exit and start at floor
+ else if (event == EV_click4_hold) {
+ //blink_once();
+ blip();
+ // reset button sequence to avoid activating anything in ramp mode
+ current_event = 0;
+ // ... and back to ramp mode
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+
+ // 5 clicks: exit and turn on at ceiling level
+ else if (event == EV_5clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+
+ #if NUM_CHANNEL_MODES > 1
+ // 3H: next channel mode
+ else if (event == EV_click3_hold) {
+ if (0 == (arg % TICKS_PER_SECOND)) {
+ // pretend the user clicked 3 times to change channels
+ return channel_mode_state(EV_3clicks, 0);
+ }
+ }
+ #endif
+
+ ////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
+
+ #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI)
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // if simple UI but not extended simple UI
+
+ #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 = cfg.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
+ cfg.indicator_led_mode = (mode << 2) + (cfg.indicator_led_mode & 0x03);
+ // redundant, sleep tick does the same thing
+ //indicator_led_update(cfg.indicator_led_mode >> 2, arg);
+ #elif defined(USE_AUX_RGB_LEDS)
+ #endif
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #elif defined(USE_AUX_RGB_LEDS)
+ // 7 clicks: change RGB aux LED pattern
+ else if (event == EV_7clicks) {
+ uint8_t mode = (cfg.rgb_led_lockout_mode >> 4) + 1;
+ mode = mode % RGB_LED_NUM_PATTERNS;
+ cfg.rgb_led_lockout_mode = (mode << 4) | (cfg.rgb_led_lockout_mode & 0x0f);
+ rgb_led_update(cfg.rgb_led_lockout_mode, 0);
+ save_config();
+ blink_once();
+ return EVENT_HANDLED;
+ }
+ // 7H: change RGB aux LED color
+ else if (event == EV_click7_hold) {
+ setting_rgb_mode_now = 1;
+ if (0 == (arg & 0x3f)) {
+ uint8_t mode = (cfg.rgb_led_lockout_mode & 0x0f) + 1;
+ mode = mode % RGB_LED_NUM_COLORS;
+ cfg.rgb_led_lockout_mode = mode | (cfg.rgb_led_lockout_mode & 0xf0);
+ //save_config();
+ }
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
+ return EVENT_HANDLED;
+ }
+ // 7H, release: save new color
+ else if (event == EV_click7_hold_release) {
+ setting_rgb_mode_now = 0;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #if defined(USE_EXTENDED_SIMPLE_UI) && defined(USE_SIMPLE_UI)
+ ////////// Every action below here is blocked in the Extended Simple UI //////////
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // if extended simple UI
+
+ #ifdef USE_AUTOLOCK
+ // 10H: configure the autolock option
+ else if (event == EV_click10_hold) {
+ push_state(autolock_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+#ifdef USE_AUTOLOCK
+// set the auto-lock timer to N minutes, where N is the number of clicks
+void autolock_config_save(uint8_t step, uint8_t value) {
+ cfg.autolock_time = value;
+}
+
+uint8_t autolock_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg, 1, autolock_config_save);
+}
+#endif // #ifdef USE_AUTOLOCK
+
diff --git a/ui/anduril/lockout-mode.h b/ui/anduril/lockout-mode.h
new file mode 100644
index 0000000..c2703a0
--- /dev/null
+++ b/ui/anduril/lockout-mode.h
@@ -0,0 +1,16 @@
+// lockout-mode.h: Lockout mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// soft lockout
+uint8_t lockout_state(Event event, uint16_t arg);
+
+#ifdef USE_AUTOLOCK
+#ifndef DEFAULT_AUTOLOCK_TIME
+#define DEFAULT_AUTOLOCK_TIME 0 // autolock time in minutes, 0 = disabled
+#endif
+uint8_t autolock_config_state(Event event, uint16_t arg);
+#endif
+
diff --git a/ui/anduril/misc.c b/ui/anduril/misc.c
new file mode 100644
index 0000000..1b92d6f
--- /dev/null
+++ b/ui/anduril/misc.c
@@ -0,0 +1,42 @@
+// misc.c: Misc extra functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "misc.h"
+
+/* no longer used
+void blink_confirm(uint8_t num) {
+ uint8_t brightness = actual_level;
+ uint8_t bump = actual_level + BLINK_BRIGHTNESS;
+ if (bump > MAX_LEVEL) bump = 0;
+ for (; num>0; num--) {
+ set_level(bump);
+ delay_4ms(10/4);
+ set_level(brightness);
+ if (num > 1) { delay_4ms(100/4); }
+ }
+}
+*/
+
+// make a short, visible pulse
+// (either brighter or darker, depending on current brightness)
+void blink_once() {
+ uint8_t brightness = actual_level;
+ uint8_t bump = brightness + BLINK_BRIGHTNESS;
+ if (bump > MAX_LEVEL) bump = 0;
+
+ set_level(bump);
+ delay_4ms(BLINK_ONCE_TIME/4);
+ set_level(brightness);
+}
+
+// Just go dark for a moment to indicate to user that something happened
+void blip() {
+ uint8_t temp = actual_level;
+ set_level(0);
+ delay_4ms(3);
+ set_level(temp);
+}
+
diff --git a/ui/anduril/misc.h b/ui/anduril/misc.h
new file mode 100644
index 0000000..0f2992a
--- /dev/null
+++ b/ui/anduril/misc.h
@@ -0,0 +1,10 @@
+// misc.h: Misc extra functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+//void blink_confirm(uint8_t num); // no longer used
+void blink_once();
+void blip();
+
diff --git a/ui/anduril/momentary-mode.c b/ui/anduril/momentary-mode.c
new file mode 100644
index 0000000..a765142
--- /dev/null
+++ b/ui/anduril/momentary-mode.c
@@ -0,0 +1,67 @@
+// momentary-mode.c: Momentary mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "momentary-mode.h"
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+ // init strobe mode, if relevant
+ #ifdef USE_STROBE_STATE
+ if ((event == EV_enter_state) && (momentary_mode != 0)) {
+ strobe_state(event, arg);
+ }
+ #endif
+
+ // light up when the button is pressed; go dark otherwise
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ momentary_active = 1;
+ // 0 = ramping, 1 = strobes
+ if (momentary_mode == 0) {
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ momentary_active = 0;
+ set_level(0);
+ //go_to_standby = 1; // sleep while light is off
+ return EVENT_HANDLED;
+ }
+
+ // Sleep, dammit! (but wait a few seconds first)
+ // (because standby mode uses such little power that it can interfere
+ // with exiting via tailcap loosen+tighten unless you leave power
+ // disconnected for several seconds, so we want to be awake when that
+ // happens to speed up the process)
+ else if (event == EV_tick) {
+ #ifdef USE_STROBE_STATE
+ if (momentary_active) {
+ // 0 = ramping, non-zero = strobes
+ if (momentary_mode != 0) {
+ return strobe_state(event, arg);
+ }
+ }
+ else {
+ #endif
+ if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds
+ go_to_standby = 1; // sleep while light is off
+ // turn off lighted button
+ #ifdef USE_INDICATOR_LED
+ indicator_led(0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(0, 0);
+ #endif
+ }
+ #ifdef USE_STROBE_STATE
+ }
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
diff --git a/ui/anduril/momentary-mode.h b/ui/anduril/momentary-mode.h
new file mode 100644
index 0000000..d774801
--- /dev/null
+++ b/ui/anduril/momentary-mode.h
@@ -0,0 +1,11 @@
+// momentary-mode.h: Momentary mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// momentary / signalling mode
+uint8_t momentary_state(Event event, uint16_t arg);
+uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
+uint8_t momentary_active = 0; // boolean, true if active *right now*
+
diff --git a/ui/anduril/off-mode.c b/ui/anduril/off-mode.c
new file mode 100644
index 0000000..0a381b7
--- /dev/null
+++ b/ui/anduril/off-mode.c
@@ -0,0 +1,384 @@
+// off-mode.c: "Off" mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "off-mode.h"
+
+#ifdef USE_SUNSET_TIMER
+#include "sunset-timer.h"
+#endif
+
+// set level smooth maybe
+void off_state_set_level(uint8_t level);
+
+
+uint8_t off_state(Event event, uint16_t arg) {
+
+ // turn emitter off when entering state
+ if (event == EV_enter_state) {
+ // turn off
+ off_state_set_level(0);
+ #ifdef USE_SMOOTH_STEPS
+ // don't go to sleep while animating
+ arg |= smooth_steps_in_progress;
+ #endif
+ ticks_since_on = 0;
+ #if NUM_CHANNEL_MODES > 1
+ // reset to ramp mode's channel when light turns off
+ channel_mode = cfg.channel_mode;
+ #endif
+ #ifdef USE_INDICATOR_LED
+ // redundant, sleep tick does the same thing
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, 0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ // redundant, sleep tick does the same thing
+ //rgb_led_update(cfg.rgb_led_off_mode, 0);
+ #endif
+ #ifdef USE_SUNSET_TIMER
+ sunset_timer = 0; // needs a reset in case previous timer was aborted
+ #endif
+ // sleep while off (lower power use)
+ // (unless delay requested; give the ADC some time to catch up)
+ if (! arg) { go_to_standby = 1; }
+ return EVENT_HANDLED;
+ }
+
+ // go back to sleep eventually if we got bumped but didn't leave "off" state
+ else if (event == EV_tick) {
+ if (arg > HOLD_TIMEOUT
+ #ifdef USE_SMOOTH_STEPS
+ && (! smooth_steps_in_progress)
+ #endif
+ ) {
+ go_to_standby = 1;
+ #ifdef USE_INDICATOR_LED
+ // redundant, sleep tick does the same thing
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
+ #elif defined(USE_AUX_RGB_LEDS)
+ // redundant, sleep tick does the same thing
+ //rgb_led_update(cfg.rgb_led_off_mode, arg);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+
+ #if defined(TICK_DURING_STANDBY)
+ // blink the indicator LED, maybe
+ else if (event == EV_sleep_tick) {
+ if (ticks_since_on < 255) ticks_since_on ++;
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ // reset to manual memory level when timer expires
+ if (cfg.manual_memory &&
+ (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
+ manual_memory_restore();
+ }
+ #endif
+ #ifdef USE_INDICATOR_LED
+ indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(cfg.rgb_led_off_mode, arg);
+ #endif
+
+ #ifdef USE_AUTOLOCK
+ // lock the light after being off for N minutes
+ uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE;
+ if ((cfg.autolock_time > 0) && (arg > ticks)) {
+ set_state(lockout_state, 0);
+ }
+ #endif // ifdef USE_AUTOLOCK
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #if (B_TIMING_ON == B_PRESS_T)
+ // hold (initially): go to lowest level (floor), but allow abort for regular click
+ else if (event == EV_click1_press) {
+ off_state_set_level(nearest_level(1));
+ return EVENT_HANDLED;
+ }
+ #endif // B_TIMING_ON == B_PRESS_T
+
+ // hold: go to lowest level
+ else if (event == EV_click1_hold) {
+ #if (B_TIMING_ON == B_PRESS_T)
+ #ifdef MOON_TIMING_HINT
+ if (arg == 0) {
+ // let the user know they can let go now to stay at moon
+ blip();
+ } else
+ #endif
+ #else // B_RELEASE_T or B_TIMEOUT_T
+ off_state_set_level(nearest_level(1));
+ #endif
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ if (cfg.dont_ramp_after_moon) {
+ return EVENT_HANDLED;
+ }
+ #endif
+ // don't start ramping immediately;
+ // give the user time to release at moon level
+ //if (arg >= HOLD_TIMEOUT) { // smaller
+ if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) { // more consistent
+ set_state(steady_state, 1);
+ }
+ return EVENT_HANDLED;
+ }
+
+ // hold, release quickly: go to lowest level (floor)
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+
+ #if (B_TIMING_ON != B_TIMEOUT_T)
+ // 1 click (before timeout): go to memorized level, but allow abort for double click
+ else if (event == EV_click1_release) {
+ #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
+ // this clause probably isn't used by any configs any more
+ // but is included just in case someone configures it this way
+ if (cfg.manual_memory) {
+ manual_memory_restore();
+ }
+ #endif
+ off_state_set_level(nearest_level(memorized_level));
+ return EVENT_HANDLED;
+ }
+ #endif // if (B_TIMING_ON != B_TIMEOUT_T)
+
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ #if (B_TIMING_ON != B_TIMEOUT_T)
+ set_state(steady_state, memorized_level);
+ #else
+ // FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer
+ // (need to duplicate manual mem logic here, probably)
+ set_state(steady_state, memorized_level);
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ // click, hold: momentary at ceiling or turbo
+ else if (event == EV_click2_hold) {
+ ticks_since_on = 0; // momentary turbo is definitely "on"
+ uint8_t turbo_level; // how bright is "turbo"?
+
+ #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior
+ uint8_t style_2c = cfg.ramp_2c_style;
+ #ifdef USE_SIMPLE_UI
+ // simple UI has its own turbo config
+ if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
+ #endif
+ // 0 = ceiling
+ // 1+ = full power
+ if (0 == style_2c) turbo_level = nearest_level(MAX_LEVEL);
+ else turbo_level = MAX_LEVEL;
+ #else
+ // simple UI: ceiling
+ // full UI: full power
+ #ifdef USE_SIMPLE_UI
+ if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL);
+ else
+ #endif
+ turbo_level = MAX_LEVEL;
+ #endif
+
+ off_state_set_level(turbo_level);
+ return EVENT_HANDLED;
+ }
+ else if (event == EV_click2_hold_release) {
+ off_state_set_level(0);
+ return EVENT_HANDLED;
+ }
+
+ // 2 clicks: highest mode (ceiling)
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+
+ // 3 clicks (initial press): off, to prep for later events
+ else if (event == EV_click3_press) {
+ #ifdef USE_SMOOTH_STEPS
+ // immediately cancel any animations in progress
+ smooth_steps_in_progress = 0;
+ #endif
+ off_state_set_level(0);
+ return EVENT_HANDLED;
+ }
+
+ #ifdef USE_BATTCHECK
+ // 3 clicks: battcheck mode / blinky mode group 1
+ else if (event == EV_3clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_LOCKOUT_MODE
+ // 4 clicks: soft lockout
+ else if (event == EV_4clicks) {
+ blink_once();
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
+ // 13 clicks and hold the last click: invoke factory reset (reboot)
+ else if (event == EV_click13_hold) {
+ reboot();
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_VERSION_CHECK
+ // 15+ clicks: show the version number
+ else if (event == EV_15clicks) {
+ set_state(version_check_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_SIMPLE_UI
+ // 10 clicks, but hold last click: turn simple UI off (or configure it)
+ else if ((event == EV_click10_hold) && (!arg)) {
+ if (cfg.simple_ui_active) { // turn off simple UI
+ blink_once();
+ cfg.simple_ui_active = 0;
+ save_config();
+ }
+ else { // configure simple UI ramp
+ push_state(simple_ui_config_state, 0);
+ }
+ return EVENT_HANDLED;
+ }
+
+ ////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
+
+ #ifndef USE_EXTENDED_SIMPLE_UI
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // ifndef USE_EXTENDED_SIMPLE_UI
+ #endif // ifdef USE_SIMPLE_UI
+
+ // click, click, long-click: strobe mode
+ #ifdef USE_STROBE_STATE
+ else if (event == EV_click3_hold) {
+ set_state(strobe_state, 0);
+ return EVENT_HANDLED;
+ }
+ #elif defined(USE_BORING_STROBE_STATE)
+ else if (event == EV_click3_hold) {
+ set_state(boring_strobe_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_INDICATOR_LED
+ // 7 clicks: change indicator LED mode
+ else if (event == EV_7clicks) {
+ uint8_t mode = (cfg.indicator_led_mode & 3) + 1;
+ #ifdef TICK_DURING_STANDBY
+ mode = mode & 3;
+ #else
+ mode = mode % 3;
+ #endif
+ #ifdef INDICATOR_LED_SKIP_LOW
+ if (mode == 1) { mode ++; }
+ #endif
+ cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode;
+ // redundant, sleep tick does the same thing
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #elif defined(USE_AUX_RGB_LEDS)
+ // 7 clicks: change RGB aux LED pattern
+ else if (event == EV_7clicks) {
+ uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1;
+ mode = mode % RGB_LED_NUM_PATTERNS;
+ cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f);
+ rgb_led_update(cfg.rgb_led_off_mode, 0);
+ save_config();
+ blink_once();
+ return EVENT_HANDLED;
+ }
+ // 7 clicks (hold last): change RGB aux LED color
+ else if (event == EV_click7_hold) {
+ setting_rgb_mode_now = 1;
+ if (0 == (arg & 0x3f)) {
+ uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1;
+ mode = mode % RGB_LED_NUM_COLORS;
+ cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0);
+ //save_config();
+ }
+ rgb_led_update(cfg.rgb_led_off_mode, arg);
+ return EVENT_HANDLED;
+ }
+ else if (event == EV_click7_hold_release) {
+ setting_rgb_mode_now = 0;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif // end 7 clicks
+
+ ////////// Every action below here is blocked in the Extended Simple UI //////////
+
+ #ifdef USE_SIMPLE_UI
+ #ifdef USE_EXTENDED_SIMPLE_UI
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // ifdef USE_EXTENDED_SIMPLE_UI
+
+ // 10 clicks: enable simple UI
+ else if (event == EV_10clicks) {
+ blink_once();
+ cfg.simple_ui_active = 1;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SIMPLE_UI
+
+ #ifdef USE_MOMENTARY_MODE
+ // 5 clicks: momentary mode
+ else if (event == EV_5clicks) {
+ blink_once();
+ set_state(momentary_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_TACTICAL_MODE
+ // 6 clicks: tactical mode
+ else if (event == EV_6clicks) {
+ blink_once();
+ set_state(tactical_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_GLOBALS_CONFIG
+ // 9 clicks, but hold last click: configure misc global settings
+ else if ((event == EV_click9_hold) && (!arg)) {
+ push_state(globals_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+void off_state_set_level(uint8_t level) {
+ // this pattern gets used a few times, so reduce duplication
+ #ifdef USE_SMOOTH_STEPS
+ if (cfg.smooth_steps_style) set_level_smooth(level, 8);
+ else
+ #endif
+ set_level(level);
+}
+
diff --git a/ui/anduril/off-mode.h b/ui/anduril/off-mode.h
new file mode 100644
index 0000000..d07fff1
--- /dev/null
+++ b/ui/anduril/off-mode.h
@@ -0,0 +1,12 @@
+// off-mode.h: "Off" mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// was the light in an "on" mode within the past second or so?
+uint8_t ticks_since_on = 0;
+
+// when the light is "off" or in standby
+uint8_t off_state(Event event, uint16_t arg);
+
diff --git a/ui/anduril/ramp-mode-fsm.h b/ui/anduril/ramp-mode-fsm.h
new file mode 100644
index 0000000..edfd6db
--- /dev/null
+++ b/ui/anduril/ramp-mode-fsm.h
@@ -0,0 +1,38 @@
+// ramp-mode-fsm.h: FSM config for ramping functions in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// enable FSM's ramping features
+#define USE_RAMPING
+
+// do smooth adjustments when compensating for temperature
+#ifdef USE_THERMAL_REGULATION
+#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments
+#endif
+
+// brightness to use when no memory is set
+// FIXME: this is only here to stop an error in fsm-ramping.c,
+// which should be fixed by using a different symbol instead
+// (like BUTTON_LED_BRIGHT_LEVEL or RAMP_HALFWAY_LEVEL or something)
+#ifndef DEFAULT_LEVEL
+#define DEFAULT_LEVEL MAX_1x7135
+#endif
+
+// requires the ability to measure time while "off"
+#ifdef USE_MANUAL_MEMORY_TIMER
+#define TICK_DURING_STANDBY
+#endif
+
+// ensure the jump start feature gets compiled in if needed
+#ifdef DEFAULT_JUMP_START_LEVEL
+#define USE_JUMP_START
+#endif
+
+// include an extra config mode for random stuff which doesn't fit elsewhere
+#if defined(USE_JUMP_START) || \
+ (defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING))
+#define USE_GLOBALS_CONFIG
+#endif
+
diff --git a/ui/anduril/ramp-mode.c b/ui/anduril/ramp-mode.c
new file mode 100644
index 0000000..4611b4f
--- /dev/null
+++ b/ui/anduril/ramp-mode.c
@@ -0,0 +1,741 @@
+// ramp-mode.c: Ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "ramp-mode.h"
+
+#ifdef USE_SUNSET_TIMER
+#include "sunset-timer.h"
+#endif
+
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.h"
+#endif
+
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ static int8_t ramp_direction = 1;
+ #if (B_TIMING_OFF == B_RELEASE_T)
+ // if the user double clicks, we need to abort turning off,
+ // and this stores the level to return to
+ static uint8_t level_before_off = 0;
+ #endif
+
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = cfg.channel_mode;
+ #endif
+
+ // make sure ramp globals are correct...
+ // ... but they already are; no need to do it here
+ //ramp_update_config();
+ //nearest_level(1); // same effect, takes less space
+
+ uint8_t mode_min = ramp_floor;
+ uint8_t mode_max = ramp_ceil;
+ uint8_t step_size;
+ if (cfg.ramp_style) { step_size = ramp_discrete_step_size; }
+ else { step_size = 1; }
+
+ // how bright is "turbo"?
+ uint8_t turbo_level;
+ #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior
+ uint8_t style_2c = cfg.ramp_2c_style;
+ #ifdef USE_SIMPLE_UI
+ // simple UI has its own turbo config
+ if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
+ #endif
+ // 0 = no turbo
+ // 1 = Anduril 1 direct to turbo
+ // 2 = Anduril 2 direct to ceiling, or turbo if already at ceiling
+ if (0 == style_2c) turbo_level = mode_max;
+ else if (1 == style_2c) turbo_level = MAX_LEVEL;
+ else {
+ if (memorized_level < mode_max) { turbo_level = mode_max; }
+ else { turbo_level = MAX_LEVEL; }
+ }
+ #elif defined(USE_2C_MAX_TURBO) // Anduril 1 style always
+ // simple UI: to/from ceiling
+ // full UI: to/from turbo (Anduril1 behavior)
+ #ifdef USE_SIMPLE_UI
+ if (cfg.simple_ui_active) turbo_level = mode_max;
+ else
+ #endif
+ turbo_level = MAX_LEVEL;
+ #else // Anduril 2 style always
+ // simple UI: to/from ceiling
+ // full UI: to/from ceiling if mem < ceiling,
+ // or to/from turbo if mem >= ceiling
+ if ((memorized_level < mode_max)
+ #ifdef USE_SIMPLE_UI
+ || cfg.simple_ui_active
+ #endif
+ ) { turbo_level = mode_max; }
+ else { turbo_level = MAX_LEVEL; }
+ #endif
+
+ #ifdef USE_SUNSET_TIMER
+ // handle the shutoff timer first
+ uint8_t sunset_active = sunset_timer; // save for comparison
+ // clock tick
+ sunset_timer_state(event, arg);
+ // if the timer was just turned on
+ if (sunset_timer && (! sunset_active)) {
+ sunset_timer_orig_level = actual_level;
+ }
+ // if the timer just expired, shut off
+ else if (sunset_active && (! sunset_timer)) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SUNSET_TIMER
+
+ // turn LED on when we first enter the mode
+ if ((event == EV_enter_state) || (event == EV_reenter_state)) {
+ #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
+ momentary_mode = 0; // 0 = ramping, 1 = strobes
+ #endif
+ // if we just got back from config mode, go back to memorized level
+ if (event == EV_reenter_state) {
+ arg = memorized_level;
+ }
+ // remember this level, unless it's moon or turbo
+ if ((arg > mode_min) && (arg < mode_max))
+ memorized_level = arg;
+ // use the requested level even if not memorized
+ arg = nearest_level(arg);
+ set_level_and_therm_target(arg);
+ ramp_direction = 1;
+ return EVENT_HANDLED;
+ }
+ #if (B_TIMING_OFF == B_RELEASE_T)
+ // 1 click (early): off, if configured for early response
+ else if (event == EV_click1_release) {
+ level_before_off = actual_level;
+ set_level_and_therm_target(0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks (early): abort turning off, if configured for early response
+ else if (event == EV_click2_press) {
+ set_level_and_therm_target(level_before_off);
+ return EVENT_HANDLED;
+ }
+ #endif // if (B_TIMING_OFF == B_RELEASE_T)
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ if (actual_level < turbo_level) {
+ set_level_and_therm_target(turbo_level);
+ }
+ else {
+ set_level_and_therm_target(memorized_level);
+ }
+ #ifdef USE_SUNSET_TIMER
+ reset_sunset_timer();
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ #ifdef USE_LOCKOUT_MODE
+ // 4 clicks: shortcut to lockout mode
+ else if (event == EV_4clicks) {
+ set_level(0);
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ // hold: change brightness (brighter, dimmer)
+ // click, hold: change brightness (dimmer)
+ else if ((event == EV_click1_hold) || (event == EV_click2_hold)) {
+ // ramp infrequently in stepped mode
+ if (cfg.ramp_style && (arg % HOLD_TIMEOUT != 0))
+ return EVENT_HANDLED;
+ #ifdef USE_RAMP_SPEED_CONFIG
+ // ramp slower if user configured things that way
+ if ((! cfg.ramp_style) && (arg % ramp_speed))
+ return EVENT_HANDLED;
+ #endif
+ #ifdef USE_SMOOTH_STEPS
+ // if a brightness transition is already happening,
+ // don't interrupt it
+ // (like 2C for full turbo then 1H to smooth ramp down
+ // ... without this clause, it flickers because it trips
+ // the "blink at ramp ceil" clause below, over and over)
+ if (smooth_steps_in_progress) return EVENT_HANDLED;
+ #endif
+ // fix ramp direction on first frame if necessary
+ if (!arg) {
+ // click, hold should always go down if possible
+ if (event == EV_click2_hold) { ramp_direction = -1; }
+ // make it ramp down instead, if already at max
+ else if (actual_level >= mode_max) { ramp_direction = -1; }
+ // make it ramp up if already at min
+ // (off->hold->stepped_min->release causes this state)
+ else if (actual_level <= mode_min) { ramp_direction = 1; }
+ }
+ // if the button is stuck, err on the side of safety and ramp down
+ else if ((arg > TICKS_PER_SECOND * 5
+ #ifdef USE_RAMP_SPEED_CONFIG
+ // FIXME: count from time actual_level hits mode_max,
+ // not from beginning of button hold
+ * ramp_speed
+ #endif
+ ) && (actual_level >= mode_max)) {
+ ramp_direction = -1;
+ }
+ #ifdef USE_LOCKOUT_MODE
+ // if the button is still stuck, lock the light
+ else if ((arg > TICKS_PER_SECOND * 10
+ #ifdef USE_RAMP_SPEED_CONFIG
+ // FIXME: count from time actual_level hits mode_min,
+ // not from beginning of button hold
+ * ramp_speed
+ #endif
+ ) && (actual_level <= mode_min)) {
+ blink_once();
+ set_state(lockout_state, 0);
+ }
+ #endif
+ memorized_level = nearest_level((int16_t)actual_level \
+ + (step_size * ramp_direction));
+ #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ // FIXME: blinks at beginning of smooth_steps animation instead
+ // of the end, so it should blink when actual_level reaches a
+ // threshold, instead of when memorized_level does
+ // (one possible fix is to just remove mid-ramp blinks entirely,
+ // and just blink only when it hits the top while going up)
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_CEIL
+ // FIXME: only blink at top when going up, not down
+ || (memorized_level == mode_max)
+ #endif
+ #ifdef BLINK_AT_RAMP_FLOOR
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ blip();
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = cfg.ramp_style;
+ cfg.ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ cfg.ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (cfg.ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ blip();
+ }
+ #endif
+ set_level_and_therm_target(memorized_level);
+ #ifdef USE_SUNSET_TIMER
+ reset_sunset_timer();
+ #endif
+ return EVENT_HANDLED;
+ }
+ // reverse ramp direction on hold release
+ else if ((event == EV_click1_hold_release)
+ || (event == EV_click2_hold_release)) {
+ ramp_direction = -ramp_direction;
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ else if (event == EV_tick) {
+ // un-reverse after 1 second
+ if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
+
+ #ifdef USE_SUNSET_TIMER
+ // reduce output if shutoff timer is active
+ if (sunset_timer) {
+ uint8_t dimmed_level = sunset_timer_orig_level * sunset_timer / sunset_timer_peak;
+ uint8_t dimmed_level_next = sunset_timer_orig_level * (sunset_timer-1) / sunset_timer_peak;
+ uint8_t dimmed_level_delta = dimmed_level - dimmed_level_next;
+ dimmed_level -= dimmed_level_delta * (sunset_ticks/TICKS_PER_SECOND) / 60;
+ if (dimmed_level < 1) dimmed_level = 1;
+
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(dimmed_level);
+ target_level = dimmed_level;
+ #else
+ set_level_and_therm_target(dimmed_level);
+ #endif
+ }
+ #endif // ifdef USE_SUNSET_TIMER
+
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ int16_t diff = gradual_target - actual_level;
+ static uint16_t ticks_since_adjust = 0;
+ ticks_since_adjust++;
+ if (diff) {
+ uint16_t ticks_per_adjust = 256;
+ if (diff < 0) {
+ //diff = -diff;
+ if (actual_level > THERM_FASTER_LEVEL) {
+ #ifdef THERM_HARD_TURBO_DROP
+ ticks_per_adjust >>= 2;
+ #endif
+ ticks_per_adjust >>= 2;
+ }
+ } else {
+ // rise at half speed
+ ticks_per_adjust <<= 1;
+ }
+ while (diff) {
+ ticks_per_adjust >>= 1;
+ //diff >>= 1;
+ diff /= 2; // because shifting produces weird behavior
+ }
+ if (ticks_since_adjust > ticks_per_adjust)
+ {
+ gradual_tick();
+ ticks_since_adjust = 0;
+ }
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
+ return EVENT_HANDLED;
+ }
+
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by an amount proportional to how far we are above the ceiling
+ else if (event == EV_temperature_high) {
+ #if 0
+ blip();
+ #endif
+ #ifdef THERM_HARD_TURBO_DROP
+ //if (actual_level > THERM_FASTER_LEVEL) {
+ if (actual_level == MAX_LEVEL) {
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(THERM_FASTER_LEVEL);
+ target_level = THERM_FASTER_LEVEL;
+ #else
+ set_level_and_therm_target(THERM_FASTER_LEVEL);
+ #endif
+ } else
+ #endif
+ if (actual_level > MIN_THERM_STEPDOWN) {
+ int16_t stepdown = actual_level - arg;
+ if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN;
+ else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepdown);
+ #else
+ set_level(stepdown);
+ #endif
+ }
+ return 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 0
+ blip();
+ #endif
+ if (actual_level < target_level) {
+ //int16_t stepup = actual_level + (arg>>1);
+ int16_t stepup = actual_level + arg;
+ if (stepup > target_level) stepup = target_level;
+ else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepup);
+ #else
+ set_level(stepup);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // temperature is within target window
+ // (so stop trying to adjust output)
+ else if (event == EV_temperature_okay) {
+ // if we're still adjusting output... stop after the current step
+ if (gradual_target > actual_level)
+ gradual_target = actual_level + 1;
+ else if (gradual_target < actual_level)
+ gradual_target = actual_level - 1;
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
+ #endif // ifdef USE_THERMAL_REGULATION
+
+ ////////// Every action below here is blocked in the simple UI //////////
+ // That is, unless we specifically want to enable 3C for smooth/stepped selection in Simple UI
+ #if defined(USE_SIMPLE_UI) && !defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ // 3 clicks: toggle smooth vs discrete ramping
+ // (and/or 6 clicks when there are multiple channel modes)
+ // (handle 3C here anyway, when all but 1 mode is disabled)
+ else if ((event == EV_3clicks)
+ #if NUM_CHANNEL_MODES > 1
+ || (event == EV_6clicks)
+ ) {
+ // detect if > 1 channel mode is enabled,
+ // and if so, fall through so channel mode code can handle it
+ // otherwise, change the ramp style
+ if (event == EV_3clicks) {
+ uint8_t enabled = 0;
+ for (uint8_t m=0; m<NUM_CHANNEL_MODES; m++)
+ enabled += channel_mode_enabled(m);
+ if (enabled > 1)
+ return EVENT_NOT_HANDLED;
+ }
+ #else
+ ) {
+ #endif
+
+ cfg.ramp_style = !cfg.ramp_style;
+ save_config();
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ blip();
+ memorized_level = nearest_level(actual_level);
+ set_level_and_therm_target(memorized_level);
+ #ifdef USE_SUNSET_TIMER
+ reset_sunset_timer();
+ #endif
+ return EVENT_HANDLED;
+ }
+
+ // If we allowed 3C in Simple UI, now block further actions
+ #if defined(USE_SIMPLE_UI) && defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ // 3H: momentary turbo (on lights with no tint ramping)
+ // (or 4H when tint ramping is available)
+ else if ((event == EV_click3_hold)
+ #ifdef USE_CHANNEL_MODE_ARGS
+ || (event == EV_click4_hold)
+ #endif
+ ) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // ramp tint if tint exists in this mode
+ if ((event == EV_click3_hold)
+ && (channel_has_args(channel_mode)))
+ return EVENT_NOT_HANDLED;
+ #endif
+ if (! arg) { // first frame only, to allow thermal regulation to work
+ #ifdef USE_2C_STYLE_CONFIG
+ uint8_t tl = style_2c ? MAX_LEVEL : turbo_level;
+ set_level_and_therm_target(tl);
+ #else
+ set_level_and_therm_target(turbo_level);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ else if ((event == EV_click3_hold_release)
+ #ifdef USE_CHANNEL_MODE_ARGS
+ || (event == EV_click4_hold_release)
+ #endif
+ ) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // ramp tint if tint exists in this mode
+ if ((event == EV_click3_hold_release)
+ && (channel_has_args(channel_mode)))
+ return EVENT_NOT_HANDLED;
+ #endif
+ set_level_and_therm_target(memorized_level);
+ return EVENT_HANDLED;
+ }
+
+ #ifdef USE_MOMENTARY_MODE
+ // 5 clicks: shortcut to momentary mode
+ else if (event == EV_5clicks) {
+ set_level(0);
+ set_state(momentary_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_RAMP_CONFIG
+ // 7H: configure this ramp mode
+ else if (event == EV_click7_hold) {
+ push_state(ramp_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_MANUAL_MEMORY
+ else if (event == EV_10clicks) {
+ // turn on manual memory and save current brightness
+ manual_memory_save();
+ save_config();
+ blink_once();
+ return EVENT_HANDLED;
+ }
+ else if (event == EV_click10_hold) {
+ #ifdef USE_RAMP_EXTRAS_CONFIG
+ // let user configure a bunch of extra ramp options
+ push_state(ramp_extras_config_state, 0);
+ #else // manual mem, but no timer
+ // turn off manual memory; go back to automatic
+ if (0 == arg) {
+ cfg.manual_memory = 0;
+ save_config();
+ blink_once();
+ }
+ #endif
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_MANUAL_MEMORY
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_RAMP_CONFIG
+void ramp_config_save(uint8_t step, uint8_t value) {
+
+ // 0 = smooth ramp, 1 = stepped ramp, 2 = simple UI's ramp
+ uint8_t style = cfg.ramp_style;
+ #ifdef USE_SIMPLE_UI
+ if (current_state == simple_ui_config_state) style = 2;
+ #endif
+
+ #if defined(USE_SIMPLE_UI) && defined(USE_2C_STYLE_CONFIG)
+ // simple UI config is weird...
+ // has some ramp extras after floor/ceil/steps
+ if (4 == step) {
+ cfg.ramp_2c_style_simple = value;
+ }
+ else
+ #endif
+
+ // save adjusted value to the correct slot
+ if (value) {
+ // ceiling value is inverted
+ if (step == 2) value = MAX_LEVEL + 1 - value;
+
+ // which option are we configuring?
+ // TODO? maybe rearrange definitions to avoid the need for this table
+ // (move all ramp values into a single array?)
+ uint8_t *steps[] = { cfg.ramp_floors, cfg.ramp_ceils, cfg.ramp_stepss };
+ uint8_t *option;
+ option = steps[step-1];
+ option[style] = value;
+ }
+}
+
+uint8_t ramp_config_state(Event event, uint16_t arg) {
+ #ifdef USE_RAMP_SPEED_CONFIG
+ const uint8_t num_config_steps = 3;
+ #else
+ uint8_t num_config_steps = cfg.ramp_style + 2;
+ #endif
+ return config_state_base(event, arg,
+ num_config_steps, ramp_config_save);
+}
+
+#ifdef USE_SIMPLE_UI
+uint8_t simple_ui_config_state(Event event, uint16_t arg) {
+ #if defined(USE_2C_STYLE_CONFIG)
+ #define SIMPLE_UI_NUM_MENU_ITEMS 4
+ #else
+ #define SIMPLE_UI_NUM_MENU_ITEMS 3
+ #endif
+ return config_state_base(event, arg,
+ SIMPLE_UI_NUM_MENU_ITEMS,
+ ramp_config_save);
+}
+#endif
+#endif // #ifdef USE_RAMP_CONFIG
+
+#ifdef USE_RAMP_EXTRAS_CONFIG
+void ramp_extras_config_save(uint8_t step, uint8_t value) {
+ // item 1: disable manual memory, go back to automatic
+ if (manual_memory_config_step == step) {
+ cfg.manual_memory = 0;
+ }
+
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ // item 2: set manual memory timer duration
+ // FIXME: should be limited to (65535 / SLEEP_TICKS_PER_MINUTE)
+ // to avoid overflows or impossibly long timeouts
+ // (by default, the effective limit is 145, but it allows up to 255)
+ else if (manual_memory_timer_config_step == step) {
+ cfg.manual_memory_timer = value;
+ }
+ #endif
+
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ // item 3: ramp up after hold-from-off for moon?
+ // 0 = yes, ramp after moon
+ // 1+ = no, stay at moon
+ else if (dont_ramp_after_moon_config_step == step) {
+ cfg.dont_ramp_after_moon = value;
+ }
+ #endif
+
+ #ifdef USE_2C_STYLE_CONFIG
+ // item 4: Anduril 1 2C turbo, or Anduril 2 2C ceiling?
+ // 1 = Anduril 1, 2C turbo
+ // 2+ = Anduril 2, 2C ceiling
+ else if (ramp_2c_style_config_step == step) {
+ cfg.ramp_2c_style = value;
+ }
+ #endif
+
+ #ifdef USE_SMOOTH_STEPS
+ else if (smooth_steps_style_config_step == step) {
+ cfg.smooth_steps_style = value;
+ }
+ #endif
+}
+
+uint8_t ramp_extras_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ ramp_extras_config_num_steps - 1,
+ ramp_extras_config_save);
+}
+#endif
+
+#ifdef USE_GLOBALS_CONFIG
+void globals_config_save(uint8_t step, uint8_t value) {
+ if (0) {}
+ #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+ else if (step == tint_style_config_step) { cfg.tint_ramp_style = value; }
+ #endif
+ #ifdef USE_JUMP_START
+ else if (step == jump_start_config_step) { cfg.jump_start_level = value; }
+ #endif
+}
+
+uint8_t globals_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ globals_config_num_steps - 1,
+ globals_config_save);
+}
+#endif
+
+// 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) {
+ // 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
+
+ // ensure all globals are correct
+ ramp_update_config();
+
+ // bounds check
+ uint8_t mode_min = ramp_floor;
+ uint8_t mode_max = ramp_ceil;
+ uint8_t num_steps = cfg.ramp_stepss[1
+ #ifdef USE_SIMPLE_UI
+ + cfg.simple_ui_active
+ #endif
+ ];
+ // special case for 1-step ramp... use halfway point between floor and ceiling
+ if (cfg.ramp_style && (1 == num_steps)) {
+ uint8_t mid = (mode_max + mode_min) >> 1;
+ return mid;
+ }
+ if (target < mode_min) return mode_min;
+ if (target > mode_max) return mode_max;
+ // the rest isn't relevant for smooth ramping
+ if (! cfg.ramp_style) return target;
+
+ uint8_t ramp_range = mode_max - mode_min;
+ ramp_discrete_step_size = ramp_range / (num_steps-1);
+ uint8_t this_level = mode_min;
+
+ for(uint8_t i=0; i<num_steps; i++) {
+ this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1));
+ int16_t diff = target - this_level;
+ if (diff < 0) diff = -diff;
+ if (diff <= (ramp_discrete_step_size>>1))
+ return this_level;
+ }
+ return this_level;
+}
+
+// ensure ramp globals are correct
+void ramp_update_config() {
+ uint8_t which = cfg.ramp_style;
+ #ifdef USE_SIMPLE_UI
+ if (cfg.simple_ui_active) { which = 2; }
+ #endif
+
+ ramp_floor = cfg.ramp_floors[which];
+ ramp_ceil = cfg.ramp_ceils[which];
+}
+
+#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS)
+void set_level_and_therm_target(uint8_t level) {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = level;
+ #endif
+ #ifdef USE_SMOOTH_STEPS
+ // if adjusting by more than 1 ramp level,
+ // animate the step change (if smooth steps enabled)
+ uint8_t diff = (level > actual_level)
+ ? (level - actual_level) : (actual_level - level);
+ if (smooth_steps_in_progress
+ || (cfg.smooth_steps_style && (diff > 1)))
+ set_level_smooth(level, 4);
+ else
+ #endif
+ set_level(level);
+}
+#else
+#define set_level_and_therm_target(level) set_level(level)
+#endif
+
+void manual_memory_restore() {
+ memorized_level = cfg.manual_memory;
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = cfg.channel_mode = cfg.manual_memory_channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ cfg.channel_mode_args[i] = cfg.manual_memory_channel_args[i];
+ #endif
+}
+
+void manual_memory_save() {
+ cfg.manual_memory = actual_level;
+ #if NUM_CHANNEL_MODES > 1
+ cfg.manual_memory_channel_mode = channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ cfg.manual_memory_channel_args[i] = cfg.channel_mode_args[i];
+ #endif
+}
+
+#ifdef USE_SUNSET_TIMER
+void reset_sunset_timer() {
+ if (sunset_timer) {
+ sunset_timer_orig_level = actual_level;
+ sunset_timer_peak = sunset_timer;
+ sunset_ticks = 0;
+ }
+}
+#endif
+
diff --git a/ui/anduril/ramp-mode.h b/ui/anduril/ramp-mode.h
new file mode 100644
index 0000000..59c8db0
--- /dev/null
+++ b/ui/anduril/ramp-mode.h
@@ -0,0 +1,224 @@
+// ramp-mode.h: Ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifndef RAMP_LENGTH
+#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
+#endif
+
+// thermal properties, if not defined per-driver
+#ifndef MIN_THERM_STEPDOWN
+// TODO: Replace MAX_Xx7135 with MAX_CH1, MAX_CH2, MAX_REGULATED, etc
+#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to
+#endif
+#ifndef THERM_FASTER_LEVEL
+ #ifdef MAX_Nx7135
+ #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high
+ #else
+ #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high
+ #endif
+#endif
+
+#if defined(USE_SIMPLE_UI)
+// TODO: Move these settings to config-default.h?
+// start in the simple UI after each factory reset?
+#ifndef SIMPLE_UI_ACTIVE
+#define SIMPLE_UI_ACTIVE 1
+#endif
+#ifndef SIMPLE_UI_FLOOR
+#define SIMPLE_UI_FLOOR (RAMP_SIZE*2/15)
+#endif
+#ifndef SIMPLE_UI_CEIL
+#define SIMPLE_UI_CEIL (RAMP_SIZE*9/15)
+#endif
+#ifndef SIMPLE_UI_STEPS
+#define SIMPLE_UI_STEPS 5
+#endif
+#endif
+
+
+// configure the timing of turning on/off in regular ramp mode
+// press: react as soon as the button is pressed
+#define B_PRESS_T 0
+// release: react as soon as the button is released
+#define B_RELEASE_T 1
+// timeout: react as soon as we're sure the user isn't doing a double-click
+#define B_TIMEOUT_T 2
+// defaults are release on, timeout off
+#ifndef B_TIMING_ON
+//#define B_TIMING_ON B_PRESS_T
+#define B_TIMING_ON B_RELEASE_T
+#endif
+#ifndef B_TIMING_OFF
+//#define B_TIMING_OFF B_RELEASE_T
+#define B_TIMING_OFF B_TIMEOUT_T
+#endif
+
+
+// default ramp options if not overridden earlier per-driver
+#ifndef RAMP_STYLE
+#define RAMP_STYLE 0 // smooth default
+#endif
+#ifndef DEFAULT_RAMP_SPEED
+#define DEFAULT_RAMP_SPEED 1 // WDT ticks per "frame", must be 1 or more
+#endif
+#ifndef RAMP_SMOOTH_FLOOR
+ #define RAMP_SMOOTH_FLOOR 1
+#endif
+#ifndef RAMP_SMOOTH_CEIL
+ #if PWM_CHANNELS == 3
+ #define RAMP_SMOOTH_CEIL MAX_Nx7135
+ #else
+ #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
+ #endif
+#endif
+#ifndef RAMP_DISCRETE_FLOOR
+ #define RAMP_DISCRETE_FLOOR 20
+#endif
+#ifndef RAMP_DISCRETE_CEIL
+ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#endif
+#ifndef RAMP_DISCRETE_STEPS
+ #define RAMP_DISCRETE_STEPS 7
+#endif
+
+// mile marker(s) partway up the ramp
+// default: blink only at border between regulated and FET
+#ifdef BLINK_AT_RAMP_MIDDLE
+ // FIXME: remove PWM_CHANNELS, use some other abstraction
+ #if PWM_CHANNELS >= 3
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
+ #ifndef BLINK_AT_RAMP_MIDDLE_2
+ #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
+ #endif
+ #endif
+ #else
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
+ #endif
+ #endif
+#endif
+
+
+// ramping mode and its related config mode
+uint8_t steady_state(Event event, uint16_t arg);
+
+#ifdef USE_RAMP_CONFIG
+uint8_t ramp_config_state(Event event, uint16_t arg);
+void ramp_config_save(uint8_t step, uint8_t value);
+#ifdef USE_SIMPLE_UI
+uint8_t simple_ui_config_state(Event event, uint16_t arg);
+#endif
+#endif
+
+#if defined(USE_MANUAL_MEMORY_TIMER) || defined(USE_RAMP_AFTER_MOON_CONFIG) || defined(USE_2C_STYLE_CONFIG) || defined(USE_AUTO_SUNSET)
+#define USE_RAMP_EXTRAS_CONFIG
+#endif
+#ifdef USE_RAMP_EXTRAS_CONFIG
+uint8_t ramp_extras_config_state(Event event, uint16_t arg);
+#endif
+
+// calculate the nearest ramp level which would be valid at the moment
+// (is a no-op for smooth ramp, but limits discrete ramp to only the
+// correct levels for the user's config)
+uint8_t nearest_level(int16_t target);
+
+// ensure ramp globals are correct
+void ramp_update_config();
+
+#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS)
+// brightness before thermal step-down
+uint8_t target_level = 0;
+void set_level_and_therm_target(uint8_t level);
+#else
+#define set_level_and_therm_target(level) set_level(level)
+#endif
+
+
+// brightness control
+uint8_t memorized_level = DEFAULT_LEVEL;
+#ifdef USE_MANUAL_MEMORY
+ void manual_memory_restore();
+ void manual_memory_save();
+ #ifndef DEFAULT_MANUAL_MEMORY
+ #define DEFAULT_MANUAL_MEMORY 0
+ #endif
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ #ifndef DEFAULT_MANUAL_MEMORY_TIMER
+ #define DEFAULT_MANUAL_MEMORY_TIMER 0
+ #endif
+ #endif
+#endif
+
+#ifndef DEFAULT_2C_STYLE_SIMPLE
+ #define DEFAULT_2C_STYLE_SIMPLE 0
+#endif
+
+#ifdef USE_2C_STYLE_CONFIG
+#ifndef DEFAULT_2C_STYLE
+#define DEFAULT_2C_STYLE 2
+#endif
+
+#ifdef USE_2C_MAX_TURBO
+#error Cannot use USE_2C_MAX_TURBO and USE_2C_STYLE_CONFIG at the same time.
+#endif
+#endif
+
+#ifdef USE_RAMP_SPEED_CONFIG
+#define ramp_speed (cfg.ramp_stepss[0])
+#endif
+#ifdef USE_RAMP_AFTER_MOON_CONFIG
+#ifndef DEFAULT_DONT_RAMP_AFTER_MOON
+#define DEFAULT_DONT_RAMP_AFTER_MOON 0
+#endif
+#endif
+// current values, regardless of style
+uint8_t ramp_floor = RAMP_SMOOTH_FLOOR;
+uint8_t ramp_ceil = RAMP_SMOOTH_CEIL;
+
+uint8_t ramp_discrete_step_size; // don't set this
+
+#ifdef USE_SUNSET_TIMER
+uint8_t sunset_timer_orig_level = 0;
+void reset_sunset_timer();
+#endif
+
+#ifdef USE_RAMP_EXTRAS_CONFIG
+typedef enum {
+ ramp_extras_cfg_zero = 0,
+ manual_memory_config_step,
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ manual_memory_timer_config_step,
+ #endif
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ dont_ramp_after_moon_config_step,
+ #endif
+ #ifdef USE_2C_STYLE_CONFIG
+ ramp_2c_style_config_step,
+ #endif
+ #ifdef USE_SMOOTH_STEPS
+ smooth_steps_style_config_step,
+ #endif
+ ramp_extras_config_num_steps
+} ramp_extras_config_steps_e;
+#endif
+
+#ifdef USE_GLOBALS_CONFIG
+typedef enum {
+ globals_cfg_zero = 0,
+ #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+ tint_style_config_step,
+ #endif
+ #ifdef USE_JUMP_START
+ jump_start_config_step,
+ #endif
+ globals_config_num_steps
+} globals_config_steps_e;
+
+void globals_config_save(uint8_t step, uint8_t value);
+uint8_t globals_config_state(Event event, uint16_t arg);
+#endif
+
diff --git a/ui/anduril/smooth-steps.c b/ui/anduril/smooth-steps.c
new file mode 100644
index 0000000..9631e41
--- /dev/null
+++ b/ui/anduril/smooth-steps.c
@@ -0,0 +1,47 @@
+// smooth-steps.c: Smooth step adjustments for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "smooth-steps.h"
+
+#ifdef USE_SMOOTH_STEPS
+
+// one iteration of main loop()
+void smooth_steps_iter() {
+ if (actual_level == smooth_steps_target) {
+ set_level(smooth_steps_target);
+ smooth_steps_in_progress = 0;
+ // restore prev_level when animation ends
+ prev_level = smooth_steps_start;
+ }
+ else if (smooth_steps_target > actual_level) {
+ // power-linear(ish) ascent
+ // (jump by ~20% of remaining distance on each frame)
+ uint8_t diff = smooth_steps_target - actual_level;
+ uint8_t this = diff / smooth_steps_speed;
+ if (!this) this = 1;
+ set_level(actual_level + this);
+ nice_delay_ms(10);
+ } else {
+ // ramp-linear descent
+ // (jump by 1 on each frame, frame rate gives constant total time)
+ uint8_t diff = smooth_steps_start - smooth_steps_target;
+ uint16_t delay = 1 + (30 * smooth_steps_speed / diff);
+ set_level(actual_level - 1);
+ // TODO? if delay < one PWM cycle, this can look a little weird
+ nice_delay_ms(delay);
+ }
+}
+
+void set_level_smooth(uint8_t level, uint8_t speed) {
+ smooth_steps_target = level;
+ smooth_steps_speed = speed; // higher = slower
+ smooth_steps_in_progress = 1;
+ // for setting prev_level after animation ends
+ smooth_steps_start = actual_level;
+}
+
+#endif
+
diff --git a/ui/anduril/smooth-steps.h b/ui/anduril/smooth-steps.h
new file mode 100644
index 0000000..a553af2
--- /dev/null
+++ b/ui/anduril/smooth-steps.h
@@ -0,0 +1,19 @@
+// smooth-steps.h: Smooth step adjustments for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifdef USE_SMOOTH_STEPS
+
+uint8_t smooth_steps_start;
+uint8_t smooth_steps_target;
+uint8_t smooth_steps_in_progress;
+uint8_t smooth_steps_speed;
+
+void smooth_steps_iter();
+
+void set_level_smooth(uint8_t level, uint8_t speed);
+
+#endif
+
diff --git a/ui/anduril/sos-mode.c b/ui/anduril/sos-mode.c
new file mode 100644
index 0000000..2a4e97c
--- /dev/null
+++ b/ui/anduril/sos-mode.c
@@ -0,0 +1,56 @@
+// sos-mode.c: SOS mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "sos-mode.h"
+
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+uint8_t sos_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: next blinky mode
+ else if (event == EV_2clicks) {
+ #if defined(USE_BATTCHECK_MODE)
+ set_state(battcheck_state, 0);
+ #elif defined(USE_THERMAL_REGULATION)
+ set_state(tempcheck_state, 0);
+ #elif defined(USE_BEACON_MODE)
+ set_state(beacon_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+void sos_blink(uint8_t num, uint8_t dah) {
+ #define DIT_LENGTH 200
+ for (; num > 0; num--) {
+ set_level(memorized_level);
+ nice_delay_ms(DIT_LENGTH);
+ if (dah) { // dah is 3X as long as a dit
+ nice_delay_ms(DIT_LENGTH*2);
+ }
+ set_level(0);
+ // one "off" dit between blinks
+ nice_delay_ms(DIT_LENGTH);
+ }
+ // three "off" dits (or one "dah") between letters
+ // (except for SOS, which is collectively treated as a single "letter")
+ //nice_delay_ms(DIT_LENGTH*2);
+}
+
+inline void sos_mode_iter() {
+ // one iteration of main loop()
+ //nice_delay_ms(1000);
+ sos_blink(3, 0); // S
+ sos_blink(3, 1); // O
+ sos_blink(3, 0); // S
+ nice_delay_ms(2000);
+}
+
diff --git a/ui/anduril/sos-mode.h b/ui/anduril/sos-mode.h
new file mode 100644
index 0000000..5af61be
--- /dev/null
+++ b/ui/anduril/sos-mode.h
@@ -0,0 +1,11 @@
+// sos-mode.h: SOS mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+// automatic SOS emergency signal
+uint8_t sos_state(Event event, uint16_t arg);
+#endif
+
diff --git a/ui/anduril/strobe-modes-fsm.h b/ui/anduril/strobe-modes-fsm.h
new file mode 100644
index 0000000..4d948ed
--- /dev/null
+++ b/ui/anduril/strobe-modes-fsm.h
@@ -0,0 +1,55 @@
+// strobe-modes-fsm.h: FSM config for strobe modes in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// enable the random number generator if we need it
+#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
+#define USE_PSEUDO_RAND
+#endif
+
+// party strobe uses really short pulses
+#ifdef USE_PARTY_STROBE_MODE
+#define USE_DELAY_ZERO
+#endif
+
+// candle mode is basically a bunch of stacked random triangle waves
+#if defined(USE_CANDLE_MODE)
+#define USE_TRIANGLE_WAVE
+#endif
+
+// the presence of strobe mode(s) affects how many eeprom bytes we need,
+// so it's relevant for FSM configuration
+#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE)
+#define USE_STROBE_STATE
+#endif
+
+// 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_POLICE_COLOR_STROBE_MODE
+ police_color_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;
+#define NUM_STROBES strobe_mode_END
+#endif
+
diff --git a/ui/anduril/strobe-modes.c b/ui/anduril/strobe-modes.c
new file mode 100644
index 0000000..ad17964
--- /dev/null
+++ b/ui/anduril/strobe-modes.c
@@ -0,0 +1,332 @@
+// strobe-modes.c: Strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "strobe-modes.h"
+
+#ifdef USE_STROBE_STATE
+uint8_t strobe_state(Event event, uint16_t arg) {
+ static int8_t ramp_direction = 1;
+
+ // 'st' reduces ROM size slightly
+ strobe_mode_te st = current_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) {
+ current_strobe_type = cfg.strobe_type;
+ ramp_direction = 1;
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: rotate through strobe/flasher modes
+ else if (event == EV_2clicks) {
+ current_strobe_type = cfg.strobe_type = (st + 1) % NUM_STROBES;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // 3 clicks: rotate through channel modes for the current strobe
+ else if (event == EV_3clicks) {
+ // TODO: maybe skip aux modes?
+ set_channel_mode((channel_mode + 1) % NUM_CHANNEL_MODES);
+ cfg.strobe_channels[st] = channel_mode;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 4 clicks: rotate backward through strobe/flasher modes
+ else if (event == EV_4clicks) {
+ current_strobe_type = cfg.strobe_type = (st - 1 + NUM_STROBES) % NUM_STROBES;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ // 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 = cfg.strobe_delays[st];
+ d -= ramp_direction;
+ if (d < 8) d = 8;
+ else if (d > 254) d = 254;
+ cfg.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) {
+ cfg.bike_flasher_brightness += ramp_direction;
+ if (cfg.bike_flasher_brightness < 2) cfg.bike_flasher_brightness = 2;
+ else if (cfg.bike_flasher_brightness > MAX_BIKING_LEVEL) cfg.bike_flasher_brightness = MAX_BIKING_LEVEL;
+ set_level(cfg.bike_flasher_brightness);
+ }
+ #endif
+
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 (cfg.strobe_delays[st] < 255) cfg.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 (cfg.bike_flasher_brightness > 2)
+ cfg.bike_flasher_brightness --;
+ set_level(cfg.bike_flasher_brightness);
+ }
+ #endif
+
+ return EVENT_HANDLED;
+ }
+ // release hold: save new strobe settings
+ else if (event == EV_click2_hold_release) {
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #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 EVENT_HANDLED;
+ }
+ #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 == AUTO_REVERSE_TIME) ramp_direction = 1;
+
+ pseudo_rand_seed += arg;
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe
+inline void strobe_state_iter() {
+ uint8_t st = current_strobe_type; // can't use switch() on an enum
+
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // remember channel mode for each strobe
+ channel_mode = cfg.strobe_channels[st];
+ #endif
+
+ switch(st) {
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ #ifdef USE_PARTY_STROBE_MODE
+ case party_strobe_e:
+ #endif
+ #ifdef USE_TACTICAL_STROBE_MODE
+ case tactical_strobe_e:
+ #endif
+ party_tactical_strobe_mode_iter(st);
+ break;
+ #endif
+
+ #ifdef USE_POLICE_COLOR_STROBE_MODE
+ case police_color_strobe_e:
+ police_color_strobe_iter();
+ break;
+ #endif
+
+ #ifdef USE_LIGHTNING_MODE
+ case lightning_storm_e:
+ lightning_storm_iter();
+ break;
+ #endif
+
+ #ifdef USE_BIKE_FLASHER_MODE
+ case bike_flasher_e:
+ bike_flasher_iter();
+ break;
+ #endif
+ }
+}
+#endif // ifdef USE_STROBE_STATE
+
+#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+inline void party_tactical_strobe_mode_iter(uint8_t st) {
+ // one iteration of main loop()
+ uint8_t del = cfg.strobe_delays[st];
+ // TODO: make tac strobe brightness configurable?
+ set_level(STROBE_BRIGHTNESS);
+ if (0) {} // placeholder
+ #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(STROBE_OFF_LEVEL);
+ nice_delay_ms(del); // no return check necessary on final delay
+}
+#endif
+
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter() {
+ // one iteration of main loop()
+ uint8_t del = 66;
+ // TODO: make police strobe brightness configurable
+ uint8_t bright = memorized_level;
+ //uint8_t channel = channel_mode;
+
+ for (uint8_t i=0; i<10; i++) {
+ if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1);
+ else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2);
+ set_level(bright);
+ nice_delay_ms(del >> 1);
+ set_level(STROBE_OFF_LEVEL);
+ nice_delay_ms(del);
+ }
+
+ // restore the channel when done
+ //set_channel_mode(channel);
+ channel_mode = cfg.channel_mode;
+}
+#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
+#ifndef BIKE_STROBE_ONTIME
+#define BIKE_STROBE_ONTIME 0
+#endif
+inline void bike_flasher_iter() {
+ // one iteration of main loop()
+ uint8_t burst = cfg.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 + BIKE_STROBE_ONTIME);
+ set_level(cfg.bike_flasher_brightness);
+ nice_delay_ms(65);
+ }
+ nice_delay_ms(720); // no return check necessary on final delay
+ set_level(0);
+}
+#endif
+
+#ifdef USE_CANDLE_MODE
+#include "candle-mode.c"
+#endif
+
+
+#ifdef USE_BORING_STROBE_STATE
+#include "ff-strobe-modes.c"
+#endif
+
diff --git a/ui/anduril/strobe-modes.h b/ui/anduril/strobe-modes.h
new file mode 100644
index 0000000..7dc1df4
--- /dev/null
+++ b/ui/anduril/strobe-modes.h
@@ -0,0 +1,71 @@
+// strobe-modes.h: Strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifdef USE_STROBE_STATE
+
+strobe_mode_te current_strobe_type;
+
+// which strobe mode is active?
+#ifdef USE_CANDLE_MODE
+ #define DEFAULT_STROBE candle_mode_e
+#else
+ #define DEFAULT_STROBE 0
+#endif
+
+#endif // ifdef USE_STROBE_STATE
+
+
+// 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
+
+// some drivers need to keep the regulator chip on between pulses,
+// so set this to 1 to keep the light on at moon mode between pulses,
+// and thus keep the regulator powered up for the next flash
+#ifndef STROBE_OFF_LEVEL
+#define STROBE_OFF_LEVEL 0
+#endif
+
+// party and tactical strobes
+#ifdef USE_STROBE_STATE
+uint8_t strobe_state(Event event, uint16_t arg);
+inline void strobe_state_iter();
+#endif
+
+#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+inline void party_tactical_strobe_mode_iter(uint8_t st);
+#endif
+
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter();
+#endif
+
+#ifdef USE_LIGHTNING_MODE
+inline void lightning_storm_iter();
+#endif
+
+// bike mode config options
+#ifdef USE_BIKE_FLASHER_MODE
+#define MAX_BIKING_LEVEL 120 // should be 127 or less
+inline void bike_flasher_iter();
+#endif
+
+#ifdef USE_CANDLE_MODE
+#include "candle-mode.h"
+#endif
+
+
+#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP)
+#define USE_BORING_STROBE_STATE
+#include "ff-strobe-modes.h"
+#endif
+
diff --git a/ui/anduril/sunset-timer.c b/ui/anduril/sunset-timer.c
new file mode 100644
index 0000000..e4fc512
--- /dev/null
+++ b/ui/anduril/sunset-timer.c
@@ -0,0 +1,60 @@
+// sunset-timer.c: Sunset / candle auto-shutoff functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "sunset-timer.h"
+
+uint8_t sunset_timer_state(Event event, uint16_t arg) {
+
+ #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI)
+ // No timer functions in Simple UI
+ if (cfg.simple_ui_active) return EVENT_NOT_HANDLED;
+ #endif
+
+ // reset on start
+ if (event == EV_enter_state) {
+ sunset_timer = 0;
+ sunset_ticks = 0;
+ return EVENT_HANDLED;
+ }
+ // hold: maybe "bump" the timer if it's active and almost expired
+ else if (event == EV_hold) {
+ // ramping up should "bump" the timer to extend the deadline a bit
+ if ((sunset_timer > 0) && (sunset_timer < 4)) {
+ sunset_timer = 3; // 3 minutes
+ sunset_timer_peak = 3;
+ sunset_ticks = 0; // re-start current "minute"
+ }
+ }
+ // 5H: add 5m to timer, per second, until released
+ else if (event == EV_click5_hold) {
+ if (0 == (arg % TICKS_PER_SECOND)) {
+ if (sunset_timer < (255 - SUNSET_TIMER_UNIT)) {
+ // add a few minutes to the timer
+ sunset_timer += SUNSET_TIMER_UNIT;
+ sunset_timer_peak = sunset_timer; // reset ceiling
+ sunset_ticks = 0; // reset phase
+ // let the user know something happened
+ blink_once();
+ }
+ }
+ return EVENT_HANDLED;
+ }
+ // tick: count down until time expires
+ else if (event == EV_tick) {
+ // time passed
+ sunset_ticks ++;
+ // did we reach a minute mark?
+ if (sunset_ticks >= TICKS_PER_MINUTE) {
+ sunset_ticks = 0;
+ if (sunset_timer > 0) {
+ sunset_timer --;
+ }
+ }
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
diff --git a/ui/anduril/sunset-timer.h b/ui/anduril/sunset-timer.h
new file mode 100644
index 0000000..963804e
--- /dev/null
+++ b/ui/anduril/sunset-timer.h
@@ -0,0 +1,17 @@
+// sunset-timer.h: Sunset / candle auto-shutoff functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// how many minutes to add each time the user "bumps" the timer?
+#define SUNSET_TIMER_UNIT 5
+
+#define TICKS_PER_MINUTE (TICKS_PER_SECOND*60)
+
+// automatic shutoff timer
+uint8_t sunset_timer = 0; // minutes remaining in countdown
+uint8_t sunset_timer_peak = 0; // total minutes in countdown
+uint16_t sunset_ticks = 0; // counts from 0 to TICKS_PER_MINUTE, then repeats
+uint8_t sunset_timer_state(Event event, uint16_t arg);
+
diff --git a/ui/anduril/tactical-mode.c b/ui/anduril/tactical-mode.c
new file mode 100644
index 0000000..0035496
--- /dev/null
+++ b/ui/anduril/tactical-mode.c
@@ -0,0 +1,109 @@
+// tactical-mode.c: Tactical (ish) mode for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "tactical-mode.h"
+
+
+uint8_t tactical_state(Event event, uint16_t arg) {
+ // momentary(ish) tactical mode
+ uint8_t mem_lvl = memorized_level; // save this to restore it later
+ uint8_t ret = EVENT_NOT_HANDLED;
+
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ // 1H: 1st level
+ // 2H: 2nd level
+ // 3H: 3rd level
+ // 4+: nothing
+ momentary_active = 0;
+ ret = EVENT_HANDLED;
+ uint8_t click = event & 0x0f; // click number
+ if (click <= 3) {
+ momentary_active = 1;
+ uint8_t lvl;
+ lvl = cfg.tactical_levels[click-1];
+ if ((1 <= lvl) && (lvl <= RAMP_SIZE)) { // steady output
+ memorized_level = lvl;
+ momentary_mode = 0;
+ #if NUM_CHANNEL_MODES > 1
+ // use ramp mode's channel
+ channel_mode = cfg.channel_mode;
+ #endif
+ } else { // momentary strobe mode
+ momentary_mode = 1;
+ if (lvl > RAMP_SIZE) {
+ current_strobe_type = (lvl - RAMP_SIZE - 1) % strobe_mode_END;
+ }
+ }
+ }
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ momentary_active = 0;
+ set_level(0);
+ interrupt_nice_delays(); // stop animations in progress
+ }
+
+ // delegate to momentary mode while button is pressed
+ if (momentary_active) {
+ momentary_state(event, arg);
+ }
+
+ memorized_level = mem_lvl; // restore temporarily overridden mem level
+
+ // copy lockout mode's aux LED and sleep behaviors
+ if (event == EV_enter_state) {
+ lockout_state(event, arg);
+ }
+ else if (event == EV_tick) {
+ if (! momentary_active) {
+ return lockout_state(event, arg);
+ }
+ return EVENT_HANDLED;
+ }
+ else if (event == EV_sleep_tick) {
+ return lockout_state(event, arg);
+ }
+
+ // 6 clicks: exit and turn off
+ else if (event == EV_6clicks) {
+ blink_once();
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ ////////// Every action below here is blocked in the simple UI //////////
+ // (unnecessary since this entire mode is blocked in simple UI)
+ /*
+ #ifdef USE_SIMPLE_UI
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+ */
+
+ // 7H: configure tactical mode
+ else if (event == EV_click7_hold) {
+ push_state(tactical_config_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ return ret;
+}
+
+void tactical_config_save(uint8_t step, uint8_t value) {
+ // update tac mode values
+ // 3 values
+ // each value is 1 to 150, or other:
+ // - 1..150 is a ramp level
+ // - other means "strobe mode"
+ cfg.tactical_levels[step - 1] = value;
+}
+
+uint8_t tactical_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg, 3, tactical_config_save);
+}
+
diff --git a/ui/anduril/tactical-mode.h b/ui/anduril/tactical-mode.h
new file mode 100644
index 0000000..528a796
--- /dev/null
+++ b/ui/anduril/tactical-mode.h
@@ -0,0 +1,22 @@
+// tactical-mode.h: Tactical mode for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifndef TACTICAL_LEVELS
+ // high, low, tactical strobe
+ // only do color strobe here if it's main LEDs, not aux LEDs
+ #if defined(USE_POLICE_COLOR_STROBE_MODE) && !defined(POLICE_STROBE_USES_AUX)
+ // 2-color police style strobe
+ #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3)
+ #else
+ // regular tactical strobe (1 color)
+ #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+2)
+ #endif
+#endif
+
+// tactical(ish) mode
+uint8_t tactical_state(Event event, uint16_t arg);
+uint8_t tactical_config_state(Event event, uint16_t arg);
+
diff --git a/ui/anduril/tempcheck-mode.c b/ui/anduril/tempcheck-mode.c
new file mode 100644
index 0000000..5d160bd
--- /dev/null
+++ b/ui/anduril/tempcheck-mode.c
@@ -0,0 +1,56 @@
+// tempcheck-mode.c: Temperature check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "tempcheck-mode.h"
+
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: next blinky mode
+ else if (event == EV_2clicks) {
+ #if defined(USE_BEACON_MODE)
+ set_state(beacon_state, 0);
+ #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ set_state(sos_state, 0);
+ #elif defined(USE_BATTCHECK)
+ set_state(battcheck_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ // 7H: thermal config mode
+ else if (event == EV_click7_hold) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+void thermal_config_save(uint8_t step, uint8_t value) {
+ if (value) {
+ // item 1: calibrate room temperature
+ if (step == 1) {
+ int8_t rawtemp = temperature - cfg.therm_cal_offset;
+ cfg.therm_cal_offset = value - rawtemp;
+ adc_reset = 2; // invalidate all recent temperature data
+ }
+
+ // item 2: set maximum heat limit
+ else {
+ cfg.therm_ceil = 30 + value - 1;
+ }
+ }
+
+ if (cfg.therm_ceil > MAX_THERM_CEIL) cfg.therm_ceil = MAX_THERM_CEIL;
+}
+
+uint8_t thermal_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 2, thermal_config_save);
+}
+
diff --git a/ui/anduril/tempcheck-mode.h b/ui/anduril/tempcheck-mode.h
new file mode 100644
index 0000000..15dd03e
--- /dev/null
+++ b/ui/anduril/tempcheck-mode.h
@@ -0,0 +1,12 @@
+// tempcheck-mode.h: Temperature check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#define USE_BLINK_NUM // FIXME: this only matters in an earlier header
+
+uint8_t tempcheck_state(Event event, uint16_t arg);
+uint8_t thermal_config_state(Event event, uint16_t arg);
+void thermal_config_save(uint8_t step, uint8_t value);
+
diff --git a/ui/anduril/tint-ramping.c b/ui/anduril/tint-ramping.c
new file mode 100644
index 0000000..9418113
--- /dev/null
+++ b/ui/anduril/tint-ramping.c
@@ -0,0 +1,86 @@
+// tint-ramping.c: Tint ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#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) {
+ ///// tint-toggle mode
+ // toggle once on first frame; ignore other frames
+ if (tint_style) {
+ // only respond on first frame
+ if (arg) return EVENT_NOT_HANDLED;
+
+ // force tint to be 1 or 254
+ if (tint != 254) { tint = 1; }
+ // invert between 1 and 254
+ tint = tint ^ 0xFF;
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ ///// smooth tint-ramp mode
+ // 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 <= 1) tint_ramp_direction = 1;
+ else if (tint >= 254) tint_ramp_direction = -1;
+ // remember tint after battery change
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
diff --git a/ui/anduril/tint-ramping.h b/ui/anduril/tint-ramping.h
new file mode 100644
index 0000000..19b8dde
--- /dev/null
+++ b/ui/anduril/tint-ramping.h
@@ -0,0 +1,21 @@
+// tint-ramping.h: Tint ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// 0: smooth tint ramp
+// 1: toggle tint only between two extremes
+#ifdef TINT_RAMP_TOGGLE_ONLY
+uint8_t tint_style = 1;
+#else
+uint8_t tint_style = 0;
+#endif
+
+#ifdef USE_MANUAL_MEMORY
+uint8_t manual_memory_tint;
+#endif
+
+// not actually a mode, more of a fallback under other modes
+uint8_t tint_ramping_state(Event event, uint16_t arg);
+
diff --git a/ui/anduril/version-check-mode.c b/ui/anduril/version-check-mode.c
new file mode 100644
index 0000000..a47706f
--- /dev/null
+++ b/ui/anduril/version-check-mode.c
@@ -0,0 +1,31 @@
+// version-check-mode.c: Version check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "version-check-mode.h"
+
+// empty state; logic is handled in FSM loop() instead
+uint8_t version_check_state(Event event, uint16_t arg) {
+ return EVENT_NOT_HANDLED;
+}
+
+// this happens in FSM loop()
+inline void version_check_iter() {
+ for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
+ uint8_t digit = pgm_read_byte(version_number + i) - '0';
+ if (digit < 10) blink_digit(digit);
+ else { // "buzz" for non-numeric characters
+ for(uint8_t frame=0; frame<25; frame++) {
+ set_level((frame&1) << 5);
+ nice_delay_ms(16);
+ }
+ nice_delay_ms(BLINK_SPEED * 8 / 12);
+ }
+ nice_delay_ms(300);
+ }
+
+ set_state_deferred(off_state, 0);
+}
+
diff --git a/ui/anduril/version-check-mode.h b/ui/anduril/version-check-mode.h
new file mode 100644
index 0000000..72bad3f
--- /dev/null
+++ b/ui/anduril/version-check-mode.h
@@ -0,0 +1,19 @@
+// version-check-mode.h: Version check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#define USE_BLINK_DIGIT // FIXME: does nothing unless defined earlier
+
+#ifndef MODEL_NUMBER
+// if no model number, it's a build error
+//#define MODEL_NUMBER "0000"
+#error MODEL_NUMBER undefined
+#endif
+
+#include "version.h"
+const PROGMEM uint8_t version_number[] = MODEL_NUMBER "." VERSION_NUMBER;
+uint8_t version_check_state(Event event, uint16_t arg);
+inline void version_check_iter();
+
diff --git a/ui/anduril/version.h b/ui/anduril/version.h
new file mode 100644
index 0000000..b548e13
--- /dev/null
+++ b/ui/anduril/version.h
@@ -0,0 +1,4 @@
+// this file is replaced automatically by the build script
+// set your own date here if you're not using the build script
+// otherwise, default to first human contact with the moon
+#define VERSION_NUMBER "1969-07-20"
diff --git a/ui/baton/baton.c b/ui/baton/baton.c
new file mode 100644
index 0000000..e672755
--- /dev/null
+++ b/ui/baton/baton.c
@@ -0,0 +1,188 @@
+/*
+ * Baton: Olight Baton-like 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/>.
+ */
+
+#include "hwdef-Emisar_D4.h"
+#define USE_LVP
+#define USE_THERMAL_REGULATION
+#define USE_DELAY_MS
+#include "spaghetti-monster.h"
+
+// FSM states
+uint8_t off_state(Event event, uint16_t arg);
+uint8_t steady_state(Event event, uint16_t arg);
+uint8_t lockout_state(Event event, uint16_t arg);
+
+// brightness control
+uint8_t memorized_level = 1;
+uint8_t actual_level = 0;
+#ifdef USE_THERMAL_REGULATION
+uint8_t target_level = 0;
+#endif
+
+// moon + ../../bin/level_calc.py 2 6 7135 18 10 150 FET 1 10 1500
+uint8_t pwm1_levels[] = { 3, 18, 110, 255, 255, 255, 0, };
+uint8_t pwm2_levels[] = { 0, 0, 0, 9, 58, 138, 255, };
+#define MAX_LEVEL (sizeof(pwm1_levels)-1)
+
+// set LED brightness
+void set_level(uint8_t lvl) {
+ actual_level = lvl;
+ PWM1_LVL = pwm1_levels[lvl];
+ PWM2_LVL = pwm2_levels[lvl];
+}
+
+uint8_t off_state(Event event, uint16_t arg) {
+ // turn emitter off when entering state
+ if (event == EV_enter_state) {
+ go_to_standby = 1; // sleep while off (lower power use)
+ return EVENT_HANDLED;
+ }
+ // hold (initially): go to lowest level, but allow abort for regular click
+ else if (event == EV_click1_press) {
+ set_level(0);
+ return EVENT_HANDLED;
+ }
+ // hold (longer): go to lowest level
+ else if (event == EV_click1_hold) {
+ set_state(steady_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 1 click (before timeout): go to memorized level, but allow abort for double click
+ else if (event == EV_click1_release) {
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: highest mode
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: soft lockout
+ else if (event == EV_4clicks) {
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ // turn LED on when we first enter the mode
+ if (event == EV_enter_state) {
+ // remember this level, unless it's moon or turbo
+ if ((arg > 0) && (arg < MAX_LEVEL)) memorized_level = arg;
+ // use the requested level even if not memorized
+ #ifdef USE_THERMAL_REGULATION
+ target_level = arg;
+ #endif
+ set_level(arg);
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ if (actual_level < MAX_LEVEL) { // go to turbo
+ memorized_level = actual_level; // in case we're on moon
+ #ifdef USE_THERMAL_REGULATION
+ target_level = MAX_LEVEL;
+ #endif
+ set_level(MAX_LEVEL);
+ }
+ else { // return from turbo
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // hold: change brightness
+ else if (event == EV_click1_hold) {
+ if ((arg % HOLD_TIMEOUT) == 0) {
+ memorized_level = (actual_level+1) % (MAX_LEVEL+1);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by 1 level
+ else if (event == EV_temperature_high) {
+ if (actual_level > 1) { set_level(actual_level - 1); }
+ return EVENT_HANDLED;
+ }
+ // underheating: increase by 1 level if we're lower than the target
+ else if (event == EV_temperature_low) {
+ if (actual_level < target_level) { set_level(actual_level + 1); }
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t lockout_state(Event event, uint16_t arg) {
+ // stay asleep while locked
+ if (event == EV_tick) {
+ PWM1_LVL = 0; PWM2_LVL = 0; // make sure emitters are off
+ // sleep 1 second after user stops pressing buttons
+ if (arg > TICKS_PER_SECOND) { go_to_standby = 1; }
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: exit, and turn on at "low" level
+ else if (event == EV_4clicks) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+void low_voltage() {
+ // step down by one level or turn off
+ if (actual_level > 0) {
+ set_level(actual_level - 1);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = actual_level; // don't let low temperature override LVP
+ #endif
+ }
+ else {
+ set_state(off_state, 0);
+ }
+}
+
+void setup() {
+ // blink when power is connected
+ set_level(MAX_LEVEL/2);
+ delay_ms(10);
+ set_level(0);
+
+ push_state(off_state, 0);
+}
+
+void loop() {
+}
diff --git a/ui/baton/baton.txt b/ui/baton/baton.txt
new file mode 100644
index 0000000..2f0c22f
--- /dev/null
+++ b/ui/baton/baton.txt
@@ -0,0 +1,21 @@
+This is a very simple clone of the Olight Baton interface. It is not
+exact, but it has the basics. Mostly, it exists for the purposes of
+demonstrating how to create interfaces in FSM.
+
+While off:
+
+ - 1 click: Turn on (at memorized level).
+ - Hold: Turn on (at moon level).
+ - 2 clicks: Turn on (at highest level).
+ - 4 clicks: Soft lockout mode.
+
+While on:
+
+ - 1 click: Turn off.
+ - Hold: Change the brightness. Goes up in steps, then wraps around.
+ - 2 clicks: Go to/from highest level.
+
+While locked:
+
+ - 4 clicks: Exit lockout mode.
+
diff --git a/ui/darkhorse/darkhorse.c b/ui/darkhorse/darkhorse.c
new file mode 100644
index 0000000..aa37b92
--- /dev/null
+++ b/ui/darkhorse/darkhorse.c
@@ -0,0 +1,367 @@
+/*
+ * 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/>.
+ */
+
+#include "hwdef-Emisar_D4.h"
+#define USE_LVP
+#define USE_THERMAL_REGULATION
+#define DEFAULT_THERM_CEIL 45
+#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(Event event, uint16_t arg);
+uint8_t low_mode_state(Event event, uint16_t arg);
+uint8_t med_mode_state(Event event, uint16_t arg);
+uint8_t hi_mode_state(Event event, uint16_t arg);
+uint8_t strobe_beacon_state(Event event, uint16_t arg);
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg);
+#endif
+// Not a FSM state, just handles stuff common to all low/med/hi states
+uint8_t any_mode_state(Event 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, 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:
+// 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;
+
+#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(Event 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(Event 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 EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 2 clicks: toggle primary/secondary level
+ else if (event == EV_2clicks) {
+ *primary ^= 1;
+ set_any_mode(*primary, *secondary, modes);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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(Event event, uint16_t arg) {
+ return any_mode_state(event, arg, &L1, &L2, low_modes);
+}
+
+uint8_t med_mode_state(Event event, uint16_t arg) {
+ return any_mode_state(event, arg, &M1, &M2, med_modes);
+}
+
+uint8_t hi_mode_state(Event event, uint16_t arg) {
+ return any_mode_state(event, arg, &H1, &H2, hi_modes);
+}
+
+
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+
+uint8_t strobe_beacon_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 1 click (initially): cancel current blink
+ // FIXME: this is no longer necessary; FSM does this automatically now
+ if (event == EV_click1_release) {
+ interrupt_nice_delays();
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ 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() {
+ 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
+}
+
+
diff --git a/ui/fireflies-ui/Makefile b/ui/fireflies-ui/Makefile
new file mode 100644
index 0000000..0b59898
--- /dev/null
+++ b/ui/fireflies-ui/Makefile
@@ -0,0 +1,7 @@
+all:
+ ./build-all.sh
+
+clean:
+ rm -f *.hex cfg-ff-[pr]*.h *~ *.elf *.o
+
+.phony: clean
diff --git a/ui/fireflies-ui/build-all.sh b/ui/fireflies-ui/build-all.sh
new file mode 100755
index 0000000..81ebd97
--- /dev/null
+++ b/ui/fireflies-ui/build-all.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+cp -av --no-clobber ../anduril/cfg-ff*.h .
+
+UI=fireflies-ui
+
+for TARGET in cfg-*.h ; do
+ NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
+ echo "===== $NAME ====="
+ echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ mv -f "$UI".hex "$UI".$NAME.hex
+done
diff --git a/ui/fireflies-ui/cfg-ff-e01.h b/ui/fireflies-ui/cfg-ff-e01.h
new file mode 100644
index 0000000..42c23b2
--- /dev/null
+++ b/ui/fireflies-ui/cfg-ff-e01.h
@@ -0,0 +1,44 @@
+// Fireflies EDC thrower config options for Fireflies UI
+// (uses PL47 driver)
+#include "hwdef-FF_PL47.h"
+
+// disable indicator LED; it's hardwired
+#ifdef USE_INDICATOR_LED
+#undef USE_INDICATOR_LED
+#endif
+
+// don't do this
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_CEILING
+
+// ramp shape and size
+#define RAMP_LENGTH 150
+
+// driver is a FET + 3x7135, ~413 lm at highest regulated level
+// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930
+#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255
+#define MAX_1x7135 93
+#define HALFSPEED_LEVEL 14
+#define QUARTERSPEED_LEVEL 7
+
+#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to
+
+
+// ceiling is level 130/150 (50% power)
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+
+// 20, 56, [93], 130 (93 is highest regulated)
+// (8 / 102 / 413 / 1163 + 1930 lm)
+#define RAMP_DISCRETE_FLOOR 20
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 4
+
+// ~25 lm to ~400 lm
+#define MUGGLE_FLOOR 30
+#define MUGGLE_CEILING MAX_1x7135
+
+// throttle back faster when high
+#define THERM_FASTER_LEVEL 130
+
diff --git a/ui/fireflies-ui/cfg-ff-e07-2.h b/ui/fireflies-ui/cfg-ff-e07-2.h
new file mode 100644
index 0000000..48f9c15
--- /dev/null
+++ b/ui/fireflies-ui/cfg-ff-e07-2.h
@@ -0,0 +1,27 @@
+// Fireflies E07-2 config options for Anduril / FFUI
+// mostly the same as PL47
+#include "cfg-ff-pl47.h"
+
+// ceiling is level 130/150 (50% power)
+#undef RAMP_SMOOTH_CEIL
+#define RAMP_SMOOTH_CEIL 130
+
+// 20, 56, 93, 130 (83 is highest regulated)
+// (requested config is 1%, 5%, 25%, 50%, double-click-turbo)
+// (but this doesn't allow us to hit level 83)
+#undef RAMP_DISCRETE_FLOOR
+#define RAMP_DISCRETE_FLOOR 20
+#undef RAMP_DISCRETE_CEIL
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#undef RAMP_DISCRETE_STEPS
+#define RAMP_DISCRETE_STEPS 4
+
+// regulate down faster when the FET is active, slower otherwise
+#undef THERM_FASTER_LEVEL
+#define THERM_FASTER_LEVEL 130 // throttle back faster when high
+
+// play it safe, don't try to regulate above the recommended safe level
+#ifndef THERM_HARD_TURBO_DROP
+#define THERM_HARD_TURBO_DROP
+#endif
+
diff --git a/ui/fireflies-ui/fireflies-ui.c b/ui/fireflies-ui/fireflies-ui.c
new file mode 100644
index 0000000..34f8293
--- /dev/null
+++ b/ui/fireflies-ui/fireflies-ui.c
@@ -0,0 +1,2386 @@
+/*
+ * Fireflies UI: A custom UI for Fireflies-brand flashlights.
+ * (based on Anduril by ToyKeeper)
+ *
+ * Copyright (C) 2019 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 *********/
+// UI config file name (set it here or define it at the gcc command line)
+//#define CONFIGFILE cfg-ff-pl47.h
+
+#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
+
+// 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
+// short blips while ramping
+#define BLINK_AT_RAMP_MIDDLE
+//#define BLINK_AT_RAMP_FLOOR
+#define BLINK_AT_RAMP_CEILING
+//#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
+
+// 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
+
+//Muggle mode for easy UI
+#define USE_MUGGLE_MODE
+
+// 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
+
+// dual-switch support (second switch is a tail clicky)
+//#define START_AT_MEMORIZED_LEVEL
+
+/***** specific settings for known driver types *****/
+#include "tk.h"
+#include incfile(CONFIGFILE)
+
+///// Fireflies-specific configuration
+// disable ramp config
+#ifdef USE_RAMP_CONFIG
+#undef USE_RAMP_CONFIG
+#endif
+
+// no muggle mode
+#ifdef USE_MUGGLE_MODE
+#undef USE_MUGGLE_MODE
+#endif
+
+// turn off strobe mode entirely; we're replacing it
+#ifdef USE_BIKE_FLASHER_MODE
+#undef USE_BIKE_FLASHER_MODE
+#endif
+#ifdef USE_PARTY_STROBE_MODE
+#undef USE_PARTY_STROBE_MODE
+#endif
+#ifdef USE_TACTICAL_STROBE_MODE
+#undef USE_TACTICAL_STROBE_MODE
+#endif
+#ifdef USE_LIGHTNING_MODE
+#undef USE_LIGHTNING_MODE
+#endif
+#ifdef USE_CANDLE_MODE
+#undef USE_CANDLE_MODE
+#endif
+
+// remove other blinkies too
+#ifdef USE_GOODNIGHT_MODE
+#undef USE_GOODNIGHT_MODE
+#endif
+#ifdef USE_BEACON_MODE
+#undef USE_BEACON_MODE
+#endif
+
+// use these strobes instead
+#define USE_POLICE_STROBE_MODE
+#define USE_SOS_MODE
+
+// thermal config mode on 10 clicks from off
+#define USE_TENCLICK_THERMAL_CONFIG
+
+///// end Fireflies-specific configuration
+
+// 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
+#ifndef RAMP_LENGTH
+#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
+#endif
+#define MAX_BIKING_LEVEL 120 // should be 127 or less
+#define USE_BATTCHECK
+
+#if defined(USE_MUGGLE_MODE)
+#ifndef MUGGLE_FLOOR
+#define MUGGLE_FLOOR 22
+#endif
+#ifndef MUGGLE_CEILING
+#define MUGGLE_CEILING (MAX_1x7135+20)
+#endif
+#endif
+#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
+
+// full FET strobe can be a bit much... use max regulated level instead,
+// if there's a bright enough regulated level
+#ifdef MAX_Nx7135
+#define STROBE_BRIGHTNESS MAX_Nx7135
+#else
+#define STROBE_BRIGHTNESS MAX_LEVEL
+#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)
+#define USE_BORING_STROBE_STATE
+#endif
+
+// auto-detect how many eeprom bytes
+#define USE_EEPROM
+typedef enum {
+ ramp_style_e,
+ #ifdef USE_RAMP_CONFIG
+ ramp_smooth_floor_e,
+ ramp_smooth_ceil_e,
+ ramp_discrete_floor_e,
+ ramp_discrete_ceil_e,
+ ramp_discrete_steps_e,
+ #endif
+ #ifdef USE_TINT_RAMPING
+ tint_e,
+ #endif
+ #ifdef USE_STROBE_STATE
+ strobe_type_e,
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ strobe_delays_0_e,
+ strobe_delays_1_e,
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ bike_flasher_brightness_e,
+ #endif
+ #ifdef USE_BEACON_MODE
+ beacon_seconds_e,
+ #endif
+ #ifdef USE_MUGGLE_MODE
+ muggle_mode_active_e,
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ therm_ceil_e,
+ therm_cal_offset_e,
+ #endif
+ #ifdef USE_INDICATOR_LED
+ indicator_led_mode_e,
+ #endif
+ eeprom_indexes_e_END
+} eeprom_indexes_e;
+#define EEPROM_BYTES eeprom_indexes_e_END
+
+#ifdef START_AT_MEMORIZED_LEVEL
+#define USE_EEPROM_WL
+#define EEPROM_WL_BYTES 1
+#endif
+
+// auto-configure other stuff...
+#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
+#define USE_PSEUDO_RAND
+#endif
+
+#if defined(USE_CANDLE_MODE)
+#ifndef USE_TRIANGLE_WAVE
+#define USE_TRIANGLE_WAVE
+#endif
+#endif
+
+#include "spaghetti-monster.h"
+
+
+// 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 strobe_state boring_strobe_state // use the right strobes
+#define NUM_BORING_STROBES 2
+#endif
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_THERMAL_REGULATION
+uint8_t tempcheck_state(Event event, uint16_t arg);
+uint8_t thermal_config_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_GOODNIGHT_MODE
+// 1-hour ramp down from low, then automatic off
+uint8_t goodnight_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_BEACON_MODE
+// beacon mode and its related config mode
+uint8_t beacon_state(Event event, uint16_t arg);
+uint8_t beacon_config_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);
+// momentary / signalling mode
+uint8_t momentary_state(Event event, uint16_t arg);
+uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
+uint8_t momentary_active = 0; // boolean, true if active *right now*
+#ifdef USE_MUGGLE_MODE
+// muggle mode, super-simple, hard to exit
+uint8_t muggle_state(Event event, uint16_t arg);
+uint8_t muggle_mode_active = 0;
+#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)
+void indicator_blink(uint8_t arg);
+#endif
+
+// remember stuff even after battery was changed
+void load_config();
+void save_config();
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl();
+#endif
+
+// default ramp options if not overridden earlier per-driver
+#ifndef RAMP_STYLE
+#define RAMP_STYLE 0 // smooth default
+#endif
+#ifndef RAMP_SMOOTH_FLOOR
+ #define RAMP_SMOOTH_FLOOR 1
+#endif
+#ifndef RAMP_SMOOTH_CEIL
+ #if PWM_CHANNELS == 3
+ #define RAMP_SMOOTH_CEIL MAX_Nx7135
+ #else
+ #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
+ #endif
+#endif
+#ifndef RAMP_DISCRETE_FLOOR
+ #define RAMP_DISCRETE_FLOOR 20
+#endif
+#ifndef RAMP_DISCRETE_CEIL
+ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#endif
+#ifndef RAMP_DISCRETE_STEPS
+ #define RAMP_DISCRETE_STEPS 7
+#endif
+
+// mile marker(s) partway up the ramp
+// default: blink only at border between regulated and FET
+#ifdef BLINK_AT_RAMP_MIDDLE
+ #if PWM_CHANNELS >= 3
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
+ #ifndef BLINK_AT_RAMP_MIDDLE_2
+ #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
+ #endif
+ #endif
+ #else
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
+ #endif
+ #endif
+#endif
+
+// brightness control
+#ifndef DEFAULT_LEVEL
+#define DEFAULT_LEVEL MAX_1x7135
+#endif
+uint8_t memorized_level = DEFAULT_LEVEL;
+// smooth vs discrete ramping
+volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete
+volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR;
+volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL;
+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
+
+#ifdef USE_INDICATOR_LED
+ // bits 2-3 control lockout mode
+ // bits 0-1 control "off" mode
+ // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
+ #ifdef INDICATOR_LED_DEFAULT_MODE
+ uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE;
+ #else
+ #ifdef USE_INDICATOR_LED_WHILE_RAMPING
+ //uint8_t indicator_led_mode = (1<<2) + 2;
+ uint8_t indicator_led_mode = (2<<2) + 1;
+ #else
+ uint8_t indicator_led_mode = (3<<2) + 1;
+ #endif
+ #endif
+#endif
+
+// calculate the nearest ramp level which would be valid at the moment
+// (is a no-op for smooth ramp, but limits discrete ramp to only the
+// correct levels for the user's config)
+uint8_t nearest_level(int16_t target);
+
+#ifdef USE_THERMAL_REGULATION
+// brightness before thermal step-down
+uint8_t target_level = 0;
+#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;
+
+// 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
+
+#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+// party / tactical strobe timing
+volatile uint8_t strobe_delays[] = { 40, 67 }; // party strobe, tactical strobe
+#endif
+
+// bike mode config options
+#ifdef USE_BIKE_FLASHER_MODE
+volatile uint8_t bike_flasher_brightness = MAX_1x7135;
+#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
+
+#ifdef USE_BEACON_MODE
+// beacon timing
+volatile uint8_t beacon_seconds = 2;
+#endif
+
+
+uint8_t off_state(Event event, uint16_t arg) {
+ // turn emitter off when entering state
+ if (event == EV_enter_state) {
+ set_level(0);
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode & 0x03);
+ #endif
+ // sleep while off (lower power use)
+ go_to_standby = 1;
+ return EVENT_HANDLED;
+ }
+ // 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;
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode & 0x03);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ // blink the indicator LED, maybe
+ else if (event == EV_sleep_tick) {
+ if ((indicator_led_mode & 0b00000011) == 0b00000011) {
+ indicator_blink(arg);
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ // hold (initially): go to lowest level (floor), but allow abort for regular click
+ else if (event == EV_click1_press) {
+ set_level(nearest_level(1));
+ return EVENT_HANDLED;
+ }
+ // hold: go to lowest level
+ else if (event == EV_click1_hold) {
+ #ifdef MOON_TIMING_HINT
+ if (arg == 0) {
+ // let the user know they can let go now to stay at moon
+ blip();
+ } else
+ #endif
+ // don't start ramping immediately;
+ // give the user time to release at moon level
+ //if (arg >= HOLD_TIMEOUT) { // smaller
+ if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
+ set_state(steady_state, 1);
+ }
+ return EVENT_HANDLED;
+ }
+ // hold, release quickly: go to lowest level (floor)
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // click, hold: go to highest level (ceiling) (for ramping down)
+ else if (event == EV_click2_hold) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: highest mode (ceiling)
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 3 clicks (initial press): off, to prep for later events
+ else if (event == EV_click3_press) {
+ set_level(0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_BATTCHECK
+ // 3 clicks: battcheck mode / blinky mode group 1
+ else if (event == EV_3clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // click, click, long-click: strobe mode
+ #ifdef USE_STROBE_STATE
+ else if (event == EV_click3_hold) {
+ set_state(strobe_state, 0);
+ return EVENT_HANDLED;
+ }
+ #elif defined(USE_BORING_STROBE_STATE)
+ else if (event == EV_click3_hold) {
+ set_state(boring_strobe_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 4 clicks: soft lockout
+ else if (event == EV_4clicks) {
+ blink_confirm(2);
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 5 clicks: momentary mode
+ else if (event == EV_5clicks) {
+ blink_confirm(1);
+ set_state(momentary_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_MUGGLE_MODE
+ // 6 clicks: muggle mode
+ else if (event == EV_6clicks) {
+ blink_confirm(1);
+ set_state(muggle_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ #ifdef USE_INDICATOR_LED
+ // 7 clicks: change indicator LED mode
+ else if (event == EV_7clicks) {
+ uint8_t mode = (indicator_led_mode & 3) + 1;
+ #ifdef TICK_DURING_STANDBY
+ mode = mode & 3;
+ #else
+ mode = mode % 3;
+ #endif
+ #ifdef INDICATOR_LED_SKIP_LOW
+ if (mode == 1) { mode ++; }
+ #endif
+ indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
+ indicator_led(mode);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 8 clicks: temperature check
+ else if (event == EV_8clicks) {
+ set_state(tempcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_TENCLICK_THERMAL_CONFIG
+ // 10 clicks: thermal config mode
+ else if (event == EV_10clicks) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ uint8_t mode_min = ramp_smooth_floor;
+ uint8_t mode_max = ramp_smooth_ceil;
+ uint8_t ramp_step_size = 1;
+ #ifdef USE_REVERSING
+ static int8_t ramp_direction = 1;
+ #endif
+ if (ramp_style) {
+ mode_min = ramp_discrete_floor;
+ mode_max = ramp_discrete_ceil;
+ 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)) {
+ momentary_mode = 0; // 0 = ramping, 1 = strobes
+ // if we just got back from config mode, go back to memorized level
+ if (event == EV_reenter_state) {
+ arg = memorized_level;
+ }
+ // remember this level, unless it's moon or turbo
+ if ((arg > mode_min) && (arg < mode_max))
+ memorized_level = arg;
+ // use the requested level even if not memorized
+ arg = nearest_level(arg);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = arg;
+ #endif
+ set_level(arg);
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ if (actual_level < MAX_LEVEL) {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = MAX_LEVEL;
+ #endif
+ // true turbo, not the mode-specific ceiling
+ set_level(MAX_LEVEL);
+ }
+ else {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: toggle smooth vs discrete ramping
+ else if (event == EV_3clicks) {
+ ramp_style = !ramp_style;
+ memorized_level = nearest_level(actual_level);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ //set_level_gradually(lvl);
+ #endif
+ #endif
+ save_config();
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ blip();
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_RAMP_CONFIG
+ // 4 clicks: configure this ramp mode
+ else if (event == EV_4clicks) {
+ push_state(ramp_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // hold: change brightness (brighter)
+ else if (event == EV_click1_hold) {
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_REVERSING
+ // fix ramp direction on first frame if necessary
+ if (!arg) {
+ // make it ramp down instead, if already at max
+ if (actual_level >= mode_max) { ramp_direction = -1; }
+ // make it ramp up if already at min
+ // (off->hold->stepped_min->release causes this state)
+ else if (actual_level <= mode_min) { ramp_direction = 1; }
+ }
+ memorized_level = nearest_level((int16_t)actual_level \
+ + (ramp_step_size * ramp_direction));
+ #else
+ memorized_level = nearest_level((int16_t)actual_level + ramp_step_size);
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_CEILING
+ || (memorized_level == mode_max)
+ #endif
+ #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR)
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ blip();
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ blip();
+ }
+ #endif
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL)
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ #ifdef USE_REVERSING
+ ramp_direction = -ramp_direction;
+ #endif
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl();
+ #endif
+ return EVENT_HANDLED;
+ }
+ #endif
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return EVENT_HANDLED;
+ }
+ // TODO? make it ramp up instead, if already at min?
+ memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_FLOOR
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ blip();
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ blip();
+ }
+ #endif
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #ifdef START_AT_MEMORIZED_LEVEL
+ // click, release, hold, release: save new ramp level (if necessary)
+ else if (event == EV_click2_hold_release) {
+ save_config_wl();
+ return EVENT_HANDLED;
+ }
+ #endif
+ #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
+ else if (event == EV_tick) {
+ #ifdef USE_REVERSING
+ // un-reverse after 1 second
+ if (arg == TICKS_PER_SECOND) ramp_direction = 1;
+ #endif
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // make thermal adjustment speed scale with magnitude
+ if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
+ return EVENT_HANDLED; // 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.8)) for x in (1,2,4,8,16,32,64,128)]
+ //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5};
+ // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)]
+ //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3};
+ // [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 EVENT_HANDLED;
+ }
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by an amount proportional to how far we are above the ceiling
+ else if (event == EV_temperature_high) {
+ #if 0
+ blip();
+ #endif
+ #ifdef THERM_HARD_TURBO_DROP
+ if (actual_level > THERM_FASTER_LEVEL) {
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(THERM_FASTER_LEVEL);
+ #else
+ set_level(THERM_FASTER_LEVEL);
+ #endif
+ target_level = THERM_FASTER_LEVEL;
+ } 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 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 0
+ blip();
+ #endif
+ if (actual_level < target_level) {
+ //int16_t stepup = actual_level + (arg>>1);
+ int16_t stepup = actual_level + arg;
+ if (stepup > target_level) stepup = target_level;
+ else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepup);
+ #else
+ set_level(stepup);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#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;
+
+ momentary_mode = 1; // 0 = ramping, 1 = strobes
+
+ #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 EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: rotate through strobe/flasher modes
+ else if (event == EV_2clicks) {
+ strobe_type = (st + 1) % NUM_STROBES;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // release hold: save new strobe settings
+ else if (event == EV_click2_hold_release) {
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #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 EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+#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
+ if (del < 42) delay_zero();
+ else nice_delay_ms(1);
+ }
+ #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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ ramp_direction = -ramp_direction;
+ return EVENT_HANDLED;
+ }
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ ramp_direction = 1;
+ if (candle_mode_brightness > 1)
+ candle_mode_brightness --;
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ 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;
+
+ momentary_mode = 1; // 0 = ramping, 1 = strobes
+
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 2 clicks: rotate through strobe/flasher modes
+ else if (event == EV_2clicks) {
+ boring_strobe_type = (st + 1) % NUM_BORING_STROBES;
+ return EVENT_HANDLED;
+ }
+ 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
+
+#ifdef USE_SOS_MODE
+void sos_blink(uint8_t num, uint8_t dah) {
+ #define DIT_LENGTH 200
+ for (; num > 0; num--) {
+ set_level(memorized_level);
+ nice_delay_ms(DIT_LENGTH);
+ if (dah) { // dah is 3X as long as a dit
+ nice_delay_ms(DIT_LENGTH*2);
+ }
+ set_level(0);
+ // one "off" dit between blinks
+ nice_delay_ms(DIT_LENGTH);
+ }
+ // three "off" dits (or one "dah") between letters
+ nice_delay_ms(DIT_LENGTH*2);
+}
+
+inline void sos_mode_iter() {
+ // one iteration of main loop()
+ nice_delay_ms(1000);
+ sos_blink(3, 0); // S
+ sos_blink(3, 1); // O
+ sos_blink(3, 0); // S
+ nice_delay_ms(1000);
+}
+#endif // #ifdef USE_SOS_MODE
+#endif // #ifdef USE_BORING_STROBE_STATE
+
+
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #if defined(USE_GOODNIGHT_MODE) || defined(USE_BEACON_MODE)
+ // 2 clicks: next mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_GOODNIGHT_MODE
+ set_state(goodnight_state, 0);
+ #elif defined(USE_BEACON_MODE)
+ set_state(beacon_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+
+#ifdef USE_THERMAL_REGULATION
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #if 0 // not part of a loop in this UI
+ // 2 clicks: battcheck mode
+ else if (event == EV_2clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 4 clicks: thermal config mode
+ else if (event == EV_4clicks) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#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 EVENT_HANDLED;
+ }
+ // TODO: use sleep ticks to measure time between pulses,
+ // to save power
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_THERMAL_REGULATION
+ set_state(tempcheck_state, 0);
+ #else
+ set_state(battcheck_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: beacon config mode
+ else if (event == EV_4clicks) {
+ push_state(beacon_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ 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) {
+ static uint16_t ticks_since_stepdown = 0;
+ // blink on start
+ if (event == EV_enter_state) {
+ ticks_since_stepdown = 0;
+ blink_confirm(2);
+ set_level(GOODNIGHT_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: beacon mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_BEACON_MODE
+ set_state(beacon_state, 0);
+ #elif defined(USE_TEMPCHECK_MODE)
+ set_state(tempcheck_state, 0);
+ #endif
+ return EVENT_HANDLED;
+ }
+ // tick: step down (maybe) or off (maybe)
+ else if (event == EV_tick) {
+ if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) {
+ ticks_since_stepdown = 0;
+ set_level(actual_level-1);
+ if (! actual_level) {
+ #if 0 // test blink, to help measure timing
+ set_level(MAX_LEVEL>>2);
+ delay_4ms(8/2);
+ set_level(0);
+ #endif
+ set_state(off_state, 0);
+ }
+ }
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#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
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ #ifdef LOCKOUT_MOON_LOWEST
+ // Use lowest moon configured
+ uint8_t lvl = ramp_smooth_floor;
+ if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor;
+ set_level(lvl);
+ #elif defined(LOCKOUT_MOON_FANCY)
+ uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor };
+ if ((event & 0x0f) == 2) {
+ set_level(levels[ramp_style^1]);
+ } else {
+ set_level(levels[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
+ #endif
+ if (event == EV_tick) {
+ if (arg > TICKS_PER_SECOND*2) {
+ go_to_standby = 1;
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode >> 2);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ else if (event == EV_sleep_tick) {
+ if ((indicator_led_mode & 0b00001100) == 0b00001100) {
+ indicator_blink(arg);
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ #ifdef USE_INDICATOR_LED
+ // 3 clicks: rotate through indicator LED modes (lockout mode)
+ else if (event == EV_3clicks) {
+ 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);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #if 0 // old method, deprecated in favor of "7 clicks from off"
+ // click, click, hold: rotate through indicator LED modes (off mode)
+ else if (event == EV_click3_hold) {
+ #ifndef USE_INDICATOR_LED_WHILE_RAMPING
+ // if main LED obscures aux LEDs, turn it off
+ set_level(0);
+ #endif
+ #ifdef TICK_DURING_STANDBY
+ uint8_t mode = (arg >> 5) & 3;
+ #else
+ uint8_t mode = (arg >> 5) % 3;
+ #endif
+ #ifdef INDICATOR_LED_SKIP_LOW
+ if (mode == 1) { mode ++; }
+ #endif
+ indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
+ #ifdef TICK_DURING_STANDBY
+ if (mode == 3)
+ indicator_led(mode & (arg&3));
+ else
+ indicator_led(mode);
+ #else
+ indicator_led(mode);
+ #endif
+ //save_config();
+ return EVENT_HANDLED;
+ }
+ // click, click, hold, release: save indicator LED mode (off mode)
+ else if (event == EV_click3_hold_release) {
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+ #endif
+ // 4 clicks: exit
+ else if (event == EV_4clicks) {
+ blink_confirm(1);
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+ // TODO: momentary strobe here? (for light painting)
+
+ // init strobe mode, if relevant
+ if ((event == EV_enter_state) && (momentary_mode == 1)) {
+ strobe_state(event, arg);
+ }
+
+ // light up when the button is pressed; go dark otherwise
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ momentary_active = 1;
+ // 0 = ramping, 1 = strobes
+ if (momentary_mode == 0) {
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ momentary_active = 0;
+ set_level(0);
+ //go_to_standby = 1; // sleep while light is off
+ return EVENT_HANDLED;
+ }
+
+ // Sleep, dammit! (but wait a few seconds first)
+ // (because standby mode uses such little power that it can interfere
+ // with exiting via tailcap loosen+tighten unless you leave power
+ // disconnected for several seconds, so we want to be awake when that
+ // happens to speed up the process)
+ else if (event == EV_tick) {
+ if (momentary_active) {
+ // 0 = ramping, 1 = strobes
+ if (momentary_mode == 1) {
+ return strobe_state(event, arg);
+ }
+ }
+ else {
+ if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds
+ go_to_standby = 1; // sleep while light is off
+ // TODO: lighted button should use lockout config?
+ }
+ }
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_MUGGLE_MODE
+uint8_t muggle_state(Event event, uint16_t arg) {
+ static int8_t ramp_direction;
+ static int8_t muggle_off_mode;
+
+ // turn LED off when we first enter the mode
+ if (event == EV_enter_state) {
+ ramp_direction = 1;
+
+ #ifdef START_AT_MEMORIZED_LEVEL
+ memorized_level = arg;
+ muggle_off_mode = 0;
+ set_level(memorized_level);
+
+ if (! muggle_mode_active) { // don't write eeprom at every boot
+ muggle_mode_active = 1;
+ save_config();
+ }
+ #else
+ muggle_mode_active = 1;
+ save_config();
+
+ muggle_off_mode = 1;
+ //memorized_level = MAX_1x7135;
+ memorized_level = (MUGGLE_FLOOR + MUGGLE_CEILING) / 2;
+ #endif
+ return EVENT_HANDLED;
+ }
+ // initial press: moon hint
+ else if (event == EV_click1_press) {
+ if (muggle_off_mode)
+ set_level(MUGGLE_FLOOR);
+ }
+ // initial release: direct to memorized level
+ else if (event == EV_click1_release) {
+ if (muggle_off_mode)
+ set_level(memorized_level);
+ }
+ // if the user keeps pressing, turn off
+ else if (event == EV_click2_press) {
+ muggle_off_mode = 1;
+ set_level(0);
+ }
+ // 1 click: on/off
+ else if (event == EV_1click) {
+ muggle_off_mode ^= 1;
+ if (muggle_off_mode) {
+ set_level(0);
+ }
+ /*
+ else {
+ set_level(memorized_level);
+ }
+ */
+ return EVENT_HANDLED;
+ }
+ // hold: change brightness
+ else if (event == EV_click1_hold) {
+ // ramp at half speed
+ if (arg & 1) return EVENT_HANDLED;
+
+ // if off, start at bottom
+ if (muggle_off_mode) {
+ muggle_off_mode = 0;
+ ramp_direction = 1;
+ set_level(MUGGLE_FLOOR);
+ }
+ else {
+ uint8_t m;
+ m = actual_level;
+ // ramp down if already at ceiling
+ if ((arg <= 1) && (m >= MUGGLE_CEILING)) ramp_direction = -1;
+ // ramp
+ m += ramp_direction;
+ if (m < MUGGLE_FLOOR)
+ m = MUGGLE_FLOOR;
+ if (m > MUGGLE_CEILING)
+ m = MUGGLE_CEILING;
+ memorized_level = m;
+ set_level(m);
+ }
+ return EVENT_HANDLED;
+ }
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ ramp_direction = -ramp_direction;
+ #ifdef START_AT_MEMORIZED_LEVEL
+ save_config_wl(); // momentary use should retain brightness level
+ #endif
+ return EVENT_HANDLED;
+ }
+ /*
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ ramp_direction = 1;
+ if (memorized_level > MUGGLE_FLOOR)
+ memorized_level = actual_level - 1;
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ */
+ // 6 clicks: exit muggle mode
+ else if (event == EV_6clicks) {
+ blink_confirm(1);
+ muggle_mode_active = 0;
+ save_config();
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // tick: housekeeping
+ else if (event == EV_tick) {
+ // un-reverse after 1 second
+ if (arg == TICKS_PER_SECOND) ramp_direction = 1;
+
+ // turn off, but don't go to the main "off" state
+ if (muggle_off_mode) {
+ if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second
+ go_to_standby = 1; // sleep while light is off
+ }
+ }
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_THERMAL_REGULATION
+ // overheating is handled specially in muggle mode
+ else if(event == EV_temperature_high) {
+ #if 0
+ blip();
+ #endif
+ // step down proportional to the amount of overheating
+ uint8_t new = actual_level - arg;
+ if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; }
+ set_level(new);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // low voltage is handled specially in muggle mode
+ else if(event == EV_voltage_low) {
+ uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
+ if (lvl >= MUGGLE_FLOOR) {
+ set_level(lvl);
+ } else {
+ muggle_off_mode = 1;
+ }
+ return EVENT_HANDLED;
+ }
+
+ 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(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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ //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
+ uint8_t val;
+ if (ramp_style) { // discrete / stepped ramp
+
+ 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;
+
+ } else { // smooth ramp
+
+ val = config_state_values[0];
+ if (val) { ramp_smooth_floor = val; }
+
+ val = config_state_values[1];
+ if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; }
+
+ }
+}
+
+uint8_t ramp_config_state(Event event, uint16_t arg) {
+ uint8_t num_config_steps;
+ num_config_steps = 2 + ramp_style;
+ return config_state_base(event, arg,
+ num_config_steps, ramp_config_save);
+}
+#endif // #ifdef USE_RAMP_CONFIG
+
+
+#ifdef USE_THERMAL_REGULATION
+void thermal_config_save() {
+ // parse values
+ uint8_t val;
+
+ // calibrate room temperature
+ val = config_state_values[0];
+ if (val) {
+ int8_t rawtemp = temperature - therm_cal_offset;
+ therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
+ }
+
+ val = config_state_values[1];
+ if (val) {
+ // set maximum heat limit
+ therm_ceil = 30 + val - 1;
+ }
+ if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
+}
+
+uint8_t thermal_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 2, thermal_config_save);
+}
+#endif // #ifdef USE_THERMAL_REGULATION
+
+
+#ifdef USE_BEACON_MODE
+void beacon_config_save() {
+ // parse values
+ uint8_t val = config_state_values[0];
+ if (val) {
+ beacon_seconds = val;
+ }
+}
+
+uint8_t beacon_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 1, beacon_config_save);
+}
+
+inline void beacon_mode_iter() {
+ // one iteration of main loop()
+ set_level(memorized_level);
+ nice_delay_ms(100);
+ set_level(0);
+ nice_delay_ms(((beacon_seconds) * 1000) - 100);
+}
+#endif // #ifdef USE_BEACON_MODE
+
+
+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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ 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_smooth_floor;
+ uint8_t mode_max = ramp_smooth_ceil;
+ if (ramp_style) {
+ mode_min = ramp_discrete_floor;
+ mode_max = ramp_discrete_ceil;
+ }
+ if (target < mode_min) return mode_min;
+ if (target > mode_max) return mode_max;
+ // the rest isn't relevant for smooth ramping
+ if (! ramp_style) return target;
+
+ uint8_t ramp_range = 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));
+ int16_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);
+ }
+}
+
+// Just go dark for a moment to indicate to user that something happened
+void blip() {
+ uint8_t temp = actual_level;
+ set_level(0);
+ delay_4ms(3);
+ set_level(temp);
+}
+
+
+#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
+// beacon-like mode for the indicator LED
+void indicator_blink(uint8_t arg) {
+ #define USE_FANCIER_BLINKING_INDICATOR
+ #ifdef USE_FANCIER_BLINKING_INDICATOR
+
+ // fancy blink, set off/low/high levels here:
+ uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0};
+ indicator_led(seq[arg & 15]);
+
+ #else // basic blink, 1/8th duty cycle
+
+ if (! (arg & 7)) {
+ indicator_led(2);
+ }
+ else {
+ indicator_led(0);
+ }
+
+ #endif
+}
+#endif
+
+
+void load_config() {
+ if (load_eeprom()) {
+ ramp_style = eeprom[ramp_style_e];
+ #ifdef USE_RAMP_CONFIG
+ ramp_smooth_floor = eeprom[ramp_smooth_floor_e];
+ ramp_smooth_ceil = eeprom[ramp_smooth_ceil_e];
+ ramp_discrete_floor = eeprom[ramp_discrete_floor_e];
+ ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e];
+ ramp_discrete_steps = eeprom[ramp_discrete_steps_e];
+ #endif
+ #ifdef USE_TINT_RAMPING
+ tint = eeprom[tint_e];
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl?
+ strobe_delays[0] = eeprom[strobe_delays_0_e];
+ strobe_delays[1] = eeprom[strobe_delays_1_e];
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ bike_flasher_brightness = eeprom[bike_flasher_brightness_e];
+ #endif
+ #ifdef USE_BEACON_MODE
+ beacon_seconds = eeprom[beacon_seconds_e];
+ #endif
+ #ifdef USE_MUGGLE_MODE
+ muggle_mode_active = eeprom[muggle_mode_active_e];
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ therm_ceil = eeprom[therm_ceil_e];
+ therm_cal_offset = eeprom[therm_cal_offset_e];
+ #endif
+ #ifdef USE_INDICATOR_LED
+ indicator_led_mode = eeprom[indicator_led_mode_e];
+ #endif
+ }
+ #ifdef START_AT_MEMORIZED_LEVEL
+ if (load_eeprom_wl()) {
+ memorized_level = eeprom_wl[0];
+ }
+ #endif
+}
+
+void save_config() {
+ eeprom[ramp_style_e] = ramp_style;
+ #ifdef USE_RAMP_CONFIG
+ eeprom[ramp_smooth_floor_e] = ramp_smooth_floor;
+ eeprom[ramp_smooth_ceil_e] = ramp_smooth_ceil;
+ eeprom[ramp_discrete_floor_e] = ramp_discrete_floor;
+ eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil;
+ eeprom[ramp_discrete_steps_e] = ramp_discrete_steps;
+ #endif
+ #ifdef USE_TINT_RAMPING
+ eeprom[tint_e] = tint;
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl?
+ eeprom[strobe_delays_0_e] = strobe_delays[0];
+ eeprom[strobe_delays_1_e] = strobe_delays[1];
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ eeprom[bike_flasher_brightness_e] = bike_flasher_brightness;
+ #endif
+ #ifdef USE_BEACON_MODE
+ eeprom[beacon_seconds_e] = beacon_seconds;
+ #endif
+ #ifdef USE_MUGGLE_MODE
+ eeprom[muggle_mode_active_e] = muggle_mode_active;
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ eeprom[therm_ceil_e] = therm_ceil;
+ eeprom[therm_cal_offset_e] = therm_cal_offset;
+ #endif
+ #ifdef USE_INDICATOR_LED
+ eeprom[indicator_led_mode_e] = indicator_led_mode;
+ #endif
+
+ save_eeprom();
+}
+
+#ifdef START_AT_MEMORIZED_LEVEL
+void save_config_wl() {
+ eeprom_wl[0] = memorized_level;
+ save_eeprom_wl();
+}
+#endif
+
+
+void low_voltage() {
+ StatePtr state = current_state;
+
+ // TODO: turn off aux LED(s) when power is really low
+
+ if (0) {} // placeholder
+
+ #ifdef USE_STROBE_STATE
+ // "step down" from strobe to something low
+ else if (state == strobe_state) {
+ set_state(steady_state, RAMP_SIZE/6);
+ }
+ #endif
+
+ // in normal or muggle mode, step down or turn off
+ //else if ((state == steady_state) || (state == muggle_state)) {
+ else 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;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // not needed?
+ //set_level_gradually(lvl);
+ #endif
+ #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() {
+ #ifdef START_AT_MEMORIZED_LEVEL
+ // dual switch: e-switch + power clicky
+ // power clicky acts as a momentary mode
+ load_config();
+
+ #ifdef USE_MUGGLE_MODE
+ if (muggle_mode_active)
+ push_state(muggle_state, memorized_level);
+ else
+ #endif
+ if (button_is_pressed())
+ // hold button to go to moon
+ push_state(steady_state, 1);
+ else
+ // otherwise use memory
+ push_state(steady_state, memorized_level);
+
+ #else // if not START_AT_MEMORIZED_LEVEL
+
+ // blink at power-on to let user know power is connected
+ set_level(RAMP_SIZE/8);
+ delay_4ms(3);
+ set_level(0);
+
+ load_config();
+
+ #ifdef USE_TINT_RAMPING
+ // add tint ramping underneath every other state
+ push_state(tint_ramping_state, 0);
+ #endif // ifdef USE_TINT_RAMPING
+
+ #ifdef USE_MUGGLE_MODE
+ if (muggle_mode_active)
+ push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2);
+ else
+ #endif
+ push_state(off_state, 0);
+
+ #endif // ifdef START_AT_MEMORIZED_LEVEL
+}
+
+
+void loop() {
+
+ StatePtr state = current_state;
+
+ if (0) {}
+
+ #ifdef USE_STROBE_STATE
+ else if ((state == strobe_state)
+ || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes
+ uint8_t st = strobe_type;
+
+ switch(st) {
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ #ifdef USE_PARTY_STROBE_MODE
+ case party_strobe_e:
+ #endif
+ #ifdef USE_TACTICAL_STROBE_MODE
+ case tactical_strobe_e:
+ #endif
+ party_tactical_strobe_mode_iter(st);
+ break;
+ #endif
+
+ #ifdef USE_LIGHTNING_MODE
+ case lightning_storm_e:
+ lightning_storm_iter();
+ break;
+ #endif
+
+ #ifdef USE_BIKE_FLASHER_MODE
+ case bike_flasher_e:
+ bike_flasher_iter();
+ break;
+ #endif
+ }
+
+ }
+ #endif // #ifdef USE_STROBE_STATE
+
+ #ifdef USE_BORING_STROBE_STATE
+ else if ((state == boring_strobe_state)
+ || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes
+ switch(boring_strobe_type) {
+ #ifdef USE_POLICE_STROBE_MODE
+ case 0: // police strobe
+ police_strobe_iter();
+ break;
+ #endif
+
+ #ifdef USE_SOS_MODE
+ default: // SOS
+ sos_mode_iter();
+ break;
+ #endif
+ }
+ }
+ #endif // #ifdef USE_BORING_STROBE_STATE
+
+ #ifdef USE_BATTCHECK
+ else if (state == battcheck_state) {
+ battcheck();
+ }
+ #endif
+
+ #ifdef USE_BEACON_MODE
+ else if (state == beacon_state) {
+ beacon_mode_iter();
+ }
+ #endif
+
+ #ifdef USE_THERMAL_REGULATION
+ // TODO: blink out therm_ceil during thermal_config_state?
+ else if (state == tempcheck_state) {
+ blink_num(temperature);
+ nice_delay_ms(1000);
+ }
+ #endif
+
+ #ifdef USE_IDLE_MODE
+ else {
+ // doze until next clock tick
+ idle_mode();
+ }
+ #endif
+
+}
diff --git a/ui/meteor/meteor.c b/ui/meteor/meteor.c
new file mode 100644
index 0000000..9c1c000
--- /dev/null
+++ b/ui/meteor/meteor.c
@@ -0,0 +1,556 @@
+/*
+ * Meteor: Meteor M43 clone UI for SpaghettiMonster.
+ * (in progress, not really in a usable state yet)
+ *
+ * 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/>.
+ */
+
+#include "hwdef-Emisar_D4.h"
+#define USE_LVP
+#define USE_THERMAL_REGULATION
+#define DEFAULT_THERM_CEIL 45
+#define USE_RAMPING
+#define RAMP_LENGTH 150
+#define USE_BATTCHECK
+#define BATTCHECK_6bars
+#define DONT_DELAY_AFTER_BATTCHECK
+//#define USE_EEPROM
+//#define EEPROM_BYTES 5
+#include "spaghetti-monster.h"
+
+// FSM states
+uint8_t base_off_state(Event event, uint16_t arg);
+uint8_t ui1_off_state(Event event, uint16_t arg);
+uint8_t ui2_off_state(Event event, uint16_t arg);
+uint8_t ui3_off_state(Event event, uint16_t arg);
+uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group);
+uint8_t ui1_on_state(Event event, uint16_t arg);
+uint8_t ui2_on_state(Event event, uint16_t arg);
+uint8_t ui3_on_state(Event event, uint16_t arg);
+uint8_t beacon_state(Event event, uint16_t arg);
+uint8_t battcheck_state(Event event, uint16_t arg);
+uint8_t strobe_state(Event event, uint16_t arg);
+uint8_t biking_state(Event event, uint16_t arg);
+uint8_t lockout_state(Event event, uint16_t arg);
+uint8_t momentary_state(Event event, uint16_t arg);
+// Not a FSM state, just handles stuff common to all low/med/hi states
+uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes);
+
+#ifdef USE_EEPROM
+void load_config();
+void save_config();
+#endif
+
+// fixed output levels
+uint8_t levels[] = {3, 16, 30, 43, 56, 70, 83, 96, 110, 123, 137, MAX_LEVEL};
+// select an interface
+uint8_t UI = 1; // 1, 2, or 3
+// UI1
+uint8_t UI1_mode = 0;
+uint8_t UI1_mode1 = 1;
+uint8_t UI1_mode2 = 1;
+uint8_t UI1_group1[] = {0, 2};
+uint8_t UI1_group2[] = {6, 9};
+// UI2
+uint8_t UI2_mode = 0;
+uint8_t UI2_mode1 = 1;
+uint8_t UI2_mode2 = 0;
+uint8_t UI2_mode3 = 0;
+uint8_t UI2_mode4 = 0; // doesn't matter, makes other code easier
+uint8_t UI2_group1[] = { 0, 2}; // moon, low
+uint8_t UI2_group2[] = { 4, 6}; // mid1, mid2
+uint8_t UI2_group3[] = { 8, 10}; // high1, high2
+uint8_t UI2_group4[] = {11, 11}; // turbo only
+// UI3 can access all levels, with 3 different mode memory slots
+uint8_t UI3_mode = 0;
+uint8_t UI3_mode1 = 2;
+uint8_t UI3_mode2 = 5;
+uint8_t UI3_mode3 = 8;
+
+#ifdef USE_THERMAL_REGULATION
+// brightness before thermal step-down
+uint8_t target_level = 0;
+#endif
+
+void set_any_mode(uint8_t mode, uint8_t *group) {
+ set_level(levels[group[mode]]);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = actual_level;
+ #endif
+}
+
+void blink_fast() {
+ set_level(MAX_LEVEL/2);
+ delay_4ms(8/4);
+ set_level(0);
+}
+
+uint8_t base_off_state(Event 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;
+ // ensure we're in a real off state, not the base
+ switch(UI) {
+ case 1: set_state(ui1_off_state, 0); break;
+ case 2: set_state(ui2_off_state, 0); break;
+ default: set_state(ui3_off_state, 0); break;
+ }
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: strobe mode
+ else if (event == EV_3clicks) {
+ set_state(beacon_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: battcheck mode
+ else if (event == EV_4clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 5 clicks: battcheck mode
+ else if (event == EV_5clicks) {
+ set_state(biking_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 6 clicks: soft lockout mode
+ else if (event == EV_6clicks) {
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 9 clicks: activate UI1
+ else if (event == EV_9clicks) {
+ blink_fast();
+ set_state(ui1_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 10 clicks: activate UI2
+ else if (event == EV_10clicks) {
+ blink_fast();
+ set_state(ui2_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 11 clicks: activate UI3
+ else if (event == EV_11clicks) {
+ blink_fast();
+ set_state(ui3_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t ui1_off_state(Event event, uint16_t arg) {
+ UI = 1;
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 1 click: low modes
+ if (event == EV_1click) {
+ set_any_mode(UI1_mode1, UI1_group1);
+ set_state(ui1_on_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: high modes
+ else if (event == EV_2clicks) {
+ set_any_mode(UI1_mode2, UI1_group2);
+ set_state(ui1_on_state, 1);
+ return EVENT_HANDLED;
+ }
+ // hold: turbo
+ else if (event == EV_hold) {
+ if (arg == 0) {
+ set_level(MAX_LEVEL);
+ }
+ //set_state(ui1_on_state, 3);
+ return EVENT_HANDLED;
+ }
+ // release hold: off
+ else if (event == EV_click1_hold_release) {
+ set_state(base_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return base_off_state(event, arg);
+}
+
+uint8_t ui2_off_state(Event event, uint16_t arg) {
+ UI = 2;
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 1 click: low modes
+ if (event == EV_1click) {
+ set_any_mode(UI2_mode1, UI2_group1);
+ set_state(ui2_on_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: high modes
+ else if (event == EV_2clicks) {
+ set_any_mode(UI2_mode3, UI2_group3);
+ set_state(ui2_on_state, 2);
+ return EVENT_HANDLED;
+ }
+ // hold: turbo
+ else if (event == EV_hold) {
+ if (arg == 0) {
+ set_level(MAX_LEVEL);
+ }
+ //set_state(ui1_on_state, 3);
+ return EVENT_HANDLED;
+ }
+ // release hold: off
+ else if (event == EV_click1_hold_release) {
+ set_state(base_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return base_off_state(event, arg);
+}
+
+uint8_t ui3_off_state(Event event, uint16_t arg) {
+ UI = 3;
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 1 click: memory slot 1
+ if (event == EV_1click) {
+ set_level(levels[UI3_mode1]);
+ set_state(ui3_on_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: memory slot 2
+ else if (event == EV_2clicks) {
+ set_level(levels[UI3_mode2]);
+ set_state(ui3_on_state, 1);
+ return EVENT_HANDLED;
+ }
+ // Click, hold: memory slot 3
+ else if (event == EV_click2_hold) {
+ set_level(levels[UI3_mode3]);
+ set_state(ui3_on_state, 2);
+ return EVENT_HANDLED;
+ }
+ // hold: turbo
+ else if (event == EV_hold) {
+ if (arg == 0) {
+ set_level(MAX_LEVEL);
+ }
+ //set_state(ui1_on_state, 3);
+ return EVENT_HANDLED;
+ }
+ // release hold: off
+ else if (event == EV_click1_hold_release) {
+ set_state(base_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return base_off_state(event, arg);
+}
+
+uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(base_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by an amount proportional to how far we are above the ceiling
+ else if (event == EV_temperature_high) {
+ if (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 ui1_on_state(Event event, uint16_t arg) {
+ // turn on LED when entering the mode
+ static uint8_t *mode = &UI1_mode1;
+ static uint8_t *group = UI1_group1;
+ if (event == EV_enter_state) {
+ UI1_mode = arg;
+ }
+ if (UI1_mode == 0) {
+ mode = &UI1_mode1;
+ group = UI1_group1;
+ }
+ else {
+ mode = &UI1_mode2;
+ group = UI1_group2;
+ }
+
+ if (event == EV_enter_state) {
+ set_any_mode(*mode, group);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: toggle moon/low or mid/high
+ else if (event == EV_2clicks) {
+ *mode ^= 1;
+ set_any_mode(*mode, group);
+ return EVENT_HANDLED;
+ }
+ // hold: turbo
+ else if (event == EV_hold) {
+ if (arg == 0) set_level(MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // release: exit turbo
+ else if (event == EV_click1_hold_release) {
+ set_any_mode(*mode, group);
+ return EVENT_HANDLED;
+ }
+ return base_on_state(event, arg, mode, group);
+}
+
+uint8_t ui2_on_state(Event event, uint16_t arg) {
+ // turn on LED when entering the mode
+ static uint8_t *mode = &UI2_mode1;
+ static uint8_t *group = UI2_group1;
+ if (event == EV_enter_state) {
+ UI2_mode = arg;
+ }
+ switch (UI2_mode) {
+ case 0:
+ mode = &UI2_mode1;
+ group = UI2_group1;
+ break;
+ case 1:
+ mode = &UI2_mode2;
+ group = UI2_group2;
+ break;
+ case 2:
+ mode = &UI2_mode3;
+ group = UI2_group3;
+ break;
+ default: // turbo only
+ mode = &UI2_mode4;
+ group = UI2_group4;
+ break;
+ }
+
+ if (event == EV_enter_state) {
+ set_any_mode(*mode, group);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: toggle moon/low, mid1/mid2, or high1/high2
+ else if (event == EV_2clicks) {
+ *mode ^= 1;
+ set_any_mode(*mode, group);
+ return EVENT_HANDLED;
+ }
+ // hold: rotate through low/mid/high/turbo
+ else if (event == EV_hold) {
+ if (arg % HOLD_TIMEOUT == 0) {
+ UI2_mode = (UI2_mode + 1) & 3;
+ }
+ else if (arg % HOLD_TIMEOUT == 1) {
+ set_any_mode(*mode, group);
+ }
+ return EVENT_HANDLED;
+ }
+ return base_on_state(event, arg, mode, group);
+}
+
+uint8_t ui3_on_state(Event event, uint16_t arg) {
+ // turn on LED when entering the mode
+ static uint8_t *mode = &UI3_mode1;
+ if (event == EV_enter_state) {
+ UI3_mode = arg;
+ }
+ // 2 clicks: rotate through mode1/mode2/mode3
+ else if (event == EV_2clicks) {
+ UI3_mode = (UI3_mode + 1) % 3;
+ }
+ // short click, long click: rotate through mode3/mode2/mode1
+ /*
+ else if (event == EV_click1_hold) {
+ if (arg % HOLD_TIMEOUT == 0)
+ UI3_mode = (UI3_mode + 4) % 3;
+ }
+ */
+ switch (UI3_mode) {
+ case 0:
+ mode = &UI3_mode1;
+ break;
+ case 1:
+ mode = &UI3_mode2;
+ break;
+ default:
+ mode = &UI3_mode3;
+ break;
+ }
+
+ if ((event == EV_enter_state) || (event == EV_2clicks)) {
+ set_level(levels[*mode]);
+ return EVENT_HANDLED;
+ }
+ // short click, long click: rotate through mode3/mode2/mode1
+ /*
+ else if (event == EV_click1_hold) {
+ set_level(levels[*mode]);
+ return EVENT_HANDLED;
+ }
+ */
+ // hold: turbo
+ // Click, hold: ramp up
+ // release hold, hold again: ramp in opposite direction
+ return base_on_state(event, arg, mode, levels);
+}
+
+
+uint8_t blinky_base_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(base_off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t beacon_state(Event event, uint16_t arg) {
+ return blinky_base_state(event, arg);
+}
+
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t strobe_state(Event event, uint16_t arg) {
+ return blinky_base_state(event, arg);
+}
+
+uint8_t biking_state(Event event, uint16_t arg) {
+ return blinky_base_state(event, arg);
+}
+
+uint8_t lockout_state(Event event, uint16_t arg) {
+ return blinky_base_state(event, arg);
+}
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+ return blinky_base_state(event, arg);
+}
+
+
+void low_voltage() {
+ if ((current_state == ui1_on_state) ||
+ (current_state == ui2_on_state) ||
+ (current_state == ui3_on_state)) {
+ if (actual_level > 5) {
+ set_level(actual_level >> 1);
+ }
+ else {
+ set_state(base_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);
+}
+
+#ifdef USE_EEPROM
+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();
+}
+#endif
+
+void setup() {
+ set_level(RAMP_SIZE/8);
+ delay_4ms(3);
+ set_level(0);
+
+ #ifdef USE_EEPROM
+ load_config();
+ #endif
+
+ push_state(base_off_state, 0);
+}
+
+void loop() {
+ if (0) {}
+ /*
+ 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(base_off_state, 0);
+ }
+ #endif
+}
+
+
diff --git a/ui/momentary/momentary.c b/ui/momentary/momentary.c
new file mode 100644
index 0000000..c7a8cf1
--- /dev/null
+++ b/ui/momentary/momentary.c
@@ -0,0 +1,80 @@
+/*
+ * Momentary: Very simple example UI for SpaghettiMonster.
+ * Is intended to be the simplest possible FSM e-switch UI.
+ * The light is on while the button is held; off otherwise.
+ *
+ * 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/>.
+ */
+
+#include "hwdef-Emisar_D4.h"
+#define USE_LVP
+#define USE_DEBUG_BLINK
+#include "spaghetti-monster.h"
+
+volatile uint8_t brightness;
+volatile uint8_t on_now;
+
+void light_on() {
+ on_now = 1;
+ PWM1_LVL = brightness;
+ PWM2_LVL = 0;
+}
+
+void light_off() {
+ on_now = 0;
+ PWM1_LVL = 0;
+ PWM2_LVL = 0;
+}
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+
+ if (event == EV_click1_press) {
+ brightness = 255;
+ light_on();
+ empty_event_sequence(); // don't attempt to parse multiple clicks
+ return 0;
+ }
+
+ else if (event == EV_release) {
+ light_off();
+ empty_event_sequence(); // don't attempt to parse multiple clicks
+ go_to_standby = 1; // sleep while light is off
+ return 0;
+ }
+
+ return 1; // event not handled
+}
+
+// LVP / low-voltage protection
+void low_voltage() {
+ if (brightness > 0) {
+ debug_blink(3);
+ brightness >>= 1;
+ if (on_now) light_on();
+ } else {
+ debug_blink(8);
+ light_off();
+ go_to_standby = 1;
+ }
+}
+
+void setup() {
+ debug_blink(2);
+ push_state(momentary_state, 0);
+}
+
+void loop() { }
+
diff --git a/ui/ramping-ui/ramping-ui.c b/ui/ramping-ui/ramping-ui.c
new file mode 100644
index 0000000..583498a
--- /dev/null
+++ b/ui/ramping-ui/ramping-ui.c
@@ -0,0 +1,359 @@
+/*
+ * Ramping-UI: Ramping 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/>.
+ */
+
+#include "hwdef-Emisar_D4.h"
+#define USE_LVP
+#define USE_THERMAL_REGULATION
+#define DEFAULT_THERM_CEIL 32
+#define USE_DELAY_MS
+#define USE_DELAY_ZERO
+#define USE_RAMPING
+#define USE_BATTCHECK
+#define BATTCHECK_VpT
+#define RAMP_LENGTH 150
+#include "spaghetti-monster.h"
+
+// FSM states
+uint8_t off_state(Event event, uint16_t arg);
+uint8_t steady_state(Event event, uint16_t arg);
+uint8_t strobe_state(Event event, uint16_t arg);
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg);
+uint8_t tempcheck_state(Event event, uint16_t arg);
+#endif
+
+// brightness control
+uint8_t memorized_level = MAX_1x7135;
+// smooth vs discrete ramping
+uint8_t ramp_step_size = 1;
+
+#ifdef USE_THERMAL_REGULATION
+// brightness before thermal step-down
+uint8_t target_level = 0;
+#endif
+
+// strobe timing
+volatile uint8_t strobe_delay = 67;
+volatile uint8_t strobe_type = 0; // 0 == party strobe, 1 == tactical strobe
+
+
+uint8_t off_state(Event 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_level(1);
+ return EVENT_HANDLED;
+ }
+ // 1 click (before timeout): go to memorized level, but allow abort for double click
+ else if (event == EV_click1_release) {
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks (initial press): off, to prep for later events
+ else if (event == EV_click2_press) {
+ set_level(0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: highest mode
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: strobe mode
+ else if (event == EV_3clicks) {
+ set_state(strobe_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 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 EVENT_HANDLED;
+ }
+ // hold, release quickly: go to lowest level
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+ // click, hold: go to highest level (for ramping down)
+ else if (event == EV_click2_hold) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ // turn LED on when we first enter the mode
+ if (event == EV_enter_state) {
+ // remember this level, unless it's moon or turbo
+ if ((arg > 1) && (arg < MAX_LEVEL))
+ memorized_level = arg;
+ // use the requested level even if not memorized
+ #ifdef USE_THERMAL_REGULATION
+ target_level = arg;
+ #endif
+ set_level(arg);
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ if (actual_level < MAX_LEVEL) {
+ memorized_level = actual_level; // in case we're on moon
+ #ifdef USE_THERMAL_REGULATION
+ target_level = MAX_LEVEL;
+ #endif
+ set_level(MAX_LEVEL);
+ }
+ else {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: go to strobe modes
+ else if (event == EV_3clicks) {
+ set_state(strobe_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: toggle smooth vs discrete ramping
+ else if (event == EV_4clicks) {
+ if (ramp_step_size == 1) ramp_step_size = MAX_LEVEL/6;
+ else ramp_step_size = 1;
+ set_level(0);
+ delay_4ms(20/4);
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ // hold: change brightness (brighter)
+ else if (event == EV_click1_hold) {
+ // ramp slower in discrete mode
+ if (arg % ramp_step_size != 0) {
+ return EVENT_HANDLED;
+ }
+ // FIXME: make it ramp down instead, if already at max
+ if (actual_level + ramp_step_size < MAX_LEVEL)
+ memorized_level = actual_level + ramp_step_size;
+ else memorized_level = MAX_LEVEL;
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ // only blink once for each threshold
+ if ((memorized_level != actual_level)
+ && ((memorized_level == MAX_1x7135)
+ || (memorized_level == MAX_LEVEL))) {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ // ramp slower in discrete mode
+ if (arg % ramp_step_size != 0) {
+ return EVENT_HANDLED;
+ }
+ // FIXME: make it ramp up instead, if already at min
+ if (actual_level > ramp_step_size)
+ memorized_level = (actual_level-ramp_step_size);
+ else
+ memorized_level = 1;
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ // only blink once for each threshold
+ if ((memorized_level != actual_level)
+ && ((memorized_level == MAX_1x7135)
+ || (memorized_level == 1))) {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #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 strobe_state(Event event, uint16_t arg) {
+ if (event == EV_enter_state) {
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: toggle party strobe vs tactical strobe
+ else if (event == EV_2clicks) {
+ strobe_type ^= 1;
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: go back to regular modes
+ else if (event == EV_3clicks) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // hold: change speed (go faster)
+ else if (event == EV_click1_hold) {
+ if ((arg & 1) == 0) {
+ if (strobe_delay > 8) strobe_delay --;
+ }
+ return EVENT_HANDLED;
+ }
+ // click, hold: change speed (go slower)
+ else if (event == EV_click2_hold) {
+ if ((arg & 1) == 0) {
+ if (strobe_delay < 255) strobe_delay ++;
+ }
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ set_state(tempcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+
+void low_voltage() {
+ // "step down" from strobe to something low
+ if (current_state == strobe_state) {
+ set_state(steady_state, RAMP_SIZE/6);
+ }
+ // in normal mode, step down by half or turn off
+ else if (current_state == steady_state) {
+ if (actual_level > 1) {
+ set_level(actual_level >> 1);
+ }
+ else {
+ set_state(off_state, 0);
+ }
+ }
+ // all other modes, just turn off when voltage is low
+ else {
+ 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() {
+ if (current_state == strobe_state) {
+ set_level(MAX_LEVEL);
+ if (strobe_type == 0) { // party strobe
+ if (strobe_delay < 30) delay_zero();
+ else delay_ms(1);
+ } else { //tactical strobe
+ nice_delay_ms(strobe_delay >> 1);
+ }
+ set_level(0);
+ nice_delay_ms(strobe_delay);
+ }
+ #ifdef USE_BATTCHECK
+ else if (current_state == battcheck_state) {
+ battcheck();
+ }
+ else if (current_state == tempcheck_state) {
+ blink_num(temperature);
+ nice_delay_ms(1000);
+ }
+ #endif
+}
diff --git a/ui/rampingios/Makefile b/ui/rampingios/Makefile
new file mode 100644
index 0000000..8db198e
--- /dev/null
+++ b/ui/rampingios/Makefile
@@ -0,0 +1,7 @@
+all:
+ ./build-all.sh
+
+clean:
+ rm -f *.hex cfg-*.h *~ *.elf *.o
+
+.phony: clean
diff --git a/ui/rampingios/build-all.sh b/ui/rampingios/build-all.sh
new file mode 100755
index 0000000..106dc15
--- /dev/null
+++ b/ui/rampingios/build-all.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+cp -av ../anduril/cfg-emisar*.h .
+
+UI=rampingiosv3
+
+for TARGET in cfg-*.h ; do
+ NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
+ echo "===== $NAME ====="
+ echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ mv -f "$UI".hex "$UI".$NAME.hex
+done
diff --git a/ui/rampingios/rampingios-v3.html b/ui/rampingios/rampingios-v3.html
new file mode 100644
index 0000000..f72d1ec
--- /dev/null
+++ b/ui/rampingios/rampingios-v3.html
@@ -0,0 +1,501 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scape=1.0; user-scalable=false;">
+ <link rel="apple-touch-icon" href="/phil/avatar-iphone.png">
+ <title>RampingIOS V3 Manual :: Phil! Gold</title>
+
+ <meta name="DC.title" content="Phil! Gold">
+
+ <link rel="openid2.provider" href="https://www.google.com/accounts/o8/ud" />
+ <link rel="openid2.local_id" href="https://plus.google.com/116471799767513262335" />
+ <!-- Old one preserved, because some sites use the provider as an account key (bad them). -->
+ <!--link rel="openid.server" href="http://www.livejournal.com/openid/server.bml"-->
+ <!--link rel="openid.delegate" href="http://phil-g.livejournal.com/"-->
+
+ <link rel="alternate" type="application/rss+xml" title="RSS" href="http://aperiodic.net/phil/archives/index.rss">
+ <link rel="stylesheet" type="text/css" href="/phil/default.css">
+ <link rel="stylesheet" type="text/css" href="/phil/blog.css">
+
+</head>
+
+<body>
+
+<div id="header">
+ <h1>RampingIOS V3 Manual</h1>
+</div>
+
+<div id="content">
+<h2>Tue, 28 Aug 2018</h2>
+
+<div class="story-title" id="rampingios-v3">
+ <h3>RampingIOS V3 Manual</h3>
+ <div class="title-links">
+ 9:47AM |
+ <a href="http://aperiodic.net/phil/archives/Geekery" >Geekery</a> |
+
+ <a href="http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html" title="permalink for RampingIOS V3 Manual" rel="bookmark">#</a>
+ </div>
+</div>
+
+<p><figure style="float: right">
+ <a href="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3ui.png-20180807025443-zdamv4ixtu49o7hm-1/rampingiosv3-ui.png">
+ <!-- img width="256em" src="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3.svg-20180807025420-q28902kbav01123w-1/rampingiosv3.svg" -->
+ <img width="256em" src="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3ui.png-20180807025443-zdamv4ixtu49o7hm-1/rampingiosv3-ui.png">
+ </a>
+ <figcaption>RampingIOS V3 UI diagram</figcaption>
+</figure></p>
+
+<p>The Emisar <a href="https://intl-outdoor.com/emisar-d4s-26650-high-power-led-flashlight-p-932.html">D4S</a> flashlights use a firmware named RampingIOS
+V3. (The Emisar <a href="https://intl-outdoor.com/emisar-d4-high-power-led-flashlight-p-921.html">D4</a>, <a href="https://intl-outdoor.com/emisar-d1-mini-thrower-p-922.html">D1</a>, and <a href="https://intl-outdoor.com/emisar-d1s-thrower-p-926.html">D1S</a>
+all use <a href="http://aperiodic.net/phil/archives/Geekery/rampingios-v2.html">RampingIOS V2</a>.) There's not really a manual; the
+only thing we get is the diagram on the right. It's reasonably
+comprehensive, but there's a fair amount of detail it merely summarizes,
+so I thought a textual manual would be nice.</p>
+
+<p>The Emisar D4S only works when the head and tailcap are tightened fully.
+You can physically lock it out--prevent it from turning on
+accidentally--by simply loosening the tailcap a small amount. A quarter
+turn will do it.</p>
+
+<p>Emisar lights are known for their ramping interfaces. Rather than have a
+small number of distinct brightness levels, they can vary their brightness
+anywhere between their lowest and highest levels, like a light on a
+dimmer. The D4S is in ramping mode by default, but it also has a stepped
+mode that can be configured to be closer to how non-ramping lights work.</p>
+
+<p>Each mode--ramping and stepped--can have differently-configured brightness
+floors and ceilings.</p>
+
+<p>The driver for the D4S has two different chipsets. At low brightness
+levels, a fairly-efficient but low-power chipset (called a <em>7135</em>) is
+used. These lowest brightness levels are called the "<em>regulated levels</em>".
+Each regulated level will always be the same brightness regardless of how
+much charge the battery has. Above a particular brightness level, the
+light switches over to a less-efficient but high-power chipset (called a
+<em>FET</em>). These levels are called "<em>direct-drive</em>". The brightness of the
+direct-drive levels is directly related to the battery's charge level; the
+more charged the battery, the brighter the levels. The light is at its
+most efficient, in terms of power used for every lumen generated, at the
+brightest regulated level. When the light is first powered by tightening
+the tailcap, it will default to this level.</p>
+
+<p>At higher brightness levels, the light's LEDs generate a lot of heat. If
+the light exceeds its configured maximum temperature, it will begin
+dimming itself automatically until the temperature drops below the allowed
+maximum.</p>
+
+<p>The D4S has a set of cyan-colored auxiliary LEDs that can be on when the
+main LEDs are off. You can configure the behavior of the aux LEDs.</p>
+
+<h4>Basic Usage</h4>
+
+<p>The default mode for the light is ramping mode. Triple-pressing the
+button (<strong>3 clicks</strong>) while the light is on will toggle between ramping
+and stepped mode.</p>
+
+<p>While the light is off, press and release the button (<strong>1 click</strong>) to turn
+it on. It will turn on at the last-used brightness level. (This is
+called "<em>mode memory</em>".) Immediately after loosening and tightening the
+tailcap (or after changing the battery), the memorized level will be the
+light's max regulated level.</p>
+
+<p>When the light is on, 1 click will turn it off. The current brightness
+level will be memorized for future use. There's a fraction of a second
+delay between pressing the button and the light actually turning off.
+That's because of the way the light processes input; it's waiting to make
+sure you're only going to press the button once (since multiple presses
+will trigger other actions).</p>
+
+<p>When the light is on, holding the button down will brighten the light. In
+ramping mode, the brightness will increase gradually ("<em>ramping up</em>"). In
+stepped mode, the light will jump through increasing brightness levels.
+If you press, release, and then hold the button, it will begin dimming.
+In ramping mode, the brightness will decrease gradually ("<em>ramping
+down</em>"). In stepped mode, the light will jump through decreasing
+brightness levels. While the light is changing, if you release the button
+and immediately hold it again, the direction (dimming or brightening) will
+switch.</p>
+
+<p>In ramping mode, while the light is ramping, it'll briefly blink off and
+on again at two different brightness levels: the maximum regulated level
+and the brightness ceiling.</p>
+
+<p>While the light is off, double-pressing the button (<strong>2 clicks</strong>) will
+immediately jump to the brightness ceiling.</p>
+
+<p>While the light is on, <strong>2 clicks</strong> will jump to the maximum brightness
+level, regardless of the configured brightness ceiling. Another two
+clicks will go back to the previous brightness level.</p>
+
+<p>While the light is off, if you hold the button the light will turn on at
+its lowest level. If you continue holding the button, the light will
+begin brightening from there.</p>
+
+<h5>Configuration Menus</h5>
+
+<p>The light has several different configuration modes. Each of those modes
+works more or less the same way. The mode will have a series of menu
+items that it will go through. For each menu item, the light will first
+blink a number of times corresponding to the item number (first, second,
+etc.) After that, the light will begin fluttering on and off fairly
+quickly. While the light is fluttering, you can click the button a number
+of times; the light will count the number of button presses and use that
+number as its new configuration for that menu item. After a short period
+of time, the fluttering will stop and the light will move on to the next
+menu item. After the light has gone through all of the menu items, it
+will return to whatever mode it was in before entering the configuration
+mode.</p>
+
+<p>If you don't press the button during a particular menu item's fluttering,
+that item will remain unchanged.</p>
+
+<h5>Configuring the Basic Modes</h5>
+
+<p>While the light is on, <strong>4 clicks</strong> will enter ramping or stepped
+configuration mode, depending on which mode the light was in before the 4
+clicks.</p>
+
+<p>For ramping mode, there are two menu options:</p>
+
+<ol>
+<li>Brightness floor (default 1/150)</li>
+<li>Brightness ceiling (default 150/150)</li>
+</ol>
+
+<p>During the floor configuration, press the button equal to the number of
+ramping levels (out of 150) at which the floor should be. To set the
+lowest possible floor, click the button once.</p>
+
+<p>The ceiling is configured similarly, but you press the button equal to the
+number of steps away from maximum brightness. To set the highest possible
+ceiling (at max brightness), click the button once.</p>
+
+<p>For stepped mode, there are three menu options:</p>
+
+<ol>
+<li>Brightness floor (default 20/150)</li>
+<li>Brightness ceiling (default 120/150)</li>
+<li>Number of steps (default 7)</li>
+</ol>
+
+<h4>Other Modes</h4>
+
+<p>The other modes largely involve multiple clicks from off. Most of them
+are not generally needed for everyday use, but they supplement the light's
+basic operations.</p>
+
+<h5>BattCheck/TempCheck Modes</h5>
+
+<p>From off, <strong>3 clicks</strong> will enter "BattCheck" mode, which blinks out the
+current battery voltage. First it blinks the number of volts, then it
+pauses, then it blinks out the tenths of volts. Thus, if the battery were
+at 3.5 volts, the light would blink three times, pause, then five times.
+For zeroes, it gives a very short blink.</p>
+
+<p>A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8
+volts to be an empty battery and won't turn on if the battery is at or
+below 2.8 volts.</p>
+
+<p>The voltage sequence will continue blinking until you turn off the light
+with a single click.</p>
+
+<p>While the light is in BattCheck mode, <strong>2 clicks</strong> will enter TempCheck
+mode. Instead of blinking out the battery voltage, the light will start
+blinking out its current temperature in degrees Celsius, first the tens
+digit then the units digit. Like BattCheck mode, the light will continue
+blinking out the temperature until you turn it off with a single click.</p>
+
+<p>While the light is in TempCheck mode, <strong>4 clicks</strong> will enter thermal
+configuration mode. See the thermal configuration mode documentation
+below for how that works.</p>
+
+<h5>Tactical Mode</h5>
+
+<p>From off, <strong>4 clicks</strong> will enter "tactical" or "momentary" mode. The
+light will flash once to show that it's entered the mode. The auxiliary
+LEDs will turn off (if they were on). In tactical mode, the light will
+turn on at its memorized brightness for as long as the button is being
+held down. It will turn off as soon as the button is released.</p>
+
+<p>There's no button press combination that will exit tactical mode. To exit
+it, you will have to partially unscrew and retighten the tailcap.</p>
+
+<h5>Lockout Mode</h5>
+
+<p>From off, <strong>6 clicks</strong> will enter lockout mode. The light will flash
+twice to show that it's entered the mode. There's a separate aux LED mode
+for lockout mode, so you can tell whether the light is in lockout or not.</p>
+
+<p>In lockout mode, pressing the button will turn on the light at its lowest
+brightness ("<em>moonlight mode</em>") for as long as the button is held down.</p>
+
+<p>Another 6 clicks will exit lockout mode. The light will flash twice to
+show that it's left the mode.</p>
+
+<p>While in lockout mode, <strong>3 clicks</strong> will cycle through the various
+settings for the aux LEDs in lockout mode. The four modes are, in order:
+low, high, blink (on high), and off. The default mode is blink.</p>
+
+<p>Remember that loosening the tailcap a quarter turn will also lock out the
+light. Using the 6 clicks is called "<em>electronic lockout</em>", while turning
+the tailcap is "<em>physical lockout</em>".</p>
+
+<h5>Aux LED Configuration</h5>
+
+<p>From off, <strong>7 clicks</strong> will cycle to the next aux LED mode. The four
+modes are, in order: low, high, blink (on high), and off. The default
+mode is low.</p>
+
+<h5>Beacon Mode</h5>
+
+<p>From off, <strong>8 clicks</strong> will enter beacon mode. In beacon mode, the light
+will blink on and off every few seconds.</p>
+
+<p>By default, the light will blink every two seconds. To change the timing,
+use <strong>4 clicks</strong> while in beacon mode. The light will enter a one-item
+menu. During the flickering for input, press the button a number of times
+equal to the number of seconds between blinks.</p>
+
+<p>1 click will exit beacon mode.</p>
+
+<h5>Thermal Configuration Mode</h5>
+
+<p>From off, <strong>10 clicks</strong> will enter thermal configuration mode.</p>
+
+<p>The menu items here are:</p>
+
+<ol>
+<li>Current temperature (every click is one degree Celsius)</li>
+<li>Temperature ceiling (every click is one degree <em>above 30°C</em>)</li>
+</ol>
+
+<p>The "current temperature" item can be used to adjust the calibration of
+the light's temperature sensor. To use it, make sure the light has been
+off long enough that all of its components have cooled (or warmed) to the
+ambient temperature. Check the ambient temperature using a thermometer
+you trust. Go to thermal configuration mode, and enter the current
+temperature by clicking the button a number of times equal to the
+temperature in degrees Celsius. (If it's 22°C, click the button 22
+times.)</p>
+
+<p>You can check the default calibration by entering TempCheck mode from a
+room-temperature light. The D4Ss are supposed to go through a temperature
+calibration at the factory, so hopefully most of them won't need manual
+thermal calibration.</p>
+
+<p>The temperature ceiling is simply the highest temperature the light should
+be allowed to reach. Once it hits its temperature ceiling, it will
+progressively dim itself until the temperature stabilizes below the
+ceiling. Note that the number of clicks in that menu option is added to
+<em>30</em> to reach the actual ceiling. (Thus, you can't set a ceiling below
+31°C.) The maximum allowed ceiling is 70°C.</p>
+
+<p>The default temperature ceiling is 45°C.</p>
+
+
+</div>
+
+<div id="sidebar">
+ <h2>Static</h2>
+ <ul>
+ <li><a href="/phil/prompt/">zsh prompt</a></li>
+ <li><a href="/phil/pgp/">PGP</a></li>
+ <li><a href="/phil/ssh/">SSH</a></li>
+ <li><a href="/phil/MTA/">MTA</a></li>
+ <li><a href="/phil/tutorials/">tutorials</a></li>
+ <li><a href="/phil/configs/">config files</a></li>
+ <li><a href="http://www.flickr.com/photos/phil_g/sets/1671829/">desktop</a></li>
+ <li><a href="http://www.librarything.com/catalog/asciiphil">books I own</a></li>
+ <li><a href="/phil/stuff/">stuff I'm giving away</a></li>
+ <li><a href="/phil/drwho/">Dr. Who eps I have</a></li>
+ <li><a href="http://del.icio.us/phil_g">bookmarks</a></li>
+ <li><a href="http://www.flickr.com/photos/phil_g/">photos</a></li>
+ <li><a href="/phil/about.html">about</a></li>
+ </ul>
+
+ <h2>Directory</h2>
+ <ul class="categories">
+<li><a href="http://aperiodic.net/phil/archives/index.html">Root</a> (143)
+<ul>
+<li><a href="http://aperiodic.net/phil/archives/Books/index.html">Books</a> (32)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Events/index.html">Events</a> (7)
+<ul>
+<li><a href="http://aperiodic.net/phil/archives/Events/Burning_Man/index.html">Burning Man</a> (3)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Events/Camping/index.html">Camping</a> (2)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Events/PDF/index.html">PDF</a> (2)
+</li>
+</ul>
+</li>
+<li class="this-category">Geekery (36)
+<ul>
+<li><a href="http://aperiodic.net/phil/archives/Geekery/Test/index.html">Test</a> (3)
+</li>
+</ul>
+</li>
+<li><a href="http://aperiodic.net/phil/archives/General/index.html">General</a> (24)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Links/index.html">Links</a> (12)
+<ul>
+<li><a href="http://aperiodic.net/phil/archives/Links/Slashdot/index.html">Slashdot</a> (1)
+</li>
+</ul>
+</li>
+<li><a href="http://aperiodic.net/phil/archives/MTA/index.html">MTA</a> (22)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Recipes/index.html">Recipes</a> (4)
+</li>
+<li><a href="http://aperiodic.net/phil/archives/Video_Games/index.html">Video Games</a> (6)
+<ul>
+<li><a href="http://aperiodic.net/phil/archives/Video_Games/FFXI/index.html">FFXI</a> (1)
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+
+ <h2>Archive</h2>
+ <table class="month-calendar"><caption class="month-calendar-head"><a title="December 2017 (1)" href="http://aperiodic.net/phil/archives/2017/12/">&larr;</a><a title="August 2018 (2)" href="http://aperiodic.net/phil/archives/2018/08/">August</a>&rarr;</caption>
+ <tr>
+ <th class="month-calendar-day-head Sunday">Sun</th>
+ <th class="month-calendar-day-head Monday">Mon</th>
+ <th class="month-calendar-day-head Tuesday">Tue</th>
+ <th class="month-calendar-day-head Wednesday">Wed</th>
+ <th class="month-calendar-day-head Thursday">Thu</th>
+ <th class="month-calendar-day-head Friday">Fri</th>
+ <th class="month-calendar-day-head Saturday">Sat</th>
+ </tr>
+ <tr>
+ <td class="month-calendar-day-noday Sunday">&nbsp;</td>
+ <td class="month-calendar-day-noday Monday">&nbsp;</td>
+ <td class="month-calendar-day-noday Tuesday">&nbsp;</td>
+<td class="month-calendar-day-nolink Wednesday">1</td>
+<td class="month-calendar-day-nolink Thursday">2</td>
+<td class="month-calendar-day-nolink Friday">3</td>
+<td class="month-calendar-day-nolink Saturday">4</td>
+</tr>
+ <tr>
+<td class="month-calendar-day-nolink Sunday">5</td>
+<td class="month-calendar-day-nolink Monday">6</td>
+<td class="month-calendar-day-nolink Tuesday">7</td>
+<td class="month-calendar-day-nolink Wednesday">8</td>
+<td class="month-calendar-day-nolink Thursday">9</td>
+<td class="month-calendar-day-nolink Friday">10</td>
+<td class="month-calendar-day-nolink Saturday">11</td>
+</tr>
+ <tr>
+<td class="month-calendar-day-nolink Sunday">12</td>
+<td class="month-calendar-day-nolink Monday">13</td>
+<td class="month-calendar-day-nolink Tuesday">14</td>
+<td class="month-calendar-day-nolink Wednesday">15</td>
+<td class="month-calendar-day-nolink Thursday">16</td>
+<td class="month-calendar-day-nolink Friday">17</td>
+<td class="month-calendar-day-nolink Saturday">18</td>
+</tr>
+ <tr>
+<td class="month-calendar-day-nolink Sunday">19</td>
+<td class="month-calendar-day-nolink Monday">20</td>
+<td class="month-calendar-day-nolink Tuesday">21</td>
+<td class="month-calendar-day-nolink Wednesday">22</td>
+<td class="month-calendar-day-nolink Thursday">23</td>
+<td class="month-calendar-day-nolink Friday">24</td>
+<td class="month-calendar-day-nolink Saturday">25</td>
+</tr>
+ <tr>
+<td class="month-calendar-day-link Sunday"><a title="Sunday, 26 August 2018 (1)" href="http://aperiodic.net/phil/archives/2018/08/26/">26</a></td>
+<td class="month-calendar-day-nolink Monday">27</td>
+<td class="month-calendar-day-this-day Tuesday"><a title="Tuesday, 28 August 2018 (current) (1)" href="http://aperiodic.net/phil/archives/2018/08/28/">28</a></td>
+<td class="month-calendar-day-nolink Wednesday">29</td>
+<td class="month-calendar-day-nolink Thursday">30</td>
+<td class="month-calendar-day-nolink Friday">31</td>
+ <td class="month-calendar-day-noday Saturday">&nbsp;</td>
+</tr>
+</table>
+
+ <table class="year-calendar"><caption class="year-calendar-head"><a title="2017 (3)" href="http://aperiodic.net/phil/archives/2017/">&larr;</a><a title="2018 (2)" href="http://aperiodic.net/phil/archives/2018/">2018</a>&rarr;</caption><tr><th class="year-calendar-subhead" colspan="6">Months</th></tr>
+<tr>
+<td class="year-calendar-month-nolink">Jan</td>
+<td class="year-calendar-month-nolink">Feb</td>
+<td class="year-calendar-month-nolink">Mar</td>
+<td class="year-calendar-month-nolink">Apr</td>
+<td class="year-calendar-month-nolink">May</td>
+<td class="year-calendar-month-nolink">Jun</td>
+</tr>
+<tr>
+<td class="year-calendar-month-nolink">Jul</td>
+<td class="year-calendar-this-month"><a title="August 2018 (2)" href="http://aperiodic.net/phil/archives/2018/08/">Aug</a></td><td class="year-calendar-month-future">Sep</td>
+<td class="year-calendar-month-future">Oct</td>
+<td class="year-calendar-month-future">Nov</td>
+<td class="year-calendar-month-future">Dec</td>
+</tr>
+</table>
+
+
+ <h2>Search</h2>
+ <form method="GET" action="http://www.google.com/custom">
+ <p style="text-align: center">
+ <input type="hidden" name="domains" value="aperiodic.net">
+ <input type="hidden" name="cof" VALUE="GALT:#66CC6;S:http://aperiodic.net/phil/;GL:2;VLC:#CC66CC;AH:center;BGC:#000000;LC:#6666CC;GFNT:#666666;ALC:#CC6666;T:#CCCCCC;GIMP:#FFFFFF;AWFID:d3f1afbc39619250;">
+ <input type="hidden" name="sitesearch" value="aperiodic.net">
+ <input name="q" type="text" size="25" maxlength="255" style="width: 10em"><br>
+ Powered by <a href="http://www.google.com/"><span class="google-G1">G</span><span class="google-o1">o</span><span class="google-o2">o</span><span class="google-g2">g</span><span class="google-l">l</span><span class="google-e">e</span></a>
+ </p>
+ </form>
+
+
+ <h2>Currently Reading</h2>
+
+ <div id="wb55a1f3ca64835526140c06560a0205e"></div>
+ <script type="text/javascript" charset="UTF-8"
+ src="http://www.librarything.com/widget_get.php?userid=asciiphil&theID=wb55a1f3ca64835526140c06560a0205e">
+ </script>
+
+ <h2>Recent Books</h2>
+
+ <div id="wa13cfbb5a4be4d629ebe322b83f1e2f6"></div>
+ <script type="text/javascript" charset="UTF-8"
+ src="http://www.librarything.com/widget_get.php?userid=asciiphil&theID=wa13cfbb5a4be4d629ebe322b83f1e2f6">
+ </script>
+
+</div>
+
+<div class="hr"><hr></div>
+<address><a href="mailto:phil_g@pobox.com">Phil! Gold</a></address>
+<p class="footer">
+<a href="/phil/">Back to main page.</a><br>
+</p>
+<ul class="validation">
+ <li>
+ <a href="http://validator.w3.org/check/referer">
+ <img src="/phil/pics/html401.png" alt="Valid HTML 4.01">
+ </a>
+ </li>
+
+ <li>
+ <a href="http://jigsaw.w3.org/css-validator/check/referer">
+ <img src="/phil/pics/validcss.png" alt="Valid CSS 2">
+ </a>
+ </li>
+
+ <li>
+ <a href="http://aperiodic.net/phil/archives/index.rss">
+ <img src="/phil/pics/rss10.png" alt="RSS syndication">
+ </a>
+ </li>
+
+ <li>
+ <a href="http://feedvalidator.org/check?url=http://aperiodic.net/phil/archives/index.rss">
+ <img src="/phil/pics/validrss.png" alt="Valid RSS 1.0">
+ </a>
+ </li>
+
+</ul>
+
+</html>
diff --git a/ui/rampingios/rampingios-v3.md b/ui/rampingios/rampingios-v3.md
new file mode 100644
index 0000000..bc0e2b7
--- /dev/null
+++ b/ui/rampingios/rampingios-v3.md
@@ -0,0 +1,262 @@
+RampingIOS V3 Manual
+
+This Markdown-formatted manual was contributed by phil_g under a
+Creative Commons CC0 waiver:
+ http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html
+ https://creativecommons.org/publicdomain/zero/1.0/
+
+
+<figure style="float: right">
+ <a href="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3ui.png-20180807025443-zdamv4ixtu49o7hm-1/rampingiosv3-ui.png">
+ <!-- img width="256em" src="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3.svg-20180807025420-q28902kbav01123w-1/rampingiosv3.svg" -->
+ <img width="256em" src="https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/download/head:/rampingiosv3ui.png-20180807025443-zdamv4ixtu49o7hm-1/rampingiosv3-ui.png">
+ </a>
+ <figcaption>RampingIOS V3 UI diagram</figcaption>
+</figure>
+
+The Emisar [D4S][emisar-d4s] flashlights use a firmware named RampingIOS
+V3. (The Emisar [D4][emisar-d4], [D1][emisar-d1], and [D1S][emisar-d1s]
+all use [RampingIOS V2][rampingios-v2].) There's not really a manual; the
+only thing we get is the diagram on the right. It's reasonably
+comprehensive, but there's a fair amount of detail it merely summarizes,
+so I thought a textual manual would be nice.
+
+ [emisar-d4]: https://intl-outdoor.com/emisar-d4-high-power-led-flashlight-p-921.html
+ [emisar-d1]: https://intl-outdoor.com/emisar-d1-mini-thrower-p-922.html
+ [emisar-d1s]: https://intl-outdoor.com/emisar-d1s-thrower-p-926.html
+ [emisar-d4s]: https://intl-outdoor.com/emisar-d4s-26650-high-power-led-flashlight-p-932.html
+ [rampingios-v2]: http://aperiodic.net/phil/archives/Geekery/rampingios-v2.html
+
+The Emisar D4S only works when the head and tailcap are tightened fully.
+You can physically lock it out--prevent it from turning on
+accidentally--by simply loosening the tailcap a small amount. A quarter
+turn will do it.
+
+Emisar lights are known for their ramping interfaces. Rather than have a
+small number of distinct brightness levels, they can vary their brightness
+anywhere between their lowest and highest levels, like a light on a
+dimmer. The D4S is in ramping mode by default, but it also has a stepped
+mode that can be configured to be closer to how non-ramping lights work.
+
+Each mode--ramping and stepped--can have differently-configured brightness
+floors and ceilings.
+
+The driver for the D4S has two different chipsets. At low brightness
+levels, a fairly-efficient but low-power chipset (called a *7135*) is
+used. These lowest brightness levels are called the "*regulated levels*".
+Each regulated level will always be the same brightness regardless of how
+much charge the battery has. Above a particular brightness level, the
+light switches over to a less-efficient but high-power chipset (called a
+*FET*). These levels are called "*direct-drive*". The brightness of the
+direct-drive levels is directly related to the battery's charge level; the
+more charged the battery, the brighter the levels. The light is at its
+most efficient, in terms of power used for every lumen generated, at the
+brightest regulated level. When the light is first powered by tightening
+the tailcap, it will default to this level.
+
+At higher brightness levels, the light's LEDs generate a lot of heat. If
+the light exceeds its configured maximum temperature, it will begin
+dimming itself automatically until the temperature drops below the allowed
+maximum.
+
+The D4S has a set of cyan-colored auxiliary LEDs that can be on when the
+main LEDs are off. You can configure the behavior of the aux LEDs.
+
+#### Basic Usage
+
+The default mode for the light is ramping mode. Triple-pressing the
+button (**3 clicks**) while the light is on will toggle between ramping
+and stepped mode.
+
+While the light is off, press and release the button (**1 click**) to turn
+it on. It will turn on at the last-used brightness level. (This is
+called "*mode memory*".) Immediately after loosening and tightening the
+tailcap (or after changing the battery), the memorized level will be the
+light's max regulated level.
+
+When the light is on, 1 click will turn it off. The current brightness
+level will be memorized for future use. There's a fraction of a second
+delay between pressing the button and the light actually turning off.
+That's because of the way the light processes input; it's waiting to make
+sure you're only going to press the button once (since multiple presses
+will trigger other actions).
+
+When the light is on, holding the button down will brighten the light. In
+ramping mode, the brightness will increase gradually ("*ramping up*"). In
+stepped mode, the light will jump through increasing brightness levels.
+If you press, release, and then hold the button, it will begin dimming.
+In ramping mode, the brightness will decrease gradually ("*ramping
+down*"). In stepped mode, the light will jump through decreasing
+brightness levels. While the light is changing, if you release the button
+and immediately hold it again, the direction (dimming or brightening) will
+switch.
+
+In ramping mode, while the light is ramping, it'll briefly blink off and
+on again at two different brightness levels: the maximum regulated level
+and the brightness ceiling.
+
+While the light is off, double-pressing the button (**2 clicks**) will
+immediately jump to the brightness ceiling.
+
+While the light is on, **2 clicks** will jump to the maximum brightness
+level, regardless of the configured brightness ceiling. Another two
+clicks will go back to the previous brightness level.
+
+While the light is off, if you hold the button the light will turn on at
+its lowest level. If you continue holding the button, the light will
+begin brightening from there.
+
+##### Configuration Menus
+
+The light has several different configuration modes. Each of those modes
+works more or less the same way. The mode will have a series of menu
+items that it will go through. For each menu item, the light will first
+blink a number of times corresponding to the item number (first, second,
+etc.) After that, the light will begin fluttering on and off fairly
+quickly. While the light is fluttering, you can click the button a number
+of times; the light will count the number of button presses and use that
+number as its new configuration for that menu item. After a short period
+of time, the fluttering will stop and the light will move on to the next
+menu item. After the light has gone through all of the menu items, it
+will return to whatever mode it was in before entering the configuration
+mode.
+
+If you don't press the button during a particular menu item's fluttering,
+that item will remain unchanged.
+
+##### Configuring the Basic Modes
+
+While the light is on, **4 clicks** will enter ramping or stepped
+configuration mode, depending on which mode the light was in before the 4
+clicks.
+
+For ramping mode, there are two menu options:
+
+ 1. Brightness floor (default 1/150)
+ 2. Brightness ceiling (default 150/150)
+
+During the floor configuration, press the button equal to the number of
+ramping levels (out of 150) at which the floor should be. To set the
+lowest possible floor, click the button once.
+
+The ceiling is configured similarly, but you press the button equal to the
+number of steps away from maximum brightness. To set the highest possible
+ceiling (at max brightness), click the button once.
+
+For stepped mode, there are three menu options:
+
+ 1. Brightness floor (default 20/150)
+ 2. Brightness ceiling (default 120/150)
+ 3. Number of steps (default 7)
+
+#### Other Modes
+
+The other modes largely involve multiple clicks from off. Most of them
+are not generally needed for everyday use, but they supplement the light's
+basic operations.
+
+##### BattCheck/TempCheck Modes
+
+From off, **3 clicks** will enter "BattCheck" mode, which blinks out the
+current battery voltage. First it blinks the number of volts, then it
+pauses, then it blinks out the tenths of volts. Thus, if the battery were
+at 3.5 volts, the light would blink three times, pause, then five times.
+For zeroes, it gives a very short blink.
+
+A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8
+volts to be an empty battery and won't turn on if the battery is at or
+below 2.8 volts.
+
+The voltage sequence will continue blinking until you turn off the light
+with a single click.
+
+While the light is in BattCheck mode, **2 clicks** will enter TempCheck
+mode. Instead of blinking out the battery voltage, the light will start
+blinking out its current temperature in degrees Celsius, first the tens
+digit then the units digit. Like BattCheck mode, the light will continue
+blinking out the temperature until you turn it off with a single click.
+
+While the light is in TempCheck mode, **4 clicks** will enter thermal
+configuration mode. See the thermal configuration mode documentation
+below for how that works.
+
+##### Tactical Mode
+
+From off, **4 clicks** will enter "tactical" or "momentary" mode. The
+light will flash once to show that it's entered the mode. The auxiliary
+LEDs will turn off (if they were on). In tactical mode, the light will
+turn on at its memorized brightness for as long as the button is being
+held down. It will turn off as soon as the button is released.
+
+There's no button press combination that will exit tactical mode. To exit
+it, you will have to partially unscrew and retighten the tailcap.
+
+##### Lockout Mode
+
+From off, **6 clicks** will enter lockout mode. The light will flash
+twice to show that it's entered the mode. There's a separate aux LED mode
+for lockout mode, so you can tell whether the light is in lockout or not.
+
+In lockout mode, pressing the button will turn on the light at its lowest
+brightness ("*moonlight mode*") for as long as the button is held down.
+
+Another 6 clicks will exit lockout mode. The light will flash twice to
+show that it's left the mode.
+
+While in lockout mode, **3 clicks** will cycle through the various
+settings for the aux LEDs in lockout mode. The four modes are, in order:
+low, high, blink (on high), and off. The default mode is blink.
+
+Remember that loosening the tailcap a quarter turn will also lock out the
+light. Using the 6 clicks is called "*electronic lockout*", while turning
+the tailcap is "*physical lockout*".
+
+##### Aux LED Configuration
+
+From off, **7 clicks** will cycle to the next aux LED mode. The four
+modes are, in order: low, high, blink (on high), and off. The default
+mode is low.
+
+##### Beacon Mode
+
+From off, **8 clicks** will enter beacon mode. In beacon mode, the light
+will blink on and off every few seconds.
+
+By default, the light will blink every two seconds. To change the timing,
+use **4 clicks** while in beacon mode. The light will enter a one-item
+menu. During the flickering for input, press the button a number of times
+equal to the number of seconds between blinks.
+
+1 click will exit beacon mode.
+
+##### Thermal Configuration Mode
+
+From off, **10 clicks** will enter thermal configuration mode.
+
+The menu items here are:
+
+ 1. Current temperature (every click is one degree Celsius)
+ 2. Temperature ceiling (every click is one degree *above 30°C*)
+
+The "current temperature" item can be used to adjust the calibration of
+the light's temperature sensor. To use it, make sure the light has been
+off long enough that all of its components have cooled (or warmed) to the
+ambient temperature. Check the ambient temperature using a thermometer
+you trust. Go to thermal configuration mode, and enter the current
+temperature by clicking the button a number of times equal to the
+temperature in degrees Celsius. (If it's 22°C, click the button 22
+times.)
+
+You can check the default calibration by entering TempCheck mode from a
+room-temperature light. The D4Ss are supposed to go through a temperature
+calibration at the factory, so hopefully most of them won't need manual
+thermal calibration.
+
+The temperature ceiling is simply the highest temperature the light should
+be allowed to reach. Once it hits its temperature ceiling, it will
+progressively dim itself until the temperature stabilizes below the
+ceiling. Note that the number of clicks in that menu option is added to
+*30* to reach the actual ceiling. (Thus, you can't set a ceiling below
+31°C.) The maximum allowed ceiling is 70°C.
+
+The default temperature ceiling is 45°C.
diff --git a/ui/rampingios/rampingios-v3.txt b/ui/rampingios/rampingios-v3.txt
new file mode 100644
index 0000000..4598a76
--- /dev/null
+++ b/ui/rampingios/rampingios-v3.txt
@@ -0,0 +1,324 @@
+RampingIOS V3 Manual
+http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html
+
+Tue, 28 Aug 2018
+9:47AM | Geekery | #
+
+
+RampingIOS V3 Manual
+--------------------
+
+[rampingiosv3-ui.png] RampingIOS V3 UI diagram
+
+The Emisar D4S flashlights use a firmware named RampingIOS V3. (The Emisar D4,
+D1, and D1S all use RampingIOS V2.) There's not really a manual; the only thing
+we get is the diagram on the right. It's reasonably comprehensive, but there's
+a fair amount of detail it merely summarizes, so I thought a textual manual
+would be nice.
+
+The Emisar D4S only works when the head and tailcap are tightened fully. You
+can physically lock it out--prevent it from turning on accidentally--by simply
+loosening the tailcap a small amount. A quarter turn will do it.
+
+Emisar lights are known for their ramping interfaces. Rather than have a small
+number of distinct brightness levels, they can vary their brightness anywhere
+between their lowest and highest levels, like a light on a dimmer. The D4S is
+in ramping mode by default, but it also has a stepped mode that can be
+configured to be closer to how non-ramping lights work.
+
+Each mode--ramping and stepped--can have differently-configured brightness
+floors and ceilings.
+
+The driver for the D4S has two different chipsets. At low brightness levels, a
+fairly-efficient but low-power chipset (called a 7135) is used. These lowest
+brightness levels are called the "regulated levels". Each regulated level will
+always be the same brightness regardless of how much charge the battery has.
+Above a particular brightness level, the light switches over to a
+less-efficient but high-power chipset (called a FET). These levels are called "
+direct-drive". The brightness of the direct-drive levels is directly related to
+the battery's charge level; the more charged the battery, the brighter the
+levels. The light is at its most efficient, in terms of power used for every
+lumen generated, at the brightest regulated level. When the light is first
+powered by tightening the tailcap, it will default to this level.
+
+At higher brightness levels, the light's LEDs generate a lot of heat. If the
+light exceeds its configured maximum temperature, it will begin dimming itself
+automatically until the temperature drops below the allowed maximum.
+
+The D4S has a set of cyan-colored auxiliary LEDs that can be on when the main
+LEDs are off. You can configure the behavior of the aux LEDs.
+
+
+Basic Usage
+-----------
+
+The default mode for the light is ramping mode. Triple-pressing the button (3
+clicks) while the light is on will toggle between ramping and stepped mode.
+
+While the light is off, press and release the button (1 click) to turn it on.
+It will turn on at the last-used brightness level. (This is called "mode memory
+".) Immediately after loosening and tightening the tailcap (or after changing
+the battery), the memorized level will be the light's max regulated level.
+
+When the light is on, 1 click will turn it off. The current brightness level
+will be memorized for future use. There's a fraction of a second delay between
+pressing the button and the light actually turning off. That's because of the
+way the light processes input; it's waiting to make sure you're only going to
+press the button once (since multiple presses will trigger other actions).
+
+When the light is on, holding the button down will brighten the light. In
+ramping mode, the brightness will increase gradually ("ramping up"). In stepped
+mode, the light will jump through increasing brightness levels. If you press,
+release, and then hold the button, it will begin dimming. In ramping mode, the
+brightness will decrease gradually ("ramping down"). In stepped mode, the light
+will jump through decreasing brightness levels. While the light is changing, if
+you release the button and immediately hold it again, the direction (dimming or
+brightening) will switch.
+
+In ramping mode, while the light is ramping, it'll briefly blink off and on
+again at two different brightness levels: the maximum regulated level and the
+brightness ceiling.
+
+While the light is off, double-pressing the button (2 clicks) will immediately
+jump to the brightness ceiling.
+
+While the light is on, 2 clicks will jump to the maximum brightness level,
+regardless of the configured brightness ceiling. Another two clicks will go
+back to the previous brightness level.
+
+While the light is off, if you hold the button the light will turn on at its
+lowest level. If you continue holding the button, the light will begin
+brightening from there.
+
+
+Configuration Menus
+-------------------
+
+The light has several different configuration modes. Each of those modes works
+more or less the same way. The mode will have a series of menu items that it
+will go through. For each menu item, the light will first blink a number of
+times corresponding to the item number (first, second, etc.) After that, the
+light will begin fluttering on and off fairly quickly. While the light is
+fluttering, you can click the button a number of times; the light will count
+the number of button presses and use that number as its new configuration for
+that menu item. After a short period of time, the fluttering will stop and the
+light will move on to the next menu item. After the light has gone through all
+of the menu items, it will return to whatever mode it was in before entering
+the configuration mode.
+
+If you don't press the button during a particular menu item's fluttering, that
+item will remain unchanged.
+
+
+Configuring the Basic Modes
+
+While the light is on, 4 clicks will enter ramping or stepped configuration
+mode, depending on which mode the light was in before the 4 clicks.
+
+For ramping mode, there are two menu options:
+
+ 1. Brightness floor (default 1/150)
+ 2. Brightness ceiling (default 150/150)
+
+During the floor configuration, press the button equal to the number of ramping
+levels (out of 150) at which the floor should be. To set the lowest possible
+floor, click the button once.
+
+The ceiling is configured similarly, but you press the button equal to the
+number of steps away from maximum brightness. To set the highest possible
+ceiling (at max brightness), click the button once.
+
+For stepped mode, there are three menu options:
+
+ 1. Brightness floor (default 20/150)
+ 2. Brightness ceiling (default 120/150)
+ 3. Number of steps (default 7)
+
+
+Other Modes
+-----------
+
+The other modes largely involve multiple clicks from off. Most of them are not
+generally needed for everyday use, but they supplement the light's basic
+operations.
+
+
+BattCheck/TempCheck Modes
+
+From off, 3 clicks will enter "BattCheck" mode, which blinks out the current
+battery voltage. First it blinks the number of volts, then it pauses, then it
+blinks out the tenths of volts. Thus, if the battery were at 3.5 volts, the
+light would blink three times, pause, then five times. For zeroes, it gives a
+very short blink.
+
+A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 volts
+to be an empty battery and won't turn on if the battery is at or below 2.8
+volts.
+
+The voltage sequence will continue blinking until you turn off the light with a
+single click.
+
+While the light is in BattCheck mode, 2 clicks will enter TempCheck mode.
+Instead of blinking out the battery voltage, the light will start blinking out
+its current temperature in degrees Celsius, first the tens digit then the units
+digit. Like BattCheck mode, the light will continue blinking out the
+temperature until you turn it off with a single click.
+
+While the light is in TempCheck mode, 4 clicks will enter thermal configuration
+mode. See the thermal configuration mode documentation below for how that
+works.
+
+
+Tactical Mode
+
+From off, 4 clicks will enter "tactical" or "momentary" mode. The light will
+flash once to show that it's entered the mode. The auxiliary LEDs will turn off
+(if they were on). In tactical mode, the light will turn on at its memorized
+brightness for as long as the button is being held down. It will turn off as
+soon as the button is released.
+
+There's no button press combination that will exit tactical mode. To exit it,
+you will have to partially unscrew and retighten the tailcap.
+
+
+Lockout Mode
+
+From off, 6 clicks will enter lockout mode. The light will flash twice to show
+that it's entered the mode. There's a separate aux LED mode for lockout mode,
+so you can tell whether the light is in lockout or not.
+
+In lockout mode, pressing the button will turn on the light at its lowest
+brightness ("moonlight mode") for as long as the button is held down.
+
+Another 6 clicks will exit lockout mode. The light will flash twice to show
+that it's left the mode.
+
+While in lockout mode, 3 clicks will cycle through the various settings for the
+aux LEDs in lockout mode. The four modes are, in order: low, high, blink (on
+high), and off. The default mode is blink.
+
+Remember that loosening the tailcap a quarter turn will also lock out the
+light. Using the 6 clicks is called "electronic lockout", while turning the
+tailcap is "physical lockout".
+
+
+Aux LED Configuration
+
+From off, 7 clicks will cycle to the next aux LED mode. The four modes are, in
+order: low, high, blink (on high), and off. The default mode is low.
+
+
+Beacon Mode
+
+From off, 8 clicks will enter beacon mode. In beacon mode, the light will blink
+on and off every few seconds.
+
+By default, the light will blink every two seconds. To change the timing, use 4
+clicks while in beacon mode. The light will enter a one-item menu. During the
+flickering for input, press the button a number of times equal to the number of
+seconds between blinks.
+
+1 click will exit beacon mode.
+
+
+Thermal Configuration Mode
+
+From off, 10 clicks will enter thermal configuration mode.
+
+The menu items here are:
+
+ 1. Current temperature (every click is one degree Celsius)
+ 2. Temperature ceiling (every click is one degree above 30?C)
+
+The "current temperature" item can be used to adjust the calibration of the
+light's temperature sensor. To use it, make sure the light has been off long
+enough that all of its components have cooled (or warmed) to the ambient
+temperature. Check the ambient temperature using a thermometer you trust. Go to
+thermal configuration mode, and enter the current temperature by clicking the
+button a number of times equal to the temperature in degrees Celsius. (If it's
+22?C, click the button 22 times.)
+
+You can check the default calibration by entering TempCheck mode from a
+room-temperature light. The D4Ss are supposed to go through a temperature
+calibration at the factory, so hopefully most of them won't need manual thermal
+calibration.
+
+The temperature ceiling is simply the highest temperature the light should be
+allowed to reach. Once it hits its temperature ceiling, it will progressively
+dim itself until the temperature stabilizes below the ceiling. Note that the
+number of clicks in that menu option is added to 30 to reach the actual
+ceiling. (Thus, you can't set a ceiling below 31?C.) The maximum allowed
+ceiling is 70?C.
+
+The default temperature ceiling is 45?C.
+
+
+Static
+
+ * zsh prompt
+ * PGP
+ * SSH
+ * MTA
+ * tutorials
+ * config files
+ * desktop
+ * books I own
+ * stuff I'm giving away
+ * Dr. Who eps I have
+ * bookmarks
+ * photos
+ * about
+
+Directory
+
+ * Root (143)
+ + Books (32)
+ + Events (7)
+ o Burning Man (3)
+ o Camping (2)
+ o PDF (2)
+ + Geekery (36)
+ o Test (3)
+ + General (24)
+ + Links (12)
+ o Slashdot (1)
+ + MTA (22)
+ + Recipes (4)
+ + Video Games (6)
+ o FFXI (1)
+
+Archive
+
+ ?August?
+Sun Mon Tue Wed Thu Fri Sat
+ 1 2 3 4
+5 6 7 8 9 10 11
+12 13 14 15 16 17 18
+19 20 21 22 23 24 25
+26 27 28 29 30 31
+
+ ?2018?
+ Months
+Jan Feb Mar Apr May Jun
+Jul Aug Sep Oct Nov Dec
+
+Search
+
+[ ]
+Powered by Google
+
+Currently Reading
+
+Recent Books
+
+-------------------------------------------------------------------------------
+
+Copyright (C) 2018 Phil Gold
+
+Back to main page.
+
+ * Valid HTML 4.01
+ * Valid CSS 2
+ * RSS syndication
+ * Valid RSS 1.0
+
diff --git a/ui/rampingios/rampingiosv3-ui.png b/ui/rampingios/rampingiosv3-ui.png
new file mode 100644
index 0000000..d02dbf6
--- /dev/null
+++ b/ui/rampingios/rampingiosv3-ui.png
Binary files differ
diff --git a/ui/rampingios/rampingiosv3.c b/ui/rampingios/rampingiosv3.c
new file mode 100644
index 0000000..e990a5a
--- /dev/null
+++ b/ui/rampingios/rampingiosv3.c
@@ -0,0 +1,1253 @@
+/*
+ * RampingIOS V3: FSM-based version of RampingIOS V2 UI, with upgrades.
+ *
+ * Copyright (C) 2018-2019 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 *********/
+// Anduril config file name (set it here or define it at the gcc command line)
+//#define CONFIGFILE cfg-emisar-d4s.h
+
+#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
+#define USE_TENCLICK_THERMAL_CONFIG // ten clicks from off -> thermal config mode
+
+// 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
+// short blips while ramping
+#define BLINK_AT_RAMP_MIDDLE
+//#define BLINK_AT_RAMP_FLOOR
+#define BLINK_AT_RAMP_CEILING
+//#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
+
+// battery readout style (pick one)
+#define BATTCHECK_VpT
+//#define BATTCHECK_8bars // FIXME: breaks build
+//#define BATTCHECK_4bars // FIXME: breaks build
+
+// enable beacon mode
+#define USE_BEACON_MODE
+
+// make the ramps configurable by the user
+#define USE_RAMP_CONFIG
+
+/***** specific settings for known driver types *****/
+#include "tk.h"
+#include incfile(CONFIGFILE)
+
+
+// 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
+#ifndef RAMP_LENGTH
+#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
+#endif
+#define USE_BATTCHECK
+#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
+
+// try to auto-detect how many eeprom bytes
+#define USE_EEPROM
+#define EEPROM_BYTES_BASE 7
+
+#ifdef USE_INDICATOR_LED
+#define EEPROM_INDICATOR_BYTES 1
+#else
+#define EEPROM_INDICATOR_BYTES 0
+#endif
+
+#ifdef USE_THERMAL_REGULATION
+#define EEPROM_THERMAL_BYTES 2
+#else
+#define EEPROM_THERMAL_BYTES 0
+#endif
+
+#define EEPROM_BYTES (EEPROM_BYTES_BASE+EEPROM_INDICATOR_BYTES+EEPROM_THERMAL_BYTES)
+
+
+#include "spaghetti-monster.h"
+
+
+// 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_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_THERMAL_REGULATION
+#define USE_BLINK_NUM
+uint8_t tempcheck_state(Event event, uint16_t arg);
+uint8_t thermal_config_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_BEACON_MODE
+// beacon mode and its related config mode
+uint8_t beacon_state(Event event, uint16_t arg);
+uint8_t beacon_config_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);
+// momentary / signalling mode
+uint8_t momentary_state(Event event, uint16_t arg);
+
+// 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);
+#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
+void indicator_blink(uint8_t arg);
+#endif
+
+// remember stuff even after battery was changed
+void load_config();
+void save_config();
+
+// default ramp options if not overridden earlier per-driver
+#ifndef RAMP_SMOOTH_FLOOR
+ #define RAMP_SMOOTH_FLOOR 1
+#endif
+#ifndef RAMP_SMOOTH_CEIL
+ #if PWM_CHANNELS == 3
+ #define RAMP_SMOOTH_CEIL MAX_Nx7135
+ #else
+ #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
+ #endif
+#endif
+#ifndef RAMP_DISCRETE_FLOOR
+ #define RAMP_DISCRETE_FLOOR 20
+#endif
+#ifndef RAMP_DISCRETE_CEIL
+ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#endif
+#ifndef RAMP_DISCRETE_STEPS
+ #define RAMP_DISCRETE_STEPS 7
+#endif
+
+// mile marker(s) partway up the ramp
+// default: blink only at border between regulated and FET
+#ifdef BLINK_AT_RAMP_MIDDLE
+ #if PWM_CHANNELS >= 3
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
+ #ifndef BLINK_AT_RAMP_MIDDLE_2
+ #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
+ #endif
+ #endif
+ #else
+ #ifndef BLINK_AT_RAMP_MIDDLE_1
+ #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
+ #endif
+ #endif
+#endif
+
+// brightness control
+#ifndef DEFAULT_LEVEL
+#define DEFAULT_LEVEL MAX_1x7135
+#endif
+uint8_t memorized_level = DEFAULT_LEVEL;
+// smooth vs discrete ramping
+volatile uint8_t ramp_style = 0; // 0 = smooth, 1 = discrete
+volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR;
+volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL;
+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
+
+#ifdef USE_INDICATOR_LED
+ // bits 2-3 control lockout mode
+ // bits 0-1 control "off" mode
+ // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
+ #ifdef INDICATOR_LED_DEFAULT_MODE
+ uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE;
+ #else
+ #ifdef USE_INDICATOR_LED_WHILE_RAMPING
+ //uint8_t indicator_led_mode = (1<<2) + 2;
+ uint8_t indicator_led_mode = (2<<2) + 1;
+ #else
+ uint8_t indicator_led_mode = (3<<2) + 1;
+ #endif
+ #endif
+#endif
+
+// calculate the nearest ramp level which would be valid at the moment
+// (is a no-op for smooth ramp, but limits discrete ramp to only the
+// correct levels for the user's config)
+uint8_t nearest_level(int16_t target);
+
+#ifdef USE_THERMAL_REGULATION
+// brightness before thermal step-down
+uint8_t target_level = 0;
+#endif
+
+#ifdef USE_BEACON_MODE
+// beacon timing
+volatile uint8_t beacon_seconds = 2;
+#endif
+
+
+uint8_t off_state(Event event, uint16_t arg) {
+ // turn emitter off when entering state
+ if (event == EV_enter_state) {
+ set_level(0);
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode & 0x03);
+ #endif
+ // sleep while off (lower power use)
+ go_to_standby = 1;
+ return EVENT_HANDLED;
+ }
+ // 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;
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode & 0x03);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ // blink the indicator LED, maybe
+ else if (event == EV_sleep_tick) {
+ if ((indicator_led_mode & 0b00000011) == 0b00000011) {
+ indicator_blink(arg);
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ // hold (initially): go to lowest level (floor), but allow abort for regular click
+ else if (event == EV_click1_press) {
+ set_level(nearest_level(1));
+ return EVENT_HANDLED;
+ }
+ // hold: go to lowest level
+ else if (event == EV_click1_hold) {
+ #ifdef MOON_TIMING_HINT
+ if (arg == 0) {
+ // let the user know they can let go now to stay at moon
+ uint8_t temp = actual_level;
+ set_level(0);
+ delay_4ms(3);
+ set_level(temp);
+ } else
+ #endif
+ // don't start ramping immediately;
+ // give the user time to release at moon level
+ //if (arg >= HOLD_TIMEOUT) { // smaller
+ if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
+ set_state(steady_state, 1);
+ }
+ return EVENT_HANDLED;
+ }
+ // hold, release quickly: go to lowest level (floor)
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // click, hold: go to highest level (ceiling) (for ramping down)
+ else if (event == EV_click2_hold) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: highest mode (ceiling)
+ else if (event == EV_2clicks) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 3 clicks (initial press): off, to prep for later events
+ else if (event == EV_click3_press) {
+ set_level(0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_BATTCHECK
+ // 3 clicks: battcheck mode / blinky mode group 1
+ else if (event == EV_3clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 4 clicks: momentary
+ else if (event == EV_4clicks) {
+ blink_confirm(1);
+ set_state(momentary_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 6 clicks: lockout mode
+ else if (event == EV_6clicks) {
+ blink_confirm(2);
+ set_state(lockout_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_INDICATOR_LED
+ // 7 clicks: next aux LED mode
+ else if (event == EV_7clicks) {
+ blink_confirm(1);
+ uint8_t mode = (indicator_led_mode & 3) + 1;
+ #ifdef TICK_DURING_STANDBY
+ mode = mode & 3;
+ #else
+ mode = mode % 3;
+ #endif
+ #ifdef INDICATOR_LED_SKIP_LOW
+ if (mode == 1) { mode ++; }
+ #endif
+ indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
+ indicator_led(mode);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 8 clicks: beacon mode
+ else if (event == EV_8clicks) {
+ set_state(beacon_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_TENCLICK_THERMAL_CONFIG
+ // 10 clicks: thermal config mode
+ else if (event == EV_10clicks) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t steady_state(Event event, uint16_t arg) {
+ uint8_t mode_min = ramp_smooth_floor;
+ uint8_t mode_max = ramp_smooth_ceil;
+ uint8_t ramp_step_size = 1;
+ #ifdef USE_REVERSING
+ static int8_t ramp_direction = 1;
+ #endif
+ if (ramp_style) {
+ mode_min = ramp_discrete_floor;
+ mode_max = ramp_discrete_ceil;
+ 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
+ arg = nearest_level(arg);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = arg;
+ #endif
+ set_level(arg);
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ return EVENT_HANDLED;
+ }
+ // 1 click: off
+ else if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: go to/from highest level
+ else if (event == EV_2clicks) {
+ if (actual_level < MAX_LEVEL) {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = MAX_LEVEL;
+ #endif
+ // true turbo, not the mode-specific ceiling
+ set_level(MAX_LEVEL);
+ }
+ else {
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ set_level(memorized_level);
+ }
+ return EVENT_HANDLED;
+ }
+ // 3 clicks: toggle smooth vs discrete ramping
+ else if (event == EV_3clicks) {
+ ramp_style = !ramp_style;
+ memorized_level = nearest_level(actual_level);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ //set_level_gradually(lvl);
+ #endif
+ #endif
+ save_config();
+ set_level(0);
+ delay_4ms(20/4);
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_RAMP_CONFIG
+ // 4 clicks: configure this ramp mode
+ else if (event == EV_4clicks) {
+ push_state(ramp_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // hold: change brightness (brighter)
+ else if (event == EV_click1_hold) {
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_REVERSING
+ // make it ramp down instead, if already at max
+ if ((arg <= 1) && (actual_level >= mode_max)) {
+ ramp_direction = -1;
+ }
+ memorized_level = nearest_level((int16_t)actual_level \
+ + (ramp_step_size * ramp_direction));
+ #else
+ memorized_level = nearest_level((int16_t)actual_level + ramp_step_size);
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_CEILING
+ || (memorized_level == mode_max)
+ #endif
+ #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR)
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ #endif
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #if defined(USE_REVERSING)
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ #ifdef USE_REVERSING
+ ramp_direction = -ramp_direction;
+ #endif
+ return EVENT_HANDLED;
+ }
+ #endif
+ // click, hold: change brightness (dimmer)
+ else if (event == EV_click2_hold) {
+ #ifdef USE_REVERSING
+ ramp_direction = 1;
+ #endif
+ // ramp slower in discrete mode
+ if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
+ return EVENT_HANDLED;
+ }
+ // TODO? make it ramp up instead, if already at min?
+ memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
+ #ifdef USE_THERMAL_REGULATION
+ target_level = memorized_level;
+ #endif
+ #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE)
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) && (
+ 0 // for easier syntax below
+ #ifdef BLINK_AT_RAMP_MIDDLE_1
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
+ #endif
+ #ifdef BLINK_AT_RAMP_MIDDLE_2
+ || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
+ #endif
+ #ifdef BLINK_AT_RAMP_FLOOR
+ || (memorized_level == mode_min)
+ #endif
+ )) {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ #endif
+ #if defined(BLINK_AT_STEPS)
+ uint8_t foo = ramp_style;
+ ramp_style = 1;
+ uint8_t nearest = nearest_level((int16_t)actual_level);
+ ramp_style = foo;
+ // only blink once for each threshold
+ if ((memorized_level != actual_level) &&
+ (ramp_style == 0) &&
+ (memorized_level == nearest)
+ )
+ {
+ set_level(0);
+ delay_4ms(8/4);
+ }
+ #endif
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
+ else if (event == EV_tick) {
+ #ifdef USE_REVERSING
+ // un-reverse after 1 second
+ if (arg == TICKS_PER_SECOND) ramp_direction = 1;
+ #endif
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // make thermal adjustment speed scale with magnitude
+ if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
+ return EVENT_HANDLED; // 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.8)) for x in (1,2,4,8,16,32,64,128)]
+ //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5};
+ // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)]
+ //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3};
+ // [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 EVENT_HANDLED;
+ }
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ // overheating: drop by an amount proportional to how far we are above the ceiling
+ else if (event == EV_temperature_high) {
+ #if 0
+ uint8_t foo = actual_level;
+ set_level(0);
+ delay_4ms(2);
+ set_level(foo);
+ #endif
+ #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
+ target_level = THERM_FASTER_LEVEL;
+ } 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 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 0
+ uint8_t foo = actual_level;
+ set_level(0);
+ delay_4ms(2);
+ set_level(foo);
+ #endif
+ if (actual_level < target_level) {
+ //int16_t stepup = actual_level + (arg>>1);
+ int16_t stepup = actual_level + arg;
+ if (stepup > target_level) stepup = target_level;
+ else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ set_level_gradually(stepup);
+ #else
+ set_level(stepup);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ set_state(tempcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+
+#ifdef USE_THERMAL_REGULATION
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: thermal config mode
+ else if (event == EV_4clicks) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#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 EVENT_HANDLED;
+ }
+ // TODO: use sleep ticks to measure time between pulses,
+ // to save power
+ // 4 clicks: beacon config mode
+ else if (event == EV_4clicks) {
+ push_state(beacon_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif // #ifdef USE_BEACON_MODE
+
+
+uint8_t lockout_state(Event event, uint16_t arg) {
+ #ifdef MOON_DURING_LOCKOUT_MODE
+ // momentary(ish) moon mode during lockout
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ #ifdef LOCKOUT_MOON_LOWEST
+ // Use lowest moon configured
+ uint8_t lvl = ramp_smooth_floor;
+ if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor;
+ set_level(lvl);
+ #elif defined(LOCKOUT_MOON_FANCY)
+ uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor };
+ if ((event & 0x0f) == 2) {
+ set_level(levels[ramp_style^1]);
+ } else {
+ set_level(levels[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
+ #endif
+ if (event == EV_tick) {
+ if (arg > TICKS_PER_SECOND*2) {
+ go_to_standby = 1;
+ #ifdef USE_INDICATOR_LED
+ indicator_led(indicator_led_mode >> 2);
+ #endif
+ }
+ return EVENT_HANDLED;
+ }
+ #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
+ else if (event == EV_sleep_tick) {
+ if ((indicator_led_mode & 0b00001100) == 0b00001100) {
+ indicator_blink(arg);
+ }
+ return EVENT_HANDLED;
+ }
+ #endif
+ #ifdef USE_INDICATOR_LED
+ // 3 clicks: rotate through indicator LED modes (lockout mode)
+ else if (event == EV_3clicks) {
+ 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);
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 6 clicks: exit
+ else if (event == EV_6clicks) {
+ blink_confirm(1);
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t momentary_state(Event event, uint16_t arg) {
+ // TODO: momentary strobe here? (for light painting)
+
+ // light up when the button is pressed; go dark otherwise
+ // button is being held
+ if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
+ set_level(memorized_level);
+ return EVENT_HANDLED;
+ }
+ // button was released
+ else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
+ set_level(0);
+ //go_to_standby = 1; // sleep while light is off
+ return EVENT_HANDLED;
+ }
+
+ // Sleep, dammit! (but wait a few seconds first)
+ // (because standby mode uses such little power that it can interfere
+ // with exiting via tailcap loosen+tighten unless you leave power
+ // disconnected for several seconds, so we want to be awake when that
+ // happens to speed up the process)
+ else if ((event == EV_tick) && (actual_level == 0)) {
+ if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds
+ go_to_standby = 1; // sleep while light is off
+ // TODO: lighted button should use lockout config?
+ }
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+// 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ //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
+ uint8_t val;
+ if (ramp_style) { // discrete / stepped ramp
+
+ 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;
+
+ } else { // smooth ramp
+
+ val = config_state_values[0];
+ if (val) { ramp_smooth_floor = val; }
+
+ val = config_state_values[1];
+ if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; }
+
+ }
+}
+
+uint8_t ramp_config_state(Event event, uint16_t arg) {
+ uint8_t num_config_steps;
+ num_config_steps = 2 + ramp_style;
+ return config_state_base(event, arg,
+ num_config_steps, ramp_config_save);
+}
+#endif // #ifdef USE_RAMP_CONFIG
+
+
+#ifdef USE_THERMAL_REGULATION
+void thermal_config_save() {
+ // parse values
+ uint8_t val;
+
+ // calibrate room temperature
+ val = config_state_values[0];
+ if (val) {
+ int8_t rawtemp = temperature - therm_cal_offset;
+ therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
+ }
+
+ val = config_state_values[1];
+ if (val) {
+ // set maximum heat limit
+ therm_ceil = 30 + val - 1;
+ }
+ if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
+}
+
+uint8_t thermal_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 2, thermal_config_save);
+}
+#endif // #ifdef USE_THERMAL_REGULATION
+
+
+#ifdef USE_BEACON_MODE
+void beacon_config_save() {
+ // parse values
+ uint8_t val = config_state_values[0];
+ if (val) {
+ beacon_seconds = val;
+ }
+}
+
+uint8_t beacon_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 1, beacon_config_save);
+}
+
+inline void beacon_mode_iter() {
+ // one iteration of main loop()
+ set_level(memorized_level);
+ nice_delay_ms(100);
+ set_level(0);
+ nice_delay_ms(((beacon_seconds) * 1000) - 100);
+}
+#endif // #ifdef USE_BEACON_MODE
+
+
+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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ 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_smooth_floor;
+ uint8_t mode_max = ramp_smooth_ceil;
+ if (ramp_style) {
+ mode_min = ramp_discrete_floor;
+ mode_max = ramp_discrete_ceil;
+ }
+ if (target < mode_min) return mode_min;
+ if (target > mode_max) return mode_max;
+ // the rest isn't relevant for smooth ramping
+ if (! ramp_style) return target;
+
+ uint8_t ramp_range = 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));
+ int16_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);
+ }
+}
+
+
+#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
+// beacon-like mode for the indicator LED
+void indicator_blink(uint8_t arg) {
+ #define USE_FANCIER_BLINKING_INDICATOR
+ #ifdef USE_FANCIER_BLINKING_INDICATOR
+
+ // fancy blink, set off/low/high levels here:
+ uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0};
+ indicator_led(seq[arg & 15]);
+
+ #else // basic blink, 1/8th duty cycle
+
+ if (! (arg & 7)) {
+ indicator_led(2);
+ }
+ else {
+ indicator_led(0);
+ }
+
+ #endif
+}
+#endif
+
+
+void load_config() {
+ if (load_eeprom()) {
+ ramp_style = eeprom[0];
+ #ifdef USE_RAMP_CONFIG
+ ramp_smooth_floor = eeprom[1];
+ ramp_smooth_ceil = eeprom[2];
+ ramp_discrete_floor = eeprom[3];
+ ramp_discrete_ceil = eeprom[4];
+ ramp_discrete_steps = eeprom[5];
+ #endif
+ #ifdef USE_BEACON_MODE
+ beacon_seconds = eeprom[6];
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ therm_ceil = eeprom[EEPROM_BYTES_BASE];
+ therm_cal_offset = eeprom[EEPROM_BYTES_BASE+1];
+ #endif
+ #ifdef USE_INDICATOR_LED
+ indicator_led_mode = eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES];
+ #endif
+ }
+}
+
+void save_config() {
+ eeprom[0] = ramp_style;
+ #ifdef USE_RAMP_CONFIG
+ eeprom[1] = ramp_smooth_floor;
+ eeprom[2] = ramp_smooth_ceil;
+ eeprom[3] = ramp_discrete_floor;
+ eeprom[4] = ramp_discrete_ceil;
+ eeprom[5] = ramp_discrete_steps;
+ #endif
+ #ifdef USE_BEACON_MODE
+ eeprom[6] = beacon_seconds;
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ eeprom[EEPROM_BYTES_BASE ] = therm_ceil;
+ eeprom[EEPROM_BYTES_BASE+1] = therm_cal_offset;
+ #endif
+ #ifdef USE_INDICATOR_LED
+ eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES] = indicator_led_mode;
+ #endif
+
+ save_eeprom();
+}
+
+void low_voltage() {
+ StatePtr state = current_state;
+
+ // TODO: turn off aux LED(s) when power is really low
+
+ if (0) {} // placeholder
+
+ // in normal mode, step down or turn off
+ else 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;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // not needed?
+ //set_level_gradually(lvl);
+ #endif
+ #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() {
+ // blink at power-on to let user know power is connected
+ set_level(RAMP_SIZE/8);
+ delay_4ms(3);
+ set_level(0);
+
+ load_config();
+
+ push_state(off_state, 0);
+}
+
+
+void loop() {
+
+ StatePtr state = current_state;
+
+ if (0) {}
+
+ #ifdef USE_BATTCHECK
+ else if (state == battcheck_state) {
+ battcheck();
+ }
+ #endif
+
+ #ifdef USE_BEACON_MODE
+ else if (state == beacon_state) {
+ beacon_mode_iter();
+ }
+ #endif
+
+ #ifdef USE_THERMAL_REGULATION
+ // TODO: blink out therm_ceil during thermal_config_state?
+ else if (state == tempcheck_state) {
+ blink_num(temperature);
+ nice_delay_ms(1000);
+ }
+ #endif
+
+ #ifdef USE_IDLE_MODE
+ else {
+ // doze until next clock tick
+ idle_mode();
+ }
+ #endif
+
+}
diff --git a/ui/rampingios/rampingiosv3.svg b/ui/rampingios/rampingiosv3.svg
new file mode 100644
index 0000000..bc9e6b3
--- /dev/null
+++ b/ui/rampingios/rampingiosv3.svg
@@ -0,0 +1,4113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="18.056446cm"
+ height="26.675991cm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.1 r15371"
+ sodipodi:docname="rampingiosv3.svg"
+ inkscape:export-filename="/tmp/rampingiosv3-ui.png"
+ inkscape:export-xdpi="114.26005"
+ inkscape:export-ydpi="114.26005">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient18929">
+ <stop
+ id="stop18927"
+ offset="0"
+ style="stop-color:#0000ff;stop-opacity:0" />
+ <stop
+ id="stop18925"
+ offset="1"
+ style="stop-color:#0000ff;stop-opacity:1" />
+ </linearGradient>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker61712"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path61710"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#7777ff;fill-opacity:1;fill-rule:evenodd;stroke:#7777ff;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker50318"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow1Mstart">
+ <path
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ id="path50316"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker49438"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path49436"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#7777ff;fill-opacity:1;fill-rule:evenodd;stroke:#7777ff;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker48588"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow1Mstart">
+ <path
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ id="path48586"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker12699"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path12697"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker12161"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path9801"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path9792"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.8,0,0,0.8,10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker11118"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path11116"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker10592"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path10590"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path9798"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker9610"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path9608"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <pattern
+ inkscape:stockid="Sand (bitmap)"
+ id="pattern7084"
+ height="256"
+ width="256"
+ patternUnits="userSpaceOnUse">
+ <!-- Seamless texture provided by FreeSeamlessTextures.com -->
+ <!-- License: creative commons attribution -->
+ <image
+ xlink:href="data:image/jpeg;base64,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 /p1//9k= "
+ y="0"
+ x="0"
+ id="image7082"
+ height="260"
+ width="260" />
+ </pattern>
+ <linearGradient
+ id="linearGradient1694"
+ inkscape:collect="always">
+ <stop
+ id="stop1690"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:0.74496644" />
+ <stop
+ id="stop1692"
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5876">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop5878" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop5880" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5850"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop5852" />
+ </linearGradient>
+ <pattern
+ inkscape:stockid="Sand (bitmap)"
+ id="sand_bitmap"
+ height="256"
+ width="256"
+ patternUnits="userSpaceOnUse">
+ <!-- Seamless texture provided by FreeSeamlessTextures.com -->
+ <!-- License: creative commons attribution -->
+ <image
+ xlink:href="data:image/jpeg;base64,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 /p1//9k= "
+ y="0"
+ x="0"
+ id="image9"
+ height="260"
+ width="260" />
+ </pattern>
+ <linearGradient
+ id="linearGradient4336">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop4338" />
+ <stop
+ id="stop5884"
+ offset="0.5"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="1"
+ id="stop4340" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4304">
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="0"
+ id="stop4306" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.39655173;"
+ offset="1"
+ id="stop4308" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4296">
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="0"
+ id="stop4298" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.28627452;"
+ offset="1"
+ id="stop4300" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4276">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop4278" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop4280" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6735"
+ osb:paint="gradient">
+ <stop
+ style="stop-color:#000000;stop-opacity:0.31764707;"
+ offset="0"
+ id="stop6737" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop6739" />
+ </linearGradient>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3850"
+ d="M -10.666667,0 -14.933333,4.2666667 0,0 -14.933333,-4.2666667 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.85333335pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path3856"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <inkscape:path-effect
+ effect="skeletal"
+ id="path-effect3841"
+ is_visible="true"
+ pattern="m 0,5 10,5 V 0 Z"
+ copytype="single_stretched"
+ prop_scale="1"
+ scale_y_rel="false"
+ spacing="0"
+ normal_offset="0"
+ tang_offset="0"
+ prop_units="false"
+ vertical_pattern="false"
+ fuse_tolerance="0" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-5"
+ style="overflow:visible">
+ <path
+ id="path3856-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-0"
+ style="overflow:visible">
+ <path
+ id="path3856-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-58"
+ style="overflow:visible">
+ <path
+ id="path3856-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible">
+ <path
+ id="path3856-54"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-3"
+ style="overflow:visible">
+ <path
+ id="path3856-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-03"
+ style="overflow:visible">
+ <path
+ id="path3856-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-2"
+ style="overflow:visible">
+ <path
+ id="path3856-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-4"
+ style="overflow:visible">
+ <path
+ id="path3856-00"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-7"
+ style="overflow:visible">
+ <path
+ id="path3856-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-76"
+ style="overflow:visible">
+ <path
+ id="path3856-64"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-6"
+ style="overflow:visible">
+ <path
+ id="path3856-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-65"
+ style="overflow:visible">
+ <path
+ id="path3856-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-1"
+ style="overflow:visible">
+ <path
+ id="path3856-01"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-29"
+ style="overflow:visible">
+ <path
+ id="path3856-60"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-71"
+ style="overflow:visible">
+ <path
+ id="path3856-83"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-26"
+ style="overflow:visible">
+ <path
+ id="path3856-67"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-61"
+ style="overflow:visible">
+ <path
+ id="path3856-86"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-48"
+ style="overflow:visible">
+ <path
+ id="path3856-867"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5244"
+ style="overflow:visible">
+ <path
+ id="path5246"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5248"
+ style="overflow:visible">
+ <path
+ id="path5250"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5252"
+ style="overflow:visible">
+ <path
+ id="path5254"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5256"
+ style="overflow:visible">
+ <path
+ id="path5258"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5260"
+ style="overflow:visible">
+ <path
+ id="path5262"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5264"
+ style="overflow:visible">
+ <path
+ id="path5266"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-05"
+ style="overflow:visible">
+ <path
+ id="path3856-52"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-33"
+ style="overflow:visible">
+ <path
+ id="path3856-38"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-61-6"
+ style="overflow:visible">
+ <path
+ id="path3856-86-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-64"
+ style="overflow:visible">
+ <path
+ id="path3856-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-43"
+ style="overflow:visible">
+ <path
+ id="path3856-33"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-33-5"
+ style="overflow:visible">
+ <path
+ id="path3856-38-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-60"
+ style="overflow:visible">
+ <path
+ id="path3856-96"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-59"
+ style="overflow:visible">
+ <path
+ id="path3856-969"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-36"
+ style="overflow:visible">
+ <path
+ id="path3856-02"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-8"
+ style="overflow:visible">
+ <path
+ id="path3856-80"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-08"
+ style="overflow:visible">
+ <path
+ id="path3856-28"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-81"
+ style="overflow:visible">
+ <path
+ id="path3856-89"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-57"
+ style="overflow:visible">
+ <path
+ id="path3856-66"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-22"
+ style="overflow:visible">
+ <path
+ id="path3856-31"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-42"
+ style="overflow:visible">
+ <path
+ id="path3856-50"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-84"
+ style="overflow:visible">
+ <path
+ id="path3856-06"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-04"
+ style="overflow:visible">
+ <path
+ id="path3856-62"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-32"
+ style="overflow:visible">
+ <path
+ id="path3856-866"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-66"
+ style="overflow:visible">
+ <path
+ id="path3856-78"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-72"
+ style="overflow:visible">
+ <path
+ id="path3856-19"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-49"
+ style="overflow:visible">
+ <path
+ id="path3856-34"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-96"
+ style="overflow:visible">
+ <path
+ id="path3856-61"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-23"
+ style="overflow:visible">
+ <path
+ id="path3856-18"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-10"
+ style="overflow:visible">
+ <path
+ id="path3856-87"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-63"
+ style="overflow:visible">
+ <path
+ id="path3856-317"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-01"
+ style="overflow:visible">
+ <path
+ id="path3856-10"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-16"
+ style="overflow:visible">
+ <path
+ id="path3856-55"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-86"
+ style="overflow:visible">
+ <path
+ id="path3856-51"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-62"
+ style="overflow:visible">
+ <path
+ id="path3856-91"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-62-7"
+ style="overflow:visible">
+ <path
+ id="path3856-91-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-06"
+ style="overflow:visible">
+ <path
+ id="path3856-26"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-79"
+ style="overflow:visible">
+ <path
+ id="path3856-20"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-37"
+ style="overflow:visible">
+ <path
+ id="path3856-59"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-12"
+ style="overflow:visible">
+ <path
+ id="path3856-93"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-45"
+ style="overflow:visible">
+ <path
+ id="path3856-03"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-79-1"
+ style="overflow:visible">
+ <path
+ id="path3856-20-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320"
+ style="overflow:visible">
+ <path
+ id="path3856-615"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320-4"
+ style="overflow:visible">
+ <path
+ id="path3856-615-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320-5"
+ style="overflow:visible">
+ <path
+ id="path3856-615-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374"
+ style="overflow:visible">
+ <path
+ id="path3856-525"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374-7"
+ style="overflow:visible">
+ <path
+ id="path3856-525-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30"
+ style="overflow:visible">
+ <path
+ id="path3856-786"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1-2"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1-2-8"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4-0-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374-3"
+ style="overflow:visible">
+ <path
+ id="path3856-525-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367"
+ style="overflow:visible">
+ <path
+ id="path3856-53"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5228"
+ style="overflow:visible">
+ <path
+ id="path5226"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-606"
+ style="overflow:visible">
+ <path
+ id="path3856-261"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5228-8"
+ style="overflow:visible">
+ <path
+ id="path5226-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-62-6"
+ style="overflow:visible">
+ <path
+ id="path3856-91-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-6"
+ style="overflow:visible">
+ <path
+ id="path3856-53-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-597"
+ style="overflow:visible">
+ <path
+ id="path3856-76"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-6-3"
+ style="overflow:visible">
+ <path
+ id="path3856-53-1-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker12699-6"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path12697-3"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-79-1-9"
+ style="overflow:visible">
+ <path
+ id="path3856-20-0-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-37-1"
+ style="overflow:visible">
+ <path
+ id="path3856-59-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-45-0"
+ style="overflow:visible">
+ <path
+ id="path3856-03-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-0"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-8"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-7"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-09"
+ style="overflow:visible">
+ <path
+ id="path3856-25"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-09-2"
+ style="overflow:visible">
+ <path
+ id="path3856-25-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-4"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-0-4"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-4-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-8-4"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-8-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-8-2"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-8-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-7-0"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-7-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-31"
+ style="overflow:visible">
+ <path
+ id="path3856-103"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-0"
+ style="overflow:visible">
+ <path
+ id="path3856-53-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-31-1"
+ style="overflow:visible">
+ <path
+ id="path3856-103-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-0-3"
+ style="overflow:visible">
+ <path
+ id="path3856-53-3-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-0-3-6"
+ style="overflow:visible">
+ <path
+ id="path3856-53-3-8-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-31-1-0"
+ style="overflow:visible">
+ <path
+ id="path3856-103-9-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-45-0-6"
+ style="overflow:visible">
+ <path
+ id="path3856-03-8-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-37-1-8"
+ style="overflow:visible">
+ <path
+ id="path3856-59-2-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-6-3-2"
+ style="overflow:visible">
+ <path
+ id="path3856-53-1-6-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-597-9"
+ style="overflow:visible">
+ <path
+ id="path3856-76-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-6-0"
+ style="overflow:visible">
+ <path
+ id="path3856-53-1-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker12699-3"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path12697-2"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#009d00;fill-opacity:1;fill-rule:evenodd;stroke:#009d00;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-79-1-1"
+ style="overflow:visible">
+ <path
+ id="path3856-20-0-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker7637-9"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow1Mstart"
+ inkscape:collect="always">
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ id="path7635-9" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-45-1"
+ style="overflow:visible">
+ <path
+ id="path3856-03-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-37-9"
+ style="overflow:visible">
+ <path
+ id="path3856-59-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-79-0"
+ style="overflow:visible">
+ <path
+ id="path3856-20-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-62-7-5"
+ style="overflow:visible">
+ <path
+ id="path3856-91-0-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-62-70"
+ style="overflow:visible">
+ <path
+ id="path3856-91-4"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1694"
+ id="linearGradient1364-8"
+ x1="415.74805"
+ y1="495.00006"
+ x2="415.74805"
+ y2="215.43314"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(904.95044,-71.333651)" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-14"
+ style="overflow:visible">
+ <path
+ id="path3856-69"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-9"
+ style="overflow:visible">
+ <path
+ id="path3856-53-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker42788"
+ style="overflow:visible">
+ <path
+ id="path42786"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88"
+ style="overflow:visible">
+ <path
+ id="path3856-868"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker42794"
+ style="overflow:visible">
+ <path
+ id="path42792"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker42798"
+ style="overflow:visible">
+ <path
+ id="path42796"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker42802"
+ style="overflow:visible">
+ <path
+ id="path42800"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-5"
+ style="overflow:visible">
+ <path
+ id="path3856-868-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-9-8"
+ style="overflow:visible">
+ <path
+ id="path3856-53-2-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-1"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-0"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-7"
+ style="overflow:visible">
+ <path
+ id="path3856-868-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker61712-0"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path61710-6"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#7777ff;fill-opacity:1;fill-rule:evenodd;stroke:#7777ff;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-0-4"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-5-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-0-4-8"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-5-7-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-7-6"
+ style="overflow:visible">
+ <path
+ id="path3856-868-7-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-0-3-6-6"
+ style="overflow:visible">
+ <path
+ id="path3856-53-3-8-4-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-31-1-0-9"
+ style="overflow:visible">
+ <path
+ id="path3856-103-9-0-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5-0"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5-6"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5-7"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5-2"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-5-5"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-86-92"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-31-1-0-9-9"
+ style="overflow:visible">
+ <path
+ id="path3856-103-9-0-1-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-02"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-51"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-10"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-367-3-50"
+ style="overflow:visible">
+ <path
+ id="path3856-53-8-64"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-0-4-9"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-5-7-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker14034-2-9"
+ style="overflow:visible"
+ inkscape:isstock="true"
+ inkscape:collect="always">
+ <path
+ inkscape:connector-curvature="0"
+ id="path14032-9-7"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-597-9-5"
+ style="overflow:visible">
+ <path
+ id="path3856-76-6-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-3"
+ style="overflow:visible">
+ <path
+ id="path3856-868-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-3-9"
+ style="overflow:visible">
+ <path
+ id="path3856-868-1-6"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-3-9-3"
+ style="overflow:visible">
+ <path
+ id="path3856-868-1-6-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-3-9-3-4"
+ style="overflow:visible">
+ <path
+ id="path3856-868-1-6-3-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-4"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-1"
+ style="overflow:visible">
+ <path
+ id="path3856-868-78"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-7"
+ style="overflow:visible">
+ <path
+ id="path3856-786-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-15"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1-8"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4-07"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1-2-1"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4-0-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-30-8-1-2-8-1"
+ style="overflow:visible">
+ <path
+ id="path3856-786-4-4-0-9-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374-3-3"
+ style="overflow:visible">
+ <path
+ id="path3856-525-6-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374-7-4"
+ style="overflow:visible">
+ <path
+ id="path3856-525-4-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-374-2"
+ style="overflow:visible">
+ <path
+ id="path3856-525-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320-5-6"
+ style="overflow:visible">
+ <path
+ id="path3856-615-6-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320-4-9"
+ style="overflow:visible">
+ <path
+ id="path3856-615-7-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-320-7"
+ style="overflow:visible">
+ <path
+ id="path3856-615-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-12-2"
+ style="overflow:visible">
+ <path
+ id="path3856-93-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <linearGradient
+ gradientTransform="translate(929.54564,-74.94436)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient7283"
+ id="linearGradient7285"
+ x1="548.74939"
+ y1="603.33936"
+ x2="548.74939"
+ y2="671.01959"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient7283">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop7279" />
+ <stop
+ style="stop-color:#ff0000;stop-opacity:0;"
+ offset="1"
+ id="stop7281" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4336"
+ id="radialGradient7151"
+ cx="359.77701"
+ cy="645.03406"
+ fx="359.77701"
+ fy="645.03406"
+ r="88.283775"
+ gradientTransform="matrix(1,0,0,0.45837941,929.54564,270.2418)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4336"
+ id="radialGradient5882"
+ cx="-305.73798"
+ cy="215.58585"
+ fx="-305.73798"
+ fy="215.58585"
+ r="82.355362"
+ gradientTransform="matrix(0.91925378,0.02375671,-0.01750658,0.91984172,-20.913072,24.544353)"
+ gradientUnits="userSpaceOnUse" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-3"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-5"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-7"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-0"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-7"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-07"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-2"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-2"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-6"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-76"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-9-4-4-35"
+ style="overflow:visible">
+ <path
+ id="path3856-868-6-1-0-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18929"
+ id="radialGradient18931"
+ cx="1287.2483"
+ cy="838.64679"
+ fx="1287.2483"
+ fy="838.64679"
+ r="69.243234"
+ gradientTransform="matrix(1.6991232,0.01607634,-0.00691966,0.73134605,-894.14208,204.61155)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18929"
+ id="radialGradient18931-2"
+ cx="1287.2483"
+ cy="838.64679"
+ fx="1287.2483"
+ fy="838.64679"
+ r="69.243233"
+ gradientTransform="matrix(1.6991232,0.01607634,-0.00691966,0.73134605,-715.73169,39.063279)"
+ gradientUnits="userSpaceOnUse" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-2"
+ style="overflow:visible">
+ <path
+ id="path3856-868-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4)"
+ d="m 1109.1006,543.61207 c -3.0273,101.10987 20.2381,108.5803 120.8038,109.2893"
+ id="path5213-6-8-4-9-9-1-3-0-0-33"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-7-2"
+ style="overflow:visible">
+ <path
+ id="path3856-868-7-8"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-7-2-1"
+ style="overflow:visible">
+ <path
+ id="path3856-868-7-8-9"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-7-2-6"
+ style="overflow:visible">
+ <path
+ id="path3856-868-7-8-1"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.2050864"
+ inkscape:cx="341.22418"
+ inkscape:cy="504.11322"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ gridtolerance="1"
+ inkscape:window-width="2453"
+ inkscape:window-height="1415"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ fit-margin-top="0.5"
+ fit-margin-left="0.5"
+ fit-margin-right="0.5"
+ fit-margin-bottom="0.5"
+ units="cm"
+ inkscape:snap-global="false"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3065"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="cm"
+ spacingx="37.795276"
+ spacingy="37.795276"
+ originx="-1038.3034"
+ originy="-33.268489" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-1038.3034,-81.024808)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1065.9954"
+ y="133.45045"
+ id="text3021-8-4-1-8-8-2-3"><tspan
+ sodipodi:role="line"
+ x="1065.9954"
+ y="133.45045"
+ style="font-size:29.33333397px;line-height:1.25;stroke-width:1.06666672"
+ id="tspan56267">Ramps:</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5159-4"
+ width="416.73956"
+ height="363.7735"
+ x="1058.2677"
+ y="101.00932" />
+ <g
+ id="g14689"
+ transform="translate(-36.45207,-51.863501)">
+ <text
+ id="text3021-8-4-1-8-8-7-4"
+ y="699.71509"
+ x="1639.6669"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:18.66666603px;line-height:1;text-align:center;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke-width:1.06666672"
+ y="699.71509"
+ x="1639.6669"
+ id="tspan3023-4-0-1-9-63-3-3"
+ sodipodi:role="line">Thermal</tspan><tspan
+ id="tspan4199-0"
+ style="font-size:18.66666603px;line-height:1;text-align:center;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke-width:1.06666672"
+ y="718.38177"
+ x="1639.6669"
+ sodipodi:role="line">Cfg</tspan></text>
+ <ellipse
+ ry="31.258007"
+ rx="50.681404"
+ cy="699.95654"
+ cx="1638.769"
+ id="path3639-4-4-6-3"
+ style="fill:none;stroke:#ff0000;stroke-width:2.39758635;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333344, 2.13333344;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-09)"
+ d="m 1526.1143,542.42127 c 38.1021,-33.86186 102.6,-14.46467 75.3226,68.88748"
+ id="path5213-6-8-4-9-9-8-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g14683"
+ transform="translate(-52.278414,208.69872)">
+ <text
+ inkscape:transform-center-y="10.020862"
+ inkscape:transform-center-x="-5.2422885"
+ id="text3021-8-4-1-8-8-2-8-5"
+ y="511.39166"
+ x="1639.1299"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:85.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:18.66666603px;line-height:85.00000238%;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ y="511.39166"
+ x="1639.1299"
+ id="tspan3023-4-0-1-9-63-8-83-9"
+ sodipodi:role="line">Beacon</tspan><tspan
+ style="font-size:18.66666603px;line-height:85.00000238%;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ y="527.2583"
+ x="1639.1299"
+ sodipodi:role="line"
+ id="tspan14667">Cfg</tspan></text>
+ <ellipse
+ inkscape:transform-center-y="8.5053065"
+ inkscape:transform-center-x="-3.1895436"
+ ry="21.797352"
+ rx="50.681404"
+ cy="512.03644"
+ cx="1639.2438"
+ id="path3639-4-4-9-1-4"
+ style="fill:none;stroke:#000000;stroke-width:2.39758635;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:2.13333344;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-2)"
+ d="m 1336.1309,751.93617 c 54.5084,-15.91562 5.2824,-32.40651 63.036,-62.65111"
+ id="path5213-6-8-4-9-9-8-3-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-09-2)"
+ d="m 1356.8036,913.47851 c 110.056,-5.75796 -71.5174,-184.74962 172.4057,-190.29891"
+ id="path5213-6-8-4-9-9-8-4-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient1364-8);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect1356-0"
+ width="37.795277"
+ height="264.56693"
+ x="1320.6984"
+ y="144.09947" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="-312.03278"
+ y="1346.9868"
+ id="text3021-8-4-1-8-8-3-4"
+ transform="rotate(-90)"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-63-6-2"
+ x="-312.03278"
+ y="1346.9868"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">Ramp</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1268.1858"
+ y="203.77095"
+ id="text3021-8-4-1-8-6-14-2-4-7-7-9"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1268.1858"
+ y="203.77095"
+ id="tspan3613-9-7-5-6"
+ style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Ceil</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1263.0579"
+ y="426.17856"
+ id="text3021-8-4-1-8-6-14-2-4-7-7-3-1"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1263.0579"
+ y="426.17856"
+ id="tspan3613-9-7-5-5-0"
+ style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Floor</tspan></text>
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-70)"
+ d="M 1419.75,188.60753 V 372.1669"
+ id="path5213-6-8-2-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-7-5)"
+ d="M 1400.1843,366.71374 V 191.75683"
+ id="path5213-6-8-2-9-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#009d00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="-239.43126"
+ y="1515.5392"
+ id="text3021-8-4-1-8-6-14-2-4-7-7-3-3-2"
+ transform="matrix(0,-1.0975814,0.91109416,0,0,0)"><tspan
+ sodipodi:role="line"
+ x="-239.43126"
+ y="1515.5392"
+ style="font-size:17.06666756px;line-height:1.25;fill:#009d00;fill-opacity:1;stroke-width:1.06666672"
+ id="tspan2698-2">Turbo</tspan></text>
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-79-0)"
+ d="m 1159.4045,270.23739 c 108.4581,4.22547 23.1574,-88.17063 154.3238,-89.5992"
+ id="path5213-6-8-4-9-9-8-2-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-37-9)"
+ d="m 1161.2023,305.96846 c 19.9433,0.68416 37.9411,1.8706 43.0277,22.9023 23.0349,95.24415 139.8266,163.92069 134.3257,47.65282"
+ id="path5213-6-8-4-9-9-9-2-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#marker7637-9);marker-end:url(#Arrow1Mend-45-1)"
+ d="m 1314.3007,333.29999 c -90.8252,5.74785 -43.5378,-60.02663 -137.7344,-45.56247"
+ id="path5213-6-8-4-9-9-6-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 1320.7414,332.0074 h 37.7525"
+ id="path15687-90"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1109.0515"
+ y="-593.28467"
+ id="text16320-2"
+ transform="rotate(42.138927)"><tspan
+ sodipodi:role="line"
+ id="tspan16318-8"
+ x="1109.0515"
+ y="-593.28467">Mem</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-399.5589"
+ y="1453.8257"
+ id="text16326-8"
+ transform="rotate(-90)"><tspan
+ sodipodi:role="line"
+ id="tspan16324-0"
+ x="-399.5589"
+ y="1453.8257">Regulated Hybrid -------------- Direct Drive</tspan></text>
+ <g
+ id="g16453-4"
+ transform="translate(988.22664,-147.99262)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path16424-0"
+ d="M 453.54332,291.02364 V 555.59057"
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path16426-9"
+ d="M 467.5047,555.59057 H 453.54332"
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path16426-5-1"
+ d="M 467.5047,480.00002 H 453.54332"
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path16426-3-9"
+ d="M 467.5047,291.02364 H 453.54332"
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <path
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 1319.7047,180.764 h 37.7525"
+ id="path15687-2-6"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0)"
+ d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 133.489,-146.16407 131.2384,-34.5044"
+ id="path5213-6-8-4-9-9-9-29-5-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ <path
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 1319.7047,381.98134 h 37.7525"
+ id="path15687-9-5"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-14)"
+ d="m 1361.0391,358.39365 c 26.3813,0.76852 22.4829,57.6582 -8.9055,81.08668 -31.3884,23.42848 -96.7886,5.87049 -137.724,-26.36711"
+ id="path5213-6-8-4-9-9-1-42"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ <g
+ id="g47455"
+ transform="translate(-394.62442,281.246)">
+ <text
+ inkscape:transform-center-y="10.020862"
+ inkscape:transform-center-x="-5.2422885"
+ id="text3021-8-4-1-8-8-2-8-5-64"
+ y="119.67136"
+ x="1502.3363"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672"
+ y="119.67136"
+ x="1502.3363"
+ id="tspan3023-4-0-1-9-63-8-83-9-1"
+ sodipodi:role="line">Ramp</tspan></text>
+ <ellipse
+ inkscape:transform-center-y="11.351831"
+ inkscape:transform-center-x="-4.2569368"
+ ry="29.092394"
+ rx="67.643234"
+ cy="122.72764"
+ cx="1535.9958"
+ id="path3639-4-4-9-1-4-2"
+ style="fill:none;stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <text
+ inkscape:transform-center-y="10.020862"
+ inkscape:transform-center-x="-5.2422885"
+ id="text3021-8-4-1-8-8-2-8-5-6-8"
+ y="142.59866"
+ x="1515.2289"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672"
+ y="142.59866"
+ x="1515.2289"
+ id="tspan3023-4-0-1-9-63-8-83-9-9-8"
+ sodipodi:role="line">Cfg</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88)"
+ d="m 1499.6915,166.15809 c 49.5295,21.01119 54.6565,-32.82142 113.6548,0"
+ id="path5213-6-8-4-9-9-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88)"
+ d="m 1498.5459,209.88219 c 49.5295,21.01119 54.6565,-32.82141 113.6548,0"
+ id="path5213-6-8-4-9-9-9-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:2.13333344;stroke-opacity:1;marker-end:url(#Arrow1Mend-88)"
+ d="m 1499.0847,386.36671 c 49.5295,21.01119 54.6565,-32.82142 113.6548,0"
+ id="path5213-6-8-4-9-9-8-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:5.11919975;stroke-opacity:1;marker-end:url(#Arrow1Mend-88)"
+ d="m 1499.0847,431.64591 c 49.5295,21.01119 54.6565,-32.82142 113.6548,0"
+ id="path5213-6-8-4-9-9-1-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1508.8501"
+ y="125.85442"
+ id="text3021-8-4-1-8-6-1-5-3"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-6-4-1-8"
+ x="1508.8501"
+ y="125.85442"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">Actions</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1368.1968"
+ y="205.90257"
+ id="text3021-8-4-1-8-6-14-2-4-0"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1368.1968"
+ y="205.90257"
+ id="tspan3613-9-4"
+ style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">1 Fast Click</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1400.5447"
+ y="254.90924"
+ id="text3021-8-4-1-8-6-14-2-4-6-7"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1400.5447"
+ y="254.90924"
+ id="tspan3613-9-8-6"
+ style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Hold</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1364.4215"
+ y="446.51736"
+ id="text3021-8-4-1-8-6-14-2-4-7-8"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1364.4215"
+ y="446.51736"
+ id="tspan3613-9-7-9"
+ style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">3 Fast Clicks</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#7777ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1363.7708"
+ y="497.13034"
+ id="text3021-8-4-1-8-6-14-2-4-1-0"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1363.7708"
+ y="497.13034"
+ id="tspan3613-9-1-6"
+ style="font-size:17.06666756px;line-height:1.25;fill:#7777ff;fill-opacity:1;stroke-width:1.06666672">Other Action</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5159-8"
+ width="129.83826"
+ height="363.63089"
+ x="1491.3889"
+ y="100.98911" />
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-9)"
+ d="m 1499.2534,277.65781 c 49.5295,21.01119 54.6565,-32.82142 113.6548,0"
+ id="path5213-6-8-4-9-9-56-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.80000067, 2.13333344;stroke-dashoffset:2.98666692;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-9)"
+ d="m 1498.1078,321.38191 c 49.5295,21.01119 54.6565,-32.82141 113.6548,0"
+ id="path5213-6-8-4-9-9-9-29-90"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#009d00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1365.7407"
+ y="328.28253"
+ id="text3021-8-4-1-8-6-14-2-4-12-3"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1365.7407"
+ y="328.28253"
+ id="tspan3613-9-70-3"
+ style="font-size:17.06666756px;line-height:1.25;fill:#009d00;fill-opacity:1;stroke-width:1.06666672">2 Fast Clicks</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#009d00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1373.6346"
+ y="377.28925"
+ id="text3021-8-4-1-8-6-14-2-4-6-9-3"
+ transform="scale(1.0975814,0.91109416)"><tspan
+ sodipodi:role="line"
+ x="1373.6346"
+ y="377.28925"
+ id="tspan3613-9-8-3-7"
+ style="font-size:17.06666756px;line-height:1.25;fill:#009d00;fill-opacity:1;stroke-width:1.06666672">Click, Hold</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:58.66666794px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="-608.83911"
+ y="1684.3434"
+ id="text5250-2"
+ transform="rotate(-90)"><tspan
+ y="1684.3434"
+ x="-608.83911"
+ sodipodi:role="line"
+ id="tspan6532-6">RampingIOS V3</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666667, 4.26666667;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-5)"
+ d="m 1138.7763,513.59537 c 2.4,42.76971 21.5485,46.01827 55.7548,50.73993"
+ id="path5213-6-8-4-9-9-8-3-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9)"
+ d="m 874.51802,524.00086 c -12.1825,60.27066 -20.8223,402.16388 133.00958,463.61058"
+ id="path5213-6-8-4-9-9-1-3-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4)"
+ d="m 1108.922,569.09989 c -0.8727,158.93719 0.4237,191.93185 105.6048,192.31379"
+ id="path5213-6-8-4-9-9-1-3-0-0-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-35-3"
+ inkscape:transform-center-x="-2.6057619"
+ inkscape:transform-center-y="-24.409989"
+ transform="translate(2.0536898,-2.6404583)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0-3-68"
+ id="textPath18895"><tspan
+ id="tspan7287-62-7"> 7 Clicks</tspan></textPath></text>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4)"
+ d="m 1109.1006,543.61207 c -3.0273,101.10987 20.2381,108.5803 120.8038,109.2893"
+ id="path5213-6-8-4-9-9-1-3-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ transform="translate(980.51459,-148.00986)"
+ id="g23629-5-4">
+ <ellipse
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.26666689;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-2-0-8"
+ cx="140.59442"
+ cy="435.21741"
+ rx="48.48732"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="112.51038"
+ y="444.62402"
+ id="text3021-8-9-7"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-6-0"
+ x="112.51038"
+ y="444.62402"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">OFF</tspan></text>
+ </g>
+ <g
+ transform="translate(969.27179,76.164687)"
+ id="g23629-5-4-8">
+ <ellipse
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.26666689;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-2-0-8-6"
+ cx="140.59442"
+ cy="435.21741"
+ rx="48.48732"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="112.51038"
+ y="444.62402"
+ id="text3021-8-9-7-2"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-6-0-4"
+ x="112.51038"
+ y="444.62402"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">OFF</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-9-8)"
+ d="m 1364.0175,147.54301 c 29.3791,49.772 28.7391,140.19451 -0.7576,180.57634"
+ id="path5213-6-8-4-9-9-56-7-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.133, 2.133;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0)"
+ d="m 905.55573,555.02274 c 8.2381,55.86142 12.4246,16.66987 28.876,-16.227 18.1418,-36.27675 65.1509,-72.19798 76.57317,0.99224"
+ id="path5213-6-8-4-9-9-1-3-0-0-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-7)"
+ d="m 983.17997,330.24226 c -47.6705,-9.8516 -110.3689,-9.28576 -158.1487,2.96046"
+ id="path5213-6-8-4-9-9-3-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:none;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.133, 2.133;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0-4)"
+ d="m 881.66303,554.67556 c 0,0 25.6454,8.80842 22.0934,-6.76915"
+ id="path5213-6-8-4-9-9-1-3-0-0-6-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:none;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.133, 2.133;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0-4-8)"
+ d="m 1366.1809,151.11654 c 0,0 8.8213,16.58792 -2.1634,-3.57353"
+ id="path5213-6-8-4-9-9-1-3-0-0-6-4-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2"
+ inkscape:transform-center-x="1.1517845"
+ inkscape:transform-center-y="-2.5788507"
+ transform="translate(11.068361,12.736197)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0-6"
+ id="textPath20030"><tspan
+ id="tspan7287-5-5-6"> 4 Clicks</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0"
+ inkscape:transform-center-x="0.36232032"
+ inkscape:transform-center-y="-2.4072198"
+ transform="translate(3.9941843,-3.1212455)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0"
+ id="textPath20018"><tspan
+ id="tspan7287-5-5-6-6"> 4 Clicks</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0-6"
+ inkscape:transform-center-x="-1.5564693"
+ inkscape:transform-center-y="-1.9746618"
+ transform="translate(4.5029255)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0"
+ id="textPath20024"><tspan
+ id="tspan7287-5-5-6-6-4"> Click, Click, Hold</tspan></textPath></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-7-6)"
+ d="m 805.17382,500.33641 c 0.4912,-26.15636 0.9882,-169.24427 7.3904,-226.98309"
+ id="path5213-6-8-4-9-9-3-4-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0-9"
+ inkscape:transform-center-x="0.36232032"
+ inkscape:transform-center-y="-2.4072198"
+ x="4.0937777"
+ y="-2.4259422"
+ transform="translate(3.7169557,-2.2115172)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0-3"
+ id="textPath20021"><tspan
+ id="tspan7287-5-5-6-6-1"> 6 Clicks</tspan></textPath></text>
+ <path
+ style="fill:none;stroke:none;stroke-width:1.32938921;stroke-miterlimit:4;stroke-dasharray:1.32938933, 1.32938933;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0-4-9)"
+ d="m 1400.1241,365.79536 c 0,0 0.043,-10.40514 -0.024,5.00754"
+ id="path5213-6-8-4-9-9-1-3-0-0-6-4-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:4.05333376;stroke-opacity:1;marker-start:url(#marker14034-2-9);marker-end:url(#Arrow1Mend-597-9-5)"
+ d="m 1159.7225,198.62656 c 67.9862,-0.44112 68.743,-36.42258 0.1793,-35.70399"
+ id="path5213-6-8-4-9-9-8-7-3-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g18567"
+ transform="translate(13.62988,7.3367765)">
+ <text
+ id="text56273"
+ y="160.56241"
+ x="1067.6877"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ id="tspan56275"
+ y="160.56241"
+ x="1067.6877"
+ sodipodi:role="line">Smooth </tspan></text>
+ <ellipse
+ ry="14.669211"
+ rx="39.313492"
+ cy="154.67567"
+ cx="1101.8171"
+ id="path11546"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#010109;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <g
+ id="g8460">
+ <text
+ id="text3021-8-4-1-8-8-2-3-0"
+ y="787.48492"
+ x="1434.2471"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="787.48492"
+ x="1434.2471"
+ id="tspan3023-4-0-1-9-63-8-8-2"
+ sodipodi:role="line">Ramp Cfg</tspan><tspan
+ id="tspan39566"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="802.48492"
+ x="1434.2471"
+ sodipodi:role="line" /><tspan
+ id="tspan39568"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="802.48492"
+ x="1434.2471"
+ sodipodi:role="line" /><tspan
+ id="tspan39570"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="802.48492"
+ x="1434.2471"
+ sodipodi:role="line" /><tspan
+ id="tspan39590"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="802.48492"
+ x="1434.2471"
+ sodipodi:role="line" /></text>
+ <g
+ id="g16522-3"
+ transform="translate(9.0949204,-123.27684)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#7777ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1810.7634"
+ y="915.72546"
+ id="text7289-7-6-21-8-8"
+ transform="rotate(-0.20350505)"
+ inkscape:transform-center-x="1.8963668"
+ inkscape:transform-center-y="-2.0513889"><tspan
+ sodipodi:role="line"
+ id="tspan7287-5-5-7-5-6"
+ x="1810.7634"
+ y="915.72546"> 4 Clicks</tspan></text>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:5.11919975;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-3-9-3)"
+ d="m 1814.2439,904.32938 c -2.5427,0.005 -34.933,0.0197 -38.794,0"
+ id="path5213-6-8-4-9-9-1-3-8-4-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ <g
+ id="g16522-3-8"
+ transform="translate(9.0949217,89.635331)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#7777ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1810.7634"
+ y="915.72546"
+ id="text7289-7-6-21-8-8-8"
+ transform="rotate(-0.20350505)"
+ inkscape:transform-center-x="1.8963668"
+ inkscape:transform-center-y="-2.0513889"><tspan
+ sodipodi:role="line"
+ id="tspan7287-5-5-7-5-6-9"
+ x="1810.7634"
+ y="915.72546"> 4 Clicks</tspan></text>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:5.11919975;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-3-9-3-4)"
+ d="m 1814.2439,904.32938 c -2.5427,0.005 -34.933,0.0197 -38.794,0"
+ id="path5213-6-8-4-9-9-1-3-8-4-0-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ <rect
+ y="758.40918"
+ x="1424.8558"
+ height="310.87778"
+ width="275.93164"
+ id="rect5159-4-9"
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <text
+ id="text39556"
+ y="806.21515"
+ x="1444.4535"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ id="tspan39558"
+ y="806.21515"
+ x="1444.4535"
+ sodipodi:role="line">1. Floor</tspan><tspan
+ id="tspan58144"
+ y="822.34851"
+ x="1444.4535"
+ sodipodi:role="line"> (click N times for level N)</tspan><tspan
+ id="tspan39562"
+ y="838.48181"
+ x="1444.4535"
+ sodipodi:role="line">2. Ceiling</tspan><tspan
+ id="tspan58146"
+ y="854.61517"
+ x="1444.4535"
+ sodipodi:role="line"> (click N times for 1 + Turbo - N)</tspan><tspan
+ id="tspan39606"
+ y="870.74847"
+ x="1444.4535"
+ sodipodi:role="line">3. Number of steps</tspan><tspan
+ id="tspan43997"
+ y="886.88184"
+ x="1444.4535"
+ sodipodi:role="line"> (stepped ramp only)</tspan></text>
+ <text
+ id="text39623"
+ y="929.23431"
+ x="1444.4535"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="929.23431"
+ x="1444.4535"
+ id="tspan39621"
+ sodipodi:role="line">1. Current temperature</tspan><tspan
+ id="tspan14663"
+ y="945.36768"
+ x="1444.4535"
+ sodipodi:role="line"> (click N times for N deg C)</tspan><tspan
+ id="tspan14661"
+ y="961.50098"
+ x="1444.4535"
+ sodipodi:role="line">2. Temperature limit</tspan><tspan
+ id="tspan39625"
+ y="977.63434"
+ x="1444.4535"
+ sodipodi:role="line"> (click N times for 30 C + N)</tspan></text>
+ <text
+ id="text39629"
+ y="1019.1274"
+ x="1444.4535"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="1019.1274"
+ x="1444.4535"
+ id="tspan39627"
+ sodipodi:role="line">1. Beacon speed</tspan><tspan
+ id="tspan39631"
+ y="1035.2607"
+ x="1444.4535"
+ sodipodi:role="line"> (click N times</tspan><tspan
+ id="tspan43995"
+ y="1051.394"
+ x="1444.4535"
+ sodipodi:role="line"> for N seconds per flash)</tspan></text>
+ <text
+ id="text3021-8-4-1-8-8-2-3-0-5"
+ y="910.76184"
+ x="1436.6729"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan39596-4"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="910.76184"
+ x="1436.6729"
+ sodipodi:role="line">Thermal Cfg</tspan><tspan
+ id="tspan39590-9"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="925.76184"
+ x="1436.6729"
+ sodipodi:role="line" /></text>
+ <text
+ id="text3021-8-4-1-8-8-2-3-0-3"
+ y="1000.3971"
+ x="1434.2471"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan39590-4"
+ style="font-size:24px;line-height:1.25;stroke-width:1.06666672"
+ y="1000.3971"
+ x="1434.2471"
+ sodipodi:role="line">Beacon Cfg</tspan></text>
+ <g
+ transform="translate(9.094912)"
+ id="g16522">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#7777ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1810.7634"
+ y="915.72546"
+ id="text7289-7-6-21-8"
+ transform="rotate(-0.20350505)"
+ inkscape:transform-center-x="1.8963668"
+ inkscape:transform-center-y="-2.0513889"><tspan
+ sodipodi:role="line"
+ id="tspan7287-5-5-7-5"
+ x="1810.7634"
+ y="915.72546"> 4 Clicks</tspan></text>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:5.11919975;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-3-9)"
+ d="m 1814.2439,904.32938 c -2.5427,0.005 -34.933,0.0197 -38.794,0"
+ id="path5213-6-8-4-9-9-1-3-8-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
+ <g
+ id="g18572"
+ transform="translate(-76.57207,43.516866)">
+ <rect
+ ry="3"
+ rx="3"
+ y="142.29279"
+ x="1152.8596"
+ height="25.367319"
+ width="78.318939"
+ id="rect11665"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#010109;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <text
+ id="text56273-7"
+ y="160.56241"
+ x="1154.8745"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ id="tspan56275-6"
+ y="160.56241"
+ x="1154.8745"
+ sodipodi:role="line">Stepped</tspan></text>
+ </g>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-4)"
+ d="m 1117.6932,712.70686 c -0.3699,-0.68086 -0.7041,-2.21056 -1.9908,-6.7818"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:type="star"
+ style="fill:url(#radialGradient5882);fill-opacity:1;stroke:#000000;stroke-width:3.75277472;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4327"
+ sodipodi:sides="7"
+ sodipodi:cx="-309.93027"
+ sodipodi:cy="215.58585"
+ sodipodi:r1="84.671265"
+ sodipodi:r2="50.343449"
+ sodipodi:arg1="0"
+ sodipodi:arg2="0.68976368"
+ inkscape:flatsided="false"
+ inkscape:rounded="0.007"
+ inkscape:randomized="0"
+ d="m -225.259,215.58585 c -0.0935,0.38012 -45.66671,31.68359 -45.83651,32.0363 -0.11206,0.23275 14.1915,34.05419 13.95691,34.16236 -0.35549,0.16391 -53.24395,-15.94928 -53.62558,-15.86212 -0.25184,0.0575 -17.77639,32.3278 -18.00722,32.21184 -0.34979,-0.17574 -20.72741,-51.57201 -21.0335,-51.81604 -0.20198,-0.16104 -36.35829,6.25792 -36.41154,6.00514 -0.0807,-0.38305 27.39728,-48.35997 27.39724,-48.75142 -3e-5,-0.25833 -27.56167,-24.52431 -27.39724,-24.72355 0.24917,-0.30192 54.89127,-8.73189 55.1973,-8.97599 0.20194,-0.16109 1.98946,-36.83923 2.24774,-36.8349 0.39141,0.007 41.05102,37.47148 41.43267,37.55854 0.25185,0.0574 30.04248,-21.41346 30.20013,-21.20882 0.23891,0.3101 -3.70149,55.45806 -3.5316,55.81073 0.11211,0.23273 35.47289,10.13708 35.4112,10.38793 z"
+ transform="matrix(0.92047755,0,0,0.7899176,1570.1356,490.73628)"
+ inkscape:transform-center-x="-3.8589074" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1283.365"
+ y="661.61969"
+ id="text3021-8-4-1-8"><tspan
+ sodipodi:role="line"
+ x="1283.365"
+ y="661.61969"
+ style="font-size:24px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan4155">Tactical</tspan></text>
+ <path
+ style="fill:none;stroke:#009d00;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-12-2)"
+ d="m 1365.5639,545.80243 c 85.2437,-57.56024 -28.9149,97.17858 67.2262,45"
+ id="path5213-6-8-4-9-9-8-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3202-5"
+ d="m 1288.4731,602.92569 c -0.4528,-0.77951 -1.0803,-1.93107 -1.3943,-2.55904 -1.0737,-2.14685 -4.3132,-7.98949 -5.87,-10.58711 -4.3266,-7.21903 -7.6037,-10.33824 -10.9595,-10.43127 -1.1187,-0.0309 -1.3542,0.0431 -2.4509,0.77103 -1.2711,0.84373 -2.1683,1.85544 -3.5537,4.00743 -0.4567,0.70938 -0.958,1.29061 -1.114,1.29161 -0.156,0.001 -0.9033,-0.54296 -1.6607,-1.20878 -7.1051,-6.24594 -14.3753,-9.53314 -19.4236,-8.7823 -3.48,0.51759 -5.0204,1.56173 -6.3938,4.33401 l -0.8858,1.78805 0.011,2.61009 c 0.011,2.85728 0.4129,4.7602 1.6033,7.59226 1.304,3.10248 4.2705,7.39835 7.3473,10.63996 0.8946,0.9425 1.5704,1.71317 1.5019,1.71263 -0.4463,-0.003 -7.9187,-2.69233 -10.6412,-3.82902 -9.6708,-4.03772 -16.4977,-8.35341 -21.7399,-13.74329 -4.3679,-4.49084 -7.1775,-8.79859 -8.874,-13.60588 -1.423,-4.03226 -1.7223,-8.61526 -0.7973,-12.20916 1.2721,-4.94273 4.1587,-9.44906 9.1295,-14.25253 8.9547,-8.65325 21.3332,-14.86929 39.844,-20.00822 2.5963,-0.72078 4.771,-1.26013 4.8326,-1.19854 0.062,0.0616 -0.1839,0.42841 -0.5455,0.81514 -3.4868,3.729 -6.1246,8.13223 -6.7636,11.29025 -0.8015,3.96135 0.9708,8.08749 4.5023,10.48189 3.6869,2.49979 7.1211,3.43584 12.0741,3.29111 2.5161,-0.0735 2.8886,-0.1349 3.9401,-0.64934 2.5532,-1.24918 4.3141,-3.15132 6.0569,-6.54273 1.7409,-3.38781 2.0096,-4.74402 2.7417,-13.83851 0.2853,-3.54386 0.5748,-6.74799 0.6434,-7.12032 0.2923,-1.58661 0.8641,-1.01936 2.6229,2.60221 0.6806,1.40141 1.4217,2.64659 1.6468,2.76707 0.2885,0.15436 1.0531,0.1497 2.5905,-0.0158 1.632,-0.17572 2.9123,-0.17461 5.0851,0.004 2.8669,0.2362 2.91,0.23344 3.39,-0.21752 0.2674,-0.25122 0.8726,-1.35826 1.3449,-2.46008 0.4722,-1.10183 1.099,-2.36999 1.3928,-2.81817 l 0.5342,-0.81485 0.2758,0.52623 c 0.4689,0.89501 0.8326,3.8019 1.4248,11.38899 0.4971,6.36774 1.4643,9.24832 4.3881,13.06838 2.6506,3.46298 4.5846,4.27584 9.7379,4.09266 3.3642,-0.11958 5.0057,-0.46765 7.4657,-1.58306 4.2373,-1.92126 6.8173,-4.97803 7.4071,-8.77563 0.4545,-2.92687 -0.5377,-6.24976 -2.9384,-9.84036 -1.1827,-1.7688 -2.478,-3.40213 -3.8135,-4.80832 -0.5385,-0.5671 -0.7241,-0.88226 -0.5194,-0.88226 0.4592,0 9.5441,2.56459 12.6219,3.56306 22.3994,7.26655 37.858,19.25169 41.1432,31.89844 1.8774,7.22754 -0.9294,15.82998 -7.7794,23.84266 -2.308,2.69978 -3.5406,3.82883 -7.087,6.49171 -6.1933,4.65046 -11.5703,7.47767 -20.5514,10.80591 -2.9406,1.08974 -6.411,2.26689 -6.7199,2.27938 -0.042,0.001 0.5459,-0.65721 1.3072,-1.46427 4.3463,-4.60728 7.4331,-9.51352 8.6471,-13.74407 0.6134,-2.13752 0.7622,-5.66081 0.313,-7.41075 -0.6515,-2.53772 -1.9946,-4.20202 -4.2556,-5.27337 -1.3335,-0.63187 -1.4404,-0.64784 -4.2739,-0.63813 -2.6104,0.01 -3.086,0.0691 -4.6708,0.59188 -3.9756,1.31118 -8.4584,3.98094 -12.5592,7.47975 -1.1418,0.97413 -2.1694,1.76995 -2.2835,1.76848 -0.1142,-10e-4 -0.6268,-0.63027 -1.139,-1.39734 -2.7305,-4.08839 -5.1372,-5.32966 -8.1429,-4.19963 -1.7271,0.64937 -3.2592,2.01781 -5.3119,4.74462 -2.2156,2.9433 -4.0421,5.9743 -8.3381,13.83722 -1.7466,3.19688 -3.2017,5.8431 -3.2335,5.88048 -0.032,0.0373 -0.4282,-0.56984 -0.881,-1.34934 z"
+ style="fill:url(#radialGradient7151);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="csscsscsssccsscsssssscssssssssscccssscsssssssssssssscsssscsscssssccc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1223.3358"
+ y="572.08801"
+ id="text3021-8-4-1-8-1"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-7"
+ x="1223.3358"
+ y="572.08801"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">BattCheck</tspan></text>
+ <ellipse
+ style="fill:#505050;fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-4-4"
+ cx="1288.233"
+ cy="763.54663"
+ rx="67.643234"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#c0c0c0;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1239.5554"
+ y="772.37585"
+ id="text3021-8-4-1-8-8"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-63"
+ x="1239.5554"
+ y="772.37585"
+ style="font-size:25.60000038px;line-height:1.25;fill:#c0c0c0;fill-opacity:1;stroke-width:1.06666672">Lockout</tspan></text>
+ <ellipse
+ style="fill:url(#linearGradient7285);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-4-4-3"
+ cx="1480.8682"
+ cy="565.26556"
+ rx="67.643234"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1418.7936"
+ y="571.9975"
+ id="text3021-8-4-1-8-60"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-2"
+ x="1418.7936"
+ y="571.9975"
+ style="font-size:21.33333397px;line-height:1.25;stroke-width:1.06666672">TempCheck</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1239.9597"
+ y="921.75562"
+ id="text3021-8-4-1-8-8-2"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-63-8"
+ x="1239.9597"
+ y="921.75562"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">Beacon</tspan></text>
+ <ellipse
+ style="fill:none;stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-4-4-9"
+ cx="1288.6375"
+ cy="913.93665"
+ rx="67.643234"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1288.1405"
+ y="996.59717"
+ id="text3021-8-4-1-8-8-7"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-0-1-9-63-3"
+ x="1288.1405"
+ y="996.59717"
+ style="font-size:25.60000038px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke-width:1.06666672">Thermal</tspan><tspan
+ sodipodi:role="line"
+ x="1288.1405"
+ y="1028.5972"
+ style="font-size:25.60000038px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke-width:1.06666672"
+ id="tspan4199">Cfg</tspan></text>
+ <ellipse
+ style="fill:none;stroke:#ff0000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-4-4-6"
+ cx="1288.7599"
+ cy="999.38477"
+ rx="67.643234"
+ ry="41.719299" />
+ <ellipse
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.26666689;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-2-9"
+ cx="658.7066"
+ cy="1047.7236"
+ rx="48.48732"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="630.6225"
+ y="1057.1302"
+ id="text3021-8-47"><tspan
+ sodipodi:role="line"
+ id="tspan3023-4-8"
+ x="630.6225"
+ y="1057.1302"
+ style="font-size:25.60000038px;line-height:1.25;stroke-width:1.06666672">OFF</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-320-7)"
+ d="m 697.35842,924.31834 c 49.5295,21.01119 21.574,57.08215 0.2652,95.96446"
+ id="path5213-6-8-4-9-9-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-320-4-9)"
+ d="m 543.52862,921.79149 c 34.1247,72.78151 53.8989,0.009 84.6129,95.20691"
+ id="path5213-6-8-4-9-9-5-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-320-5-6)"
+ d="m 468.52094,717.28118 c 65.4701,1.8183 82.3057,-31.9173 105.9487,-90.40865"
+ id="path5213-6-8-4-9-9-5-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-374-2)"
+ d="m 472.95584,647.89594 c 24.7807,-1.21217 41.0401,-15.20503 74.2737,-36.42676"
+ id="path5213-6-8-4-9-9-1-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-374-7-4)"
+ d="m 474.39624,552.67716 c 33.8721,8.38428 35.3977,6.33668 77.2175,25.52105"
+ id="path5213-6-8-4-9-9-1-4-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-30-7)"
+ d="m 726.25616,420.9873 c 28.3163,57.37668 52.2535,139.95605 111.2518,172.77747"
+ id="path5213-6-8-4-9-9-1-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-30-8-15)"
+ d="m 713.26226,422.61475 c 15.1842,76.56958 72.9689,230.33933 151.16,268.21152"
+ id="path5213-6-8-4-9-9-1-8-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-30-8-1-8)"
+ d="m 696.03886,421.21509 c 15.6894,96.26755 75.5376,329.69142 157.7694,374.63467"
+ id="path5213-6-8-4-9-9-1-8-3-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-30-8-1-2-1)"
+ d="m 681.57206,417.59823 c 2.5574,139.19904 62.548,390.30058 173.064,451.4063"
+ id="path5213-6-8-4-9-9-1-8-3-9-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-30-8-1-2-8-1)"
+ d="m 669.34276,409.84061 c -23.6324,190.71681 59.0125,466.74032 187.2929,535.84062"
+ id="path5213-6-8-4-9-9-1-8-3-9-6-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1231.9467"
+ y="673.88409"
+ id="text5202"><tspan
+ sodipodi:role="line"
+ id="tspan5200"
+ x="1231.9467"
+ y="673.88409">(momentary)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="920.47632"
+ y="900.1734"
+ id="text7289"
+ transform="rotate(-19.048886)"
+ inkscape:transform-center-x="21.194928"
+ inkscape:transform-center-y="-11.551537"><tspan
+ sodipodi:role="line"
+ id="tspan7287"
+ x="920.47632"
+ y="900.1734">3 Clicks</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="891.51764"
+ y="-353.34348"
+ id="text7289-3"
+ transform="rotate(50.003142)"
+ inkscape:transform-center-x="-3.1244591"
+ inkscape:transform-center-y="-24.312134"><tspan
+ sodipodi:role="line"
+ id="tspan7287-6"
+ x="891.51764"
+ y="-353.34348">4 Clicks</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1201.4386"
+ y="-316.4696"
+ id="text7289-7"
+ transform="rotate(54.064752)"
+ inkscape:transform-center-x="-4.7794388"
+ inkscape:transform-center-y="-23.958945"><tspan
+ sodipodi:role="line"
+ id="tspan7287-5"
+ x="1201.4386"
+ y="-316.4696">6 Clicks</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-35"
+ inkscape:transform-center-x="-2.6057619"
+ inkscape:transform-center-y="-24.409989"
+ transform="translate(2.0536898,-2.6404583)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0-3-67"
+ id="textPath18899"><tspan
+ id="tspan7287-62"> 8 Clicks</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-9"
+ inkscape:transform-center-x="-2.9817013"
+ inkscape:transform-center-y="-24.428981"
+ transform="translate(2.0536898,-2.6404583)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-3-0-0-3-8"
+ startOffset="50%"
+ id="textPath18903"><tspan
+ id="tspan15689"> 10 Clicks</tspan></textPath></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:2.13333333, 8.53333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-374-3-3)"
+ d="m 471.69754,802.31437 c 71.9236,-4.7836 141.3973,-47.3479 138.5594,-175.71244"
+ id="path5213-6-8-4-9-9-1-4-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <ellipse
+ style="fill:url(#radialGradient18931);stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;fill-opacity:1"
+ id="path3639-4-4-9-8"
+ cx="1287.2483"
+ cy="838.64679"
+ rx="67.643234"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1288.3867"
+ y="844.58032"
+ id="text3021-8-4-1-8-8-2-0"><tspan
+ sodipodi:role="line"
+ x="1288.3867"
+ y="844.58032"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530">Aux LED</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1287.882"
+ y="862.0556"
+ id="text3021-8-4-1-8-8-2-0-5"><tspan
+ sodipodi:role="line"
+ x="1287.882"
+ y="862.0556"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530-7">mode</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1284.6385"
+ y="827.58423"
+ id="text3021-8-4-1-8-8-2-0-5-4"><tspan
+ sodipodi:role="line"
+ x="1284.6385"
+ y="827.58423"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530-7-5">next</tspan></text>
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-7)"
+ d="m 874.33081,937.81505 c -0.8727,171.59185 22.2064,184.87845 105.8123,186.09015"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-0)"
+ d="m 873.74391,862.1219 c -0.8727,171.5919 22.2064,184.8784 105.8123,186.0902"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-07)"
+ d="m 874.33071,1023.4833 c -0.8727,171.5918 22.2064,184.8783 105.8123,186.0901"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-2)"
+ d="m 1109.3743,645.79672 c -0.8727,158.93719 -1.63,191.93185 103.5511,192.31379"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-68"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-6)"
+ d="m 1109.8871,722.67091 c -0.8727,158.9372 -1.0432,191.93186 104.1379,192.3138"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-67"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#7777ff;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:2.1329999, 2.1329999;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-4-35)"
+ d="m 1110.6079,808.09274 c -0.8727,158.93724 0.2162,192.96916 105.3973,193.35116"
+ id="path5213-6-8-4-9-9-1-3-0-0-3-8"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <ellipse
+ style="fill:url(#radialGradient18931-2);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3639-4-4-9-8-6"
+ cx="1465.6587"
+ cy="673.09851"
+ rx="67.643234"
+ ry="29.092394" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1466.7971"
+ y="679.03204"
+ id="text3021-8-4-1-8-8-2-0-1"><tspan
+ sodipodi:role="line"
+ x="1466.7971"
+ y="679.03204"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530-5">lockout LED</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1466.2924"
+ y="696.50732"
+ id="text3021-8-4-1-8-8-2-0-5-7"><tspan
+ sodipodi:role="line"
+ x="1466.2924"
+ y="696.50732"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530-7-59">mode</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="1463.049"
+ y="662.03595"
+ id="text3021-8-4-1-8-8-2-0-5-4-4"><tspan
+ sodipodi:role="line"
+ x="1463.049"
+ y="662.03595"
+ style="font-size:21.33333397px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:1.06666672"
+ id="tspan8530-7-5-3">next</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0-5"
+ inkscape:transform-center-x="0.36232032"
+ inkscape:transform-center-y="-2.4072198"
+ transform="translate(2.9338426,2.6404583)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-1-42"
+ id="textPath20567"><tspan
+ id="tspan7287-5-5-6-6-0"> 4 Clicks</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0-5-9"
+ inkscape:transform-center-x="0.36232032"
+ inkscape:transform-center-y="-2.4072198"
+ x="2.6404583"
+ y="-2.6404583"
+ transform="translate(2.3470741,-2.0536898)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-8-4"
+ id="textPath20570"><tspan
+ id="tspan7287-5-5-6-6-0-8"> 4 Clicks</tspan></textPath></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text7289-7-6-2-0-5-7"
+ inkscape:transform-center-x="0.36232032"
+ inkscape:transform-center-y="-2.4072198"
+ x="-1.4669213"
+ y="-3.520611"
+ transform="translate(-0.58676852,-3.2272268)"><textPath
+ xlink:href="#path5213-6-8-4-9-9-8-4-4"
+ id="textPath20573"><tspan
+ id="tspan7287-5-5-6-6-0-80"> 4 Clicks</tspan></textPath></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-7-2)"
+ d="m 1347.6426,532.88115 c 28.3161,-33.90911 -58.4398,-54.76032 -187.1937,-34.00596"
+ id="path5213-6-8-4-9-9-3-4-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 1502.9065,536.58006 c 28.3161,-33.90911 -117.5677,-45.05348 -245.4416,-46.00955"
+ id="path5213-6-8-4-9-9-3-4-6-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-7-2-6)"
+ d="m 1346.2394,930.44842 c 122.4168,138.24568 -22.1872,139.99868 -157.1836,132.73448 -106.969,-5.7562 -130.2218,-49.2021 -109.224,-521.14862"
+ id="path5213-6-8-4-9-9-3-4-6-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csc" />
+ </g>
+</svg>
diff --git a/ui/werner/Makefile b/ui/werner/Makefile
new file mode 100644
index 0000000..21d85f7
--- /dev/null
+++ b/ui/werner/Makefile
@@ -0,0 +1,7 @@
+all:
+ ./build-all.sh
+
+clean:
+ rm -f cfg-*.h *.hex *~ *.elf *.o
+
+.phony: clean
diff --git a/ui/werner/README b/ui/werner/README
new file mode 100644
index 0000000..5fe392d
--- /dev/null
+++ b/ui/werner/README
@@ -0,0 +1,56 @@
+This is a Werner-style interface for dual-switch lights
+(e-switch + clicky switch). What that means is:
+
+While the clicky switch is off:
+
+ - Click the clicky switch: Turn on, at the last-used level. The clicky
+ switch works as a momentary mode.
+
+ - Click the clicky switch while holding the e-switch: Go into sort of a
+ utility mode.
+
+While on, in a normal steady mode:
+
+ - Click the clicky switch: Turn off.
+
+ - Click the e-switch: Brighter. One step per click.
+
+ - Hold the e-switch: Dimmer. Keep holding to go down multiple steps.
+
+While in standby, in utility mode:
+
+ - Click the e-switch: Turn on.
+
+ - Hold the e-switch: Turn on at lowest level.
+
+ - Double-click the e-switch: Turn on at highest level.
+
+ - Triple-click the e-switch: Battery check mode.
+
+ - Quad-click the e-switch: Ramp config mode.
+
+While in battery check mode:
+
+ - Click either switch: Turn off.
+
+ - Double-click the e-switch: Go to temperature check mode.
+
+While in temperature check mode:
+
+ - Click either switch: Turn off.
+
+ - Double-click the e-switch: Go to battery check mode.
+
+ - Quad-click the e-switch: Go to thermal config mode.
+
+Ramp config mode and thermal config mode work the same as in Anduril or
+RampingIOS V3. The options are:
+
+ - Ramp config mode:
+ 1. Floor level.
+ 2. Ceiling level.
+ 3. Number of steps.
+
+ - Thermal config mode:
+ 1. Calibrate sensor by entering current temperature in C.
+ 2. Set temperature limit to 30 C + N clicks.
diff --git a/ui/werner/build-all.sh b/ui/werner/build-all.sh
new file mode 100755
index 0000000..b114101
--- /dev/null
+++ b/ui/werner/build-all.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+cp -av ../anduril/cfg*.h .
+
+UI=werner
+
+for TARGET in cfg-*.h ; do
+ NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
+ echo "===== $NAME ====="
+ ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }')
+ if [ -z "$ATTINY" ]; then ATTINY=85 ; fi
+ echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ mv -f "$UI".hex "$UI".$NAME.hex
+done
diff --git a/ui/werner/werner.c b/ui/werner/werner.c
new file mode 100644
index 0000000..f3241ee
--- /dev/null
+++ b/ui/werner/werner.c
@@ -0,0 +1,715 @@
+/*
+ * 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 CONFIGFILE cfg-emisar-d4.h
+
+#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
+
+// 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 *****/
+#ifdef CONFIGFILE
+#include "tk.h"
+#include incfile(CONFIGFILE)
+#else
+#error You need to define CONFIGFILE
+#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 USE_IDLE_MODE // reduce power use while awake and no tasks are pending
+
+// 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(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);
+uint8_t ramp_config_state(Event event, uint16_t arg);
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg);
+#endif
+#ifdef USE_THERMAL_REGULATION
+uint8_t tempcheck_state(Event event, uint16_t arg);
+uint8_t thermal_config_state(Event event, uint16_t arg);
+#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);
+
+// 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(Event 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // hold (initially): go to lowest level, but allow abort for regular click
+ else if (event == EV_click1_press) {
+ set_level(nearest_level(1));
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // hold, release quickly: go to lowest level
+ else if (event == EV_click1_hold_release) {
+ set_state(steady_state, 1);
+ return EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 1 click: regular mode
+ else if (event == EV_1click) {
+ set_state(steady_state, memorized_level);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks (initial press): off, to prep for later events
+ else if (event == EV_click2_press) {
+ set_level(0);
+ return EVENT_HANDLED;
+ }
+ // click, hold: go to highest level (for ramping down)
+ else if (event == EV_click2_hold) {
+ set_state(steady_state, MAX_LEVEL);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: highest mode
+ else if (event == EV_2clicks) {
+ set_state(steady_state, nearest_level(MAX_LEVEL));
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_BATTCHECK
+ // 3 clicks: battcheck mode / blinky mode group
+ else if (event == EV_3clicks) {
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ // 4 clicks: configure ramp
+ else if (event == EV_4clicks) {
+ push_state(ramp_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+
+
+uint8_t steady_state(Event 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // hold: dimmer
+ else if (event == EV_click1_hold) {
+ // ramp slower in discrete mode
+ if (arg % HOLD_TIMEOUT != 0) {
+ return EVENT_HANDLED;
+ }
+ 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 EVENT_HANDLED;
+ }
+ // reverse ramp direction on hold release
+ else if (event == EV_click1_hold_release) {
+ save_config_wl();
+ return EVENT_HANDLED;
+ }
+ #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 EVENT_HANDLED; // 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 EVENT_HANDLED;
+ }
+ #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 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) {
+ //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 EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+
+
+#ifdef USE_BATTCHECK
+uint8_t battcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ #ifdef USE_THERMAL_REGULATION
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ blink_confirm(2);
+ set_state(tempcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+#ifdef USE_THERMAL_REGULATION
+uint8_t tempcheck_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 2 clicks: battcheck mode
+ else if (event == EV_2clicks) {
+ blink_confirm(1);
+ set_state(battcheck_state, 0);
+ return EVENT_HANDLED;
+ }
+ // 4 clicks: thermal config mode
+ else if (event == EV_4clicks) {
+ push_state(thermal_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ 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(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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ //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(Event 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 - therm_cal_offset;
+ therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
+ }
+
+ val = config_state_values[1];
+ if (val) {
+ // set maximum heat limit
+ therm_ceil = 30 + val - 1;
+ }
+ if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
+}
+
+uint8_t thermal_config_state(Event event, uint16_t arg) {
+ return config_state_base(event, arg,
+ 2, thermal_config_save);
+}
+#endif
+
+
+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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ // 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 EVENT_HANDLED;
+ }
+ 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));
+ int16_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;
+
+ 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);
+ nice_delay_ms(1000);
+ }
+ #endif
+
+ #ifdef USE_IDLE_MODE
+ else {
+ // doze until next clock tick
+ idle_mode();
+ }
+ #endif
+
+}