1
// Anduril: Narsil-inspired UI for SpaghettiMonster.
2
// (Anduril is Aragorn's sword, the blade Narsil reforged)
3
// Copyright (C) 2017-2023 Selene ToyKeeper
4
// SPDX-License-Identifier: GPL-3.0-or-later
2
* Anduril: Narsil-inspired UI for SpaghettiMonster.
3
* (Anduril is Aragorn's sword, the blade Narsil reforged)
5
* Copyright (C) 2017-2019 Selene Scriven
7
* This program is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program. If not, see <http://www.gnu.org/licenses/>.
7
* Usually a program would be structured like this...
12
* ... in each source file.
13
* ... and each library and part of the program would be linked together.
15
* But this doesn't follow that pattern, because it's using the
17
* flag to reduce the compiled size. It lets us fit more features
18
* in a tiny MCU chip's ROM.
20
* So the structure is like this instead...
21
* - App-level configuration headers
23
* - Per build target config
24
* - Library-level configuration headers
25
* - Library code (FSM itself)
27
* - App code (all of it, inline)
29
* Don't do this in regular programs. It's weird and kind of gross.
30
* But in this case it gives us a bunch of much-needed space, so... woot.
32
* Also, there are a ton of compile-time options because it needs to build
33
* a bunch of different versions and each one needs to be trimmed as small
34
* as possible. These are mostly "USE" flags.
21
37
/********* User-configurable options *********/
38
#include "config-default.h"
40
/********* specific settings for known driver types *********/
22
41
// Anduril config file name (set it here or define it at the gcc command line)
23
//#define CONFIGFILE cfg-blf-q8.h
27
// parameters for this defined below or per-driver
28
#define USE_THERMAL_REGULATION
29
#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this
31
#define USE_FACTORY_RESET
32
//#define USE_SOFT_FACTORY_RESET // only needed on models which can't use hold-button-at-boot
34
// dual-switch support (second switch is a tail clicky)
35
// (currently incompatible with factory reset)
36
//#define START_AT_MEMORIZED_LEVEL
38
// include a function to blink out the firmware version
39
#define USE_VERSION_CHECK
41
// short blip when crossing from "click" to "hold" from off
42
// (helps the user hit moon mode exactly, instead of holding too long
44
#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T
45
// short blips while ramping
46
#define BLINK_AT_RAMP_MIDDLE
47
//#define BLINK_AT_RAMP_FLOOR
48
#define BLINK_AT_RAMP_CEILING
49
//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode
51
// ramp down via regular button hold if a ramp-up ended <1s ago
52
// ("hold, release, hold" ramps down instead of up)
55
// add a runtime option to switch between automatic memory (default)
56
// and manual memory (only available if compiled in)
57
// (manual memory makes 1-click-from-off start at the same level each time)
58
// (the level can be set explicitly with 5 clicks from on,
59
// or the user can go back to automatic with click-click-click-click-hold)
60
#define USE_MANUAL_MEMORY
62
// battery readout style (pick one)
64
//#define BATTCHECK_8bars // FIXME: breaks build
65
//#define BATTCHECK_4bars // FIXME: breaks build
67
// enable/disable various strobe modes
68
#define USE_BIKE_FLASHER_MODE
69
#define USE_PARTY_STROBE_MODE
70
#define USE_TACTICAL_STROBE_MODE
71
#define USE_LIGHTNING_MODE
72
#define USE_CANDLE_MODE
74
// enable sunset (goodnight) mode
75
#define USE_GOODNIGHT_MODE
76
#define GOODNIGHT_TIME 60 // minutes (approximately)
77
#define GOODNIGHT_LEVEL 24 // ~11 lm
80
#define USE_BEACON_MODE
82
// enable momentary mode
83
#define USE_MOMENTARY_MODE
85
//Muggle mode for easy UI
86
#define USE_MUGGLE_MODE
88
// make the ramps configurable by the user
89
#define USE_RAMP_CONFIG
91
// boring strobes nobody really likes, but sometimes flashlight companies want
92
// (these replace the fun strobe group,
93
// so don't enable them at the same time as any of the above strobes)
94
//#define USE_POLICE_STROBE_MODE
95
//#define USE_SOS_MODE
96
//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode
97
//#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group
99
// cut clock speed at very low modes for better efficiency
100
// (defined here so config files can override it)
101
#define USE_DYNAMIC_UNDERCLOCKING
103
/***** specific settings for known driver types *****/
42
//#define CFG_H cfg-blf-q8.h
105
#include incfile(CONFIGFILE)
108
// brightness to use when no memory is set
109
#ifndef DEFAULT_LEVEL
110
#define DEFAULT_LEVEL MAX_1x7135
113
// thermal properties, if not defined per-driver
114
#ifndef MIN_THERM_STEPDOWN
115
#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to
117
#ifndef THERM_FASTER_LEVEL
119
#define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high
121
#define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high
124
#ifdef USE_THERMAL_REGULATION
125
#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments
129
/********* Configure SpaghettiMonster *********/
130
#define USE_DELAY_ZERO
133
#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
135
#define MAX_BIKING_LEVEL 120 // should be 127 or less
136
#define USE_BATTCHECK
138
#if defined(USE_MUGGLE_MODE)
140
#define MUGGLE_FLOOR 22
142
#ifndef MUGGLE_CEILING
143
#define MUGGLE_CEILING (MAX_1x7135+20)
45
#include incfile(CFG_H)
48
/********* Include headers which need to be before FSM *********/
50
// enable FSM features needed by basic ramping functions
51
#include "ramp-mode-fsm.h"
53
#ifdef USE_FACTORY_RESET
54
#include "factory-reset-fsm.h"
57
#ifdef USE_BATTCHECK_MODE
58
#include "battcheck-mode-fsm.h"
61
#ifdef USE_LOCKOUT_MODE
62
#include "lockout-mode-fsm.h"
65
// enable FSM features needed by strobe modes
66
#include "strobe-modes-fsm.h"
68
// figure out how many bytes of eeprom are needed,
69
// based on which UI features are enabled
70
// (include this one last)
71
#include "load-save-config-fsm.h"
74
/********* bring in FSM / SpaghettiMonster *********/
146
75
#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
148
// full FET strobe can be a bit much... use max regulated level instead,
149
// if there's a bright enough regulated level
150
#ifndef STROBE_BRIGHTNESS
152
#define STROBE_BRIGHTNESS MAX_Nx7135
154
#define STROBE_BRIGHTNESS MAX_LEVEL
158
#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE)
159
#define USE_STROBE_STATE
162
#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP)
163
#define USE_BORING_STROBE_STATE
166
// auto-detect how many eeprom bytes
170
#ifdef USE_RAMP_CONFIG
173
ramp_discrete_floor_e,
174
ramp_discrete_ceil_e,
175
ramp_discrete_steps_e,
177
#ifdef USE_MANUAL_MEMORY
180
#ifdef USE_TINT_RAMPING
183
#ifdef USE_STROBE_STATE
186
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
190
#ifdef USE_BIKE_FLASHER_MODE
191
bike_flasher_brightness_e,
193
#ifdef USE_BEACON_MODE
196
#ifdef USE_MUGGLE_MODE
197
muggle_mode_active_e,
199
#ifdef USE_THERMAL_REGULATION
203
#ifdef USE_INDICATOR_LED
204
indicator_led_mode_e,
206
#ifdef USE_AUX_RGB_LEDS
208
rgb_led_lockout_mode_e,
212
#define EEPROM_BYTES eeprom_indexes_e_END
214
#ifdef START_AT_MEMORIZED_LEVEL
215
#define USE_EEPROM_WL
216
#define EEPROM_WL_BYTES 1
219
// auto-configure other stuff...
220
#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
221
#define USE_PSEUDO_RAND
224
#if defined(USE_CANDLE_MODE)
225
#ifndef USE_TRIANGLE_WAVE
226
#define USE_TRIANGLE_WAVE
230
#ifdef USE_SOFT_FACTORY_RESET
234
77
#include "spaghetti-monster.h"
237
// configure the timing of turning on/off in regular ramp mode
238
// press: react as soon as the button is pressed
240
// release: react as soon as the button is released
241
#define B_RELEASE_T 1
242
// timeout: react as soon as we're sure the user isn't doing a double-click
243
#define B_TIMEOUT_T 2
244
// defaults are release on, timeout off
246
//#define B_TIMING_ON B_PRESS_T
247
#define B_TIMING_ON B_RELEASE_T
250
//#define B_TIMING_OFF B_RELEASE_T
251
#define B_TIMING_OFF B_TIMEOUT_T
256
uint8_t off_state(Event event, uint16_t arg);
257
// simple numeric entry config menu
258
uint8_t config_state_base(Event event, uint16_t arg,
259
uint8_t num_config_steps,
261
#define MAX_CONFIG_VALUES 3
262
uint8_t config_state_values[MAX_CONFIG_VALUES];
263
// ramping mode and its related config mode
264
uint8_t steady_state(Event event, uint16_t arg);
265
#ifdef USE_RAMP_CONFIG
266
uint8_t ramp_config_state(Event event, uint16_t arg);
268
#ifdef USE_TINT_RAMPING
269
// not actually a mode, more of a fallback under other modes
270
uint8_t tint_ramping_state(Event event, uint16_t arg);
272
// party and tactical strobes
273
#ifdef USE_STROBE_STATE
274
uint8_t strobe_state(Event event, uint16_t arg);
276
#ifdef USE_BORING_STROBE_STATE
277
uint8_t boring_strobe_state(Event event, uint16_t arg);
278
volatile uint8_t boring_strobe_type = 0;
279
void sos_blink(uint8_t num, uint8_t dah);
280
#define NUM_BORING_STROBES 2
283
uint8_t battcheck_state(Event event, uint16_t arg);
285
#ifdef USE_THERMAL_REGULATION
286
#define USE_BLINK_NUM
287
uint8_t tempcheck_state(Event event, uint16_t arg);
288
uint8_t thermal_config_state(Event event, uint16_t arg);
290
#ifdef USE_GOODNIGHT_MODE
291
// 1-hour ramp down from low, then automatic off
292
uint8_t goodnight_state(Event event, uint16_t arg);
294
#ifdef USE_BEACON_MODE
295
// beacon mode and its related config mode
296
uint8_t beacon_state(Event event, uint16_t arg);
297
uint8_t beacon_config_state(Event event, uint16_t arg);
299
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
300
// automatic SOS emergency signal
301
uint8_t sos_state(Event event, uint16_t arg);
304
#define MOON_DURING_LOCKOUT_MODE
305
// if enabled, 2nd lockout click goes to the other ramp's floor level
306
#define LOCKOUT_MOON_FANCY
307
uint8_t lockout_state(Event event, uint16_t arg);
308
#ifdef USE_MOMENTARY_MODE
309
// momentary / signalling mode
310
uint8_t momentary_state(Event event, uint16_t arg);
311
uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
312
uint8_t momentary_active = 0; // boolean, true if active *right now*
314
#ifdef USE_MUGGLE_MODE
315
// muggle mode, super-simple, hard to exit
316
uint8_t muggle_state(Event event, uint16_t arg);
317
uint8_t muggle_mode_active = 0;
320
// general helper function for config modes
321
uint8_t number_entry_state(Event event, uint16_t arg);
322
// return value from number_entry_state()
323
volatile uint8_t number_entry_value;
325
void blink_confirm(uint8_t num);
327
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
328
void indicator_blink(uint8_t arg);
330
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
331
uint8_t setting_rgb_mode_now = 0;
332
void rgb_led_update(uint8_t mode, uint8_t arg);
333
void rgb_led_voltage_readout(uint8_t bright);
345
const PROGMEM uint8_t rgb_led_colors[] = {
346
0b00000001, // 0: red
347
0b00000101, // 1: yellow
348
0b00000100, // 2: green
349
0b00010100, // 3: cyan
350
0b00010000, // 4: blue
351
0b00010001, // 5: purple
352
0b00010101, // 6: white
354
#define RGB_LED_NUM_COLORS 10
355
#define RGB_LED_NUM_PATTERNS 4
356
#ifndef RGB_LED_OFF_DEFAULT
357
//#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage
358
#define RGB_LED_OFF_DEFAULT 0x17 // low, rainbow
360
#ifndef RGB_LED_LOCKOUT_DEFAULT
361
#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
363
#ifndef RGB_RAINBOW_SPEED
364
#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames
366
uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT;
367
uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
370
#ifdef USE_FACTORY_RESET
371
void factory_reset();
374
// remember stuff even after battery was changed
377
#ifdef START_AT_MEMORIZED_LEVEL
378
void save_config_wl();
381
// default ramp options if not overridden earlier per-driver
383
#define RAMP_STYLE 0 // smooth default
385
#ifndef RAMP_SMOOTH_FLOOR
386
#define RAMP_SMOOTH_FLOOR 1
388
#ifndef RAMP_SMOOTH_CEIL
389
#if PWM_CHANNELS == 3
390
#define RAMP_SMOOTH_CEIL MAX_Nx7135
392
#define RAMP_SMOOTH_CEIL MAX_LEVEL - 30
395
#ifndef RAMP_DISCRETE_FLOOR
396
#define RAMP_DISCRETE_FLOOR 20
398
#ifndef RAMP_DISCRETE_CEIL
399
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
401
#ifndef RAMP_DISCRETE_STEPS
402
#define RAMP_DISCRETE_STEPS 7
405
// mile marker(s) partway up the ramp
406
// default: blink only at border between regulated and FET
407
#ifdef BLINK_AT_RAMP_MIDDLE
408
#if PWM_CHANNELS >= 3
409
#ifndef BLINK_AT_RAMP_MIDDLE_1
410
#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
411
#ifndef BLINK_AT_RAMP_MIDDLE_2
412
#define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135
416
#ifndef BLINK_AT_RAMP_MIDDLE_1
417
#define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135
422
// brightness control
423
uint8_t memorized_level = DEFAULT_LEVEL;
424
#ifdef USE_MANUAL_MEMORY
425
uint8_t manual_memory = 0;
427
// smooth vs discrete ramping
428
volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete
429
volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR;
430
volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL;
431
volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR;
432
volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL;
433
volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS;
434
uint8_t ramp_discrete_step_size; // don't set this
436
#ifdef USE_INDICATOR_LED
437
// bits 2-3 control lockout mode
438
// bits 0-1 control "off" mode
439
// modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
440
#ifdef INDICATOR_LED_DEFAULT_MODE
441
uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE;
443
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
444
//uint8_t indicator_led_mode = (1<<2) + 2;
445
uint8_t indicator_led_mode = (2<<2) + 1;
447
uint8_t indicator_led_mode = (3<<2) + 1;
452
// calculate the nearest ramp level which would be valid at the moment
453
// (is a no-op for smooth ramp, but limits discrete ramp to only the
454
// correct levels for the user's config)
455
uint8_t nearest_level(int16_t target);
457
#ifdef USE_THERMAL_REGULATION
458
// brightness before thermal step-down
459
uint8_t target_level = 0;
460
void set_level_and_therm_target(uint8_t level);
462
#define set_level_and_therm_target(level) set_level(level)
465
// internal numbering for strobe modes
466
#ifdef USE_STROBE_STATE
468
#ifdef USE_PARTY_STROBE_MODE
471
#ifdef USE_TACTICAL_STROBE_MODE
474
#ifdef USE_LIGHTNING_MODE
477
#ifdef USE_CANDLE_MODE
480
#ifdef USE_BIKE_FLASHER_MODE
486
const int NUM_STROBES = strobe_mode_END;
488
// which strobe mode is active?
489
#ifdef USE_CANDLE_MODE
490
volatile strobe_mode_te strobe_type = candle_mode_e;
492
volatile strobe_mode_te strobe_type = 0;
496
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
497
// party / tactical strobe timing
498
volatile uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz
501
// bike mode config options
502
#ifdef USE_BIKE_FLASHER_MODE
503
volatile uint8_t bike_flasher_brightness = MAX_1x7135;
506
#ifdef USE_CANDLE_MODE
507
uint8_t candle_mode_state(Event event, uint16_t arg);
508
uint8_t triangle_wave(uint8_t phase);
509
#ifndef CANDLE_AMPLITUDE
510
#define CANDLE_AMPLITUDE 25
514
#ifdef USE_BEACON_MODE
516
volatile uint8_t beacon_seconds = 2;
519
#ifdef USE_VERSION_CHECK
520
#define USE_BLINK_DIGIT
522
const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
523
uint8_t version_check_state(Event event, uint16_t arg);
526
uint8_t off_state(Event event, uint16_t arg) {
527
// turn emitter off when entering state
528
if (event == EV_enter_state) {
530
#ifdef USE_INDICATOR_LED
531
indicator_led(indicator_led_mode & 0x03);
532
#elif defined(USE_AUX_RGB_LEDS)
533
rgb_led_update(rgb_led_off_mode, 0);
535
// sleep while off (lower power use)
536
// (unless delay requested; give the ADC some time to catch up)
537
if (! arg) { go_to_standby = 1; }
538
return MISCHIEF_MANAGED;
540
// go back to sleep eventually if we got bumped but didn't leave "off" state
541
else if (event == EV_tick) {
542
if (arg > HOLD_TIMEOUT) {
544
#ifdef USE_INDICATOR_LED
545
indicator_led(indicator_led_mode & 0x03);
546
#elif defined(USE_AUX_RGB_LEDS)
547
rgb_led_update(rgb_led_off_mode, arg);
550
return MISCHIEF_MANAGED;
552
#if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
553
// blink the indicator LED, maybe
554
else if (event == EV_sleep_tick) {
555
#ifdef USE_INDICATOR_LED
556
if ((indicator_led_mode & 0b00000011) == 0b00000011) {
557
indicator_blink(arg);
559
#elif defined(USE_AUX_RGB_LEDS)
560
rgb_led_update(rgb_led_off_mode, arg);
562
return MISCHIEF_MANAGED;
565
#if (B_TIMING_ON == B_PRESS_T)
566
// hold (initially): go to lowest level (floor), but allow abort for regular click
567
else if (event == EV_click1_press) {
568
set_level(nearest_level(1));
569
return MISCHIEF_MANAGED;
571
#endif // B_TIMING_ON == B_PRESS_T
572
// hold: go to lowest level
573
else if (event == EV_click1_hold) {
574
#if (B_TIMING_ON == B_PRESS_T)
575
#ifdef MOON_TIMING_HINT
577
// let the user know they can let go now to stay at moon
581
#else // B_RELEASE_T or B_TIMEOUT_T
582
set_level(nearest_level(1));
584
// don't start ramping immediately;
585
// give the user time to release at moon level
586
//if (arg >= HOLD_TIMEOUT) { // smaller
587
if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
588
set_state(steady_state, 1);
590
return MISCHIEF_MANAGED;
592
// hold, release quickly: go to lowest level (floor)
593
else if (event == EV_click1_hold_release) {
594
set_state(steady_state, 1);
595
return MISCHIEF_MANAGED;
597
#if (B_TIMING_ON != B_TIMEOUT_T)
598
// 1 click (before timeout): go to memorized level, but allow abort for double click
599
else if (event == EV_click1_release) {
600
#ifdef USE_MANUAL_MEMORY
602
set_level(nearest_level(manual_memory));
605
set_level(nearest_level(memorized_level));
606
return MISCHIEF_MANAGED;
608
#endif // if (B_TIMING_ON != B_TIMEOUT_T)
609
// 1 click: regular mode
610
else if (event == EV_1click) {
611
#ifdef USE_MANUAL_MEMORY
613
set_state(steady_state, manual_memory);
616
set_state(steady_state, memorized_level);
617
return MISCHIEF_MANAGED;
619
// click, hold: go to highest level (ceiling) (for ramping down)
620
else if (event == EV_click2_hold) {
621
set_state(steady_state, MAX_LEVEL);
622
return MISCHIEF_MANAGED;
624
// 2 clicks: highest mode (ceiling)
625
else if (event == EV_2clicks) {
626
set_state(steady_state, MAX_LEVEL);
627
return MISCHIEF_MANAGED;
629
// 3 clicks (initial press): off, to prep for later events
630
else if (event == EV_click3_press) {
632
return MISCHIEF_MANAGED;
635
// 3 clicks: battcheck mode / blinky mode group 1
636
else if (event == EV_3clicks) {
637
set_state(battcheck_state, 0);
638
return MISCHIEF_MANAGED;
641
// click, click, long-click: strobe mode
642
#ifdef USE_STROBE_STATE
643
else if (event == EV_click3_hold) {
644
set_state(strobe_state, 0);
645
return MISCHIEF_MANAGED;
647
#elif defined(USE_BORING_STROBE_STATE)
648
else if (event == EV_click3_hold) {
649
set_state(boring_strobe_state, 0);
650
return MISCHIEF_MANAGED;
653
// 4 clicks: soft lockout
654
else if (event == EV_4clicks) {
656
set_state(lockout_state, 0);
657
return MISCHIEF_MANAGED;
659
#ifdef USE_MOMENTARY_MODE
660
// 5 clicks: momentary mode
661
else if (event == EV_5clicks) {
663
set_state(momentary_state, 0);
664
return MISCHIEF_MANAGED;
667
#ifdef USE_MUGGLE_MODE
668
// 6 clicks: muggle mode
669
else if (event == EV_6clicks) {
671
set_state(muggle_state, 0);
672
return MISCHIEF_MANAGED;
675
#ifdef USE_INDICATOR_LED
676
// 7 clicks: change indicator LED mode
677
else if (event == EV_7clicks) {
678
uint8_t mode = (indicator_led_mode & 3) + 1;
679
#ifdef TICK_DURING_STANDBY
684
#ifdef INDICATOR_LED_SKIP_LOW
685
if (mode == 1) { mode ++; }
687
indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
690
return MISCHIEF_MANAGED;
692
#elif defined(USE_AUX_RGB_LEDS)
693
// 7 clicks: change RGB aux LED pattern
694
else if (event == EV_7clicks) {
695
uint8_t mode = (rgb_led_off_mode >> 4) + 1;
696
mode = mode % RGB_LED_NUM_PATTERNS;
697
rgb_led_off_mode = (mode << 4) | (rgb_led_off_mode & 0x0f);
698
rgb_led_update(rgb_led_off_mode, 0);
701
return MISCHIEF_MANAGED;
703
// 7 clicks (hold last): change RGB aux LED color
704
else if (event == EV_click7_hold) {
705
setting_rgb_mode_now = 1;
706
if (0 == (arg & 0x3f)) {
707
uint8_t mode = (rgb_led_off_mode & 0x0f) + 1;
708
mode = mode % RGB_LED_NUM_COLORS;
709
rgb_led_off_mode = mode | (rgb_led_off_mode & 0xf0);
712
rgb_led_update(rgb_led_off_mode, arg);
713
return MISCHIEF_MANAGED;
715
else if (event == EV_click7_hold_release) {
716
setting_rgb_mode_now = 0;
718
return MISCHIEF_MANAGED;
720
#endif // end 7 clicks
721
#if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION)
722
// 10 clicks: thermal config mode
723
else if (event == EV_10clicks) {
724
push_state(thermal_config_state, 0);
725
return MISCHIEF_MANAGED;
728
#ifdef USE_VERSION_CHECK
729
// 15+ clicks: show the version number
730
else if (event == EV_15clicks) {
731
set_state(version_check_state, 0);
732
return MISCHIEF_MANAGED;
735
#if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
736
// 13 clicks and hold the last click: invoke factory reset (reboot)
737
else if (event == EV_click13_hold) {
739
return MISCHIEF_MANAGED;
742
return EVENT_NOT_HANDLED;
746
uint8_t steady_state(Event event, uint16_t arg) {
747
uint8_t mode_min = ramp_smooth_floor;
748
uint8_t mode_max = ramp_smooth_ceil;
749
uint8_t ramp_step_size = 1;
751
static int8_t ramp_direction = 1;
753
#if (B_TIMING_OFF == B_RELEASE_T)
754
// if the user double clicks, we need to abort turning off,
755
// and this stores the level to return to
756
static uint8_t level_before_off = 0;
759
mode_min = ramp_discrete_floor;
760
mode_max = ramp_discrete_ceil;
761
ramp_step_size = ramp_discrete_step_size;
764
// turn LED on when we first enter the mode
765
if ((event == EV_enter_state) || (event == EV_reenter_state)) {
766
#if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
767
momentary_mode = 0; // 0 = ramping, 1 = strobes
769
// if we just got back from config mode, go back to memorized level
770
if (event == EV_reenter_state) {
771
arg = memorized_level;
773
// remember this level, unless it's moon or turbo
774
if ((arg > mode_min) && (arg < mode_max))
775
memorized_level = arg;
776
// use the requested level even if not memorized
777
arg = nearest_level(arg);
778
set_level_and_therm_target(arg);
782
return MISCHIEF_MANAGED;
784
#if (B_TIMING_OFF == B_RELEASE_T)
785
// 1 click (early): off, if configured for early response
786
else if (event == EV_click1_release) {
787
level_before_off = actual_level;
788
set_level_and_therm_target(0);
789
return MISCHIEF_MANAGED;
791
// 2 clicks (early): abort turning off, if configured for early response
792
else if (event == EV_click2_press) {
793
set_level_and_therm_target(level_before_off);
794
return MISCHIEF_MANAGED;
796
#endif // if (B_TIMING_OFF == B_RELEASE_T)
798
else if (event == EV_1click) {
799
set_state(off_state, 0);
800
return MISCHIEF_MANAGED;
802
// 2 clicks: go to/from highest level
803
else if (event == EV_2clicks) {
804
if (actual_level < MAX_LEVEL) {
805
// true turbo, not the mode-specific ceiling
806
set_level_and_therm_target(MAX_LEVEL);
809
set_level_and_therm_target(memorized_level);
811
return MISCHIEF_MANAGED;
813
// 3 clicks: toggle smooth vs discrete ramping
814
else if (event == EV_3clicks) {
815
ramp_style = !ramp_style;
817
#ifdef START_AT_MEMORIZED_LEVEL
821
memorized_level = nearest_level(actual_level);
822
set_level_and_therm_target(memorized_level);
823
return MISCHIEF_MANAGED;
825
#ifdef USE_RAMP_CONFIG
826
// 4 clicks: configure this ramp mode
827
else if (event == EV_4clicks) {
828
push_state(ramp_config_state, 0);
829
return MISCHIEF_MANAGED;
832
// hold: change brightness (brighter)
833
else if (event == EV_click1_hold) {
834
// ramp slower in discrete mode
835
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
836
return MISCHIEF_MANAGED;
839
// fix ramp direction on first frame if necessary
841
// make it ramp down instead, if already at max
842
if (actual_level >= mode_max) { ramp_direction = -1; }
843
// make it ramp up if already at min
844
// (off->hold->stepped_min->release causes this state)
845
else if (actual_level <= mode_min) { ramp_direction = 1; }
847
// if the button is stuck, err on the side of safety and ramp down
848
else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) {
851
// if the button is still stuck, lock the light
852
else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) {
854
set_state(lockout_state, 0);
856
memorized_level = nearest_level((int16_t)actual_level \
857
+ (ramp_step_size * ramp_direction));
859
memorized_level = nearest_level((int16_t)actual_level + ramp_step_size);
861
#if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE)
862
// only blink once for each threshold
863
if ((memorized_level != actual_level) && (
864
0 // for easier syntax below
865
#ifdef BLINK_AT_RAMP_MIDDLE_1
866
|| (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
868
#ifdef BLINK_AT_RAMP_MIDDLE_2
869
|| (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
871
#ifdef BLINK_AT_RAMP_CEILING
872
|| (memorized_level == mode_max)
874
#if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR)
875
|| (memorized_level == mode_min)
881
#if defined(BLINK_AT_STEPS)
882
uint8_t foo = ramp_style;
884
uint8_t nearest = nearest_level((int16_t)actual_level);
886
// only blink once for each threshold
887
if ((memorized_level != actual_level) &&
889
(memorized_level == nearest)
895
set_level_and_therm_target(memorized_level);
896
return MISCHIEF_MANAGED;
898
#if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL)
899
// reverse ramp direction on hold release
900
else if (event == EV_click1_hold_release) {
902
ramp_direction = -ramp_direction;
904
#ifdef START_AT_MEMORIZED_LEVEL
907
return MISCHIEF_MANAGED;
910
// click, hold: change brightness (dimmer)
911
else if (event == EV_click2_hold) {
915
// ramp slower in discrete mode
916
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
917
return MISCHIEF_MANAGED;
919
// TODO? make it ramp up instead, if already at min?
920
memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
921
#if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE)
922
// only blink once for each threshold
923
if ((memorized_level != actual_level) && (
924
0 // for easier syntax below
925
#ifdef BLINK_AT_RAMP_MIDDLE_1
926
|| (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
928
#ifdef BLINK_AT_RAMP_MIDDLE_2
929
|| (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
931
#ifdef BLINK_AT_RAMP_FLOOR
932
|| (memorized_level == mode_min)
938
#if defined(BLINK_AT_STEPS)
939
uint8_t foo = ramp_style;
941
uint8_t nearest = nearest_level((int16_t)actual_level);
943
// only blink once for each threshold
944
if ((memorized_level != actual_level) &&
946
(memorized_level == nearest)
952
set_level_and_therm_target(memorized_level);
953
return MISCHIEF_MANAGED;
955
#ifdef START_AT_MEMORIZED_LEVEL
956
// click, release, hold, release: save new ramp level (if necessary)
957
else if (event == EV_click2_hold_release) {
959
return MISCHIEF_MANAGED;
962
#ifdef USE_MANUAL_MEMORY
963
else if (event == EV_5clicks) {
964
manual_memory = actual_level;
968
else if (event == EV_click5_hold) {
976
#if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
977
else if (event == EV_tick) {
979
// un-reverse after 1 second
980
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
982
#ifdef USE_SET_LEVEL_GRADUALLY
983
int16_t diff = gradual_target - actual_level;
984
static uint16_t ticks_since_adjust = 0;
985
ticks_since_adjust++;
987
uint16_t ticks_per_adjust = 256;
990
if (actual_level > THERM_FASTER_LEVEL) {
991
#ifdef THERM_HARD_TURBO_DROP
992
ticks_per_adjust >>= 2;
994
ticks_per_adjust >>= 2;
997
// rise at half speed
998
ticks_per_adjust <<= 1;
1001
ticks_per_adjust >>= 1;
1003
diff /= 2; // because shifting produces weird behavior
1005
if (ticks_since_adjust > ticks_per_adjust)
1008
ticks_since_adjust = 0;
1011
#endif // ifdef USE_SET_LEVEL_GRADUALLY
1012
return MISCHIEF_MANAGED;
1015
#ifdef USE_THERMAL_REGULATION
1016
// overheating: drop by an amount proportional to how far we are above the ceiling
1017
else if (event == EV_temperature_high) {
1021
#ifdef THERM_HARD_TURBO_DROP
1022
//if (actual_level > THERM_FASTER_LEVEL) {
1023
if (actual_level == MAX_LEVEL) {
1024
#ifdef USE_SET_LEVEL_GRADUALLY
1025
set_level_gradually(THERM_FASTER_LEVEL);
1026
target_level = THERM_FASTER_LEVEL;
1028
set_level_and_therm_target(THERM_FASTER_LEVEL);
1032
if (actual_level > MIN_THERM_STEPDOWN) {
1033
int16_t stepdown = actual_level - arg;
1034
if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN;
1035
else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL;
1036
#ifdef USE_SET_LEVEL_GRADUALLY
1037
set_level_gradually(stepdown);
1039
set_level(stepdown);
1042
return MISCHIEF_MANAGED;
1044
// underheating: increase slowly if we're lower than the target
1045
// (proportional to how low we are)
1046
else if (event == EV_temperature_low) {
1050
if (actual_level < target_level) {
1051
//int16_t stepup = actual_level + (arg>>1);
1052
int16_t stepup = actual_level + arg;
1053
if (stepup > target_level) stepup = target_level;
1054
else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
1055
#ifdef USE_SET_LEVEL_GRADUALLY
1056
set_level_gradually(stepup);
1061
return MISCHIEF_MANAGED;
1063
#ifdef USE_SET_LEVEL_GRADUALLY
1064
// temperature is within target window
1065
// (so stop trying to adjust output)
1066
else if (event == EV_temperature_okay) {
1067
// if we're still adjusting output... stop after the current step
1068
if (gradual_target > actual_level)
1069
gradual_target = actual_level + 1;
1070
else if (gradual_target < actual_level)
1071
gradual_target = actual_level - 1;
1072
return MISCHIEF_MANAGED;
1074
#endif // ifdef USE_SET_LEVEL_GRADUALLY
1075
#endif // ifdef USE_THERMAL_REGULATION
1076
return EVENT_NOT_HANDLED;
1080
#ifdef USE_TINT_RAMPING
1081
uint8_t tint_ramping_state(Event event, uint16_t arg) {
1082
static int8_t tint_ramp_direction = 1;
1083
static uint8_t prev_tint = 0;
1084
// don't activate auto-tint modes unless the user hits the edge
1085
// and keeps pressing for a while
1086
static uint8_t past_edge_counter = 0;
1087
// bugfix: click-click-hold from off to strobes would invoke tint ramping
1088
// in addition to changing state... so ignore any tint-ramp events which
1089
// don't look like they were meant to be here
1090
static uint8_t active = 0;
1092
// click, click, hold: change the tint
1093
if (event == EV_click3_hold) {
1094
// reset at beginning of movement
1096
active = 1; // first frame means this is for us
1097
past_edge_counter = 0; // doesn't start until user hits the edge
1099
// ignore event if we weren't the ones who handled the first frame
1100
if (! active) return EVENT_HANDLED;
1102
// change normal tints
1103
if ((tint_ramp_direction > 0) && (tint < 254)) {
1106
else if ((tint_ramp_direction < 0) && (tint > 1)) {
1109
// if the user kept pressing long enough, go the final step
1110
if (past_edge_counter == 64) {
1111
past_edge_counter ++;
1112
tint ^= 1; // 0 -> 1, 254 -> 255
1115
// if tint change stalled, let user know we hit the edge
1116
else if (prev_tint == tint) {
1117
if (past_edge_counter == 0) blip();
1118
// count up but don't wrap back to zero
1119
if (past_edge_counter < 255) past_edge_counter ++;
1122
set_level(actual_level);
1123
return EVENT_HANDLED;
1126
// click, click, hold, release: reverse direction for next ramp
1127
else if (event == EV_click3_hold_release) {
1128
active = 0; // ignore next hold if it wasn't meant for us
1130
tint_ramp_direction = -tint_ramp_direction;
1131
if (tint == 0) tint_ramp_direction = 1;
1132
else if (tint == 255) tint_ramp_direction = -1;
1133
// remember tint after battery change
1135
return EVENT_HANDLED;
1138
return EVENT_NOT_HANDLED;
1140
#endif // ifdef USE_TINT_RAMPING
1143
#ifdef USE_STROBE_STATE
1144
uint8_t strobe_state(Event event, uint16_t arg) {
1145
static int8_t ramp_direction = 1;
1147
// 'st' reduces ROM size by avoiding access to a volatile var
1148
// (maybe I should just make it nonvolatile?)
1149
strobe_mode_te st = strobe_type;
1151
#ifdef USE_MOMENTARY_MODE
1152
momentary_mode = 1; // 0 = ramping, 1 = strobes
1155
#ifdef USE_CANDLE_MODE
1156
// pass all events to candle mode, when it's active
1157
// (the code is in its own pseudo-state to keep things cleaner)
1158
if (st == candle_mode_e) {
1159
candle_mode_state(event, arg);
1163
if (0) {} // placeholder
1164
// init anything which needs to be initialized
1165
else if (event == EV_enter_state) {
1167
return MISCHIEF_MANAGED;
1170
else if (event == EV_1click) {
1171
set_state(off_state, 0);
1172
return MISCHIEF_MANAGED;
1174
// 2 clicks: rotate through strobe/flasher modes
1175
else if (event == EV_2clicks) {
1176
strobe_type = (st + 1) % NUM_STROBES;
1178
return MISCHIEF_MANAGED;
1180
// hold: change speed (go faster)
1181
// or change brightness (brighter)
1182
else if (event == EV_click1_hold) {
1183
if (0) {} // placeholder
1185
// party / tactical strobe faster
1186
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
1187
#ifdef USE_TACTICAL_STROBE_MODE
1188
else if (st <= tactical_strobe_e) {
1190
else if (st == party_strobe_e) {
1192
if ((arg & 1) == 0) {
1193
uint8_t d = strobe_delays[st];
1194
d -= ramp_direction;
1196
else if (d > 254) d = 254;
1197
strobe_delays[st] = d;
1202
// lightning has no adjustments
1203
//else if (st == lightning_storm_e) {}
1205
// biking mode brighter
1206
#ifdef USE_BIKE_FLASHER_MODE
1207
else if (st == bike_flasher_e) {
1208
bike_flasher_brightness += ramp_direction;
1209
if (bike_flasher_brightness < 2) bike_flasher_brightness = 2;
1210
else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL;
1211
set_level(bike_flasher_brightness);
1215
return MISCHIEF_MANAGED;
1217
// reverse ramp direction on hold release
1218
// ... and save new strobe settings
1219
else if (event == EV_click1_hold_release) {
1220
ramp_direction = -ramp_direction;
1222
return MISCHIEF_MANAGED;
1224
// click, hold: change speed (go slower)
1225
// or change brightness (dimmer)
1226
else if (event == EV_click2_hold) {
1229
if (0) {} // placeholder
1231
// party / tactical strobe slower
1232
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
1233
#ifdef USE_TACTICAL_STROBE_MODE
1234
else if (st <= tactical_strobe_e) {
1236
else if (st == party_strobe_e) {
1238
if ((arg & 1) == 0) {
1239
if (strobe_delays[st] < 255) strobe_delays[st] ++;
1244
// lightning has no adjustments
1245
//else if (st == lightning_storm_e) {}
1247
// biking mode dimmer
1248
#ifdef USE_BIKE_FLASHER_MODE
1249
else if (st == bike_flasher_e) {
1250
if (bike_flasher_brightness > 2)
1251
bike_flasher_brightness --;
1252
set_level(bike_flasher_brightness);
1256
return MISCHIEF_MANAGED;
1258
// release hold: save new strobe settings
1259
else if (event == EV_click2_hold_release) {
1261
return MISCHIEF_MANAGED;
1263
#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
1264
// clock tick: bump the random seed
1265
else if (event == EV_tick) {
1266
// un-reverse after 1 second
1267
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
1269
pseudo_rand_seed += arg;
1270
return MISCHIEF_MANAGED;
1273
return EVENT_NOT_HANDLED;
1276
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
1277
inline void party_tactical_strobe_mode_iter(uint8_t st) {
1278
// one iteration of main loop()
1279
uint8_t del = strobe_delays[st];
1280
// TODO: make tac strobe brightness configurable?
1281
set_level(STROBE_BRIGHTNESS);
1282
if (0) {} // placeholde0
1283
#ifdef USE_PARTY_STROBE_MODE
1284
else if (st == party_strobe_e) { // party strobe
1285
#ifdef PARTY_STROBE_ONTIME
1286
nice_delay_ms(PARTY_STROBE_ONTIME);
1288
if (del < 42) delay_zero();
1289
else nice_delay_ms(1);
1293
#ifdef USE_TACTICAL_STROBE_MODE
1294
else { //tactical strobe
1295
nice_delay_ms(del >> 1);
1299
nice_delay_ms(del); // no return check necessary on final delay
1303
#ifdef USE_LIGHTNING_MODE
1304
inline void lightning_storm_iter() {
1305
// one iteration of main loop()
1309
// turn the emitter on at a random level,
1310
// for a random amount of time between 1ms and 32ms
1311
//rand_time = 1 << (pseudo_rand() % 7);
1312
rand_time = pseudo_rand() & 63;
1313
brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64
1314
brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now
1315
brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias)
1316
if (brightness > MAX_LEVEL) brightness = MAX_LEVEL;
1317
set_level(brightness);
1318
nice_delay_ms(rand_time);
1320
// decrease the brightness somewhat more gradually, like lightning
1321
uint8_t stepdown = brightness >> 3;
1322
if (stepdown < 1) stepdown = 1;
1323
while(brightness > 1) {
1324
nice_delay_ms(rand_time);
1325
brightness -= stepdown;
1326
if (brightness < 0) brightness = 0;
1327
set_level(brightness);
1329
if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) {
1331
set_level(brightness);
1334
if (! (pseudo_rand() & 3)) {
1335
nice_delay_ms(rand_time);
1336
set_level(brightness>>1);
1340
// turn the emitter off,
1341
// for a random amount of time between 1ms and 8192ms
1342
// (with a low bias)
1343
rand_time = 1 << (pseudo_rand() % 13);
1344
rand_time += pseudo_rand() % rand_time;
1346
nice_delay_ms(rand_time); // no return check necessary on final delay
1350
#ifdef USE_BIKE_FLASHER_MODE
1351
inline void bike_flasher_iter() {
1352
// one iteration of main loop()
1353
uint8_t burst = bike_flasher_brightness << 1;
1354
if (burst > MAX_LEVEL) burst = MAX_LEVEL;
1355
for(uint8_t i=0; i<4; i++) {
1358
set_level(bike_flasher_brightness);
1361
nice_delay_ms(720); // no return check necessary on final delay
1365
#endif // ifdef USE_STROBE_STATE
1367
#ifdef USE_CANDLE_MODE
1368
uint8_t candle_mode_state(Event event, uint16_t arg) {
1369
static int8_t ramp_direction = 1;
1370
#define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15)
1371
static uint8_t candle_wave1 = 0;
1372
static uint8_t candle_wave2 = 0;
1373
static uint8_t candle_wave3 = 0;
1374
static uint8_t candle_wave2_speed = 0;
1375
// these should add up to 100
1376
#define CANDLE_WAVE1_MAXDEPTH 30
1377
#define CANDLE_WAVE2_MAXDEPTH 45
1378
#define CANDLE_WAVE3_MAXDEPTH 25
1379
static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100;
1380
static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100;
1381
static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100;
1382
static uint8_t candle_mode_brightness = 24;
1383
static uint8_t candle_mode_timer = 0;
1384
#define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds
1385
#define MINUTES_PER_CANDLE_HALFHOUR 27 // ish
1387
if (event == EV_enter_state) {
1388
candle_mode_timer = 0; // in case any time was left over from earlier
1390
return MISCHIEF_MANAGED;
1392
// 2 clicks: cancel timer
1393
else if (event == EV_2clicks) {
1394
// parent state just rotated through strobe/flasher modes,
1395
// so cancel timer... in case any time was left over from earlier
1396
candle_mode_timer = 0;
1397
return MISCHIEF_MANAGED;
1399
// hold: change brightness (brighter)
1400
else if (event == EV_click1_hold) {
1401
// ramp away from extremes
1403
if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; }
1404
else if (candle_mode_brightness <= 1) { ramp_direction = 1; }
1406
// change brightness, but not too far
1407
candle_mode_brightness += ramp_direction;
1408
if (candle_mode_brightness < 1) candle_mode_brightness = 1;
1409
else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL;
1410
return MISCHIEF_MANAGED;
1412
// reverse ramp direction on hold release
1413
else if (event == EV_click1_hold_release) {
1414
ramp_direction = -ramp_direction;
1415
return MISCHIEF_MANAGED;
1417
// click, hold: change brightness (dimmer)
1418
else if (event == EV_click2_hold) {
1420
if (candle_mode_brightness > 1)
1421
candle_mode_brightness --;
1422
return MISCHIEF_MANAGED;
1424
// 3 clicks: add 30m to candle timer
1425
else if (event == EV_3clicks) {
1426
if (candle_mode_timer < (255 - MINUTES_PER_CANDLE_HALFHOUR)) {
1427
// add 30m to the timer
1428
candle_mode_timer += MINUTES_PER_CANDLE_HALFHOUR;
1430
set_level(actual_level + 32);
1433
return MISCHIEF_MANAGED;
1435
// clock tick: animate candle brightness
1436
else if (event == EV_tick) {
1437
// un-reverse after 1 second
1438
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
1440
// self-timer dims the light during the final minute
1441
uint8_t subtract = 0;
1442
if (candle_mode_timer == 1) {
1443
subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE)
1444
* ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4))
1447
// we passed a minute mark, decrease timer if it's running
1448
if ((arg & (TICKS_PER_CANDLE_MINUTE-1)) == (TICKS_PER_CANDLE_MINUTE - 1)) {
1449
if (candle_mode_timer > 0) {
1450
candle_mode_timer --;
1451
//set_level(0); delay_4ms(2);
1452
// if the timer ran out, shut off
1453
if (! candle_mode_timer) {
1454
set_state(off_state, 0);
1458
// 3-oscillator synth for a relatively organic pattern
1460
add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8)
1461
+ ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8)
1462
+ ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8);
1463
int8_t brightness = candle_mode_brightness + add - subtract;
1464
if (brightness < 0) { brightness = 0; }
1465
set_level(brightness);
1467
// wave1: slow random LFO
1468
// TODO: make wave slower and more erratic?
1469
if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1;
1470
// wave2: medium-speed erratic LFO
1471
candle_wave2 += candle_wave2_speed;
1472
// wave3: erratic fast wave
1473
candle_wave3 += pseudo_rand() % 37;
1474
// S&H on wave2 frequency to make it more erratic
1475
if ((pseudo_rand() & 0b00111111) == 0)
1476
candle_wave2_speed = pseudo_rand() % 13;
1477
// downward sawtooth on wave2 depth to simulate stabilizing
1478
if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0))
1479
candle_wave2_depth --;
1480
// random sawtooth retrigger
1481
if (pseudo_rand() == 0) {
1483
//candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
1484
candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100);
1485
//candle_wave3_depth = 5;
1488
// downward sawtooth on wave3 depth to simulate stabilizing
1489
if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0))
1490
candle_wave3_depth --;
1491
if ((pseudo_rand() & 0b01111111) == 0)
1493
//candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
1494
candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100);
1495
return MISCHIEF_MANAGED;
1497
return EVENT_NOT_HANDLED;
1499
#endif // #ifdef USE_CANDLE_MODE
1502
#ifdef USE_BORING_STROBE_STATE
1503
uint8_t boring_strobe_state(Event event, uint16_t arg) {
1504
// police strobe and SOS, meh
1505
// 'st' reduces ROM size by avoiding access to a volatile var
1506
// (maybe I should just make it nonvolatile?)
1507
uint8_t st = boring_strobe_type;
1509
if (event == EV_enter_state) {
1510
return MISCHIEF_MANAGED;
1513
else if (event == EV_1click) {
1514
// reset to police strobe for next time
1515
boring_strobe_type = 0;
1516
set_state(off_state, 0);
1517
return MISCHIEF_MANAGED;
1519
// 2 clicks: rotate through strobe/flasher modes
1520
else if (event == EV_2clicks) {
1521
boring_strobe_type = (st + 1) % NUM_BORING_STROBES;
1522
return MISCHIEF_MANAGED;
1524
return EVENT_NOT_HANDLED;
1527
#ifdef USE_POLICE_STROBE_MODE
1528
inline void police_strobe_iter() {
1529
// one iteration of main loop()
1530
// flash at 16 Hz then 8 Hz, 8 times each
1531
for (uint8_t del=41; del<100; del+=41) {
1532
for (uint8_t f=0; f<8; f++) {
1533
set_level(STROBE_BRIGHTNESS);
1534
nice_delay_ms(del >> 1);
1541
#endif // #ifdef USE_BORING_STROBE_STATE
1544
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
1545
uint8_t sos_state(Event event, uint16_t arg) {
1547
if (event == EV_1click) {
1548
set_state(off_state, 0);
1549
return MISCHIEF_MANAGED;
1551
// 2 clicks: next mode
1552
else if (event == EV_2clicks) {
1553
#ifdef USE_THERMAL_REGULATION
1554
set_state(tempcheck_state, 0);
1556
set_state(battcheck_state, 0);
1558
return MISCHIEF_MANAGED;
1560
return EVENT_NOT_HANDLED;
1564
void sos_blink(uint8_t num, uint8_t dah) {
1565
#define DIT_LENGTH 200
1566
for (; num > 0; num--) {
1567
set_level(memorized_level);
1568
nice_delay_ms(DIT_LENGTH);
1569
if (dah) { // dah is 3X as long as a dit
1570
nice_delay_ms(DIT_LENGTH*2);
1573
// one "off" dit between blinks
1574
nice_delay_ms(DIT_LENGTH);
1576
// three "off" dits (or one "dah") between letters
1577
// (except for SOS, which is collectively treated as a single "letter")
1578
//nice_delay_ms(DIT_LENGTH*2);
1581
inline void sos_mode_iter() {
1582
// one iteration of main loop()
1583
//nice_delay_ms(1000);
1584
sos_blink(3, 0); // S
1585
sos_blink(3, 1); // O
1586
sos_blink(3, 0); // S
1587
nice_delay_ms(2000);
1589
#endif // #ifdef USE_SOS_MODE
1592
#ifdef USE_BATTCHECK
1593
uint8_t battcheck_state(Event event, uint16_t arg) {
1595
if (event == EV_1click) {
1596
set_state(off_state, 0);
1597
return MISCHIEF_MANAGED;
1599
#ifdef USE_GOODNIGHT_MODE
1600
// 2 clicks: goodnight mode
1601
else if (event == EV_2clicks) {
1602
set_state(goodnight_state, 0);
1603
return MISCHIEF_MANAGED;
1605
#elif defined(USE_BEACON_MODE)
1606
// 2 clicks: beacon mode
1607
else if (event == EV_2clicks) {
1608
set_state(beacon_state, 0);
1609
return MISCHIEF_MANAGED;
1611
#elif defined(USE_THERMAL_REGULATION)
1612
// 2 clicks: tempcheck mode
1613
else if (event == EV_2clicks) {
1614
set_state(tempcheck_state, 0);
1615
return MISCHIEF_MANAGED;
1618
return EVENT_NOT_HANDLED;
1623
#ifdef USE_THERMAL_REGULATION
1624
uint8_t tempcheck_state(Event event, uint16_t arg) {
1626
if (event == EV_1click) {
1627
set_state(off_state, 0);
1628
return MISCHIEF_MANAGED;
1630
#ifdef USE_BATTCHECK
1631
// 2 clicks: battcheck mode
1632
else if (event == EV_2clicks) {
1633
set_state(battcheck_state, 0);
1634
return MISCHIEF_MANAGED;
1637
// 4 clicks: thermal config mode
1638
else if (event == EV_4clicks) {
1639
push_state(thermal_config_state, 0);
1640
return MISCHIEF_MANAGED;
1642
return EVENT_NOT_HANDLED;
1647
#ifdef USE_BEACON_MODE
1648
uint8_t beacon_state(Event event, uint16_t arg) {
1650
if (event == EV_1click) {
1651
set_state(off_state, 0);
1652
return MISCHIEF_MANAGED;
1654
// TODO: use sleep ticks to measure time between pulses,
1656
// 2 clicks: next mode
1657
else if (event == EV_2clicks) {
1658
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
1659
set_state(sos_state, 0);
1660
#elif defined(USE_THERMAL_REGULATION)
1661
set_state(tempcheck_state, 0);
1662
#elif defined(USE_BATTCHECK)
1663
set_state(battcheck_state, 0);
1665
return MISCHIEF_MANAGED;
1667
// 4 clicks: beacon config mode
1668
else if (event == EV_4clicks) {
1669
push_state(beacon_config_state, 0);
1670
return MISCHIEF_MANAGED;
1672
return EVENT_NOT_HANDLED;
1674
#endif // #ifdef USE_BEACON_MODE
1677
#ifdef USE_GOODNIGHT_MODE
1678
#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL)
1679
uint8_t goodnight_state(Event event, uint16_t arg) {
1680
static uint16_t ticks_since_stepdown = 0;
1682
if (event == EV_enter_state) {
1683
ticks_since_stepdown = 0;
1685
set_level(GOODNIGHT_LEVEL);
1686
return MISCHIEF_MANAGED;
1689
else if (event == EV_1click) {
1690
set_state(off_state, 0);
1691
return MISCHIEF_MANAGED;
1693
// 2 clicks: next mode
1694
else if (event == EV_2clicks) {
1695
#ifdef USE_BEACON_MODE
1696
set_state(beacon_state, 0);
1697
#elif defined(USE_SOS_MODE_IN_BLINKY_GROUP)
1698
set_state(sos_state, 0);
1699
#elif defined(USE_THERMAL_REGULATION)
1700
set_state(tempcheck_state, 0);
1702
return MISCHIEF_MANAGED;
1704
// tick: step down (maybe) or off (maybe)
1705
else if (event == EV_tick) {
1706
if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) {
1707
ticks_since_stepdown = 0;
1708
set_level(actual_level-1);
1709
if (! actual_level) {
1710
#if 0 // test blink, to help measure timing
1711
set_level(MAX_LEVEL>>2);
1715
set_state(off_state, 0);
1718
return MISCHIEF_MANAGED;
1720
return EVENT_NOT_HANDLED;
1725
uint8_t lockout_state(Event event, uint16_t arg) {
1726
#ifdef MOON_DURING_LOCKOUT_MODE
1727
// momentary(ish) moon mode during lockout
1728
// button is being held
1729
#ifdef USE_AUX_RGB_LEDS
1730
// don't turn on during RGB aux LED configuration
1731
if (event == EV_click3_hold) { set_level(0); } else
1733
if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
1734
#ifdef LOCKOUT_MOON_LOWEST
1735
// Use lowest moon configured
1736
uint8_t lvl = ramp_smooth_floor;
1737
if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor;
1739
#elif defined(LOCKOUT_MOON_FANCY)
1740
uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor };
1741
if ((event & 0x0f) == 2) {
1742
set_level(levels[ramp_style^1]);
1744
set_level(levels[ramp_style]);
1747
// Use moon from current ramp
1748
set_level(nearest_level(1));
1751
// button was released
1752
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
1757
// regular event handling
1758
// conserve power while locked out
1759
// (allow staying awake long enough to exit, but otherwise
1760
// be persistent about going back to sleep every few seconds
1761
// even if the user keeps pressing the button)
1762
#ifdef USE_INDICATOR_LED
1763
if (event == EV_enter_state) {
1764
indicator_led(indicator_led_mode >> 2);
1766
#elif defined(USE_AUX_RGB_LEDS)
1767
if (event == EV_enter_state) {
1768
rgb_led_update(rgb_led_lockout_mode, 0);
1771
if (event == EV_tick) {
1772
if (arg > HOLD_TIMEOUT) {
1774
#ifdef USE_INDICATOR_LED
1775
indicator_led(indicator_led_mode >> 2);
1776
#elif defined(USE_AUX_RGB_LEDS)
1777
rgb_led_update(rgb_led_lockout_mode, arg);
1780
return MISCHIEF_MANAGED;
1782
#if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
1783
else if (event == EV_sleep_tick) {
1784
#if defined(USE_INDICATOR_LED)
1785
if ((indicator_led_mode & 0b00001100) == 0b00001100) {
1786
indicator_blink(arg);
1788
#elif defined(USE_AUX_RGB_LEDS)
1789
rgb_led_update(rgb_led_lockout_mode, arg);
1791
return MISCHIEF_MANAGED;
1794
#if defined(USE_INDICATOR_LED)
1795
// 3 clicks: rotate through indicator LED modes (lockout mode)
1796
else if (event == EV_3clicks) {
1797
#if defined(USE_INDICATOR_LED)
1798
uint8_t mode = indicator_led_mode >> 2;
1799
#ifdef TICK_DURING_STANDBY
1800
mode = (mode + 1) & 3;
1802
mode = (mode + 1) % 3;
1804
#ifdef INDICATOR_LED_SKIP_LOW
1805
if (mode == 1) { mode ++; }
1807
indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
1808
indicator_led(mode);
1809
#elif defined(USE_AUX_RGB_LEDS)
1812
return MISCHIEF_MANAGED;
1814
#elif defined(USE_AUX_RGB_LEDS)
1815
// 3 clicks: change RGB aux LED pattern
1816
else if (event == EV_3clicks) {
1817
uint8_t mode = (rgb_led_lockout_mode >> 4) + 1;
1818
mode = mode % RGB_LED_NUM_PATTERNS;
1819
rgb_led_lockout_mode = (mode << 4) | (rgb_led_lockout_mode & 0x0f);
1820
rgb_led_update(rgb_led_lockout_mode, 0);
1823
return MISCHIEF_MANAGED;
1825
// click, click, hold: change RGB aux LED color
1826
else if (event == EV_click3_hold) {
1827
setting_rgb_mode_now = 1;
1828
if (0 == (arg & 0x3f)) {
1829
uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1;
1830
mode = mode % RGB_LED_NUM_COLORS;
1831
rgb_led_lockout_mode = mode | (rgb_led_lockout_mode & 0xf0);
1834
rgb_led_update(rgb_led_lockout_mode, arg);
1835
return MISCHIEF_MANAGED;
1837
// click, click, hold, release: save new color
1838
else if (event == EV_click3_hold_release) {
1839
setting_rgb_mode_now = 0;
1841
return MISCHIEF_MANAGED;
1845
else if (event == EV_4clicks) {
1847
set_state(off_state, 0);
1848
return MISCHIEF_MANAGED;
1851
return EVENT_NOT_HANDLED;
1855
#ifdef USE_MOMENTARY_MODE
1856
uint8_t momentary_state(Event event, uint16_t arg) {
1857
// TODO: momentary strobe here? (for light painting)
1859
// init strobe mode, if relevant
1860
#ifdef USE_STROBE_STATE
1861
if ((event == EV_enter_state) && (momentary_mode == 1)) {
1862
strobe_state(event, arg);
1866
// light up when the button is pressed; go dark otherwise
1867
// button is being held
1868
if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
1869
momentary_active = 1;
1870
// 0 = ramping, 1 = strobes
1871
if (momentary_mode == 0) {
1872
set_level(memorized_level);
1874
return MISCHIEF_MANAGED;
1876
// button was released
1877
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
1878
momentary_active = 0;
1880
//go_to_standby = 1; // sleep while light is off
1881
return MISCHIEF_MANAGED;
1884
// Sleep, dammit! (but wait a few seconds first)
1885
// (because standby mode uses such little power that it can interfere
1886
// with exiting via tailcap loosen+tighten unless you leave power
1887
// disconnected for several seconds, so we want to be awake when that
1888
// happens to speed up the process)
1889
else if (event == EV_tick) {
1890
#ifdef USE_STROBE_STATE
1891
if (momentary_active) {
1892
// 0 = ramping, 1 = strobes
1893
if (momentary_mode == 1) {
1894
return strobe_state(event, arg);
1899
if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds
1900
go_to_standby = 1; // sleep while light is off
1901
// turn off lighted button
1902
#ifdef USE_INDICATOR_LED
1904
#elif defined(USE_AUX_RGB_LEDS)
1905
rgb_led_update(0, 0);
1908
#ifdef USE_STROBE_STATE
1911
return MISCHIEF_MANAGED;
1914
return EVENT_NOT_HANDLED;
1919
#ifdef USE_MUGGLE_MODE
1920
uint8_t muggle_state(Event event, uint16_t arg) {
1921
static int8_t ramp_direction;
1922
static int8_t muggle_off_mode;
1924
// turn LED off when we first enter the mode
1925
if (event == EV_enter_state) {
1928
#ifdef START_AT_MEMORIZED_LEVEL
1929
memorized_level = arg;
1930
muggle_off_mode = 0;
1931
set_level(memorized_level);
1933
if (! muggle_mode_active) { // don't write eeprom at every boot
1934
muggle_mode_active = 1;
1938
muggle_mode_active = 1;
1941
muggle_off_mode = 1;
1942
//memorized_level = MAX_1x7135;
1943
memorized_level = (MUGGLE_FLOOR + MUGGLE_CEILING) / 2;
1945
return MISCHIEF_MANAGED;
1947
// initial press: moon hint
1948
else if (event == EV_click1_press) {
1949
if (muggle_off_mode)
1950
set_level(MUGGLE_FLOOR);
1952
// initial release: direct to memorized level
1953
else if (event == EV_click1_release) {
1954
if (muggle_off_mode)
1955
set_level(memorized_level);
1957
// if the user keeps pressing, turn off
1958
else if (event == EV_click2_press) {
1959
muggle_off_mode = 1;
1963
else if (event == EV_1click) {
1964
muggle_off_mode ^= 1;
1965
if (muggle_off_mode) {
1970
set_level(memorized_level);
1973
return MISCHIEF_MANAGED;
1975
// hold: change brightness
1976
else if (event == EV_click1_hold) {
1977
// ramp at half speed
1978
if (arg & 1) return MISCHIEF_MANAGED;
1980
// if off, start at bottom
1981
if (muggle_off_mode) {
1982
muggle_off_mode = 0;
1984
set_level(MUGGLE_FLOOR);
1989
// ramp down if already at ceiling
1990
if ((arg <= 1) && (m >= MUGGLE_CEILING)) ramp_direction = -1;
1992
m += ramp_direction;
1993
if (m < MUGGLE_FLOOR)
1995
if (m > MUGGLE_CEILING)
1997
memorized_level = m;
2000
return MISCHIEF_MANAGED;
2002
// reverse ramp direction on hold release
2003
else if (event == EV_click1_hold_release) {
2004
ramp_direction = -ramp_direction;
2005
#ifdef START_AT_MEMORIZED_LEVEL
2006
save_config_wl(); // momentary use should retain brightness level
2008
return MISCHIEF_MANAGED;
2011
// click, hold: change brightness (dimmer)
2012
else if (event == EV_click2_hold) {
2014
if (memorized_level > MUGGLE_FLOOR)
2015
memorized_level = actual_level - 1;
2016
set_level(memorized_level);
2017
return MISCHIEF_MANAGED;
2020
// 6 clicks: exit muggle mode
2021
else if (event == EV_6clicks) {
2023
muggle_mode_active = 0;
2025
set_state(off_state, 0);
2026
return MISCHIEF_MANAGED;
2028
// tick: housekeeping
2029
else if (event == EV_tick) {
2030
// un-reverse after 1 second
2031
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
2033
// turn off, but don't go to the main "off" state
2034
if (muggle_off_mode) {
2035
if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second
2036
#ifdef USE_AUX_RGB_LEDS_WHILE_ON
2039
go_to_standby = 1; // sleep while light is off
2042
return MISCHIEF_MANAGED;
2044
#ifdef USE_THERMAL_REGULATION
2045
// overheating is handled specially in muggle mode
2046
else if(event == EV_temperature_high) {
2050
// ignore warnings while off
2051
if (! muggle_off_mode) {
2052
// step down proportional to the amount of overheating
2053
int16_t new = actual_level - arg;
2054
if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; }
2057
return MISCHIEF_MANAGED;
2061
// low voltage is handled specially in muggle mode
2062
else if(event == EV_voltage_low) {
2063
uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
2064
if (lvl >= MUGGLE_FLOOR) {
2067
muggle_off_mode = 1;
2069
return MISCHIEF_MANAGED;
2073
return EVENT_NOT_HANDLED;
2078
#ifdef USE_VERSION_CHECK
2079
uint8_t version_check_state(Event event, uint16_t arg) {
2080
return EVENT_NOT_HANDLED;
2085
// ask the user for a sequence of numbers, then save them and return to caller
2086
uint8_t config_state_base(Event event, uint16_t arg,
2087
uint8_t num_config_steps,
2088
void (*savefunc)()) {
2089
static uint8_t config_step;
2090
if (event == EV_enter_state) {
2093
return MISCHIEF_MANAGED;
2095
// advance forward through config steps
2096
else if (event == EV_tick) {
2097
if (config_step < num_config_steps) {
2098
push_state(number_entry_state, config_step + 1);
2101
// TODO: blink out some sort of success pattern
2104
//set_state(retstate, retval);
2107
return MISCHIEF_MANAGED;
2109
// an option was set (return from number_entry_state)
2110
else if (event == EV_reenter_state) {
2111
config_state_values[config_step] = number_entry_value;
2113
return MISCHIEF_MANAGED;
2115
//return EVENT_NOT_HANDLED;
2116
// eat all other events; don't pass any through to parent
2117
return EVENT_HANDLED;
2120
#ifdef USE_RAMP_CONFIG
2121
void ramp_config_save() {
2124
if (ramp_style) { // discrete / stepped ramp
2126
val = config_state_values[0];
2127
if (val) { ramp_discrete_floor = val; }
2129
val = config_state_values[1];
2130
if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; }
2132
val = config_state_values[2];
2133
if (val) ramp_discrete_steps = val;
2135
} else { // smooth ramp
2137
val = config_state_values[0];
2138
if (val) { ramp_smooth_floor = val; }
2140
val = config_state_values[1];
2141
if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; }
2146
uint8_t ramp_config_state(Event event, uint16_t arg) {
2147
uint8_t num_config_steps;
2148
num_config_steps = 2 + ramp_style;
2149
return config_state_base(event, arg,
2150
num_config_steps, ramp_config_save);
2152
#endif // #ifdef USE_RAMP_CONFIG
2155
#ifdef USE_THERMAL_REGULATION
2156
void thermal_config_save() {
2160
// calibrate room temperature
2161
val = config_state_values[0];
2163
int8_t rawtemp = temperature - therm_cal_offset;
2164
therm_cal_offset = val - rawtemp;
2165
adc_reset = 2; // invalidate all recent temperature data
2168
val = config_state_values[1];
2170
// set maximum heat limit
2171
therm_ceil = 30 + val - 1;
2173
if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
2176
uint8_t thermal_config_state(Event event, uint16_t arg) {
2177
return config_state_base(event, arg,
2178
2, thermal_config_save);
2180
#endif // #ifdef USE_THERMAL_REGULATION
2183
#ifdef USE_BEACON_MODE
2184
void beacon_config_save() {
2186
uint8_t val = config_state_values[0];
2188
beacon_seconds = val;
2192
uint8_t beacon_config_state(Event event, uint16_t arg) {
2193
return config_state_base(event, arg,
2194
1, beacon_config_save);
2197
inline void beacon_mode_iter() {
2198
// one iteration of main loop()
2199
set_level(memorized_level);
2202
nice_delay_ms(((beacon_seconds) * 1000) - 100);
2204
#endif // #ifdef USE_BEACON_MODE
2207
uint8_t number_entry_state(Event event, uint16_t arg) {
2208
static uint8_t value;
2209
static uint8_t blinks_left;
2210
static uint8_t entry_step;
2211
static uint16_t wait_ticks;
2212
if (event == EV_enter_state) {
2217
return MISCHIEF_MANAGED;
2219
// advance through the process:
2221
// 1: blink out the 'arg' value
2223
// 3: "buzz" while counting clicks
2225
else if (event == EV_tick) {
2227
if ((entry_step == 0) || (entry_step == 2)) {
2228
if (wait_ticks < TICKS_PER_SECOND/2)
2235
// blink out the option number
2236
else if (entry_step == 1) {
2238
if ((wait_ticks & 31) == 10) {
2239
set_level(RAMP_SIZE/4);
2241
else if ((wait_ticks & 31) == 20) {
2244
else if ((wait_ticks & 31) == 31) {
2254
else if (entry_step == 3) { // buzz while waiting for a number to be entered
2256
// buzz for N seconds after last event
2257
if ((wait_ticks & 3) == 0) {
2258
set_level(RAMP_SIZE/6);
2260
else if ((wait_ticks & 3) == 2) {
2261
set_level(RAMP_SIZE/8);
2263
// time out after 3 seconds
2264
if (wait_ticks > TICKS_PER_SECOND*3) {
2265
//number_entry_value = value;
2270
else if (entry_step == 4) {
2271
number_entry_value = value;
2274
return MISCHIEF_MANAGED;
2277
else if (event == EV_click1_release) {
2278
empty_event_sequence();
2279
if (entry_step == 3) { // only count during the "buzz"
2283
set_level(RAMP_SIZE/2);
2287
return MISCHIEF_MANAGED;
2289
return EVENT_NOT_HANDLED;
2293
// find the ramp level closest to the target,
2294
// using only the levels which are allowed in the current state
2295
uint8_t nearest_level(int16_t target) {
2297
// using int16_t here saves us a bunch of logic elsewhere,
2298
// by allowing us to correct for numbers < 0 or > 255 in one central place
2299
uint8_t mode_min = ramp_smooth_floor;
2300
uint8_t mode_max = ramp_smooth_ceil;
2302
mode_min = ramp_discrete_floor;
2303
mode_max = ramp_discrete_ceil;
2305
if (target < mode_min) return mode_min;
2306
if (target > mode_max) return mode_max;
2307
// the rest isn't relevant for smooth ramping
2308
if (! ramp_style) return target;
2310
uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor;
2311
ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1);
2312
uint8_t this_level = ramp_discrete_floor;
2314
for(uint8_t i=0; i<ramp_discrete_steps; i++) {
2315
this_level = ramp_discrete_floor + (i * (uint16_t)ramp_range / (ramp_discrete_steps-1));
2316
int16_t diff = target - this_level;
2317
if (diff < 0) diff = -diff;
2318
if (diff <= (ramp_discrete_step_size>>1))
2324
#ifdef USE_THERMAL_REGULATION
2325
void set_level_and_therm_target(uint8_t level) {
2326
target_level = level;
2331
void blink_confirm(uint8_t num) {
2332
for (; num>0; num--) {
2333
set_level(MAX_LEVEL/4);
2340
// Just go dark for a moment to indicate to user that something happened
2342
uint8_t temp = actual_level;
2349
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
2350
// beacon-like mode for the indicator LED
2351
void indicator_blink(uint8_t arg) {
2352
// turn off aux LEDs when battery is empty
2353
if (voltage < VOLTAGE_LOW) { indicator_led(0); return; }
2355
#ifdef USE_FANCIER_BLINKING_INDICATOR
2357
// fancy blink, set off/low/high levels here:
2358
uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0,
2359
0, 0, 1, 0, 0, 0, 0, 0};
2360
indicator_led(seq[arg & 15]);
2362
#else // basic blink, 1/8th duty cycle
2375
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
2376
uint8_t voltage_to_rgb() {
2377
uint8_t levels[] = {
2385
44, 6, // 6, R+G+B // skip; looks too similar to G+B
2388
uint8_t volts = voltage;
2389
if (volts < 29) return 0;
2392
for (i = 0; volts >= levels[i]; i += 2) {}
2393
uint8_t color_num = levels[(i - 2) + 1];
2394
return pgm_read_byte(rgb_led_colors + color_num);
2397
// do fancy stuff with the RGB aux LEDs
2398
// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
2399
// arg: time slice number
2400
void rgb_led_update(uint8_t mode, uint8_t arg) {
2401
static uint8_t rainbow = 0; // track state of rainbow mode
2402
static uint8_t frame = 0; // track state of animation mode
2404
// turn off aux LEDs when battery is empty
2405
// (but if voltage==0, that means we just booted and don't know yet)
2406
uint8_t volts = voltage; // save a few bytes by caching volatile value
2407
if ((volts) && (volts < VOLTAGE_LOW)) {
2409
#ifdef USE_BUTTON_LED
2415
uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more?
2416
uint8_t color = mode & 0x0f;
2418
// preview in blinking mode is awkward... use high instead
2419
if ((! go_to_standby) && (pattern > 2)) { pattern = 2; }
2422
const uint8_t *colors = rgb_led_colors;
2423
uint8_t actual_color = 0;
2424
if (color < 7) { // normal color
2425
actual_color = pgm_read_byte(colors + color);
2427
else if (color == 7) { // rainbow
2428
uint8_t speed = 0x03; // awake speed
2429
if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed
2430
if (0 == (arg & speed)) {
2431
rainbow = (rainbow + 1) % 6;
2433
actual_color = pgm_read_byte(colors + rainbow);
2436
// show actual voltage while asleep...
2437
if (go_to_standby) {
2438
actual_color = voltage_to_rgb();
2439
// choose a color based on battery voltage
2440
//if (volts >= 38) actual_color = pgm_read_byte(colors + 4);
2441
//else if (volts >= 33) actual_color = pgm_read_byte(colors + 2);
2442
//else actual_color = pgm_read_byte(colors + 0);
2444
// ... but during preview, cycle colors quickly
2446
actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1));
2450
// pick a brightness from the animation sequence
2452
// uses an odd length to avoid lining up with rainbow loop
2453
uint8_t animation[] = {2, 1, 0, 0, 0, 0, 0, 0, 0,
2454
1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
2455
frame = (frame + 1) % sizeof(animation);
2456
pattern = animation[frame];
2459
#ifdef USE_BUTTON_LED
2460
uint8_t button_led_result;
2465
#ifdef USE_BUTTON_LED
2466
button_led_result = 0;
2470
result = actual_color;
2471
#ifdef USE_BUTTON_LED
2472
button_led_result = 1;
2476
result = (actual_color << 1);
2477
#ifdef USE_BUTTON_LED
2478
button_led_result = 2;
2482
rgb_led_set(result);
2483
#ifdef USE_BUTTON_LED
2484
button_led_set(button_led_result);
2488
void rgb_led_voltage_readout(uint8_t bright) {
2489
uint8_t color = voltage_to_rgb();
2490
if (bright) color = color << 1;
2496
#ifdef USE_FACTORY_RESET
2497
void factory_reset() {
2498
// display a warning for a few seconds before doing the actual reset,
2499
// so the user has time to abort if they want
2500
#define SPLODEY_TIME 2500
2501
#define SPLODEY_STEPS 64
2502
#define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS)
2505
// wind up to an explosion
2506
for (bright=0; bright<SPLODEY_STEPS; bright++) {
2508
nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
2509
set_level(bright>>1);
2510
nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
2511
if (! button_is_pressed()) {
2516
// explode, if button pressed long enough
2518
#ifdef USE_THERMAL_REGULATION
2519
// auto-calibrate temperature... assume current temperature is 21 C
2520
config_state_values[0] = 21;
2521
config_state_values[1] = 0;
2522
thermal_config_save();
2524
// save all settings to eeprom
2525
// (assuming they're all at default because we haven't loaded them yet)
2529
for (; bright > 0; bright--) {
2531
nice_delay_ms(SPLODEY_TIME_PER_STEP/8);
2534
// explosion cancelled, fade away
2536
for (; bright > 0; bright--) {
2538
nice_delay_ms(SPLODEY_TIME_PER_STEP/3);
2545
void load_config() {
2546
if (load_eeprom()) {
2547
ramp_style = eeprom[ramp_style_e];
2548
#ifdef USE_RAMP_CONFIG
2549
ramp_smooth_floor = eeprom[ramp_smooth_floor_e];
2550
ramp_smooth_ceil = eeprom[ramp_smooth_ceil_e];
2551
ramp_discrete_floor = eeprom[ramp_discrete_floor_e];
2552
ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e];
2553
ramp_discrete_steps = eeprom[ramp_discrete_steps_e];
2555
#ifdef USE_MANUAL_MEMORY
2556
manual_memory = eeprom[manual_memory_e];
2558
#ifdef USE_TINT_RAMPING
2559
tint = eeprom[tint_e];
2561
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
2562
strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl?
2563
strobe_delays[0] = eeprom[strobe_delays_0_e];
2564
strobe_delays[1] = eeprom[strobe_delays_1_e];
2566
#ifdef USE_BIKE_FLASHER_MODE
2567
bike_flasher_brightness = eeprom[bike_flasher_brightness_e];
2569
#ifdef USE_BEACON_MODE
2570
beacon_seconds = eeprom[beacon_seconds_e];
2572
#ifdef USE_MUGGLE_MODE
2573
muggle_mode_active = eeprom[muggle_mode_active_e];
2575
#ifdef USE_THERMAL_REGULATION
2576
therm_ceil = eeprom[therm_ceil_e];
2577
therm_cal_offset = eeprom[therm_cal_offset_e];
2579
#ifdef USE_INDICATOR_LED
2580
indicator_led_mode = eeprom[indicator_led_mode_e];
2582
#ifdef USE_AUX_RGB_LEDS
2583
rgb_led_off_mode = eeprom[rgb_led_off_mode_e];
2584
rgb_led_lockout_mode = eeprom[rgb_led_lockout_mode_e];
2587
#ifdef START_AT_MEMORIZED_LEVEL
2588
if (load_eeprom_wl()) {
2589
memorized_level = eeprom_wl[0];
2594
void save_config() {
2595
eeprom[ramp_style_e] = ramp_style;
2596
#ifdef USE_RAMP_CONFIG
2597
eeprom[ramp_smooth_floor_e] = ramp_smooth_floor;
2598
eeprom[ramp_smooth_ceil_e] = ramp_smooth_ceil;
2599
eeprom[ramp_discrete_floor_e] = ramp_discrete_floor;
2600
eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil;
2601
eeprom[ramp_discrete_steps_e] = ramp_discrete_steps;
2603
#ifdef USE_MANUAL_MEMORY
2604
eeprom[manual_memory_e] = manual_memory;
2606
#ifdef USE_TINT_RAMPING
2607
eeprom[tint_e] = tint;
2609
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
2610
eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl?
2611
eeprom[strobe_delays_0_e] = strobe_delays[0];
2612
eeprom[strobe_delays_1_e] = strobe_delays[1];
2614
#ifdef USE_BIKE_FLASHER_MODE
2615
eeprom[bike_flasher_brightness_e] = bike_flasher_brightness;
2617
#ifdef USE_BEACON_MODE
2618
eeprom[beacon_seconds_e] = beacon_seconds;
2620
#ifdef USE_MUGGLE_MODE
2621
eeprom[muggle_mode_active_e] = muggle_mode_active;
2623
#ifdef USE_THERMAL_REGULATION
2624
eeprom[therm_ceil_e] = therm_ceil;
2625
eeprom[therm_cal_offset_e] = therm_cal_offset;
2627
#ifdef USE_INDICATOR_LED
2628
eeprom[indicator_led_mode_e] = indicator_led_mode;
2630
#ifdef USE_AUX_RGB_LEDS
2631
eeprom[rgb_led_off_mode_e] = rgb_led_off_mode;
2632
eeprom[rgb_led_lockout_mode_e] = rgb_led_lockout_mode;
2638
#ifdef START_AT_MEMORIZED_LEVEL
2639
void save_config_wl() {
2640
eeprom_wl[0] = memorized_level;
2646
void low_voltage() {
2647
StatePtr state = current_state;
2649
// TODO: turn off aux LED(s) when power is really low
2651
if (0) {} // placeholder
2653
#ifdef USE_STROBE_STATE
2654
// "step down" from strobe to something low
2655
else if (state == strobe_state) {
2656
set_state(steady_state, RAMP_SIZE/6);
2660
// in normal or muggle mode, step down or turn off
2661
//else if ((state == steady_state) || (state == muggle_state)) {
2662
else if (state == steady_state) {
2663
if (actual_level > 1) {
2664
uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
2665
set_level_and_therm_target(lvl);
2668
set_state(off_state, 0);
2671
// all other modes, just turn off when voltage is low
2673
set_state(off_state, 0);
79
/********* does this build target have special code to include? *********/
81
#include incfile(HWDEF_C_FILE)
84
#include incfile(CFG_C_FILE)
88
/********* Include all the regular app headers *********/
91
#include "ramp-mode.h"
92
#include "config-mode.h"
96
#ifdef USE_SUNSET_TIMER
97
#include "sunset-timer.h"
100
#ifdef USE_VERSION_CHECK
101
#include "version-check-mode.h"
104
#ifdef USE_BATTCHECK_MODE
105
#include "battcheck-mode.h"
108
#ifdef USE_BEACON_MODE
109
#include "beacon-mode.h"
112
#ifdef USE_THERMAL_REGULATION
113
#include "tempcheck-mode.h"
116
#ifdef USE_LOCKOUT_MODE
117
#include "lockout-mode.h"
120
#ifdef USE_MOMENTARY_MODE
121
#include "momentary-mode.h"
124
#ifdef USE_TACTICAL_MODE
125
#include "tactical-mode.h"
128
// allow the channel mode handler even when only 1 mode
129
// (so a tint ramp light could still use 3H even if there's no other mode)
130
#if defined(USE_CHANNEL_MODES)
131
#include "channel-modes.h"
134
#ifdef USE_FACTORY_RESET
135
#include "factory-reset.h"
138
// this one detects its own enable/disable settings
139
#include "strobe-modes.h"
142
#include "sos-mode.h"
145
#ifdef USE_SMOOTH_STEPS
146
#include "smooth-steps.h"
149
// this should be last, so other headers have a chance to declare values
150
#include "load-save-config.h"
153
/********* Include all the app logic source files *********/
154
// (is a bit weird to do things this way,
155
// but it saves a lot of space by letting us use the -fwhole-program flag)
157
#include "off-mode.c"
158
#include "ramp-mode.c"
159
#include "load-save-config.c"
160
#include "config-mode.c"
161
#include "aux-leds.c"
164
#ifdef USE_SUNSET_TIMER
165
#include "sunset-timer.c"
168
#ifdef USE_VERSION_CHECK
169
#include "version-check-mode.c"
172
#ifdef USE_BATTCHECK_MODE
173
#include "battcheck-mode.c"
176
#ifdef USE_BEACON_MODE
177
#include "beacon-mode.c"
180
#ifdef USE_THERMAL_REGULATION
181
#include "tempcheck-mode.c"
184
#ifdef USE_LOCKOUT_MODE
185
#include "lockout-mode.c"
188
#ifdef USE_MOMENTARY_MODE
189
#include "momentary-mode.c"
192
#ifdef USE_TACTICAL_MODE
193
#include "tactical-mode.c"
196
#if defined(USE_CHANNEL_MODES)
197
#include "channel-modes.c"
200
#ifdef USE_FACTORY_RESET
201
#include "factory-reset.c"
204
#ifdef USE_STROBE_STATE
205
#include "strobe-modes.c"
209
#include "sos-mode.c"
212
#ifdef USE_SMOOTH_STEPS
213
#include "smooth-steps.c"
217
// runs one time at boot, when power is connected
2679
#ifdef START_AT_MEMORIZED_LEVEL
2680
// dual switch: e-switch + power clicky
2681
// power clicky acts as a momentary mode
2684
#ifdef USE_MUGGLE_MODE
2685
if (muggle_mode_active)
2686
push_state(muggle_state, memorized_level);
2689
if (button_is_pressed())
2690
// hold button to go to moon
2691
push_state(steady_state, 1);
2693
// otherwise use memory
2694
push_state(steady_state, memorized_level);
2696
#else // if not START_AT_MEMORIZED_LEVEL
2698
// blink at power-on to let user know power is connected
2699
set_level(RAMP_SIZE/8);
2703
#ifdef USE_FACTORY_RESET
2704
if (button_is_pressed())
2710
#ifdef USE_TINT_RAMPING
2711
// add tint ramping underneath every other state
2712
push_state(tint_ramping_state, 0);
2713
#endif // ifdef USE_TINT_RAMPING
2715
#ifdef USE_MUGGLE_MODE
2716
if (muggle_mode_active)
2717
push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2);
220
#ifndef START_AT_MEMORIZED_LEVEL
222
// regular e-switch light, no hard clicky power button
224
// blink at power-on to let user know power is connected
227
#ifdef USE_FACTORY_RESET
228
if (button_is_pressed())
234
#if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER)
235
// without this, initial boot-up brightness is wrong
236
// when manual mem is enabled with a non-zero timer
237
if (cfg.manual_memory) manual_memory_restore();
240
#if defined(USE_CHANNEL_MODES)
241
// add channel mode functions underneath every other state
242
push_state(channel_mode_state, 0);
2720
245
push_state(off_state, 1);
247
#else // if START_AT_MEMORIZED_LEVEL
249
// dual switch: e-switch + power clicky
250
// power clicky acts as a momentary mode
253
#if defined(USE_CHANNEL_MODES)
254
// add channel mode functions underneath every other state
255
push_state(channel_mode_state, 0);
258
if (button_is_pressed())
259
// hold button to go to moon
260
push_state(steady_state, 1);
262
// otherwise use memory
263
push_state(steady_state, memorized_level);
2722
265
#endif // ifdef START_AT_MEMORIZED_LEVEL
270
// runs repeatedly whenever light is "on" (not in standby)
273
// "current_state" is volatile, so cache it to reduce code size
2728
274
StatePtr state = current_state;
2730
276
#ifdef USE_AUX_RGB_LEDS_WHILE_ON
2731
if (! setting_rgb_mode_now) rgb_led_voltage_readout(1);
277
// display battery charge on RGB button during use
278
if (state == steady_state)
279
rgb_led_voltage_readout(actual_level > USE_AUX_RGB_LEDS_WHILE_ON);
282
if (0) {} // placeholder
2736
284
#ifdef USE_VERSION_CHECK
2737
285
else if (state == version_check_state) {
2738
for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
2739
blink_digit(pgm_read_byte(version_number + i) - '0');
2742
// FIXME: when user interrupts with button, "off" takes an extra click
2743
// before it'll turn back on, because the click to cancel gets sent
2744
// to the "off" state instead of version_check_state
2745
//while (button_is_pressed()) {}
2746
//empty_event_sequence();
2748
set_state(off_state, 0);
286
version_check_iter();
2750
#endif // #ifdef USE_VERSION_CHECK
2752
290
#ifdef USE_STROBE_STATE
2753
291
else if ((state == strobe_state)
2754
292
#ifdef USE_MOMENTARY_MODE
2755
293
// also handle momentary strobes
2756
|| ((state == momentary_state) && (momentary_mode == 1) && (momentary_active))
295
(state == momentary_state)
296
#ifdef USE_TACTICAL_MODE
297
|| (state == tactical_state)
300
&& (momentary_mode == 1) && (momentary_active))
2759
uint8_t st = strobe_type;
2762
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
2763
#ifdef USE_PARTY_STROBE_MODE
2764
case party_strobe_e:
2766
#ifdef USE_TACTICAL_STROBE_MODE
2767
case tactical_strobe_e:
2769
party_tactical_strobe_mode_iter(st);
2773
#ifdef USE_LIGHTNING_MODE
2774
case lightning_storm_e:
2775
lightning_storm_iter();
2779
#ifdef USE_BIKE_FLASHER_MODE
2780
case bike_flasher_e:
2781
bike_flasher_iter();
2787
305
#endif // #ifdef USE_STROBE_STATE
2789
307
#ifdef USE_BORING_STROBE_STATE
2790
308
else if (state == boring_strobe_state) {
2791
switch(boring_strobe_type) {
2792
#ifdef USE_POLICE_STROBE_MODE
2793
case 0: // police strobe
2794
police_strobe_iter();
2798
#ifdef USE_SOS_MODE_IN_FF_GROUP
309
boring_strobe_state_iter();
2805
#endif // #ifdef USE_BORING_STROBE_STATE
2807
313
#ifdef USE_BATTCHECK
2808
314
else if (state == battcheck_state) {
317
// in simple mode, turn off after one readout
318
// FIXME: can eat the next button press
319
// (state changes in loop() act weird)
320
if (cfg.simple_ui_active) set_state_deferred(off_state, 0);
321
else nice_delay_ms(1000);
326
#ifdef USE_THERMAL_REGULATION
327
// TODO: blink out therm_ceil during thermal_config_state?
328
else if (state == tempcheck_state) {
329
blink_num(temperature);